mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-10-29 05:13:42 +08:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c83ac48bd2 | ||
|
|
3d159a833e | ||
|
|
4b09878bdd | ||
|
|
b0162e6a92 | ||
|
|
8ab15e5dc4 | ||
|
|
d2ac807252 | ||
|
|
0af01f6f1f | ||
|
|
013b319fab | ||
|
|
2899ba5949 | ||
|
|
a558b7e104 | ||
|
|
7a833e2233 | ||
|
|
bf65746d00 | ||
|
|
f08a7862de | ||
|
|
023a2c2f09 | ||
|
|
1bcd0f4c1a | ||
|
|
a0f3bc8ccb | ||
|
|
dea72738c1 | ||
|
|
a1d1fe7763 | ||
|
|
a39ed9764c | ||
|
|
aaa5ba99aa | ||
|
|
2113508b6d |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,4 +1,16 @@
|
||||
# 更新日志
|
||||
## v3.2.7
|
||||
* 功能重构:采用 Vant 重构移动页面,新增 MidJourney 功能
|
||||
* 功能优化:优化 PC 端 MidJourney 页面布局,新增融图和换脸功能
|
||||
* Bug修复:修复 issue [
|
||||
管理界面操作用户存在的两个问题](https://github.com/yangjian102621/chatgpt-plus/issues/117#issuecomment-1909201532)
|
||||
* 功能优化:在对话和聊天记录表中新增冗余字段 model,存储对话模型
|
||||
* Bug修复:IPhone 手机验证码触摸事件坐标错位 [issue 144](https://github.com/yangjian102621/chatgpt-plus/issues/144)
|
||||
* Bug修复:重新生成按钮功能失效问题
|
||||
* Bug修复:对话输入HTML标签不显示的问题
|
||||
* 功能优化:gpt-4-all/gpts/midjourney-plus 支持第三方平台的 API KEY
|
||||
* 功能新增:新增删除文件功能
|
||||
|
||||
## v3.2.6
|
||||
* 功能优化:恢复关闭注册系统配置项,管理员可以在后台关闭用户注册,只允许内部添加账号
|
||||
* 功能优化:兼用旧版本微信收款消息解析
|
||||
|
||||
@@ -25,23 +25,16 @@ WeChatBot = false
|
||||
AppId = ""
|
||||
Token = ""
|
||||
|
||||
[SmsConfig] # 阿里云短信服务配置
|
||||
AccessKey = ""
|
||||
AccessSecret = ""
|
||||
Product = "Dysmsapi"
|
||||
Domain = "dysmsapi.aliyuncs.com"
|
||||
Sign = ""
|
||||
CodeTempId = ""
|
||||
|
||||
[Sms] # Sms 配置,用于发送短信
|
||||
[SMS] # Sms 配置,用于发送短信
|
||||
Active = "Ali" # 当前启用的短信服务,默认使用阿里云
|
||||
[Sms.SmsBao]
|
||||
[SMS.Bao]
|
||||
Username = ""
|
||||
Password = ""
|
||||
Domain = "api.smsbao.com"
|
||||
Sign = "【极客学长】"
|
||||
CodeTemplate = "您的验证码是{code}。5分钟有效,若非本人操作,请忽略本短信。"
|
||||
[Sms.Ali]
|
||||
[SMS.Ali]
|
||||
AccessKey = ""
|
||||
AccessSecret = ""
|
||||
Product = "Dysmsapi"
|
||||
@@ -82,6 +75,7 @@ WeChatBot = false
|
||||
[[MjPlusConfigs]]
|
||||
Enabled = false
|
||||
ApiURL = "https://api.chatgpt-plus.net" # 目前暂时不支持更改
|
||||
CdnURL = "" # CND 加速的 URL,如果有的话就设置
|
||||
ApiKey = "sk-xxx"
|
||||
NotifyURL = "https://ai.r9it.com/api/mj/notify" # 这里需要改成你的域名
|
||||
|
||||
@@ -113,9 +107,9 @@ WeChatBot = false
|
||||
[HuPiPayConfig]
|
||||
Enabled = false
|
||||
Name = "wechat"
|
||||
AppId = "201906161477"
|
||||
AppSecret = "7f403199d510fb2c6f0b9f2311800e7c"
|
||||
PayURL = "https://api.xunhupay.com/payment/do.html"
|
||||
AppId = ""
|
||||
AppSecret = ""
|
||||
ApiURL = "https://api.xunhupay.com"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify"
|
||||
|
||||
[SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器
|
||||
@@ -130,5 +124,5 @@ WeChatBot = false
|
||||
Name = "wechat" # 请不要改动
|
||||
AppId = "" # 商户 ID
|
||||
PrivateKey = "" # 秘钥
|
||||
ApiURL = "https://payjs.cn/api/native"
|
||||
ApiURL = "https://payjs.cn"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
|
||||
@@ -19,7 +19,6 @@ type AppConfig struct {
|
||||
OSS OSSConfig // OSS config
|
||||
MjConfigs []MidJourneyConfig // mj AI draw service pool
|
||||
MjPlusConfigs []MidJourneyPlusConfig // MJ plus config
|
||||
ImgCdnURL string // 图片反代加速地址
|
||||
WeChatBot bool // 是否启用微信机器人
|
||||
SdConfigs []StableDiffusionConfig // sd AI draw service pool
|
||||
|
||||
@@ -51,6 +50,7 @@ type MidJourneyConfig struct {
|
||||
GuildId string // Server ID
|
||||
ChanelId string // Chanel ID
|
||||
UseCDN bool
|
||||
ImgCdnURL string // 图片反代加速地址
|
||||
DiscordAPI string
|
||||
DiscordGateway string
|
||||
}
|
||||
@@ -63,8 +63,9 @@ type StableDiffusionConfig struct {
|
||||
}
|
||||
|
||||
type MidJourneyPlusConfig struct {
|
||||
Enabled bool // 如果启用了 MidJourney Plus,将会自动禁用原生的MidJourney服务
|
||||
ApiURL string
|
||||
Enabled bool // 如果启用了 MidJourney Plus,将会自动禁用原生的MidJourney服务
|
||||
ApiURL string // api 地址
|
||||
CdnURL string // CDN 加速地址
|
||||
ApiKey string
|
||||
NotifyURL string // 任务进度更新回调地址
|
||||
}
|
||||
|
||||
@@ -9,13 +9,17 @@ func (t TaskType) String() string {
|
||||
|
||||
const (
|
||||
TaskImage = TaskType("image")
|
||||
TaskBlend = TaskType("blend")
|
||||
TaskSwapFace = TaskType("swapFace")
|
||||
TaskUpscale = TaskType("upscale")
|
||||
TaskVariation = TaskType("variation")
|
||||
)
|
||||
|
||||
// MjTask MidJourney 任务
|
||||
type MjTask struct {
|
||||
Id int `json:"id"`
|
||||
Id uint `json:"id"`
|
||||
TaskId string `json:"task_id"`
|
||||
ImgArr []string `json:"img_arr"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
SessionId string `json:"session_id"`
|
||||
Type TaskType `json:"type"`
|
||||
|
||||
@@ -135,6 +135,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -156,6 +157,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
@@ -181,6 +183,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
} else {
|
||||
chatItem.Title = prompt
|
||||
}
|
||||
chatItem.Model = req.Model
|
||||
h.db.Create(&chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ func (h *ChatHandler) sendBaiduMessage(
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -181,6 +182,7 @@ func (h *ChatHandler) sendBaiduMessage(
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
@@ -205,6 +207,7 @@ func (h *ChatHandler) sendBaiduMessage(
|
||||
} else {
|
||||
chatItem.Title = prompt
|
||||
}
|
||||
chatItem.Model = req.Model
|
||||
h.db.Create(&chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
|
||||
|
||||
// use old chat data override the chat model and role ID
|
||||
var chat model.ChatItem
|
||||
res = h.db.Where("chat_id=?", chatId).First(&chat)
|
||||
res = h.db.Where("chat_id = ?", chatId).First(&chat)
|
||||
if res.Error == nil {
|
||||
chatModel.Id = chat.ModelId
|
||||
roleId = int(chat.RoleId)
|
||||
@@ -462,11 +462,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
||||
req.Messages = nil
|
||||
break
|
||||
default:
|
||||
if req.Model == "gpt-4-all" || strings.HasPrefix(req.Model, "gpt-4-gizmo-g-") {
|
||||
apiURL = "https://gpt.bemore.lol/v1/chat/completions"
|
||||
} else {
|
||||
apiURL = apiKey.ApiURL
|
||||
}
|
||||
apiURL = apiKey.ApiURL
|
||||
}
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
|
||||
@@ -139,6 +139,7 @@ func (h *ChatHandler) sendChatGLMMessage(
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -160,6 +161,7 @@ func (h *ChatHandler) sendChatGLMMessage(
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
@@ -184,6 +186,7 @@ func (h *ChatHandler) sendChatGLMMessage(
|
||||
} else {
|
||||
chatItem.Title = prompt
|
||||
}
|
||||
chatItem.Model = req.Model
|
||||
h.db.Create(&chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: useContext,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -235,6 +236,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: useContext,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
@@ -260,6 +262,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
} else {
|
||||
chatItem.Title = prompt
|
||||
}
|
||||
chatItem.Model = req.Model
|
||||
h.db.Create(&chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ func (h *ChatHandler) sendQWenMessage(
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -181,6 +182,7 @@ func (h *ChatHandler) sendQWenMessage(
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
@@ -205,6 +207,7 @@ func (h *ChatHandler) sendQWenMessage(
|
||||
} else {
|
||||
chatItem.Title = prompt
|
||||
}
|
||||
chatItem.Model = req.Model
|
||||
h.db.Create(&chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,7 @@ func (h *ChatHandler) sendXunFeiMessage(
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -219,6 +220,7 @@ func (h *ChatHandler) sendXunFeiMessage(
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: true,
|
||||
Model: req.Model,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
@@ -243,6 +245,7 @@ func (h *ChatHandler) sendXunFeiMessage(
|
||||
} else {
|
||||
chatItem.Title = prompt
|
||||
}
|
||||
chatItem.Model = req.Model
|
||||
h.db.Create(&chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,19 +86,20 @@ func (h *MidJourneyHandler) Client(c *gin.Context) {
|
||||
// Image 创建一个绘画任务
|
||||
func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
var data struct {
|
||||
SessionId string `json:"session_id"`
|
||||
Prompt string `json:"prompt"`
|
||||
NegPrompt string `json:"neg_prompt"`
|
||||
Rate string `json:"rate"`
|
||||
Model string `json:"model"`
|
||||
Chaos int `json:"chaos"`
|
||||
Raw bool `json:"raw"`
|
||||
Seed int64 `json:"seed"`
|
||||
Stylize int `json:"stylize"`
|
||||
Img string `json:"img"`
|
||||
Tile bool `json:"tile"`
|
||||
Quality float32 `json:"quality"`
|
||||
Weight float32 `json:"weight"`
|
||||
SessionId string `json:"session_id"`
|
||||
TaskType string `json:"task_type"`
|
||||
Prompt string `json:"prompt"`
|
||||
NegPrompt string `json:"neg_prompt"`
|
||||
Rate string `json:"rate"`
|
||||
Model string `json:"model"`
|
||||
Chaos int `json:"chaos"`
|
||||
Raw bool `json:"raw"`
|
||||
Seed int64 `json:"seed"`
|
||||
Stylize int `json:"stylize"`
|
||||
ImgArr []string `json:"img_arr"`
|
||||
Tile bool `json:"tile"`
|
||||
Quality float32 `json:"quality"`
|
||||
Weight float32 `json:"weight"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
@@ -121,11 +122,8 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
if data.Chaos > 0 && !strings.Contains(prompt, "--c") && !strings.Contains(prompt, "--chaos") {
|
||||
prompt += fmt.Sprintf(" --c %d", data.Chaos)
|
||||
}
|
||||
if data.Img != "" {
|
||||
prompt = fmt.Sprintf("%s %s", data.Img, prompt)
|
||||
if data.Weight > 0 {
|
||||
prompt += fmt.Sprintf(" --iw %f", data.Weight)
|
||||
}
|
||||
if data.Weight > 0 {
|
||||
prompt += fmt.Sprintf(" --iw %f", data.Weight)
|
||||
}
|
||||
if data.Raw {
|
||||
prompt += " --style raw"
|
||||
@@ -143,6 +141,11 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
prompt += fmt.Sprintf(" %s", data.Model)
|
||||
}
|
||||
|
||||
// 处理融图和换脸的提示词
|
||||
if data.TaskType == types.TaskSwapFace.String() || data.TaskType == types.TaskBlend.String() {
|
||||
prompt = fmt.Sprintf("%s:%s", data.TaskType, strings.Join(data.ImgArr, ","))
|
||||
}
|
||||
|
||||
idValue, _ := c.Get(types.LoginUserID)
|
||||
userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
|
||||
// generate task id
|
||||
@@ -152,28 +155,37 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
job := model.MidJourneyJob{
|
||||
Type: types.TaskImage.String(),
|
||||
Type: data.TaskType,
|
||||
UserId: userId,
|
||||
TaskId: taskId,
|
||||
Progress: 0,
|
||||
Prompt: prompt,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if data.TaskType == types.TaskBlend.String() {
|
||||
data.Prompt = "融图:" + strings.Join(data.ImgArr, ",")
|
||||
} else if data.TaskType == types.TaskSwapFace.String() {
|
||||
data.Prompt = "换脸:" + strings.Join(data.ImgArr, ",")
|
||||
}
|
||||
if res := h.db.Create(&job); res.Error != nil || res.RowsAffected == 0 {
|
||||
resp.ERROR(c, "添加任务失败:"+res.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.pool.PushTask(types.MjTask{
|
||||
Id: int(job.Id),
|
||||
Id: job.Id,
|
||||
TaskId: taskId,
|
||||
SessionId: data.SessionId,
|
||||
Type: types.TaskImage,
|
||||
Prompt: fmt.Sprintf("%s %s", taskId, prompt),
|
||||
Type: types.TaskType(data.TaskType),
|
||||
Prompt: prompt,
|
||||
UserId: userId,
|
||||
ImgArr: data.ImgArr,
|
||||
})
|
||||
|
||||
client := h.pool.Clients.Get(uint(job.UserId))
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
// update user's img calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
@@ -222,7 +234,7 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.pool.PushTask(types.MjTask{
|
||||
Id: int(job.Id),
|
||||
Id: job.Id,
|
||||
SessionId: data.SessionId,
|
||||
Type: types.TaskUpscale,
|
||||
Prompt: data.Prompt,
|
||||
@@ -234,7 +246,9 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
|
||||
})
|
||||
|
||||
client := h.pool.Clients.Get(uint(job.UserId))
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
@@ -270,7 +284,7 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.pool.PushTask(types.MjTask{
|
||||
Id: int(job.Id),
|
||||
Id: job.Id,
|
||||
SessionId: data.SessionId,
|
||||
Type: types.TaskVariation,
|
||||
Prompt: data.Prompt,
|
||||
@@ -282,7 +296,9 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
})
|
||||
|
||||
client := h.pool.Clients.Get(uint(job.UserId))
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
// update user's img calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
@@ -329,19 +345,22 @@ func (h *MidJourneyHandler) JobList(c *gin.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 失败的任务直接删除
|
||||
if job.Progress == -1 {
|
||||
h.db.Delete(&model.MidJourneyJob{Id: job.Id})
|
||||
jobs = append(jobs, job)
|
||||
continue
|
||||
}
|
||||
|
||||
if item.Progress < 100 && item.ImgURL == "" && item.OrgURL != "" {
|
||||
// 正在运行中任务使用代理访问图片
|
||||
if h.App.Config.ImgCdnURL != "" {
|
||||
job.ImgURL = strings.ReplaceAll(job.OrgURL, "https://cdn.discordapp.com", h.App.Config.ImgCdnURL)
|
||||
} else {
|
||||
// discord 服务器图片需要使用代理转发图片数据流
|
||||
if strings.HasPrefix(item.OrgURL, "https://cdn.discordapp.com") {
|
||||
image, err := utils.DownloadImage(item.OrgURL, h.App.Config.ProxyURL)
|
||||
if err == nil {
|
||||
job.ImgURL = "data:image/png;base64," + base64.StdEncoding.EncodeToString(image)
|
||||
}
|
||||
} else {
|
||||
job.ImgURL = job.OrgURL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +395,9 @@ func (h *MidJourneyHandler) Remove(c *gin.Context) {
|
||||
}
|
||||
|
||||
client := h.pool.Clients.Get(data.UserId)
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -76,6 +76,12 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// fix: 这里先检查一下订单状态,如果已经支付了,就直接返回
|
||||
if order.Status == types.OrderPaidSuccess {
|
||||
resp.ERROR(c, "This order had been paid, please do not pay twice")
|
||||
return
|
||||
}
|
||||
|
||||
// 更新扫码状态
|
||||
h.db.Model(&order).UpdateColumn("status", types.OrderScanned)
|
||||
if payWay == "alipay" { // 支付宝
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"chatplus/utils/resp"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -36,6 +38,27 @@ func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, man
|
||||
return &h
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *SdJobHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.pool.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
}
|
||||
|
||||
func (h *SdJobHandler) checkLimits(c *gin.Context) bool {
|
||||
user, err := utils.GetLoginUser(c, h.db)
|
||||
if err != nil {
|
||||
|
||||
@@ -35,6 +35,7 @@ func (h *UploadHandler) Upload(c *gin.Context) {
|
||||
res := h.db.Create(&model.File{
|
||||
UserId: userId,
|
||||
Name: file.Name,
|
||||
ObjKey: file.ObjKey,
|
||||
URL: file.URL,
|
||||
Ext: file.Ext,
|
||||
Size: file.Size,
|
||||
@@ -52,7 +53,7 @@ func (h *UploadHandler) List(c *gin.Context) {
|
||||
userId := h.GetLoginUserId(c)
|
||||
var items []model.File
|
||||
var files = make([]vo.File, 0)
|
||||
h.db.Debug().Where("user_id = ?", userId).Find(&items)
|
||||
h.db.Where("user_id = ?", userId).Find(&items)
|
||||
if len(items) > 0 {
|
||||
for _, v := range items {
|
||||
var file vo.File
|
||||
@@ -68,3 +69,29 @@ func (h *UploadHandler) List(c *gin.Context) {
|
||||
|
||||
resp.SUCCESS(c, files)
|
||||
}
|
||||
|
||||
// Remove remove files
|
||||
func (h *UploadHandler) Remove(c *gin.Context) {
|
||||
userId := h.GetLoginUserId(c)
|
||||
id := h.GetInt(c, "id", 0)
|
||||
var file model.File
|
||||
tx := h.db.Where("user_id = ? AND id = ?", userId, id).First(&file)
|
||||
if tx.Error != nil || file.Id == 0 {
|
||||
resp.ERROR(c, "file not existed")
|
||||
return
|
||||
}
|
||||
|
||||
// remove database
|
||||
tx = h.db.Model(&model.File{}).Delete("id = ?", id)
|
||||
if tx.Error != nil || tx.RowsAffected == 0 {
|
||||
resp.ERROR(c, "failed to update database")
|
||||
return
|
||||
}
|
||||
// remove files
|
||||
objectKey := file.ObjKey
|
||||
if objectKey == "" {
|
||||
objectKey = file.URL
|
||||
}
|
||||
_ = h.uploaderManager.GetUploadHandler().Delete(objectKey)
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ func main() {
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.UploadHandler) {
|
||||
s.Engine.POST("/api/upload", h.Upload)
|
||||
s.Engine.GET("/api/upload/list", h.List)
|
||||
s.Engine.GET("/api/upload/remove", h.Remove)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.SmsHandler) {
|
||||
group := s.Engine.Group("/api/sms/")
|
||||
|
||||
@@ -2,6 +2,7 @@ package mj
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -11,13 +12,12 @@ import (
|
||||
// MidJourney client
|
||||
|
||||
type Client struct {
|
||||
client *req.Client
|
||||
Config types.MidJourneyConfig
|
||||
imgCdnURL string
|
||||
apiURL string
|
||||
client *req.Client
|
||||
Config types.MidJourneyConfig
|
||||
apiURL string
|
||||
}
|
||||
|
||||
func NewClient(config types.MidJourneyConfig, proxy string, imgCdnURL string) *Client {
|
||||
func NewClient(config types.MidJourneyConfig, proxy string) *Client {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
var apiURL string
|
||||
// set proxy URL
|
||||
@@ -30,10 +30,10 @@ func NewClient(config types.MidJourneyConfig, proxy string, imgCdnURL string) *C
|
||||
}
|
||||
}
|
||||
|
||||
return &Client{client: client, Config: config, apiURL: apiURL, imgCdnURL: imgCdnURL}
|
||||
return &Client{client: client, Config: config, apiURL: apiURL}
|
||||
}
|
||||
|
||||
func (c *Client) Imagine(prompt string) error {
|
||||
func (c *Client) Imagine(task types.MjTask) error {
|
||||
interactionsReq := &InteractionsRequest{
|
||||
Type: 2,
|
||||
ApplicationID: ApplicationID,
|
||||
@@ -49,7 +49,7 @@ func (c *Client) Imagine(prompt string) error {
|
||||
{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"value": prompt,
|
||||
"value": fmt.Sprintf("%s %s", task.TaskId, task.Prompt),
|
||||
},
|
||||
},
|
||||
"application_command": map[string]any{
|
||||
@@ -88,20 +88,28 @@ func (c *Client) Imagine(prompt string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Blend(task types.MjTask) error {
|
||||
return errors.New("function not implemented")
|
||||
}
|
||||
|
||||
func (c *Client) SwapFace(task types.MjTask) error {
|
||||
return errors.New("function not implemented")
|
||||
}
|
||||
|
||||
// Upscale 放大指定的图片
|
||||
func (c *Client) Upscale(index int, messageId string, hash string) error {
|
||||
func (c *Client) Upscale(task types.MjTask) error {
|
||||
flags := 0
|
||||
interactionsReq := &InteractionsRequest{
|
||||
Type: 3,
|
||||
ApplicationID: ApplicationID,
|
||||
GuildID: c.Config.GuildId,
|
||||
ChannelID: c.Config.ChanelId,
|
||||
MessageFlags: &flags,
|
||||
MessageID: &messageId,
|
||||
MessageFlags: flags,
|
||||
MessageID: task.MessageId,
|
||||
SessionID: SessionID,
|
||||
Data: map[string]any{
|
||||
"component_type": 2,
|
||||
"custom_id": fmt.Sprintf("MJ::JOB::upsample::%d::%s", index, hash),
|
||||
"custom_id": fmt.Sprintf("MJ::JOB::upsample::%d::%s", task.Index, task.MessageHash),
|
||||
},
|
||||
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
@@ -120,19 +128,19 @@ func (c *Client) Upscale(index int, messageId string, hash string) error {
|
||||
}
|
||||
|
||||
// Variation 以指定的图片的视角进行变换再创作,注意需要在对应的频道中关闭 Remix 变换,否则 Variation 指令将不会生效
|
||||
func (c *Client) Variation(index int, messageId string, hash string) error {
|
||||
func (c *Client) Variation(task types.MjTask) error {
|
||||
flags := 0
|
||||
interactionsReq := &InteractionsRequest{
|
||||
Type: 3,
|
||||
ApplicationID: ApplicationID,
|
||||
GuildID: c.Config.GuildId,
|
||||
ChannelID: c.Config.ChanelId,
|
||||
MessageFlags: &flags,
|
||||
MessageID: &messageId,
|
||||
MessageFlags: flags,
|
||||
MessageID: task.MessageId,
|
||||
SessionID: SessionID,
|
||||
Data: map[string]any{
|
||||
"component_type": 2,
|
||||
"custom_id": fmt.Sprintf("MJ::JOB::variation::%d::%s", index, hash),
|
||||
"custom_id": fmt.Sprintf("MJ::JOB::variation::%d::%s", task.Index, task.MessageHash),
|
||||
},
|
||||
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ package plus
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
logger2 "chatplus/logger"
|
||||
"chatplus/utils"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
|
||||
@@ -15,16 +19,24 @@ var logger = logger2.GetLogger()
|
||||
// Client MidJourney Plus Client
|
||||
type Client struct {
|
||||
Config types.MidJourneyPlusConfig
|
||||
apiURL string
|
||||
}
|
||||
|
||||
func NewClient(config types.MidJourneyPlusConfig) *Client {
|
||||
return &Client{Config: config}
|
||||
var apiURL string
|
||||
if config.CdnURL != "" {
|
||||
apiURL = config.CdnURL
|
||||
} else {
|
||||
apiURL = config.ApiURL
|
||||
}
|
||||
return &Client{Config: config, apiURL: apiURL}
|
||||
}
|
||||
|
||||
type ImageReq struct {
|
||||
BotType string `json:"botType"`
|
||||
Prompt string `json:"prompt"`
|
||||
Base64Array []interface{} `json:"base64Array,omitempty"`
|
||||
BotType string `json:"botType"`
|
||||
Prompt string `json:"prompt,omitempty"`
|
||||
Dimensions string `json:"dimensions,omitempty"`
|
||||
Base64Array []string `json:"base64Array,omitempty"`
|
||||
AccountFilter struct {
|
||||
InstanceId string `json:"instanceId"`
|
||||
Modes []interface{} `json:"modes"`
|
||||
@@ -49,12 +61,114 @@ type ErrRes struct {
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
func (c *Client) Imagine(prompt string) (ImageRes, error) {
|
||||
apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/imagine", c.Config.ApiURL)
|
||||
func (c *Client) Imagine(task types.MjTask) (ImageRes, error) {
|
||||
apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/imagine", c.apiURL)
|
||||
body := ImageReq{
|
||||
BotType: "MID_JOURNEY",
|
||||
Prompt: prompt,
|
||||
NotifyHook: c.Config.NotifyURL,
|
||||
BotType: "MID_JOURNEY",
|
||||
Prompt: task.Prompt,
|
||||
NotifyHook: c.Config.NotifyURL,
|
||||
Base64Array: make([]string, 0),
|
||||
}
|
||||
// 生成图片 Base64 编码
|
||||
if len(task.ImgArr) > 0 {
|
||||
imageData, err := utils.DownloadImage(task.ImgArr[0], "")
|
||||
if err != nil {
|
||||
logger.Error("error with download image: ", err)
|
||||
} else {
|
||||
body.Base64Array = append(body.Base64Array, "data:image/png;base64,"+base64.StdEncoding.EncodeToString(imageData))
|
||||
}
|
||||
|
||||
}
|
||||
var res ImageRes
|
||||
var errRes ErrRes
|
||||
r, err := req.C().R().
|
||||
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||
SetBody(body).
|
||||
SetSuccessResult(&res).
|
||||
SetErrorResult(&errRes).
|
||||
Post(apiURL)
|
||||
if err != nil {
|
||||
return ImageRes{}, fmt.Errorf("请求 API 出错:%v", err)
|
||||
}
|
||||
|
||||
if r.IsErrorState() {
|
||||
errStr, _ := io.ReadAll(r.Body)
|
||||
return ImageRes{}, fmt.Errorf("API 返回错误:%s,%v", errRes.Error.Message, string(errStr))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Blend 融图
|
||||
func (c *Client) Blend(task types.MjTask) (ImageRes, error) {
|
||||
apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/blend", c.apiURL)
|
||||
body := ImageReq{
|
||||
BotType: "MID_JOURNEY",
|
||||
Dimensions: "SQUARE",
|
||||
NotifyHook: c.Config.NotifyURL,
|
||||
Base64Array: make([]string, 0),
|
||||
}
|
||||
// 生成图片 Base64 编码
|
||||
if len(task.ImgArr) > 0 {
|
||||
for _, imgURL := range task.ImgArr {
|
||||
imageData, err := utils.DownloadImage(imgURL, "")
|
||||
if err != nil {
|
||||
logger.Error("error with download image: ", err)
|
||||
} else {
|
||||
body.Base64Array = append(body.Base64Array, "data:image/png;base64,"+base64.StdEncoding.EncodeToString(imageData))
|
||||
}
|
||||
}
|
||||
}
|
||||
var res ImageRes
|
||||
var errRes ErrRes
|
||||
r, err := req.C().R().
|
||||
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||
SetBody(body).
|
||||
SetSuccessResult(&res).
|
||||
SetErrorResult(&errRes).
|
||||
Post(apiURL)
|
||||
if err != nil {
|
||||
errStr, _ := io.ReadAll(r.Body)
|
||||
return ImageRes{}, fmt.Errorf("请求 API 出错:%v,%v", err, string(errStr))
|
||||
}
|
||||
|
||||
if r.IsErrorState() {
|
||||
return ImageRes{}, fmt.Errorf("API 返回错误:%s", errRes.Error.Message)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// SwapFace 换脸
|
||||
func (c *Client) SwapFace(task types.MjTask) (ImageRes, error) {
|
||||
apiURL := fmt.Sprintf("%s/mj-fast/mj/insight-face/swap", c.apiURL)
|
||||
// 生成图片 Base64 编码
|
||||
if len(task.ImgArr) != 2 {
|
||||
return ImageRes{}, errors.New("参数错误,必须上传2张图片")
|
||||
}
|
||||
var sourceBase64 string
|
||||
var targetBase64 string
|
||||
imageData, err := utils.DownloadImage(task.ImgArr[0], "")
|
||||
if err != nil {
|
||||
logger.Error("error with download image: ", err)
|
||||
} else {
|
||||
sourceBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
|
||||
}
|
||||
imageData, err = utils.DownloadImage(task.ImgArr[1], "")
|
||||
if err != nil {
|
||||
logger.Error("error with download image: ", err)
|
||||
} else {
|
||||
targetBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
|
||||
}
|
||||
|
||||
body := gin.H{
|
||||
"sourceBase64": sourceBase64,
|
||||
"targetBase64": targetBase64,
|
||||
"accountFilter": gin.H{
|
||||
"instanceId": "",
|
||||
},
|
||||
"notifyHook": c.Config.NotifyURL,
|
||||
"state": "",
|
||||
}
|
||||
var res ImageRes
|
||||
var errRes ErrRes
|
||||
@@ -77,13 +191,13 @@ func (c *Client) Imagine(prompt string) (ImageRes, error) {
|
||||
}
|
||||
|
||||
// Upscale 放大指定的图片
|
||||
func (c *Client) Upscale(index int, messageId string, hash string) (ImageRes, error) {
|
||||
func (c *Client) Upscale(task types.MjTask) (ImageRes, error) {
|
||||
body := map[string]string{
|
||||
"customId": fmt.Sprintf("MJ::JOB::upsample::%d::%s", index, hash),
|
||||
"taskId": messageId,
|
||||
"customId": fmt.Sprintf("MJ::JOB::upsample::%d::%s", task.Index, task.MessageHash),
|
||||
"taskId": task.MessageId,
|
||||
"notifyHook": c.Config.NotifyURL,
|
||||
}
|
||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL)
|
||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.apiURL)
|
||||
var res ImageRes
|
||||
var errRes ErrRes
|
||||
r, err := req.C().R().
|
||||
@@ -104,13 +218,13 @@ func (c *Client) Upscale(index int, messageId string, hash string) (ImageRes, er
|
||||
}
|
||||
|
||||
// Variation 以指定的图片的视角进行变换再创作,注意需要在对应的频道中关闭 Remix 变换,否则 Variation 指令将不会生效
|
||||
func (c *Client) Variation(index int, messageId string, hash string) (ImageRes, error) {
|
||||
func (c *Client) Variation(task types.MjTask) (ImageRes, error) {
|
||||
body := map[string]string{
|
||||
"customId": fmt.Sprintf("MJ::JOB::variation::%d::%s", index, hash),
|
||||
"taskId": messageId,
|
||||
"customId": fmt.Sprintf("MJ::JOB::variation::%d::%s", task.Index, task.MessageHash),
|
||||
"taskId": task.MessageId,
|
||||
"notifyHook": c.Config.NotifyURL,
|
||||
}
|
||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL)
|
||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.apiURL)
|
||||
var res ImageRes
|
||||
var errRes ErrRes
|
||||
r, err := req.C().R().
|
||||
@@ -156,7 +270,7 @@ type QueryRes struct {
|
||||
}
|
||||
|
||||
func (c *Client) QueryTask(taskId string) (QueryRes, error) {
|
||||
apiURL := fmt.Sprintf("%s/mj/task/%s/fetch", c.Config.ApiURL, taskId)
|
||||
apiURL := fmt.Sprintf("%s/mj/task/%s/fetch", c.apiURL, taskId)
|
||||
var res QueryRes
|
||||
r, err := req.C().R().SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||
SetSuccessResult(&res).
|
||||
|
||||
@@ -58,31 +58,42 @@ func (s *Service) Run() {
|
||||
}
|
||||
|
||||
// if it's reference message, check if it's this channel's message
|
||||
if task.ChannelId != "" && task.ChannelId != s.Name {
|
||||
logger.Debugf("handle other service task, name: %s, channel_id: %s, drop it.", s.Name, task.ChannelId)
|
||||
s.taskQueue.RPush(task)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
//if task.ChannelId != "" && task.ChannelId != s.Name {
|
||||
// logger.Debugf("handle other service task, name: %s, channel_id: %s, drop it.", s.Name, task.ChannelId)
|
||||
// s.taskQueue.RPush(task)
|
||||
// time.Sleep(time.Second)
|
||||
// continue
|
||||
//}
|
||||
|
||||
logger.Infof("%s handle a new MidJourney task: %+v", s.Name, task)
|
||||
var res ImageRes
|
||||
switch task.Type {
|
||||
case types.TaskImage:
|
||||
index := strings.Index(task.Prompt, " ")
|
||||
res, err = s.Client.Imagine(task.Prompt[index+1:])
|
||||
res, err = s.Client.Imagine(task)
|
||||
break
|
||||
case types.TaskUpscale:
|
||||
res, err = s.Client.Upscale(task.Index, task.MessageId, task.MessageHash)
|
||||
res, err = s.Client.Upscale(task)
|
||||
break
|
||||
case types.TaskVariation:
|
||||
res, err = s.Client.Variation(task.Index, task.MessageId, task.MessageHash)
|
||||
res, err = s.Client.Variation(task)
|
||||
break
|
||||
case types.TaskBlend:
|
||||
res, err = s.Client.Blend(task)
|
||||
break
|
||||
case types.TaskSwapFace:
|
||||
res, err = s.Client.SwapFace(task)
|
||||
break
|
||||
}
|
||||
|
||||
var job model.MidJourneyJob
|
||||
s.db.Where("id = ?", task.Id).First(&job)
|
||||
if err != nil || (res.Code != 1 && res.Code != 22) {
|
||||
logger.Error("绘画任务执行失败:", err)
|
||||
errMsg := fmt.Sprintf("%v,%s", err, res.Description)
|
||||
logger.Error("绘画任务执行失败:", errMsg)
|
||||
job.Progress = -1
|
||||
job.ErrMsg = errMsg
|
||||
// update the task progress
|
||||
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||
s.db.Updates(&job)
|
||||
// 任务失败,通知前端
|
||||
s.notifyQueue.RPush(task.UserId)
|
||||
// restore img_call quota
|
||||
@@ -95,14 +106,12 @@ func (s *Service) Run() {
|
||||
}
|
||||
logger.Infof("任务提交成功:%+v", res)
|
||||
// lock the task until the execute timeout
|
||||
s.taskStartTimes[task.Id] = time.Now()
|
||||
s.taskStartTimes[int(task.Id)] = time.Now()
|
||||
atomic.AddInt32(&s.HandledTaskNum, 1)
|
||||
// 更新任务 ID/频道
|
||||
s.db.Model(&model.MidJourneyJob{}).Where("id = ?", task.Id).UpdateColumns(map[string]interface{}{
|
||||
"task_id": res.Result,
|
||||
"channel_id": s.Name,
|
||||
})
|
||||
|
||||
job.TaskId = res.Result
|
||||
job.ChannelId = s.Name
|
||||
s.db.Updates(&job)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,26 +151,54 @@ type CBReq struct {
|
||||
} `json:"properties"`
|
||||
}
|
||||
|
||||
func (s *Service) Notify(data CBReq, job model.MidJourneyJob) error {
|
||||
|
||||
job.Progress = utils.IntValue(strings.Replace(data.Progress, "%", "", 1), 0)
|
||||
job.Prompt = data.Properties.FinalPrompt
|
||||
if data.ImageUrl != "" {
|
||||
job.OrgURL = data.ImageUrl
|
||||
}
|
||||
job.UseProxy = true
|
||||
job.MessageId = data.Id
|
||||
logger.Debugf("JOB: %+v", job)
|
||||
res := s.db.Updates(&job)
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("error with update job: %v", res.Error)
|
||||
func (s *Service) Notify(job model.MidJourneyJob) error {
|
||||
task, err := s.Client.QueryTask(job.TaskId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if data.Status == "SUCCESS" {
|
||||
// 任务执行失败了
|
||||
if task.FailReason != "" {
|
||||
s.db.Model(&model.MidJourneyJob{Id: job.Id}).UpdateColumns(map[string]interface{}{
|
||||
"progress": -1,
|
||||
"err_msg": task.FailReason,
|
||||
})
|
||||
return fmt.Errorf("task failed: %v", task.FailReason)
|
||||
}
|
||||
|
||||
if len(task.Buttons) > 0 {
|
||||
job.Hash = GetImageHash(task.Buttons[0].CustomId)
|
||||
}
|
||||
oldProgress := job.Progress
|
||||
job.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0)
|
||||
job.Prompt = task.PromptEn
|
||||
if task.ImageUrl != "" {
|
||||
if s.Client.Config.CdnURL != "" {
|
||||
job.OrgURL = strings.Replace(task.ImageUrl, s.Client.Config.ApiURL, s.Client.Config.CdnURL, 1)
|
||||
} else {
|
||||
job.OrgURL = task.ImageUrl
|
||||
}
|
||||
}
|
||||
job.MessageId = task.Id
|
||||
tx := s.db.Updates(&job)
|
||||
if tx.Error != nil {
|
||||
return fmt.Errorf("error with update database: %v", tx.Error)
|
||||
}
|
||||
if task.Status == "SUCCESS" {
|
||||
// release lock task
|
||||
atomic.AddInt32(&s.HandledTaskNum, -1)
|
||||
}
|
||||
|
||||
s.notifyQueue.RPush(job.UserId)
|
||||
// 通知前端更新任务进度
|
||||
if oldProgress != job.Progress {
|
||||
s.notifyQueue.RPush(job.UserId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetImageHash(action string) string {
|
||||
split := strings.Split(action, "::")
|
||||
if len(split) > 5 {
|
||||
return split[4]
|
||||
}
|
||||
return split[len(split)-1]
|
||||
}
|
||||
|
||||
@@ -6,11 +6,9 @@ import (
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -35,9 +33,6 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
if config.Enabled == false {
|
||||
continue
|
||||
}
|
||||
if config.ApiURL != "https://gpt.bemore.lol" && config.ApiURL != "https://api.chat-plus.net" {
|
||||
config.ApiURL = "https://api.chat-plus.net"
|
||||
}
|
||||
client := plus.NewClient(config)
|
||||
name := fmt.Sprintf("mj-service-plus-%d", k)
|
||||
servicePlus := plus.NewService(name, taskQueue, notifyQueue, 10, 600, db, client)
|
||||
@@ -54,7 +49,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
continue
|
||||
}
|
||||
// create mj client
|
||||
client := NewClient(config, appConfig.ProxyURL, appConfig.ImgCdnURL)
|
||||
client := NewClient(config, appConfig.ProxyURL)
|
||||
|
||||
name := fmt.Sprintf("MjService-%d", k)
|
||||
// create mj service
|
||||
@@ -98,6 +93,9 @@ func (p *ServicePool) CheckTaskNotify() {
|
||||
continue
|
||||
}
|
||||
client := p.Clients.Get(userId)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
err = client.Send([]byte("Task Updated"))
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -120,30 +118,33 @@ func (p *ServicePool) DownloadImages() {
|
||||
if v.OrgURL == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Infof("try to download image: %s", v.OrgURL)
|
||||
var imgURL string
|
||||
var err error
|
||||
if v.UseProxy {
|
||||
if servicePlus := p.getServicePlus(v.ChannelId); servicePlus != nil {
|
||||
task, _ := servicePlus.Client.QueryTask(v.TaskId)
|
||||
if task.ImageUrl != "" {
|
||||
imgURL, err = p.uploaderManager.GetUploadHandler().PutImg(task.ImageUrl, false)
|
||||
}
|
||||
if len(task.Buttons) > 0 {
|
||||
v.Hash = getImageHash(task.Buttons[0].CustomId)
|
||||
}
|
||||
if servicePlus := p.getServicePlus(v.ChannelId); servicePlus != nil {
|
||||
task, _ := servicePlus.Client.QueryTask(v.TaskId)
|
||||
if len(task.Buttons) > 0 {
|
||||
v.Hash = plus.GetImageHash(task.Buttons[0].CustomId)
|
||||
}
|
||||
imgURL, err = p.uploaderManager.GetUploadHandler().PutImg(v.OrgURL, false)
|
||||
} else {
|
||||
imgURL, err = p.uploaderManager.GetUploadHandler().PutImg(v.OrgURL, true)
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error("error with download image: ", err)
|
||||
logger.Errorf("error with download image %s, %v", v.OrgURL, err)
|
||||
continue
|
||||
} else {
|
||||
logger.Infof("download image %s successfully.", v.OrgURL)
|
||||
}
|
||||
|
||||
v.ImgURL = imgURL
|
||||
p.db.Updates(&v)
|
||||
|
||||
client := p.Clients.Get(uint(v.UserId))
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
err = client.Send([]byte("Task Updated"))
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -167,7 +168,7 @@ func (p *ServicePool) HasAvailableService() bool {
|
||||
}
|
||||
|
||||
func (p *ServicePool) Notify(data plus.CBReq) error {
|
||||
logger.Infof("收到任务回调:%+v", data)
|
||||
logger.Debugf("收到任务回调:%+v", data)
|
||||
var job model.MidJourneyJob
|
||||
res := p.db.Where("task_id = ?", data.Id).First(&job)
|
||||
if res.Error != nil {
|
||||
@@ -179,7 +180,7 @@ func (p *ServicePool) Notify(data plus.CBReq) error {
|
||||
return nil
|
||||
}
|
||||
if servicePlus := p.getServicePlus(job.ChannelId); servicePlus != nil {
|
||||
return servicePlus.Notify(data, job)
|
||||
return servicePlus.Notify(job)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -190,7 +191,7 @@ func (p *ServicePool) SyncTaskProgress() {
|
||||
go func() {
|
||||
var items []model.MidJourneyJob
|
||||
for {
|
||||
res := p.db.Where("progress < ?", 100).Find(&items)
|
||||
res := p.db.Where("progress >= ? AND progress < ?", 0, 100).Find(&items)
|
||||
if res.Error != nil {
|
||||
continue
|
||||
}
|
||||
@@ -211,32 +212,7 @@ func (p *ServicePool) SyncTaskProgress() {
|
||||
}
|
||||
|
||||
if servicePlus := p.getServicePlus(v.ChannelId); servicePlus != nil {
|
||||
task, err := servicePlus.Client.QueryTask(v.TaskId)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(task.Buttons) > 0 {
|
||||
v.Hash = getImageHash(task.Buttons[0].CustomId)
|
||||
}
|
||||
oldProgress := v.Progress
|
||||
v.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0)
|
||||
v.Prompt = task.PromptEn
|
||||
if task.ImageUrl != "" {
|
||||
v.OrgURL = task.ImageUrl
|
||||
}
|
||||
v.UseProxy = true
|
||||
v.MessageId = task.Id
|
||||
|
||||
p.db.Updates(&v)
|
||||
|
||||
if task.Status == "SUCCESS" {
|
||||
// release lock task
|
||||
atomic.AddInt32(&servicePlus.HandledTaskNum, -1)
|
||||
}
|
||||
// 通知前端更新任务进度
|
||||
if oldProgress != v.Progress {
|
||||
p.notifyQueue.RPush(v.UserId)
|
||||
}
|
||||
_ = servicePlus.Notify(v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,11 +231,3 @@ func (p *ServicePool) getServicePlus(name string) *plus.Service {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImageHash(action string) string {
|
||||
split := strings.Split(action, "::")
|
||||
if len(split) > 5 {
|
||||
return split[4]
|
||||
}
|
||||
return split[len(split)-1]
|
||||
}
|
||||
|
||||
@@ -65,20 +65,29 @@ func (s *Service) Run() {
|
||||
logger.Infof("%s handle a new MidJourney task: %+v", s.name, task)
|
||||
switch task.Type {
|
||||
case types.TaskImage:
|
||||
err = s.client.Imagine(task.Prompt)
|
||||
err = s.client.Imagine(task)
|
||||
break
|
||||
case types.TaskUpscale:
|
||||
err = s.client.Upscale(task.Index, task.MessageId, task.MessageHash)
|
||||
|
||||
err = s.client.Upscale(task)
|
||||
break
|
||||
case types.TaskVariation:
|
||||
err = s.client.Variation(task.Index, task.MessageId, task.MessageHash)
|
||||
err = s.client.Variation(task)
|
||||
break
|
||||
case types.TaskBlend:
|
||||
err = s.client.Blend(task)
|
||||
break
|
||||
case types.TaskSwapFace:
|
||||
err = s.client.SwapFace(task)
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error("绘画任务执行失败:", err)
|
||||
logger.Error("绘画任务执行失败:", err.Error())
|
||||
// update the task progress
|
||||
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumns(map[string]interface{}{
|
||||
"progress": -1,
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
s.notifyQueue.RPush(task.UserId)
|
||||
// restore img_call quota
|
||||
if task.Type.String() != types.TaskUpscale.String() {
|
||||
@@ -88,7 +97,7 @@ func (s *Service) Run() {
|
||||
}
|
||||
|
||||
// lock the task until the execute timeout
|
||||
s.taskStartTimes[task.Id] = time.Now()
|
||||
s.taskStartTimes[int(task.Id)] = time.Now()
|
||||
atomic.AddInt32(&s.handledTaskNum, 1)
|
||||
|
||||
}
|
||||
@@ -128,6 +137,12 @@ func (s *Service) Notify(data CBReq) {
|
||||
} else {
|
||||
tx = tx.Where("task_id = ?", split[0])
|
||||
}
|
||||
// fixed: 修复 U/V 操作任务混淆覆盖的 Bug
|
||||
if strings.Contains(data.Prompt, "** - Image #") { // for upscale
|
||||
tx = tx.Where("type = ?", types.TaskUpscale.String())
|
||||
} else if strings.Contains(data.Prompt, "** - Variations (Strong)") { // for Variations
|
||||
tx = tx.Where("type = ?", types.TaskVariation.String())
|
||||
}
|
||||
res = tx.First(&job)
|
||||
if res.Error != nil {
|
||||
logger.Warn("非法任务:", res.Error)
|
||||
@@ -143,7 +158,7 @@ func (s *Service) Notify(data CBReq) {
|
||||
job.OrgURL = data.Image.URL
|
||||
if s.client.Config.UseCDN {
|
||||
job.UseProxy = true
|
||||
job.ImgURL = strings.ReplaceAll(data.Image.URL, "https://cdn.discordapp.com", s.client.imgCdnURL)
|
||||
job.ImgURL = strings.ReplaceAll(data.Image.URL, "https://cdn.discordapp.com", s.client.Config.ImgCdnURL)
|
||||
}
|
||||
|
||||
res = s.db.Updates(&job)
|
||||
|
||||
@@ -8,8 +8,8 @@ const (
|
||||
type InteractionsRequest struct {
|
||||
Type int `json:"type"`
|
||||
ApplicationID string `json:"application_id"`
|
||||
MessageFlags *int `json:"message_flags,omitempty"`
|
||||
MessageID *string `json:"message_id,omitempty"`
|
||||
MessageFlags int `json:"message_flags,omitempty"`
|
||||
MessageID string `json:"message_id,omitempty"`
|
||||
GuildID string `json:"guild_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
SessionID string `json:"session_id"`
|
||||
|
||||
@@ -5,11 +5,13 @@ import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/utils"
|
||||
"fmt"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AliYunOss struct {
|
||||
@@ -66,10 +68,11 @@ func (s AliYunOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, objectKey),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: objectKey,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, objectKey),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -88,7 +91,7 @@ func (s AliYunOss) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error with parse image URL: %v", err)
|
||||
}
|
||||
fileExt := filepath.Ext(parse.Path)
|
||||
fileExt := utils.GetImgExt(parse.Path)
|
||||
objectKey := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
|
||||
// 上传文件字节数据
|
||||
err = s.bucket.PutObject(objectKey, bytes.NewReader(imageData))
|
||||
@@ -99,9 +102,14 @@ func (s AliYunOss) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
}
|
||||
|
||||
func (s AliYunOss) Delete(fileURL string) error {
|
||||
objectName := filepath.Base(fileURL)
|
||||
key := fmt.Sprintf("%s/%s", s.config.SubDir, objectName)
|
||||
return s.bucket.DeleteObject(key)
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
||||
} else {
|
||||
objectKey = fileURL
|
||||
}
|
||||
return s.bucket.DeleteObject(objectKey)
|
||||
}
|
||||
|
||||
var _ Uploader = AliYunOss{}
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/utils"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type LocalStorage struct {
|
||||
@@ -29,7 +30,7 @@ func (s LocalStorage) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
return File{}, fmt.Errorf("error with get form: %v", err)
|
||||
}
|
||||
|
||||
path, err := utils.GenUploadPath(s.config.BasePath, file.Filename)
|
||||
path, err := utils.GenUploadPath(s.config.BasePath, file.Filename, false)
|
||||
if err != nil {
|
||||
return File{}, fmt.Errorf("error with generate filename: %s", err.Error())
|
||||
}
|
||||
@@ -41,10 +42,11 @@ func (s LocalStorage) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
|
||||
ext := filepath.Ext(file.Filename)
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: utils.GenUploadUrl(s.config.BasePath, s.config.BaseURL, path),
|
||||
Ext: ext,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: path,
|
||||
URL: utils.GenUploadUrl(s.config.BasePath, s.config.BaseURL, path),
|
||||
Ext: ext,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -54,7 +56,7 @@ func (s LocalStorage) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
return "", fmt.Errorf("error with parse image URL: %v", err)
|
||||
}
|
||||
filename := filepath.Base(parse.Path)
|
||||
filePath, err := utils.GenUploadPath(s.config.BasePath, filename)
|
||||
filePath, err := utils.GenUploadPath(s.config.BasePath, filename, true)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error with generate image dir: %v", err)
|
||||
}
|
||||
@@ -72,6 +74,9 @@ func (s LocalStorage) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
}
|
||||
|
||||
func (s LocalStorage) Delete(fileURL string) error {
|
||||
if _, err := os.Stat(fileURL); err == nil {
|
||||
return os.Remove(fileURL)
|
||||
}
|
||||
filePath := strings.Replace(fileURL, s.config.BaseURL, s.config.BasePath, 1)
|
||||
return os.Remove(filePath)
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"chatplus/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
|
||||
type MiniOss struct {
|
||||
@@ -77,7 +78,7 @@ func (s MiniOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
fileExt := filepath.Ext(file.Filename)
|
||||
fileExt := utils.GetImgExt(file.Filename)
|
||||
filename := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
|
||||
info, err := s.client.PutObject(ctx, s.config.Bucket, filename, fileReader, file.Size, minio.PutObjectOptions{
|
||||
ContentType: file.Header.Get("Content-Type"),
|
||||
@@ -87,17 +88,23 @@ func (s MiniOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: fmt.Sprintf("%s/%s/%s", s.config.Domain, s.config.Bucket, info.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: info.Key,
|
||||
URL: fmt.Sprintf("%s/%s/%s", s.config.Domain, s.config.Bucket, info.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s MiniOss) Delete(fileURL string) error {
|
||||
objectName := filepath.Base(fileURL)
|
||||
key := fmt.Sprintf("%s/%s", s.config.SubDir, objectName)
|
||||
return s.client.RemoveObject(context.Background(), s.config.Bucket, key, minio.RemoveObjectOptions{})
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
||||
} else {
|
||||
objectKey = fileURL
|
||||
}
|
||||
return s.client.RemoveObject(context.Background(), s.config.Bucket, objectKey, minio.RemoveObjectOptions{})
|
||||
}
|
||||
|
||||
var _ Uploader = MiniOss{}
|
||||
|
||||
@@ -6,12 +6,14 @@ import (
|
||||
"chatplus/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type QinNiuOss struct {
|
||||
@@ -74,10 +76,11 @@ func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, ret.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: key,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, ret.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
|
||||
}
|
||||
@@ -97,7 +100,7 @@ func (s QinNiuOss) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error with parse image URL: %v", err)
|
||||
}
|
||||
fileExt := filepath.Ext(parse.Path)
|
||||
fileExt := utils.GetImgExt(parse.Path)
|
||||
key := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
|
||||
ret := storage.PutRet{}
|
||||
extra := storage.PutExtra{}
|
||||
@@ -110,9 +113,15 @@ func (s QinNiuOss) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
}
|
||||
|
||||
func (s QinNiuOss) Delete(fileURL string) error {
|
||||
objectName := filepath.Base(fileURL)
|
||||
key := fmt.Sprintf("%s/%s", s.config.SubDir, objectName)
|
||||
return s.manager.Delete(s.config.Bucket, key)
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
||||
} else {
|
||||
objectKey = fileURL
|
||||
}
|
||||
|
||||
return s.manager.Delete(s.config.Bucket, objectKey)
|
||||
}
|
||||
|
||||
var _ Uploader = QinNiuOss{}
|
||||
|
||||
@@ -8,10 +8,11 @@ const QiNiu = "QINIU"
|
||||
const AliYun = "ALIYUN"
|
||||
|
||||
type File struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
URL string `json:"url"`
|
||||
Ext string `json:"ext"`
|
||||
Name string `json:"name"`
|
||||
ObjKey string `json:"obj_key"`
|
||||
Size int64 `json:"size"`
|
||||
URL string `json:"url"`
|
||||
Ext string `json:"ext"`
|
||||
}
|
||||
type Uploader interface {
|
||||
PutFile(ctx *gin.Context, name string) (File, error)
|
||||
|
||||
@@ -56,7 +56,7 @@ func (js *PayJS) Pay(param JPayReq) JPayReps {
|
||||
}
|
||||
p.Add("mchid", js.config.AppId)
|
||||
|
||||
p.Add("Sign", js.sign(p))
|
||||
p.Add("sign", js.sign(p))
|
||||
|
||||
cli := http.Client{}
|
||||
apiURL := fmt.Sprintf("%s/api/native", js.config.ApiURL)
|
||||
|
||||
@@ -11,13 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type ServicePool struct {
|
||||
services []*Service
|
||||
taskQueue *store.RedisQueue
|
||||
services []*Service
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
}
|
||||
|
||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig) *ServicePool {
|
||||
services := make([]*Service, 0)
|
||||
queue := store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli)
|
||||
taskQueue := store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli)
|
||||
notifyQueue := store.NewRedisQueue("StableDiffusion_Queue", redisCli)
|
||||
// create mj client and service
|
||||
for k, config := range appConfig.SdConfigs {
|
||||
if config.Enabled == false {
|
||||
@@ -26,7 +29,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
|
||||
// create sd service
|
||||
name := fmt.Sprintf("StableDifffusion Service-%d", k)
|
||||
service := NewService(name, 1, 300, config, queue, db, manager)
|
||||
service := NewService(name, 1, 300, config, taskQueue, notifyQueue, db, manager)
|
||||
// run sd service
|
||||
go func() {
|
||||
service.Run()
|
||||
@@ -36,8 +39,10 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
}
|
||||
|
||||
return &ServicePool{
|
||||
taskQueue: queue,
|
||||
services: services,
|
||||
taskQueue: taskQueue,
|
||||
notifyQueue: notifyQueue,
|
||||
services: services,
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ type Service struct {
|
||||
httpClient *req.Client
|
||||
config types.StableDiffusionConfig
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
db *gorm.DB
|
||||
uploadManager *oss.UploaderManager
|
||||
name string // service name
|
||||
@@ -33,12 +34,13 @@ type Service struct {
|
||||
taskTimeout int64
|
||||
}
|
||||
|
||||
func NewService(name string, maxTaskNum int32, timeout int64, config types.StableDiffusionConfig, queue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager) *Service {
|
||||
func NewService(name string, maxTaskNum int32, timeout int64, config types.StableDiffusionConfig, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager) *Service {
|
||||
return &Service{
|
||||
name: name,
|
||||
config: config,
|
||||
httpClient: req.C(),
|
||||
taskQueue: queue,
|
||||
taskQueue: taskQueue,
|
||||
notifyQueue: notifyQueue,
|
||||
db: db,
|
||||
uploadManager: manager,
|
||||
taskTimeout: timeout,
|
||||
@@ -66,13 +68,18 @@ func (s *Service) Run() {
|
||||
logger.Infof("%s handle a new Stable-Diffusion task: %+v", s.name, task)
|
||||
err = s.Txt2Img(task)
|
||||
if err != nil {
|
||||
logger.Error("绘画任务执行失败:", err)
|
||||
logger.Error("绘画任务执行失败:", err.Error())
|
||||
// update the task progress
|
||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumns(map[string]interface{}{
|
||||
"progress": -1,
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
// restore img_call quota
|
||||
s.db.Model(&model.User{}).Where("id = ?", task.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
// release task num
|
||||
atomic.AddInt32(&s.handledTaskNum, -1)
|
||||
// 通知前端,任务失败
|
||||
s.notifyQueue.RPush(task.UserId)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -296,7 +303,10 @@ func (s *Service) callback(data CBReq) {
|
||||
} else { // 任务失败
|
||||
logger.Error("任务执行失败:", data.Message)
|
||||
// update the task progress
|
||||
s.db.Model(&model.SdJob{Id: uint(data.JobId)}).UpdateColumn("progress", -1)
|
||||
s.db.Model(&model.SdJob{Id: uint(data.JobId)}).UpdateColumns(map[string]interface{}{
|
||||
"progress": -1,
|
||||
"err_msg": data.Message,
|
||||
})
|
||||
// restore img_calls
|
||||
s.db.Model(&model.User{}).Where("id = ? AND img_calls > 0", data.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package wanx
|
||||
@@ -7,6 +7,7 @@ type HistoryMessage struct {
|
||||
ChatId string // 会话 ID
|
||||
UserId uint // 用户 ID
|
||||
RoleId uint // 角色 ID
|
||||
Model string // AI模型
|
||||
Type string
|
||||
Icon string
|
||||
Tokens int
|
||||
|
||||
@@ -7,7 +7,8 @@ type ChatItem struct {
|
||||
ChatId string `gorm:"column:chat_id;unique"` // 会话 ID
|
||||
UserId uint // 用户 ID
|
||||
RoleId uint // 角色 ID
|
||||
ModelId uint // 会话模型
|
||||
ModelId uint // 模型 ID
|
||||
Model string // 模型
|
||||
Title string // 会话标题
|
||||
DeletedAt gorm.DeletedAt
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ type File struct {
|
||||
Id uint `gorm:"primarykey;column:id"`
|
||||
UserId uint
|
||||
Name string
|
||||
ObjKey string
|
||||
URL string
|
||||
Ext string
|
||||
Size int64
|
||||
|
||||
@@ -15,8 +15,9 @@ type MidJourneyJob struct {
|
||||
Hash string // message hash
|
||||
Progress int
|
||||
Prompt string
|
||||
UseProxy bool // 是否使用反代加载图片
|
||||
Publish bool //是否发布图片到画廊
|
||||
UseProxy bool // 是否使用反代加载图片
|
||||
Publish bool //是否发布图片到画廊
|
||||
ErrMsg string // 报错信息
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ type SdJob struct {
|
||||
Progress int
|
||||
Prompt string
|
||||
Params string
|
||||
Publish bool //是否发布图片到画廊
|
||||
Publish bool //是否发布图片到画廊
|
||||
ErrMsg string // 报错信息
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func NewGormConfig() *gorm.Config {
|
||||
return &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Warn),
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: "chatgpt_", // 设置表前缀
|
||||
SingularTable: false, // 使用单数表名形式
|
||||
|
||||
@@ -5,6 +5,7 @@ type HistoryMessage struct {
|
||||
ChatId string `json:"chat_id"`
|
||||
UserId uint `json:"user_id"`
|
||||
RoleId uint `json:"role_id"`
|
||||
Model string `json:"model"`
|
||||
Type string `json:"type"`
|
||||
Icon string `json:"icon"`
|
||||
Tokens int `json:"tokens"`
|
||||
|
||||
@@ -7,5 +7,6 @@ type ChatItem struct {
|
||||
RoleId uint `json:"role_id"`
|
||||
ChatId string `json:"chat_id"`
|
||||
ModelId uint `json:"model_id"`
|
||||
Model string `json:"model"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package vo
|
||||
|
||||
type File struct {
|
||||
Id uint
|
||||
Id uint `json:"id"`
|
||||
UserId uint `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
ObjKey string `json:"obj_key"`
|
||||
URL string `json:"url"`
|
||||
Ext string `json:"ext"`
|
||||
Size int64 `json:"size"`
|
||||
|
||||
@@ -17,5 +17,6 @@ type MidJourneyJob struct {
|
||||
Prompt string `json:"prompt"`
|
||||
UseProxy bool `json:"use_proxy"`
|
||||
Publish bool `json:"publish"`
|
||||
ErrMsg string `json:"err_msg"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@ type SdJob struct {
|
||||
Progress int `json:"progress"`
|
||||
Prompt string `json:"prompt"`
|
||||
Publish bool `json:"publish"`
|
||||
ErrMsg string `json:"err_msg"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "7151109597841850368 一个漂亮的中国女孩,手上拿着一桶爆米花,脸上带着迷人的微笑,电影效果"
|
||||
index := strings.Index(str, " ")
|
||||
fmt.Println(str[index+1:])
|
||||
u, err := url.Parse("https://api.chat-plus.net/mj/image/1706368258238514?aaa=bbb")
|
||||
fmt.Println(u.Path, u.RawQuery, err)
|
||||
}
|
||||
|
||||
@@ -48,12 +48,12 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) {
|
||||
},
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest("GET", imageURL, nil)
|
||||
request, err := http.NewRequest("GET", imageURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// GenUploadPath 生成上传文件路径
|
||||
func GenUploadPath(basePath, filename string) (string, error) {
|
||||
func GenUploadPath(basePath, filename string, isImg bool) (string, error) {
|
||||
now := time.Now()
|
||||
dir := fmt.Sprintf("%s/%d/%d", basePath, now.Year(), now.Month())
|
||||
_, err := os.Stat(dir)
|
||||
@@ -22,7 +22,12 @@ func GenUploadPath(basePath, filename string) (string, error) {
|
||||
return "", fmt.Errorf("error with create upload dir:%v", err)
|
||||
}
|
||||
}
|
||||
fileExt := filepath.Ext(filename)
|
||||
var fileExt string
|
||||
if isImg {
|
||||
fileExt = GetImgExt(filename)
|
||||
} else {
|
||||
fileExt = filepath.Ext(filename)
|
||||
}
|
||||
return fmt.Sprintf("%s/%d%s", dir, now.UnixMicro(), fileExt), nil
|
||||
}
|
||||
|
||||
@@ -66,3 +71,11 @@ func DownloadFile(fileURL string, filepath string, proxy string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetImgExt(filename string) string {
|
||||
ext := filepath.Ext(filename)
|
||||
if ext == "" {
|
||||
return ".png"
|
||||
}
|
||||
return ext
|
||||
}
|
||||
|
||||
15
database/update-v3.2.7.sql
Normal file
15
database/update-v3.2.7.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
ALTER TABLE `chatgpt_mj_jobs` ADD `err_msg` VARCHAR(255) DEFAULT NULL COMMENT '错误信息' AFTER `publish`;
|
||||
ALTER TABLE `chatgpt_sd_jobs` ADD `err_msg` VARCHAR(255) DEFAULT NULL COMMENT '错误信息' AFTER `publish`;
|
||||
|
||||
ALTER TABLE `chatgpt_chat_items` ADD `model` VARCHAR(30) NULL COMMENT '模型名称' AFTER `model_id`;
|
||||
ALTER TABLE `chatgpt_chat_history` ADD `model` VARCHAR(30) NULL COMMENT '模型名称' AFTER `role_id`;
|
||||
|
||||
-- 初始化对话数据
|
||||
UPDATE chatgpt_chat_items s SET model=(SELECT value FROM chatgpt_chat_models WHERE id = s.model_id);
|
||||
-- 初始化聊天记录数据
|
||||
UPDATE chatgpt_chat_history s SET model=(SELECT model FROM chatgpt_chat_items WHERE chat_id = s.chat_id);
|
||||
|
||||
-- 清理对话已删除的聊天记录(可选)
|
||||
-- DELETE FROM `chatgpt_chat_history` WHERE model is NULL;
|
||||
|
||||
ALTER TABLE `chatgpt_files` ADD `obj_key` VARCHAR(100) NULL COMMENT '文件标识' AFTER `name`;
|
||||
@@ -26,23 +26,15 @@ WeChatBot = false
|
||||
AppId = ""
|
||||
Token = ""
|
||||
|
||||
[SmsConfig] # 阿里云短信服务配置
|
||||
AccessKey = ""
|
||||
AccessSecret = ""
|
||||
Product = "Dysmsapi"
|
||||
Domain = "dysmsapi.aliyuncs.com"
|
||||
Sign = ""
|
||||
CodeTempId = ""
|
||||
|
||||
[Sms] # Sms 配置,用于发送短信
|
||||
[SMS] # Sms 配置,用于发送短信
|
||||
Active = "Ali" # 当前启用的短信服务,默认使用阿里云
|
||||
[Sms.SmsBao]
|
||||
[SMS.Bao]
|
||||
Username = ""
|
||||
Password = ""
|
||||
Domain = "api.smsbao.com"
|
||||
Sign = "【极客学长】"
|
||||
CodeTemplate = "您的验证码是{code}。5分钟有效,若非本人操作,请忽略本短信。"
|
||||
[Sms.Ali]
|
||||
[SMS.Ali]
|
||||
AccessKey = ""
|
||||
AccessSecret = ""
|
||||
Product = "Dysmsapi"
|
||||
@@ -83,6 +75,7 @@ WeChatBot = false
|
||||
[[MjPlusConfigs]]
|
||||
Enabled = false
|
||||
ApiURL = "https://api.chatgpt-plus.net" # 目前暂时不支持更改
|
||||
CdnURL = "" # CND 加速的 URL,如果有的话就设置
|
||||
ApiKey = "sk-xxx"
|
||||
NotifyURL = "https://ai.r9it.com/api/mj/notify" # 这里需要改成你的域名
|
||||
|
||||
@@ -114,9 +107,9 @@ WeChatBot = false
|
||||
[HuPiPayConfig]
|
||||
Enabled = false
|
||||
Name = "wechat"
|
||||
AppId = "201906161477"
|
||||
AppSecret = "7f403199d510fb2c6f0b9f2311800e7c"
|
||||
PayURL = "https://api.xunhupay.com/payment/do.html"
|
||||
AppId = ""
|
||||
AppSecret = ""
|
||||
ApiURL = "https://api.xunhupay.com"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify"
|
||||
|
||||
[SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器
|
||||
@@ -131,5 +124,5 @@ WeChatBot = false
|
||||
Name = "wechat" # 请不要改动
|
||||
AppId = "" # 商户 ID
|
||||
PrivateKey = "" # 秘钥
|
||||
ApiURL = "https://payjs.cn/api/native"
|
||||
ApiURL = "https://payjs.cn"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
|
||||
11
web/package-lock.json
generated
11
web/package-lock.json
generated
@@ -19,7 +19,6 @@
|
||||
"json-bigint": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-emoji": "^3.0.0",
|
||||
"md-editor-v3": "^2.2.1",
|
||||
"pinia": "^2.1.4",
|
||||
"qrcode": "^1.5.3",
|
||||
@@ -7431,11 +7430,6 @@
|
||||
"markdown-it": "bin/markdown-it.js"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it-emoji": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz",
|
||||
"integrity": "sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg=="
|
||||
},
|
||||
"node_modules/markdown-it/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
@@ -17367,11 +17361,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"markdown-it-emoji": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz",
|
||||
"integrity": "sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg=="
|
||||
},
|
||||
"md-editor-v3": {
|
||||
"version": "2.11.3",
|
||||
"resolved": "https://registry.npmjs.org/md-editor-v3/-/md-editor-v3-2.11.3.tgz",
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"json-bigint": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-latex2img": "^0.0.6",
|
||||
"markdown-it-mathjax": "^2.0.0",
|
||||
"md-editor-v3": "^2.2.1",
|
||||
"pinia": "^2.1.4",
|
||||
"qrcode": "^1.5.3",
|
||||
|
||||
BIN
web/public/images/mic.gif
Normal file
BIN
web/public/images/mic.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
||||
<title>ChatGPT-Plus</title>
|
||||
</head>
|
||||
|
||||
@@ -153,6 +153,12 @@
|
||||
#app .common-layout .el-main .chat-head .iconfont {
|
||||
margin-right: 5px;
|
||||
}
|
||||
#app .common-layout .el-main .chat-head .is-circle {
|
||||
margin-left: 5px;
|
||||
}
|
||||
#app .common-layout .el-main .chat-head .is-circle .iconfont {
|
||||
margin-right: 0;
|
||||
}
|
||||
#app .common-layout .el-main .right-box {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
|
||||
@@ -199,6 +199,13 @@ $borderColor = #4676d0;
|
||||
.iconfont {
|
||||
margin-right 5px;
|
||||
}
|
||||
|
||||
.is-circle {
|
||||
margin-left 5px
|
||||
.iconfont {
|
||||
margin-right 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-box {
|
||||
@@ -250,7 +257,6 @@ $borderColor = #4676d0;
|
||||
margin-right 5px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,9 +201,6 @@
|
||||
.page-mj .inner .task-list-box .task-list-inner .title-tabs .el-tabs__active-bar {
|
||||
background-color: #47fff1;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .title-tabs .el-tabs__content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .el-textarea {
|
||||
--el-input-focus-border-color: #47fff1;
|
||||
}
|
||||
@@ -234,26 +231,49 @@
|
||||
.page-mj .inner .task-list-box .task-list-inner .el-form-item__label {
|
||||
color: #fff;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-uploader .el-upload {
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline {
|
||||
display: flex;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 300px;
|
||||
width: 120px;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-uploader .el-upload:hover {
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-uploader .el-upload .el-icon.uploader-icon {
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-uploader .el-upload .el-icon.uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-list-box {
|
||||
display: flex;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-list-box .img-item {
|
||||
width: 120px;
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-list-box .img-item .el-image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-list-box .img-item .el-button {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .task-list-inner .submit-btn {
|
||||
display: flex;
|
||||
margin: 20px 0;
|
||||
@@ -267,17 +287,17 @@
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
.page-mj .inner .task-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%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.page-mj .inner .task-list-box .running-job-list .job-item .job-item-inner {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-mj .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -287,11 +307,11 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-mj .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
@@ -301,17 +321,17 @@
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
position: relative;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
@@ -321,59 +341,58 @@
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .remove {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .remove {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item:hover .remove {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item:hover .remove {
|
||||
display: block;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .animate:hover {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image img {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image img {
|
||||
height: 240px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image .image-slot {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image .image-slot .iconfont {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image.upscale {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image.upscale {
|
||||
max-height: 310px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image.upscale img {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image.upscale img {
|
||||
height: 310px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -86,9 +86,6 @@
|
||||
.page-sd .inner .task-list-box .task-list-inner .title-tabs .el-tabs__active-bar {
|
||||
background-color: #47fff1;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .title-tabs .el-tabs__content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .el-textarea {
|
||||
--el-input-focus-border-color: #47fff1;
|
||||
}
|
||||
@@ -119,26 +116,49 @@
|
||||
.page-sd .inner .task-list-box .task-list-inner .el-form-item__label {
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-uploader .el-upload {
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 300px;
|
||||
width: 120px;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-uploader .el-upload:hover {
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-uploader .el-upload .el-icon.uploader-icon {
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-uploader .el-upload .el-icon.uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-list-box {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-list-box .img-item {
|
||||
width: 120px;
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-list-box .img-item .el-image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-list-box .img-item .el-button {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .task-list-inner .submit-btn {
|
||||
display: flex;
|
||||
margin: 20px 0;
|
||||
@@ -152,17 +172,17 @@
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
.page-sd .inner .task-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%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -172,11 +192,11 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
@@ -186,17 +206,17 @@
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
position: relative;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
@@ -206,59 +226,58 @@
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .remove {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item .remove {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item:hover .remove {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .job-item:hover .remove {
|
||||
display: block;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image img {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image img {
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image.upscale {
|
||||
max-height: 310px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale img {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image.upscale img {
|
||||
height: 310px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
.title {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.app-background {
|
||||
background-color: #282828;
|
||||
height: 100vh;
|
||||
}
|
||||
.mobile-setting .content {
|
||||
padding-top: 60px;
|
||||
}
|
||||
.mobile-setting .content .van-field__label {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
.title {
|
||||
color #fff
|
||||
text-align center
|
||||
font-size 16px
|
||||
font-weight bold
|
||||
}
|
||||
.app-background {
|
||||
background-color #282828
|
||||
height 100vh
|
||||
}
|
||||
.mobile-setting {
|
||||
.content {
|
||||
padding-top 60px
|
||||
|
||||
.van-field__label {
|
||||
width 100px
|
||||
text-align right
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,29 @@
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
.mobile-chat-list {
|
||||
background-color: #1c1c1c;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
.content .van-cell__value .chat-list-item {
|
||||
.mobile-chat-list .content .van-cell__value .chat-list-item {
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background-color: #1c1c1c;
|
||||
font-size: 14px;
|
||||
}
|
||||
.content .van-cell__value .chat-list-item .van-image {
|
||||
.mobile-chat-list .content .van-cell__value .chat-list-item .van-image {
|
||||
min-width: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.content .van-cell__value .chat-list-item .van-ellipsis {
|
||||
.mobile-chat-list .content .van-cell__value .chat-list-item .van-ellipsis {
|
||||
margin-top: 5px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.van-picker-column .picker-option {
|
||||
.mobile-chat-list .van-nav-bar .van-nav-bar__right .van-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.van-popup .picker-option {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
overflow: hidden;
|
||||
height: 20px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.van-picker-column .picker-option .van-image {
|
||||
.van-popup .picker-option .van-image {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.van-nav-bar .van-nav-bar__right .van-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.popup {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.dialog {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.field {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
@@ -1,58 +1,47 @@
|
||||
$fontSize = 16px;
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
.mobile-chat-list {
|
||||
background-color: #1c1c1c;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
.content {
|
||||
.van-cell__value {
|
||||
.chat-list-item {
|
||||
display flex
|
||||
font-size $fontSize
|
||||
color: #fff;
|
||||
background-color: #1c1c1c;
|
||||
.van-image {
|
||||
min-width 32px
|
||||
width 32px
|
||||
height 32px
|
||||
}
|
||||
.van-ellipsis {
|
||||
margin-top 5px;
|
||||
margin-left 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.van-picker-column {
|
||||
.picker-option {
|
||||
display flex
|
||||
width 100%
|
||||
padding 0 10px
|
||||
.van-image {
|
||||
width 20px;
|
||||
height 20px;
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
.van-nav-bar {
|
||||
.van-nav-bar__right {
|
||||
.van-icon {
|
||||
font-size 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.popup {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.dialog {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.field {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.mobile-chat-list {
|
||||
.content {
|
||||
.van-cell__value {
|
||||
.chat-list-item {
|
||||
display flex
|
||||
font-size 14px
|
||||
|
||||
.van-image {
|
||||
min-width 32px
|
||||
width 32px
|
||||
height 32px
|
||||
}
|
||||
|
||||
.van-ellipsis {
|
||||
margin-top 5px;
|
||||
margin-left 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.van-nav-bar {
|
||||
.van-nav-bar__right {
|
||||
.van-icon {
|
||||
font-size 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.van-popup {
|
||||
.picker-option {
|
||||
display flex
|
||||
width 100%
|
||||
padding 0 10px
|
||||
overflow hidden
|
||||
height 20px
|
||||
text-overflow ellipsis
|
||||
|
||||
.van-image {
|
||||
width 20px;
|
||||
height 20px;
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,46 @@
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
.mobile-chat .message-list-box {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 10px;
|
||||
overflow-x: auto;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
body,
|
||||
.mobile-chat,
|
||||
.van-sticky,
|
||||
.van-nav-bar,
|
||||
.van-list,
|
||||
.message-list-box,
|
||||
.van-cell,
|
||||
.chat-box,
|
||||
.van-cell-group,
|
||||
.van-field {
|
||||
background-color: #1c1c1c !important;
|
||||
color: #fff !important;
|
||||
.mobile-chat .message-list-box .van-cell {
|
||||
background: none;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
}
|
||||
.chat-box-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #1c1c1c;
|
||||
.mobile-chat .chat-box-wrapper .van-sticky .van-cell-group--inset {
|
||||
margin: 0;
|
||||
}
|
||||
.icon-box .van-icon,
|
||||
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title,
|
||||
.mobile-chat .van-nav-bar__title .van-cell__title,
|
||||
.mobile-chat .van-nav-bar__right .van-icon {
|
||||
color: #fff !important;
|
||||
.mobile-chat .chat-box-wrapper .van-sticky .van-cell-group--inset .van-cell {
|
||||
padding: 10px;
|
||||
}
|
||||
.mobile-chat .chat-box-wrapper .van-sticky .van-cell-group--inset .van-cell .icon-box .van-icon {
|
||||
font-size: 24px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.mobile-chat .chat-box-wrapper .van-sticky .van-cell-group--inset .van-cell .button-voice {
|
||||
padding: 0 2px;
|
||||
height: 30px;
|
||||
}
|
||||
.mobile-chat .chat-box-wrapper .van-sticky .van-cell-group--inset .van-cell .button-voice .el-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title {
|
||||
margin-right: 15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.mobile-chat .van-nav-bar__title .van-cell__title {
|
||||
text-align: left;
|
||||
}
|
||||
.mobile-chat .chat-list-wrapper {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
bottom: 60px;
|
||||
width: 100vw;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.mobile-chat .van-nav-bar__right .van-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.van-overlay .mic-wrapper {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
}
|
||||
.van-theme-dark .mobile-chat .message-list-box {
|
||||
background: #232425;
|
||||
}
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
.app-background {
|
||||
background-color #1c1c1c
|
||||
height 100vh
|
||||
}
|
||||
body,
|
||||
.mobile-chat,
|
||||
.van-sticky,
|
||||
.van-nav-bar,
|
||||
.van-list,
|
||||
.message-list-box
|
||||
.van-cell,
|
||||
.chat-box,
|
||||
.van-cell-group,
|
||||
.van-field {
|
||||
background-color #1c1c1c !important
|
||||
color #fff !important
|
||||
}
|
||||
.chat-box-wrapper {
|
||||
position fixed
|
||||
bottom 0
|
||||
width 100%
|
||||
background-color #1c1c1c
|
||||
}
|
||||
.icon-box .van-icon,
|
||||
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title,
|
||||
.mobile-chat .van-nav-bar__title .van-cell__title,
|
||||
.mobile-chat .van-nav-bar__right .van-icon {
|
||||
color #fff !important
|
||||
}
|
||||
.mobile-chat {
|
||||
.van-nav-bar__title {
|
||||
.van-dropdown-menu__title {
|
||||
margin-right 15px
|
||||
}
|
||||
.van-cell__title {
|
||||
text-align left
|
||||
}
|
||||
}
|
||||
.chat-list-wrapper {
|
||||
position fixed
|
||||
top 50px
|
||||
bottom 60px
|
||||
width 100vw
|
||||
overflow-y scroll
|
||||
}
|
||||
.van-nav-bar__right {
|
||||
.van-icon {
|
||||
font-size 20px
|
||||
}
|
||||
}
|
||||
}
|
||||
.mobile-chat {
|
||||
.message-list-box {
|
||||
padding-top 50px
|
||||
padding-bottom 10px
|
||||
overflow-x auto
|
||||
background #F5F5F5;
|
||||
|
||||
.van-cell {
|
||||
background none
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-box-wrapper {
|
||||
.van-sticky {
|
||||
.van-cell-group--inset {
|
||||
margin 0
|
||||
|
||||
.van-cell {
|
||||
padding 10px
|
||||
|
||||
.icon-box {
|
||||
.van-icon {
|
||||
font-size 24px
|
||||
margin-left 10px
|
||||
}
|
||||
}
|
||||
|
||||
.button-voice {
|
||||
padding 0 2px
|
||||
|
||||
.el-icon {
|
||||
font-size 24px
|
||||
}
|
||||
height 30px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.van-nav-bar__title {
|
||||
.van-dropdown-menu__title {
|
||||
margin-right 10px
|
||||
}
|
||||
|
||||
.van-cell__title {
|
||||
text-align left
|
||||
}
|
||||
}
|
||||
|
||||
.van-nav-bar__right {
|
||||
.van-icon {
|
||||
font-size 20px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.van-overlay {
|
||||
.mic-wrapper {
|
||||
display flex
|
||||
height 100vh
|
||||
justify-content center
|
||||
align-items center
|
||||
flex-flow column
|
||||
}
|
||||
}
|
||||
|
||||
.van-theme-dark {
|
||||
.mobile-chat {
|
||||
.message-list-box {
|
||||
background #232425;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
.custom-scroll ::-webkit-scrollbar {
|
||||
width: 8px; /* 滚动条宽度 */
|
||||
}
|
||||
.custom-scroll ::-webkit-scrollbar-track {
|
||||
background-color: #282c34;
|
||||
}
|
||||
.custom-scroll ::-webkit-scrollbar-thumb {
|
||||
background-color: #444;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.custom-scroll ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
.custom-scroll {
|
||||
/* 修改滚动条的颜色 */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 修改滚动条轨道的背景颜色 */
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #282C34;
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #444444;
|
||||
border-radius 8px
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块的悬停颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #666666;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
.my-tabbar {
|
||||
background-color: #171717;
|
||||
box-shadow: -3px 4px 16px #000;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.banner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #171717;
|
||||
border-bottom-right-radius: 10px;
|
||||
box-shadow: 0 -4px 13px #000;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 2 /* Add z-index */;
|
||||
}
|
||||
.banner-title {
|
||||
color: #2cc995;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 -4px 13px #000;
|
||||
}
|
||||
.mobile-home {
|
||||
background-color: #171717;
|
||||
}
|
||||
.mobile-home .van-tabbar before {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
background-color: #171717;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
z-index: -1;
|
||||
}
|
||||
.mobile-home .van-tabbar.activeTab before {
|
||||
opacity: 1;
|
||||
}
|
||||
.router-view {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
.my-tabbar {
|
||||
background-color #171717
|
||||
box-shadow -3px 4px 16px #000
|
||||
box-sizing border-box
|
||||
}
|
||||
.banner {
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
background-color #171717
|
||||
border-bottom-right-radius 10px
|
||||
box-shadow 0 -4px 13px #000
|
||||
height 40px
|
||||
width 100%
|
||||
position fixed
|
||||
z-index 2 /* Add z-index */
|
||||
}
|
||||
.banner-title {
|
||||
color #2CC995
|
||||
font-size 20px
|
||||
font-weight bold
|
||||
text-shadow 0 -4px 13px #000
|
||||
}
|
||||
.mobile-home {
|
||||
background-color #171717
|
||||
}
|
||||
.mobile-home .van-tabbar before {
|
||||
position absolute
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
height 50px
|
||||
background-color #171717
|
||||
border-top-left-radius 10px
|
||||
border-top-right-radius 10px
|
||||
opacity 0
|
||||
transition opacity 0.5s
|
||||
z-index -1
|
||||
}
|
||||
.mobile-home .van-tabbar.activeTab before {
|
||||
opacity 1
|
||||
}
|
||||
.router-view {
|
||||
margin-bottom 50px
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
}
|
||||
.page-sd .inner {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .sd-box {
|
||||
margin: 10px;
|
||||
background-color: #282828;
|
||||
min-width: 92%;
|
||||
max-width: 92%;
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-sd .inner .sd-box h2 {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .sd-box ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params {
|
||||
margin-top: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-icon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-input__suffix-inner .el-icon {
|
||||
top: 0;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content .el-icon,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner .el-icon {
|
||||
margin-left: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line.pt {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn {
|
||||
padding: 10px 15px 0 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button span {
|
||||
color: #2d3a4b;
|
||||
}
|
||||
.page-sd .inner .el-form .el-form-item__label {
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item {
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image img {
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale {
|
||||
max-height: 100%;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale img {
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog {
|
||||
background-color: #1a1b1e;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0 0 0 15px !important;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
|
||||
font-size: 60px;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
|
||||
background-color: #25262b;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
|
||||
background-color: #35363b;
|
||||
padding: 10px;
|
||||
color: #999;
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
|
||||
display: flex;
|
||||
width:100%;
|
||||
color: #a5a5a5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #35363b;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
|
||||
padding: 20px 0 10px 0;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .mj-list-item-prompt .el-icon {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.sd-box {
|
||||
background-color #282828
|
||||
min-width 92%
|
||||
max-width 92%
|
||||
padding 0px
|
||||
border-radius 0px
|
||||
color #ffffff;
|
||||
font-size 14px
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
font-size 20px
|
||||
text-align center
|
||||
color #fff
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sd-params {
|
||||
margin-top 10px
|
||||
overflow auto
|
||||
|
||||
|
||||
.param-line {
|
||||
padding 0 10px
|
||||
|
||||
.el-icon {
|
||||
position relative
|
||||
top 3px
|
||||
}
|
||||
|
||||
.el-input__suffix-inner {
|
||||
.el-icon {
|
||||
top 0
|
||||
}
|
||||
}
|
||||
|
||||
.grid-content
|
||||
.form-item-inner {
|
||||
display flex
|
||||
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
margin-top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.param-line.pt {
|
||||
padding-top 5px
|
||||
padding-bottom 5px
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding 10px 15px 0 15px
|
||||
text-align center
|
||||
|
||||
.el-button {
|
||||
width 100%
|
||||
|
||||
span {
|
||||
color #2D3A4B
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-form {
|
||||
.el-form-item__label {
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
@import "mobile/task-mobile-list.styl"
|
||||
}
|
||||
|
||||
@import "sd-task-mobile-dialog.styl"
|
||||
|
||||
|
||||
.mj-list-item-prompt {
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
cursor pointer
|
||||
position relative
|
||||
top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
138
web/src/assets/css/mobile/image-mj.css
Normal file
138
web/src/assets/css/mobile/image-mj.css
Normal file
@@ -0,0 +1,138 @@
|
||||
.mobile-mj .content .text-line {
|
||||
padding: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.mobile-mj .content .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-mj .content .text-line .van-row .van-col .rate .icon {
|
||||
text-align: center;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-row .van-col .rate .icon .van-image {
|
||||
max-width: 20px;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-row .van-col .rate .text {
|
||||
text-align: center;
|
||||
color: #555;
|
||||
}
|
||||
.mobile-mj .content .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-mj .content .text-line .van-row .van-col .model .icon {
|
||||
text-align: center;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-row .van-col .model .icon .van-image {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-row .van-col .model .text {
|
||||
text-align: center;
|
||||
color: #555;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-row .van-col .active {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-button {
|
||||
position: relative;
|
||||
}
|
||||
.mobile-mj .content .text-line .van-button .van-tag {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content {
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
.mobile-mj .content .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 {
|
||||
min-height: 100px;
|
||||
}
|
||||
.mobile-mj .content .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-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress .van-circle__text {
|
||||
color: #fff;
|
||||
}
|
||||
.mobile-mj .content .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-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon {
|
||||
text-align: center;
|
||||
}
|
||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon .iconfont {
|
||||
font-size: 24px;
|
||||
}
|
||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .text {
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content {
|
||||
padding: 0;
|
||||
}
|
||||
.mobile-mj .content .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-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .opt .opt-btn {
|
||||
padding: 3px 10px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
margin: 3px 0;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .el-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .el-image .image-slot {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .el-image .image-slot .iconfont {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .upscale {
|
||||
height: 260px;
|
||||
width: 100%;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove .el-button {
|
||||
margin-left: 5px;
|
||||
height: auto;
|
||||
padding: 5px;
|
||||
}
|
||||
193
web/src/assets/css/mobile/image-mj.styl
Normal file
193
web/src/assets/css/mobile/image-mj.styl
Normal file
@@ -0,0 +1,193 @@
|
||||
.mobile-mj {
|
||||
.content {
|
||||
.text-line {
|
||||
padding 6px
|
||||
font-size 14px
|
||||
|
||||
.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
|
||||
|
||||
.icon {
|
||||
text-align center
|
||||
|
||||
.van-image {
|
||||
max-width 20px
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align center
|
||||
color #555555
|
||||
}
|
||||
}
|
||||
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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%
|
||||
|
||||
.opt {
|
||||
.opt-btn {
|
||||
padding 3px 10px
|
||||
text-align center
|
||||
border-radius 5px
|
||||
margin 3px 0
|
||||
display block
|
||||
cursor pointer
|
||||
background-color #4E5058
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width 100%
|
||||
height 200px
|
||||
|
||||
.image-slot {
|
||||
height 100%
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
|
||||
.iconfont {
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upscale {
|
||||
height 260px
|
||||
width 100%
|
||||
}
|
||||
|
||||
.remove {
|
||||
position absolute
|
||||
right 5px
|
||||
top 5px
|
||||
|
||||
.el-button {
|
||||
margin-left 5px
|
||||
height auto
|
||||
padding 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
}
|
||||
.page-sd .inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .inner .sd-box {
|
||||
background-color: #282828;
|
||||
min-width: 92%;
|
||||
max-width: 92%;
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-sd .inner .sd-box h2 {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .sd-box ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params {
|
||||
margin-top: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-icon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-input__suffix-inner .el-icon {
|
||||
top: 0;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content .el-icon,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner .el-icon {
|
||||
margin-left: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line.pt {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn {
|
||||
padding: 10px 15px 0 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button span {
|
||||
color: #2d3a4b;
|
||||
}
|
||||
.page-sd .inner .el-form .el-form-item__label {
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item {
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image img {
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale {
|
||||
max-height: 310px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale img {
|
||||
height: 310px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog {
|
||||
background-color: #1a1b1e;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0 0 0 15px !important;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
|
||||
font-size: 60px;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
|
||||
background-color: #25262b;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
|
||||
background-color: #35363b;
|
||||
padding: 10px;
|
||||
color: #999;
|
||||
overflow: auto;
|
||||
max-height: 100px;
|
||||
min-height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
|
||||
display: flex;
|
||||
width: 100px;
|
||||
color: #a5a5a5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #35363b;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
|
||||
padding: 30px 0 10px 0;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .mj-list-item-prompt .el-icon {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.sd-box {
|
||||
background-color #282828
|
||||
min-width 92%
|
||||
max-width 92%
|
||||
padding 0px
|
||||
border-radius 0px
|
||||
color #ffffff;
|
||||
font-size 14px
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
font-size 16px
|
||||
text-align center
|
||||
color #fff
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sd-params {
|
||||
margin-top 10px
|
||||
overflow auto
|
||||
|
||||
|
||||
.param-line {
|
||||
padding 0 10px
|
||||
|
||||
.el-icon {
|
||||
position relative
|
||||
top 3px
|
||||
}
|
||||
|
||||
.el-input__suffix-inner {
|
||||
.el-icon {
|
||||
top 0
|
||||
}
|
||||
}
|
||||
|
||||
.grid-content
|
||||
.form-item-inner {
|
||||
display flex
|
||||
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
margin-top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.param-line.pt {
|
||||
padding-top 5px
|
||||
padding-bottom 5px
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding 10px 15px 0 15px
|
||||
text-align center
|
||||
|
||||
.el-button {
|
||||
width 100%
|
||||
|
||||
span {
|
||||
color #2D3A4B
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-form {
|
||||
.el-form-item__label {
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
@import "./task-list.styl"
|
||||
}
|
||||
|
||||
@import "./sd-task-dialog.styl"
|
||||
|
||||
|
||||
.mj-list-item-prompt {
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
cursor pointer
|
||||
position relative
|
||||
top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
.page-invitation {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
}
|
||||
.page-invitation .inner {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
}
|
||||
.page-invitation .inner .center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-invitation .inner .title {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.page-invitation .inner .share-box .info {
|
||||
line-height: 1.5;
|
||||
border: 1px solid #444;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.page-invitation .inner .share-box .info strong {
|
||||
color: #f56c6c;
|
||||
}
|
||||
.page-invitation .inner .share-box .invite-qrcode {
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
.page-invitation .inner .share-box .invite-url {
|
||||
margin: 10px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border: 1px solid #444;
|
||||
border-radius: 10px;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.page-invitation .inner .share-box .invite-url span {
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
top: 0px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box {
|
||||
border-radius: 10px;
|
||||
padding: 10 0px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col {
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .iconfont {
|
||||
font-size: 30px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info {
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info .text,
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info .num {
|
||||
padding: 3px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info .num {
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .yellow {
|
||||
background-color: #fec;
|
||||
color: #d68f00;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .blue {
|
||||
background-color: #d6e4ff;
|
||||
color: #1062fe;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .green {
|
||||
background-color: #e7f8eb;
|
||||
color: #2d9f46;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
.page-invitation {
|
||||
display flex
|
||||
justify-content center
|
||||
background-color #1c1c1c
|
||||
height 100vh
|
||||
overflow-x hidden
|
||||
overflow-y visible
|
||||
.inner {
|
||||
max-width 100%
|
||||
width 100%
|
||||
color #fff
|
||||
.center {
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
}
|
||||
.title {
|
||||
color #fff
|
||||
text-align center
|
||||
font-size 16px
|
||||
font-weight bold
|
||||
}
|
||||
.share-box {
|
||||
.info {
|
||||
line-height 1.5
|
||||
border 1px solid #444444
|
||||
border-radius 10px
|
||||
padding 10px
|
||||
margin 10px
|
||||
background-color #1c1c1c
|
||||
strong {
|
||||
color #f56c6c
|
||||
}
|
||||
}
|
||||
.invite-qrcode {
|
||||
text-align center
|
||||
margin 15px
|
||||
}
|
||||
.invite-url {
|
||||
margin 10px
|
||||
padding 8px
|
||||
display flex
|
||||
justify-content space-between
|
||||
border 1px solid #444444
|
||||
border-radius 10px
|
||||
background-color #1c1c1c
|
||||
span {
|
||||
position relative
|
||||
font-size 14px
|
||||
font-family 'Microsoft YaHei', '微软雅黑', Arial, sans-serif
|
||||
top 0px
|
||||
}
|
||||
}
|
||||
}
|
||||
.invite-stats {
|
||||
padding 10px 10px
|
||||
.item-box {
|
||||
border-radius 10px
|
||||
padding 10 0px
|
||||
.el-col {
|
||||
height 80px
|
||||
display flex
|
||||
align-items center
|
||||
justify-content center
|
||||
.iconfont {
|
||||
font-size 30px
|
||||
}
|
||||
.item-info {
|
||||
font-size 14px
|
||||
.text, .num {
|
||||
padding 3px 0
|
||||
text-align center
|
||||
}
|
||||
.num {
|
||||
font-size 14px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.yellow {
|
||||
background-color #ffeecc
|
||||
color #D68F00
|
||||
}
|
||||
.blue {
|
||||
background-color #D6E4FF
|
||||
color #1062FE
|
||||
}
|
||||
.green {
|
||||
background-color #E7F8EB
|
||||
color #2D9F46
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
.member {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .count-down {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .pay-qrcode {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .pay-qrcode .el-image {
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .tip {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .tip .el-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .tip .text {
|
||||
font-size: 16px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.member .el-dialog .el-dialog__body .pay-container .tip.success {
|
||||
color: #07c160;
|
||||
}
|
||||
.member .title {
|
||||
text-align: center;
|
||||
background-color: #1c1c1c;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.member .inner {
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
}
|
||||
.member .inner .user-profile {
|
||||
padding: 0 20px 0 20px;
|
||||
background-color: #1c1c1c;
|
||||
color: #fff;
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
height: 91vh;
|
||||
}
|
||||
.member .inner .user-profile .el-form-item__label {
|
||||
color: #fff;
|
||||
justify-content: start;
|
||||
}
|
||||
.member .inner .user-profile .user-opt .el-col {
|
||||
padding: 8px;
|
||||
}
|
||||
.member .inner .user-profile .user-opt .el-col .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.member .inner .product-box .info {
|
||||
padding: 0 20px 0 20px;
|
||||
}
|
||||
.member .inner .product-box .info .el-alert__description {
|
||||
font-size: 14px !important;
|
||||
color: #1c1c1c;
|
||||
margin: 0;
|
||||
}
|
||||
.member .inner .product-box .list-box {
|
||||
padding: 0 20px 0 5px;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item {
|
||||
border: 1px solid #5f5f5f;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease /* 添加过渡效果 */;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .image-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .image-container .el-image {
|
||||
width: 50px;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .image-container .el-image .el-image__inner {
|
||||
border-radius: 10px;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-title {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-title .name {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #2cc995;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info .info-line {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 2px 0;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info .info-line .label {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info .info-line .price,
|
||||
.member .inner .product-box .list-box .product-item .product-info .info-line .expire {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: right;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info .info-line .price {
|
||||
color: #f56c6c;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info .info-line .expire {
|
||||
color: #409eff;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item .product-info .pay-way {
|
||||
padding: 5px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.member .inner .product-box .list-box .product-item hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6) /* 添加阴影效果 */;
|
||||
transform: translateY(-10px) /* 向上移动10像素 */;
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
.app-background {
|
||||
background-color #1c1c1c
|
||||
height 100vh
|
||||
}
|
||||
.member {
|
||||
background-color #1c1c1c
|
||||
height 100vh
|
||||
.el-dialog {
|
||||
.el-dialog__body {
|
||||
padding-top 5px
|
||||
.pay-container {
|
||||
.count-down {
|
||||
display flex
|
||||
justify-content center
|
||||
}
|
||||
.pay-qrcode {
|
||||
display flex
|
||||
justify-content center
|
||||
.el-image {
|
||||
width 280px
|
||||
height 280px
|
||||
}
|
||||
}
|
||||
.tip {
|
||||
display flex
|
||||
justify-content center
|
||||
.el-icon {
|
||||
font-size 20px
|
||||
}
|
||||
.text {
|
||||
font-size 16px
|
||||
margin-left 10px
|
||||
}
|
||||
}
|
||||
.tip.success {
|
||||
color #07c160
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.title {
|
||||
text-align center
|
||||
background-color #1c1c1c
|
||||
font-size 16px
|
||||
color #ffffff
|
||||
padding 5px
|
||||
font-weight bold
|
||||
}
|
||||
.inner {
|
||||
color #ffffff
|
||||
overflow-x hidden
|
||||
overflow-y visible
|
||||
.user-profile {
|
||||
padding 0 20px 0 20px
|
||||
background-color #1c1c1c
|
||||
color #ffffff
|
||||
border-radius 10px
|
||||
width 100%
|
||||
height 91vh
|
||||
.el-form-item__label {
|
||||
color #ffffff
|
||||
justify-content start
|
||||
}
|
||||
.user-opt {
|
||||
.el-col {
|
||||
padding 8px
|
||||
.el-button {
|
||||
width 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.product-box {
|
||||
.info {
|
||||
.el-alert__description {
|
||||
font-size 14px !important
|
||||
color #1c1c1c
|
||||
margin 0
|
||||
}
|
||||
padding 0 20px 0 20px
|
||||
}
|
||||
.list-box {
|
||||
padding 0 20px 0 5px
|
||||
.product-item {
|
||||
border 1px solid #5f5f5f
|
||||
border-radius 6px
|
||||
overflow hidden
|
||||
cursor pointer
|
||||
transition all 0.3s ease /* 添加过渡效果 */
|
||||
.image-container {
|
||||
display flex
|
||||
justify-content center
|
||||
.el-image {
|
||||
width 50px
|
||||
.el-image__inner {
|
||||
border-radius 10px
|
||||
}
|
||||
}
|
||||
}
|
||||
.product-title {
|
||||
display flex
|
||||
padding 5px
|
||||
.name {
|
||||
width 100%
|
||||
text-align center
|
||||
font-size 16px
|
||||
font-weight bold
|
||||
color #2cc995
|
||||
}
|
||||
}
|
||||
.product-info {
|
||||
padding 10px 20px
|
||||
font-size 14px
|
||||
color #999999
|
||||
.info-line {
|
||||
display flex
|
||||
width 100%
|
||||
padding 2px 0
|
||||
.label {
|
||||
display flex
|
||||
width 100%
|
||||
}
|
||||
.price, .expire {
|
||||
display flex
|
||||
width 100%
|
||||
justify-content right
|
||||
}
|
||||
.price {
|
||||
color #f56c6c
|
||||
}
|
||||
.expire {
|
||||
color #409eff
|
||||
}
|
||||
}
|
||||
.pay-way {
|
||||
padding 5px 0
|
||||
display flex
|
||||
justify-content space-between
|
||||
}
|
||||
}
|
||||
& hover {
|
||||
box-shadow 0 0 10px rgba(71, 255, 241, 0.6) /* 添加阴影效果 */
|
||||
transform translateY(-10px) /* 向上移动10像素 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
.el-overlay-dialog .el-dialog {
|
||||
background-color: #1a1b1e;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0 0 0 15px !important;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row {
|
||||
width: 100%;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
|
||||
font-size: 60px;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
|
||||
background-color: #25262b;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
|
||||
width: 100%;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
|
||||
background-color: #35363b;
|
||||
padding: 10px;
|
||||
color: #999;
|
||||
overflow: auto;
|
||||
max-height: 100px;
|
||||
min-height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
|
||||
display: flex;
|
||||
width: 100px;
|
||||
color: #a5a5a5;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #35363b;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
|
||||
padding: 30px 0 10px 0;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
.el-overlay-dialog {
|
||||
.el-dialog {
|
||||
background-color #1a1b1e
|
||||
|
||||
.el-dialog__header {
|
||||
.el-dialog__title {
|
||||
color #F5F5F5
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding 0 0 0 15px !important
|
||||
display flex
|
||||
height 100%
|
||||
|
||||
.el-row {
|
||||
width 100%
|
||||
|
||||
.img-container {
|
||||
display flex
|
||||
justify-content center
|
||||
|
||||
.image-slot {
|
||||
display flex
|
||||
height 100vh
|
||||
align-items center
|
||||
justify-content center
|
||||
|
||||
.el-icon {
|
||||
font-size 60px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-info {
|
||||
background-color #25262b
|
||||
padding 1rem 1.5rem
|
||||
|
||||
.info-line {
|
||||
width 100%
|
||||
|
||||
.prompt {
|
||||
background-color #35363b
|
||||
padding 10px
|
||||
color #999999
|
||||
overflow auto
|
||||
max-height 100px
|
||||
min-height 50px
|
||||
|
||||
position relative
|
||||
|
||||
.el-icon {
|
||||
position absolute
|
||||
right 10px
|
||||
bottom 10px
|
||||
cursor pointer
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin-top 10px
|
||||
display flex
|
||||
|
||||
label {
|
||||
display flex
|
||||
width 100px
|
||||
color #a5a5a5
|
||||
}
|
||||
|
||||
.item-value {
|
||||
display flex
|
||||
width 100%
|
||||
background-color #35363b
|
||||
padding 2px 5px
|
||||
border-radius 5px
|
||||
color #F5F5F5
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.copy-params {
|
||||
padding 30px 0 10px 0
|
||||
|
||||
.el-button {
|
||||
width 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// end el-row
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
.task-list-box {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.task-list-box .running-job-list .job-item {
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.task-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.task-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.task-list-box .el-image img {
|
||||
height: 240px;
|
||||
}
|
||||
.task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.task-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.task-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.task-list-box .el-image.upscale {
|
||||
max-height: 310px;
|
||||
}
|
||||
.task-list-box .el-image.upscale img {
|
||||
height: 310px;
|
||||
}
|
||||
.task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
.task-list-box {
|
||||
width 100%
|
||||
padding 5px
|
||||
color #ffffff
|
||||
overflow-x hidden
|
||||
|
||||
|
||||
.running-job-list {
|
||||
.job-item {
|
||||
//border: 1px solid #454545;
|
||||
width: 100%;
|
||||
padding 2px
|
||||
background-color #555555
|
||||
|
||||
.job-item-inner {
|
||||
position relative
|
||||
height 100%
|
||||
overflow hidden
|
||||
|
||||
.progress {
|
||||
position absolute
|
||||
width 100%
|
||||
height 100%
|
||||
top 0
|
||||
left 0
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
|
||||
span {
|
||||
font-size 20px
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.finish-job-list {
|
||||
.job-item {
|
||||
width 100%
|
||||
height 100%
|
||||
border 1px solid #666666
|
||||
padding 6px
|
||||
overflow hidden
|
||||
border-radius 6px
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
|
||||
.opt {
|
||||
.opt-line {
|
||||
margin 6px 0
|
||||
|
||||
ul {
|
||||
display flex
|
||||
flex-flow row
|
||||
|
||||
li {
|
||||
margin-right 6px
|
||||
|
||||
a {
|
||||
padding 3px 0
|
||||
width 40px
|
||||
text-align center
|
||||
border-radius 5px
|
||||
display block
|
||||
cursor pointer
|
||||
background-color #4E5058
|
||||
color #ffffff
|
||||
|
||||
&:hover {
|
||||
background-color #6D6F78
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-prompt {
|
||||
font-size 20px
|
||||
cursor pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.animate {
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width 100%
|
||||
height 100%
|
||||
overflow visible
|
||||
|
||||
img {
|
||||
height 240px
|
||||
}
|
||||
|
||||
.el-image-viewer__wrapper {
|
||||
img {
|
||||
width auto
|
||||
height auto
|
||||
}
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
display flex
|
||||
flex-flow column
|
||||
justify-content center
|
||||
align-items center
|
||||
height 100%
|
||||
min-height 200px
|
||||
color #ffffff
|
||||
height 240px
|
||||
|
||||
.iconfont {
|
||||
font-size 50px
|
||||
margin-bottom 10px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-image.upscale {
|
||||
max-height 310px
|
||||
|
||||
img {
|
||||
height 310px
|
||||
}
|
||||
|
||||
.el-image-viewer__wrapper {
|
||||
img {
|
||||
width auto
|
||||
height auto
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,6 @@
|
||||
background-color: #47FFF1;
|
||||
}
|
||||
|
||||
.title-tabs .el-tabs__content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.el-textarea {
|
||||
--el-input-focus-border-color: #47FFF1;
|
||||
}
|
||||
@@ -65,31 +61,63 @@
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
.img-uploader {
|
||||
.el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width 300px;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
margin-bottom: 20px;
|
||||
// 图片上传样式
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
.img-inline {
|
||||
display flex
|
||||
|
||||
.img-uploader {
|
||||
.el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width 120px;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.uploader-icon {
|
||||
font-size: 28px
|
||||
color: #8c939d
|
||||
width 100%
|
||||
height: 120px
|
||||
text-align: center
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-icon.uploader-icon {
|
||||
font-size: 28px
|
||||
color: #8c939d
|
||||
width 100%
|
||||
height: 120px
|
||||
text-align: center
|
||||
.img-list-box {
|
||||
display flex
|
||||
|
||||
.img-item {
|
||||
width 120px
|
||||
position relative
|
||||
margin-right 10px
|
||||
|
||||
.el-image {
|
||||
width 120px
|
||||
height 120px
|
||||
border-radius 5px
|
||||
}
|
||||
|
||||
.el-button {
|
||||
position absolute
|
||||
right 5px
|
||||
top 5px
|
||||
width 20px
|
||||
height 20px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交按钮
|
||||
|
||||
.submit-btn {
|
||||
display flex
|
||||
margin: 20px 0
|
||||
@@ -105,154 +133,159 @@
|
||||
align-items center
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.running-job-list {
|
||||
.job-item {
|
||||
//border: 1px solid #454545;
|
||||
width: 100%;
|
||||
padding 2px
|
||||
background-color #555555
|
||||
|
||||
.job-item-inner {
|
||||
position relative
|
||||
height 100%
|
||||
overflow hidden
|
||||
// 任务列表
|
||||
|
||||
.progress {
|
||||
position absolute
|
||||
width 100%
|
||||
height 100%
|
||||
top 0
|
||||
left 0
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
.job-list-box {
|
||||
.running-job-list {
|
||||
.job-item {
|
||||
//border: 1px solid #454545;
|
||||
width: 100%;
|
||||
padding 2px
|
||||
background-color #555555
|
||||
|
||||
span {
|
||||
font-size 20px
|
||||
color #ffffff
|
||||
.job-item-inner {
|
||||
position relative
|
||||
height 100%
|
||||
overflow hidden
|
||||
|
||||
.progress {
|
||||
position absolute
|
||||
width 100%
|
||||
height 100%
|
||||
top 0
|
||||
left 0
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
|
||||
span {
|
||||
font-size 20px
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.finish-job-list {
|
||||
.job-item {
|
||||
width 100%
|
||||
height 100%
|
||||
border 1px solid #666666
|
||||
padding 6px
|
||||
overflow hidden
|
||||
border-radius 6px
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
position relative
|
||||
.finish-job-list {
|
||||
.job-item {
|
||||
width 100%
|
||||
height 100%
|
||||
border 1px solid #666666
|
||||
padding 6px
|
||||
overflow hidden
|
||||
border-radius 6px
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
position relative
|
||||
|
||||
.opt {
|
||||
.opt-line {
|
||||
margin 6px 0
|
||||
.opt {
|
||||
.opt-line {
|
||||
margin 6px 0
|
||||
|
||||
ul {
|
||||
display flex
|
||||
flex-flow row
|
||||
ul {
|
||||
display flex
|
||||
flex-flow row
|
||||
|
||||
li {
|
||||
margin-right 6px
|
||||
li {
|
||||
margin-right 6px
|
||||
|
||||
a {
|
||||
padding 3px 0
|
||||
width 40px
|
||||
text-align center
|
||||
border-radius 5px
|
||||
display block
|
||||
cursor pointer
|
||||
background-color #4E5058
|
||||
color #ffffff
|
||||
a {
|
||||
padding 3px 0
|
||||
width 40px
|
||||
text-align center
|
||||
border-radius 5px
|
||||
display block
|
||||
cursor pointer
|
||||
background-color #4E5058
|
||||
color #ffffff
|
||||
|
||||
&:hover {
|
||||
background-color #6D6F78
|
||||
&:hover {
|
||||
background-color #6D6F78
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-prompt {
|
||||
font-size 20px
|
||||
cursor pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-prompt {
|
||||
font-size 20px
|
||||
cursor pointer
|
||||
.remove {
|
||||
display none
|
||||
position absolute
|
||||
right 10px
|
||||
top 10px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.remove {
|
||||
display block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.animate {
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.el-image {
|
||||
width 100%
|
||||
height 100%
|
||||
overflow visible
|
||||
|
||||
img {
|
||||
height 240px
|
||||
}
|
||||
|
||||
.el-image-viewer__wrapper {
|
||||
img {
|
||||
width auto
|
||||
height auto
|
||||
}
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
display flex
|
||||
flex-flow column
|
||||
justify-content center
|
||||
align-items center
|
||||
min-height 200px
|
||||
color #ffffff
|
||||
height 240px
|
||||
|
||||
.iconfont {
|
||||
font-size 50px
|
||||
margin-bottom 10px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remove {
|
||||
display none
|
||||
position absolute
|
||||
right 10px
|
||||
top 10px
|
||||
}
|
||||
.el-image.upscale {
|
||||
max-height 310px
|
||||
|
||||
&:hover{
|
||||
.remove {
|
||||
display block
|
||||
img {
|
||||
height 310px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.animate {
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width 100%
|
||||
height 100%
|
||||
overflow visible
|
||||
|
||||
img {
|
||||
height 240px
|
||||
}
|
||||
|
||||
.el-image-viewer__wrapper {
|
||||
img {
|
||||
width auto
|
||||
height auto
|
||||
}
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
display flex
|
||||
flex-flow column
|
||||
justify-content center
|
||||
align-items center
|
||||
height 100%
|
||||
min-height 200px
|
||||
color #ffffff
|
||||
height 240px
|
||||
|
||||
.iconfont {
|
||||
font-size 50px
|
||||
margin-bottom 10px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-image.upscale {
|
||||
max-height 310px
|
||||
|
||||
img {
|
||||
height 310px
|
||||
}
|
||||
|
||||
.el-image-viewer__wrapper {
|
||||
img {
|
||||
width auto
|
||||
height auto
|
||||
.el-image-viewer__wrapper {
|
||||
img {
|
||||
width auto
|
||||
height auto
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4125778 */
|
||||
src: url('iconfont.woff2?t=1705615887594') format('woff2'),
|
||||
url('iconfont.woff?t=1705615887594') format('woff'),
|
||||
url('iconfont.ttf?t=1705615887594') format('truetype');
|
||||
src: url('iconfont.woff2?t=1708054962140') format('woff2'),
|
||||
url('iconfont.woff?t=1708054962140') format('woff'),
|
||||
url('iconfont.ttf?t=1708054962140') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-prompt:before {
|
||||
content: "\e6ce";
|
||||
}
|
||||
|
||||
.icon-share-bold:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "8017627",
|
||||
"name": "prompt",
|
||||
"font_class": "prompt",
|
||||
"unicode": "e6ce",
|
||||
"unicode_decimal": 59086
|
||||
},
|
||||
{
|
||||
"icon_id": "1132455",
|
||||
"name": "share-bold",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -122,10 +122,13 @@ export default {
|
||||
// ===============================================
|
||||
let mouseX = (navigator.vendor === 'Netscape') ? e.pageX : e.x + document.body.offsetTop
|
||||
let mouseY = (navigator.vendor === 'Netscape') ? e.pageY : e.y + document.body.offsetTop
|
||||
|
||||
if (this.calcPosType === 'screen') {
|
||||
mouseX = (navigator.vendor === 'Netscape') ? e.clientX : e.x
|
||||
mouseY = (navigator.vendor === 'Netscape') ? e.clientY : e.y
|
||||
// 兼容移动触摸事件
|
||||
if (e.touches && e.touches.length > 0) {
|
||||
mouseX = e.touches[0].clientX
|
||||
mouseY = e.touches[0].clientY
|
||||
} else {
|
||||
mouseX = e.clientX
|
||||
mouseY = e.clientY
|
||||
}
|
||||
|
||||
// 计算点击的相对位置
|
||||
|
||||
@@ -39,9 +39,13 @@
|
||||
effect="dark"
|
||||
:content="file.name"
|
||||
placement="top">
|
||||
<el-image :src="file.url" fit="fill" v-if="isImage(file.ext)" @click="insertURL(file.url)"/>
|
||||
<el-image :src="getFileIcon(file.ext)" fit="fill" v-else @click="insertURL(file.url)"/>
|
||||
<el-image :src="file.url" fit="cover" v-if="isImage(file.ext)" @click="insertURL(file.url)"/>
|
||||
<el-image :src="getFileIcon(file.ext)" fit="cover" v-else @click="insertURL(file.url)"/>
|
||||
</el-tooltip>
|
||||
|
||||
<div class="opt">
|
||||
<el-button type="danger" size="small" :icon="Delete" @click="removeFile(file)" circle/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -54,8 +58,8 @@
|
||||
import {ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {PictureFilled, Plus} from "@element-plus/icons-vue";
|
||||
import {isImage} from "@/utils/libs";
|
||||
import {Delete, PictureFilled, Plus} from "@element-plus/icons-vue";
|
||||
import {isImage, removeArrayItem} from "@/utils/libs";
|
||||
|
||||
const props = defineProps({
|
||||
userId: String,
|
||||
@@ -103,6 +107,17 @@ const afterRead = (file) => {
|
||||
})
|
||||
};
|
||||
|
||||
const removeFile = (file) => {
|
||||
httpGet('/api/upload/remove?id=' + file.id).then(() => {
|
||||
fileList.value = removeArrayItem(fileList.value, file, (v1, v2) => {
|
||||
return v1.id === v2.id
|
||||
})
|
||||
ElMessage.success("文件删除成功!")
|
||||
}).catch((e) => {
|
||||
ElMessage.error('文件删除失败:' + e.message)
|
||||
})
|
||||
}
|
||||
|
||||
const insertURL = (url) => {
|
||||
show.value = false
|
||||
emits('selected', url)
|
||||
@@ -129,6 +144,7 @@ const insertURL = (url) => {
|
||||
|
||||
.grid-content {
|
||||
margin-bottom 10px
|
||||
position relative
|
||||
|
||||
.avatar-uploader {
|
||||
width 100%
|
||||
@@ -145,6 +161,7 @@ const insertURL = (url) => {
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width 100%
|
||||
height 80px
|
||||
border 1px solid #ffffff
|
||||
border-radius 6px
|
||||
@@ -160,6 +177,19 @@ const insertURL = (url) => {
|
||||
color #20a0ff
|
||||
font-size 40px
|
||||
}
|
||||
|
||||
.opt {
|
||||
display none
|
||||
position absolute
|
||||
top 5px
|
||||
right 5px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.opt {
|
||||
display block
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ onMounted(() => {
|
||||
padding: 5px 10px;
|
||||
background-color: #98E165;
|
||||
color #444444
|
||||
font-size: 16px
|
||||
font-size: 14px
|
||||
border-radius: 5px
|
||||
line-height 1.5
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="chat-item">
|
||||
<div class="triangle"></div>
|
||||
<div class="content-box">
|
||||
<div ref="contentRef" :data-clipboard-text="orgContent" class="content" v-html="content"></div>
|
||||
<div :data-clipboard-text="orgContent" class="content content-mobile" v-html="content"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -34,17 +34,6 @@ const props = defineProps({
|
||||
default: '/images/gpt-icon.png',
|
||||
}
|
||||
});
|
||||
|
||||
const contentRef = ref(null)
|
||||
onMounted(() => {
|
||||
const clipboard = new Clipboard(contentRef.value);
|
||||
clipboard.on('success', () => {
|
||||
showNotify({type: 'success', message: '复制成功', duration: 1000})
|
||||
})
|
||||
clipboard.on('error', () => {
|
||||
showNotify({type: 'danger', message: '复制失败', duration: 2000})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@@ -95,7 +84,7 @@ onMounted(() => {
|
||||
padding: 5px 10px;
|
||||
color #444444
|
||||
background-color: #ffffff;
|
||||
font-size: 16px
|
||||
font-size: 14px
|
||||
border-radius: 5px;
|
||||
|
||||
p:last-child {
|
||||
@@ -118,6 +107,77 @@ onMounted(() => {
|
||||
max-width 100%
|
||||
}
|
||||
}
|
||||
|
||||
.code-container {
|
||||
position relative
|
||||
|
||||
.hljs {
|
||||
border-radius 10px
|
||||
line-height 1.5
|
||||
}
|
||||
|
||||
.copy-code-mobile {
|
||||
position: absolute;
|
||||
right 10px
|
||||
top 10px
|
||||
cursor pointer
|
||||
font-size 12px
|
||||
color #c1c1c1
|
||||
|
||||
&:hover {
|
||||
color #20a0ff
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.lang-name {
|
||||
display none
|
||||
position absolute;
|
||||
right 10px
|
||||
bottom 50px
|
||||
padding 2px 6px 4px 6px
|
||||
background-color #444444
|
||||
border-radius 10px
|
||||
color #00e0e0
|
||||
}
|
||||
|
||||
|
||||
// 设置表格边框
|
||||
|
||||
table {
|
||||
width 100%
|
||||
margin-bottom 1rem
|
||||
color #212529
|
||||
border-collapse collapse;
|
||||
border 1px solid #dee2e6;
|
||||
background-color #ffffff
|
||||
|
||||
thead {
|
||||
th {
|
||||
border 1px solid #dee2e6
|
||||
vertical-align: bottom
|
||||
border-bottom: 2px solid #dee2e6
|
||||
padding 10px
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
border 1px solid #dee2e6
|
||||
padding 10px
|
||||
}
|
||||
}
|
||||
|
||||
// 代码快
|
||||
|
||||
blockquote {
|
||||
margin 0
|
||||
background-color: #ebfffe;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-left: 0.5rem solid;
|
||||
border-color: #026863;
|
||||
color: #2c3e50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,24 +5,38 @@ import 'vant/lib/index.css';
|
||||
import App from './App.vue'
|
||||
import {createPinia} from "pinia";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Cell,
|
||||
CellGroup,
|
||||
Circle,
|
||||
Col,
|
||||
Collapse,
|
||||
CollapseItem,
|
||||
ConfigProvider,
|
||||
Dialog,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
Empty,
|
||||
Field,
|
||||
Form,
|
||||
Grid,
|
||||
GridItem,
|
||||
Icon,
|
||||
Image,
|
||||
ImagePreview,
|
||||
Lazyload,
|
||||
List,
|
||||
Loading,
|
||||
NavBar,
|
||||
Notify,
|
||||
Overlay,
|
||||
Picker,
|
||||
Popup,
|
||||
Row,
|
||||
Search,
|
||||
ShareSheet,
|
||||
Slider,
|
||||
Sticky,
|
||||
SwipeCell,
|
||||
Switch,
|
||||
@@ -65,6 +79,20 @@ app.use(Switch);
|
||||
app.use(Uploader);
|
||||
app.use(Tag);
|
||||
app.use(V3waterfall)
|
||||
app.use(Overlay)
|
||||
app.use(Col)
|
||||
app.use(Row)
|
||||
app.use(Slider)
|
||||
app.use(Badge)
|
||||
app.use(Collapse);
|
||||
app.use(CollapseItem);
|
||||
app.use(Grid);
|
||||
app.use(GridItem);
|
||||
app.use(Empty);
|
||||
app.use(Circle);
|
||||
app.use(Loading);
|
||||
app.use(Lazyload);
|
||||
app.use(ImagePreview);
|
||||
app.use(router).use(ElementPlus).mount('#app')
|
||||
|
||||
|
||||
|
||||
@@ -159,32 +159,23 @@ const routes = [
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/mobile/chat/session',
|
||||
name: 'mobile-chat-session',
|
||||
component: () => import('@/views/mobile/ChatSession.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'mobile',
|
||||
path: '/mobile',
|
||||
meta: {title: 'ChatPuls-智能助手V3'},
|
||||
meta: {title: 'ChatPlus-智能助手V3'},
|
||||
component: () => import('@/views/mobile/Home.vue'),
|
||||
redirect: '/mobile/chat/list',
|
||||
redirect: '/mobile/chat',
|
||||
children: [
|
||||
{
|
||||
path: '/mobile/chat/list',
|
||||
name: 'mobile-chat-list',
|
||||
path: '/mobile/chat',
|
||||
name: 'mobile-chat',
|
||||
component: () => import('@/views/mobile/ChatList.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/imageSd',
|
||||
name: 'mobile-imageSd',
|
||||
component: () => import('@/views/mobile/ImageSd.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/apps',
|
||||
name: 'mobile-apps',
|
||||
component: () => import('@/views/mobile/Apps.vue'),
|
||||
path: '/mobile/mj',
|
||||
name: 'mobile-mj',
|
||||
component: () => import('@/views/mobile/ImageMj.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/profile',
|
||||
@@ -192,12 +183,23 @@ const routes = [
|
||||
component: () => import('@/views/mobile/Profile.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/invitation',
|
||||
name: 'mobile-invitation',
|
||||
component: () => import('@/views/mobile/Invitation.vue'),
|
||||
path: '/mobile/apps',
|
||||
name: 'mobile-app',
|
||||
component: () => import('@/views/mobile/ChatApps.vue'),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/mobile/chat/session',
|
||||
name: 'mobile-chat-session',
|
||||
component: () => import('@/views/mobile/ChatSession.vue'),
|
||||
},
|
||||
{
|
||||
path: '/mobile/chat/export',
|
||||
name: 'mobile-chat-export',
|
||||
component: () => import('@/views/mobile/ChatExport.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'test',
|
||||
path: '/test',
|
||||
|
||||
@@ -166,3 +166,40 @@ export function isImage(url) {
|
||||
return expr.test(url);
|
||||
}
|
||||
|
||||
export function processContent(content) {
|
||||
//process img url
|
||||
const linkRegex = /(https?:\/\/\S+)/g;
|
||||
const links = content.match(linkRegex);
|
||||
if (links) {
|
||||
for (let link of links) {
|
||||
if (isImage(link)) {
|
||||
const index = content.indexOf(link)
|
||||
if (content.substring(index - 1, 2) !== "]") {
|
||||
content = content.replace(link, "\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理引用块
|
||||
if (content.indexOf("\n") === -1) {
|
||||
return content
|
||||
}
|
||||
|
||||
const texts = content.split("\n")
|
||||
const lines = []
|
||||
for (let txt of texts) {
|
||||
lines.push(txt)
|
||||
if (txt.startsWith(">")) {
|
||||
lines.push("\n")
|
||||
}
|
||||
}
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
export function escapeHTML(html) {
|
||||
return html.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
:icon="item.icon"
|
||||
:created-at="dateFormat(item['created_at'])"
|
||||
:tokens="item['tokens']"
|
||||
:model="model"
|
||||
:content="item.content"/>
|
||||
<chat-reply v-else-if="item.type==='reply'"
|
||||
:icon="item.icon"
|
||||
|
||||
@@ -117,12 +117,27 @@
|
||||
<span>导出会话</span>
|
||||
</el-button>
|
||||
|
||||
<el-button type="warning" @click="showFeedbackDialog = true">
|
||||
<el-icon>
|
||||
<Promotion/>
|
||||
</el-icon>
|
||||
<span>意见反馈</span>
|
||||
</el-button>
|
||||
<el-tooltip class="box-item"
|
||||
effect="dark"
|
||||
content="部署文档"
|
||||
placement="bottom">
|
||||
<a href="https://ai.r9it.com/docs/install/" target="_blank">
|
||||
<el-button type="primary" circle>
|
||||
<i class="iconfont icon-book"></i>
|
||||
</el-button>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="box-item"
|
||||
effect="dark"
|
||||
content="项目源码"
|
||||
placement="bottom">
|
||||
<a href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">
|
||||
<el-button type="success" circle>
|
||||
<i class="iconfont icon-github"></i>
|
||||
</el-button>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -204,28 +219,6 @@
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<el-dialog
|
||||
v-model="showFeedbackDialog"
|
||||
:show-close="true"
|
||||
width="340px"
|
||||
title="意见反馈"
|
||||
>
|
||||
<el-alert type="info" :closable="false">
|
||||
<div style="font-size: 14px">
|
||||
如果您对本项目有任何改进意见,您可以通过 Github
|
||||
<el-link style="color: #f56c6c; font-weight: bold;"
|
||||
href="https://github.com/yangjian102621/chatgpt-plus/issues">
|
||||
提交改进意见
|
||||
</el-link>
|
||||
或者通过扫描下面的微信二维码加入 AI 技术交流群。
|
||||
</div>
|
||||
</el-alert>
|
||||
|
||||
<div style="text-align: center;padding-top: 10px;">
|
||||
<el-image :src="wechatCardURL"/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="showNotice"
|
||||
:show-close="true"
|
||||
@@ -256,7 +249,7 @@ import {
|
||||
ArrowDown,
|
||||
Check,
|
||||
Close,
|
||||
Delete,
|
||||
Delete, Document,
|
||||
Edit,
|
||||
Plus,
|
||||
Promotion,
|
||||
@@ -266,7 +259,16 @@ import {
|
||||
VideoPause
|
||||
} from '@element-plus/icons-vue'
|
||||
import 'highlight.js/styles/a11y-dark.css'
|
||||
import {dateFormat, isImage, isMobile, randString, removeArrayItem, UUID} from "@/utils/libs";
|
||||
import {
|
||||
dateFormat,
|
||||
escapeHTML,
|
||||
isImage,
|
||||
isMobile,
|
||||
processContent,
|
||||
randString,
|
||||
removeArrayItem,
|
||||
UUID
|
||||
} from "@/utils/libs";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import hl from "highlight.js";
|
||||
import {getSessionId, getUserToken, removeUserToken} from "@/store/session";
|
||||
@@ -299,11 +301,9 @@ const showConfigDialog = ref(false);
|
||||
const isLogin = ref(false)
|
||||
const showHello = ref(true)
|
||||
const textInput = ref(null)
|
||||
const showFeedbackDialog = ref(false)
|
||||
const showNotice = ref(false)
|
||||
const notice = ref("")
|
||||
const noticeKey = ref("SYSTEM_NOTICE")
|
||||
const wechatCardURL = ref("/images/wx.png")
|
||||
|
||||
if (isMobile()) {
|
||||
router.replace("/mobile")
|
||||
@@ -353,7 +353,6 @@ onMounted(() => {
|
||||
// 获取系统配置
|
||||
httpGet("/api/admin/config/get?key=system").then(res => {
|
||||
title.value = res.data.title
|
||||
wechatCardURL.value = res.data['wechat_card_url']
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
@@ -509,6 +508,8 @@ const removeChat = function (event, chat) {
|
||||
curOpt.value = 'remove';
|
||||
}
|
||||
|
||||
const latexPlugin = require('markdown-it-latex2img')
|
||||
const mathjaxPlugin = require('markdown-it-mathjax')
|
||||
const md = require('markdown-it')({
|
||||
breaks: true,
|
||||
html: true,
|
||||
@@ -533,6 +534,8 @@ const md = require('markdown-it')({
|
||||
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn}</pre>`
|
||||
}
|
||||
});
|
||||
md.use(latexPlugin)
|
||||
md.use(mathjaxPlugin)
|
||||
|
||||
// 创建 socket 连接
|
||||
const prompt = ref('');
|
||||
@@ -567,6 +570,19 @@ const connect = function (chat_id, role_id) {
|
||||
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/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
chatData.value = []; // 初始化聊天数据
|
||||
@@ -589,15 +605,8 @@ const connect = function (chat_id, role_id) {
|
||||
} else { // 加载聊天记录
|
||||
loadChatHistory(chat_id);
|
||||
}
|
||||
|
||||
// 发送心跳消息
|
||||
clearInterval(heartbeatHandle.value)
|
||||
heartbeatHandle.value = setInterval(() => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
@@ -717,7 +726,7 @@ const sendMessage = function () {
|
||||
type: "prompt",
|
||||
id: randString(32),
|
||||
icon: loginUser.value.avatar,
|
||||
content: md.render(processContent(prompt.value)),
|
||||
content: md.render(escapeHTML(processContent(prompt.value))),
|
||||
created_at: new Date().getTime(),
|
||||
});
|
||||
|
||||
@@ -797,37 +806,6 @@ const loadChatHistory = function (chatId) {
|
||||
})
|
||||
}
|
||||
|
||||
const processContent = (content) => {
|
||||
//process img url
|
||||
const linkRegex = /(https?:\/\/\S+)/g;
|
||||
const links = content.match(linkRegex);
|
||||
if (links) {
|
||||
for (let link of links) {
|
||||
if (isImage(link)) {
|
||||
const index = content.indexOf(link)
|
||||
if (content.substring(index - 1, 2) !== "]") {
|
||||
content = content.replace(link, "\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理引用块
|
||||
if (content.indexOf("\n") === -1) {
|
||||
return content
|
||||
}
|
||||
|
||||
const texts = content.split("\n")
|
||||
const lines = []
|
||||
for (let txt of texts) {
|
||||
lines.push(txt)
|
||||
if (txt.startsWith(">")) {
|
||||
lines.push("\n")
|
||||
}
|
||||
}
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
const stopGenerate = function () {
|
||||
showStopGenerate.value = false;
|
||||
httpGet("/api/chat/stop?session_id=" + getSessionId()).then(() => {
|
||||
@@ -846,7 +824,7 @@ const reGenerate = function () {
|
||||
icon: loginUser.value.avatar,
|
||||
content: md.render(text)
|
||||
});
|
||||
socket.value.send(text);
|
||||
socket.value.send(JSON.stringify({type: "chat", content: previousText.value}));
|
||||
}
|
||||
|
||||
const chatName = ref('')
|
||||
|
||||
@@ -165,160 +165,241 @@
|
||||
</div>
|
||||
<div class="task-list-box">
|
||||
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
||||
<h2>AI绘画</h2>
|
||||
<el-form>
|
||||
<el-tabs v-model="activeName" class="title-tabs">
|
||||
<el-tab-pane label="图生图(可选)" name="图生图">
|
||||
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;
|
||||
</div>
|
||||
<div class="param-line pt">
|
||||
<el-form-item label="">
|
||||
<template #default>
|
||||
<div class="form-item-inner flex-row items-center">
|
||||
<el-input v-model="params.img" size="small" placeholder="请输入图片地址或者上传图片"
|
||||
style="width: 300px;"/>
|
||||
<el-icon @click="params.img = ''" title="清空图片">
|
||||
<DeleteFilled/>
|
||||
</el-icon>
|
||||
<el-tooltip effect="light"
|
||||
content="垫图:以某张图片为底稿参考来创作绘画 <br/> 支持 PNG 和 JPG 格式图片"
|
||||
raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="param-line">
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||
:http-request="afterRead" style="--el-color-primary:#47fff1">
|
||||
<el-image v-if="params.img !== ''" :src="params.img" fit="cover"/>
|
||||
<el-icon v-else class="uploader-icon">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</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.weight" :max="1" :step="0.01"
|
||||
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
|
||||
<el-tooltip effect="light"
|
||||
content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
|
||||
raw-content placement="right">
|
||||
<el-icon style="margin-top: 9px">
|
||||
<InfoFilled/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="图生文(可选)" name="图生文">
|
||||
<div class="text">图生文功能正在紧锣密鼓开发中,敬请期待...</div>
|
||||
<!-- <div class="param-line pt">-->
|
||||
<!-- <el-empty image-size="100px" description="功能建设中"/>-->
|
||||
<!-- </div>-->
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="融图(可选)" name="融图">
|
||||
<div class="text">融图功能正在紧锣密鼓开发中,敬请期待...</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.8)">
|
||||
<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 class="extra-params">
|
||||
<el-form>
|
||||
<el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange">
|
||||
<el-tab-pane label="文生图(可选)" name="image">
|
||||
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" @click="translatePrompt">
|
||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||
<Refresh/>
|
||||
</el-icon>
|
||||
翻译
|
||||
</el-button>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
raw-content
|
||||
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
|
||||
placement="top-end"
|
||||
>
|
||||
<el-button type="success" @click="rewritePrompt">
|
||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||
<Refresh/>
|
||||
</el-icon>
|
||||
翻译并重写
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<div class="param-line pt">
|
||||
<el-form-item label="">
|
||||
<template #default>
|
||||
<div class="form-item-inner flex-row items-center">
|
||||
<el-input v-model="params.img" size="small" placeholder="请输入图片地址或者上传图片"
|
||||
style="width: 300px;"/>
|
||||
<el-icon @click="params.img = ''" title="清空图片">
|
||||
<DeleteFilled/>
|
||||
</el-icon>
|
||||
<el-tooltip effect="light"
|
||||
content="垫图:以某张图片为底稿参考来创作绘画 <br/> 支持 PNG 和 JPG 格式图片"
|
||||
raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="param-line">
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div class="img-item" v-for="imgURL in imgList">
|
||||
<el-image :src="imgURL" fit="cover"/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||
:http-request="uploadImg" style="--el-color-primary:#47fff1">
|
||||
<el-icon class="uploader-icon">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
</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.weight" :max="1" :step="0.01"
|
||||
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
|
||||
<el-tooltip effect="light"
|
||||
content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
|
||||
raw-content placement="right">
|
||||
<el-icon style="margin-top: 9px">
|
||||
<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>
|
||||
<el-button type="primary" @click="translatePrompt(false)" :disabled="loading">
|
||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||
<Refresh/>
|
||||
</el-icon>
|
||||
翻译
|
||||
</el-button>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
raw-content
|
||||
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
|
||||
placement="top-end"
|
||||
>
|
||||
<el-button type="success" @click="rewritePrompt" :disabled="loading">
|
||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||
<Refresh/>
|
||||
</el-icon>
|
||||
翻译并重写
|
||||
</el-button>
|
||||
</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="这里输入你的英文咒语,例如:A chinese girl walking in the middle of a cobblestone street"/>
|
||||
</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>
|
||||
<el-button type="primary" @click="translatePrompt(true)" :disabled="loading">
|
||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||
<Refresh/>
|
||||
</el-icon>
|
||||
翻译
|
||||
</el-button>
|
||||
</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="blend">
|
||||
<div class="text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用文生图功能</div>
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div class="img-item" v-for="imgURL in imgList">
|
||||
<el-image :src="imgURL" fit="cover"/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||
:http-request="uploadImg" style="--el-color-primary:#47fff1">
|
||||
<el-icon class="uploader-icon">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="换脸(可选)" name="swapFace">
|
||||
<div class="text">请上传两张有脸部的图片,用右边图片的脸替换左边图片的脸</div>
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div class="img-item" v-for="imgURL in imgList">
|
||||
<el-image :src="imgURL" fit="cover"/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||
:http-request="uploadImg" style="--el-color-primary:#47fff1">
|
||||
<el-icon class="uploader-icon">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="submit-btn">
|
||||
<el-button color="#47fff1" :dark="false" @click="generate" round>立即生成</el-button>
|
||||
<div class="text-info">
|
||||
<el-tag type="success">绘图可用额度:{{ imgCalls }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="param-line pt">
|
||||
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||
ref="promptRef"
|
||||
placeholder="这里输入你的英文咒语,例如:A chinese girl walking in the middle of a cobblestone street"/>
|
||||
</div>
|
||||
<div class="job-list-box">
|
||||
<h2>任务列表</h2>
|
||||
<div class="running-job-list">
|
||||
<ItemList :items="runningJobs" v-if="runningJobs.length > 0">
|
||||
<template #default="scope">
|
||||
<div class="job-item">
|
||||
<div v-if="scope.item.progress > 0" class="job-item-inner">
|
||||
<el-image :src="scope.item['img_url']" :zoom-rate="1.2"
|
||||
:preview-src-list="[scope.item['img_url']]" fit="cover" :initial-index="0"
|
||||
loading="lazy">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">
|
||||
正在加载图片
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon>
|
||||
<Picture/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
|
||||
<div class="progress">
|
||||
<el-progress type="circle" :percentage="scope.item.progress" :width="100"
|
||||
color="#47fff1"/>
|
||||
</div>
|
||||
</div>
|
||||
<el-image fit="cover" v-else>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<i class="iconfont icon-quick-start"></i>
|
||||
<span>任务正在排队中</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</template>
|
||||
</ItemList>
|
||||
<el-empty :image-size="100" v-else/>
|
||||
</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>
|
||||
<!-- <el-button type="success">-->
|
||||
<!-- <el-icon style="margin-right: 6px;font-size: 18px;">-->
|
||||
<!-- <Refresh/>-->
|
||||
<!-- </el-icon>-->
|
||||
<!-- 翻译-->
|
||||
<!-- </el-button>-->
|
||||
</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 class="submit-btn">
|
||||
<el-button color="#47fff1" :dark="false" @click="generate" round>立即生成</el-button>
|
||||
<div class="text-info">
|
||||
<el-tag type="success">绘图可用额度:{{ imgCalls }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<h2>任务列表</h2>
|
||||
<div class="running-job-list">
|
||||
<ItemList :items="runningJobs" v-if="runningJobs.length > 0">
|
||||
<template #default="scope">
|
||||
<div class="job-item">
|
||||
<div v-if="scope.item.progress > 0" class="job-item-inner">
|
||||
<el-image :src="scope.item['img_url']" :zoom-rate="1.2"
|
||||
:preview-src-list="[scope.item['img_url']]" fit="cover" :initial-index="0"
|
||||
loading="lazy">
|
||||
<h2>创作记录</h2>
|
||||
<div class="finish-job-list">
|
||||
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0" :width="240" :gap="16">
|
||||
<template #default="scope">
|
||||
<div class="job-item">
|
||||
<el-image
|
||||
:src="scope.item['thumb_url']"
|
||||
:class="scope.item['can_opt'] ? '' : 'upscale'" :zoom-rate="1.2"
|
||||
:preview-src-list="[scope.item['img_url']]" fit="cover" :initial-index="scope.index"
|
||||
loading="lazy" v-if="scope.item.progress > 0">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">
|
||||
正在加载图片
|
||||
@@ -326,7 +407,11 @@
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<div class="image-slot" v-if="scope.item['img_url'] === ''">
|
||||
<i class="iconfont icon-loading"></i>
|
||||
<span>正在下载图片</span>
|
||||
</div>
|
||||
<div class="image-slot" v-else>
|
||||
<el-icon>
|
||||
<Picture/>
|
||||
</el-icon>
|
||||
@@ -334,109 +419,63 @@
|
||||
</template>
|
||||
</el-image>
|
||||
|
||||
<div class="progress">
|
||||
<el-progress type="circle" :percentage="scope.item.progress" :width="100"
|
||||
color="#47fff1"/>
|
||||
</div>
|
||||
</div>
|
||||
<el-image fit="cover" v-else>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<i class="iconfont icon-quick-start"></i>
|
||||
<span>任务正在排队中</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</template>
|
||||
</ItemList>
|
||||
<el-empty :image-size="100" v-else/>
|
||||
</div>
|
||||
<div class="opt" v-if="scope.item['can_opt']">
|
||||
<div class="opt-line">
|
||||
<ul>
|
||||
<li><a @click="upscale(1, scope.item)">U1</a></li>
|
||||
<li><a @click="upscale(2, scope.item)">U2</a></li>
|
||||
<li><a @click="upscale(3, scope.item)">U3</a></li>
|
||||
<li><a @click="upscale(4, scope.item)">U4</a></li>
|
||||
<li class="show-prompt">
|
||||
|
||||
<h2>创作记录</h2>
|
||||
<div class="finish-job-list">
|
||||
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0" :width="240" :gap="16">
|
||||
<template #default="scope">
|
||||
<div class="job-item">
|
||||
<el-image
|
||||
:src="scope.item['thumb_url']"
|
||||
:class="scope.item.type === 'upscale' ? 'upscale' : ''" :zoom-rate="1.2"
|
||||
:preview-src-list="[scope.item['img_url']]" fit="cover" :initial-index="scope.index"
|
||||
loading="lazy" v-if="scope.item.progress > 0">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">
|
||||
正在加载图片
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot" v-if="scope.item['img_url'] === ''">
|
||||
<i class="iconfont icon-loading"></i>
|
||||
<span>正在下载图片</span>
|
||||
</div>
|
||||
<div class="image-slot" v-else>
|
||||
<el-icon>
|
||||
<Picture/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
|
||||
<div class="opt" v-if="scope.item.type !== 'upscale'">
|
||||
<div class="opt-line">
|
||||
<ul>
|
||||
<li><a @click="upscale(1, scope.item)">U1</a></li>
|
||||
<li><a @click="upscale(2, scope.item)">U2</a></li>
|
||||
<li><a @click="upscale(3, scope.item)">U3</a></li>
|
||||
<li><a @click="upscale(4, scope.item)">U4</a></li>
|
||||
<li class="show-prompt">
|
||||
|
||||
<el-popover placement="left" title="提示词" :width="240" trigger="hover">
|
||||
<template #reference>
|
||||
<el-icon>
|
||||
<ChromeFilled/>
|
||||
</el-icon>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<div class="mj-list-item-prompt">
|
||||
<span>{{ scope.item.prompt }}</span>
|
||||
<el-icon class="copy-prompt"
|
||||
:data-clipboard-text="scope.item.prompt">
|
||||
<DocumentCopy/>
|
||||
<el-popover placement="left" title="提示词" :width="240" trigger="hover">
|
||||
<template #reference>
|
||||
<el-icon>
|
||||
<ChromeFilled/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<div class="mj-list-item-prompt">
|
||||
<span>{{ scope.item.prompt }}</span>
|
||||
<el-icon class="copy-prompt"
|
||||
:data-clipboard-text="scope.item.prompt">
|
||||
<DocumentCopy/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="opt-line">
|
||||
<ul>
|
||||
<li><a @click="variation(1, scope.item)">V1</a></li>
|
||||
<li><a @click="variation(2, scope.item)">V2</a></li>
|
||||
<li><a @click="variation(3, scope.item)">V3</a></li>
|
||||
<li><a @click="variation(4, scope.item)">V4</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="opt-line">
|
||||
<ul>
|
||||
<li><a @click="variation(1, scope.item)">V1</a></li>
|
||||
<li><a @click="variation(2, scope.item)">V2</a></li>
|
||||
<li><a @click="variation(3, scope.item)">V3</a></li>
|
||||
<li><a @click="variation(4, scope.item)">V4</a></li>
|
||||
</ul>
|
||||
<div class="remove">
|
||||
<el-button type="danger" :icon="Delete" @click="removeImage(scope.item)" circle/>
|
||||
<el-button type="warning" v-if="scope.item.publish" @click="publishImage(scope.item, false)"
|
||||
circle>
|
||||
<i class="iconfont icon-cancel-share"></i>
|
||||
</el-button>
|
||||
<el-button type="success" v-else @click="publishImage(scope.item, true)" circle>
|
||||
<i class="iconfont icon-share-bold"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ItemList>
|
||||
|
||||
<div class="remove">
|
||||
<el-button type="danger" :icon="Delete" @click="removeImage(scope.item)" circle/>
|
||||
<el-button type="warning" v-if="scope.item.publish" @click="publishImage(scope.item, false)" circle>
|
||||
<i class="iconfont icon-cancel-share"></i>
|
||||
</el-button>
|
||||
<el-button type="success" v-else @click="publishImage(scope.item, true)" circle>
|
||||
<i class="iconfont icon-share-bold"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ItemList>
|
||||
|
||||
<el-empty :image-size="100" v-else/>
|
||||
</div> <!-- end finish job list-->
|
||||
<el-empty :image-size="100" v-else/>
|
||||
</div> <!-- end finish job list-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end task list box -->
|
||||
@@ -465,6 +504,7 @@ import Clipboard from "clipboard";
|
||||
import {checkSession} from "@/action/session";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {isMobile, removeArrayItem} from "@/utils/libs";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const mjBoxHeight = ref(window.innerHeight - 150)
|
||||
@@ -516,13 +556,14 @@ const options = [
|
||||
]
|
||||
|
||||
const params = ref({
|
||||
task_type: "image",
|
||||
rate: rates[0].value,
|
||||
model: models[0].value,
|
||||
chaos: 0,
|
||||
stylize: 100,
|
||||
stylize: 0,
|
||||
seed: 0,
|
||||
img_arr: [],
|
||||
raw: false,
|
||||
img: "",
|
||||
weight: 0.25,
|
||||
prompt: "",
|
||||
neg_prompt: "",
|
||||
@@ -530,7 +571,9 @@ const params = ref({
|
||||
quality: 0
|
||||
})
|
||||
|
||||
const activeName = ref('图生图')
|
||||
const imgList = ref([])
|
||||
|
||||
const activeName = ref('image')
|
||||
|
||||
const runningJobs = ref([])
|
||||
const finishedJobs = ref([])
|
||||
@@ -541,6 +584,10 @@ const imgCalls = ref(0)
|
||||
const loading = ref(false)
|
||||
const userId = ref(0)
|
||||
|
||||
if (isMobile()) {
|
||||
router.replace("/mobile/mj")
|
||||
}
|
||||
|
||||
const rewritePrompt = () => {
|
||||
loading.value = true
|
||||
httpPost("/api/prompt/rewrite", {"prompt": params.value.prompt}).then(res => {
|
||||
@@ -552,10 +599,18 @@ const rewritePrompt = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const translatePrompt = () => {
|
||||
const translatePrompt = (negative) => {
|
||||
loading.value = true
|
||||
httpPost("/api/prompt/translate", {"prompt": params.value.prompt}).then(res => {
|
||||
params.value.prompt = res.data
|
||||
let prompt = params.value.prompt
|
||||
if (negative) {
|
||||
prompt = params.value.neg_prompt
|
||||
}
|
||||
httpPost("/api/prompt/translate", {"prompt": prompt}).then(res => {
|
||||
if (negative) {
|
||||
params.value.neg_prompt = res.data
|
||||
} else {
|
||||
params.value.prompt = res.data
|
||||
}
|
||||
loading.value = false
|
||||
}).catch(e => {
|
||||
loading.value = false
|
||||
@@ -563,6 +618,7 @@ const translatePrompt = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
@@ -572,9 +628,26 @@ const connect = () => {
|
||||
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/mj/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
|
||||
// 发送心跳消息
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
@@ -621,7 +694,8 @@ const fetchRunningJobs = (userId) => {
|
||||
if (jobs[i].progress === -1) {
|
||||
ElNotification({
|
||||
title: '任务执行失败',
|
||||
message: "任务ID:" + jobs[i]['task_id'],
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `任务ID:${jobs[i]['task_id']}<br />原因:${jobs[i]['err_msg']}`,
|
||||
type: 'error',
|
||||
})
|
||||
imgCalls.value += 1
|
||||
@@ -643,12 +717,16 @@ const fetchFinishJobs = (userId) => {
|
||||
if (jobs[i]['use_proxy']) {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?x-oss-process=image/quality,q_60&format=webp'
|
||||
} else {
|
||||
if (jobs[i].type === 'upscale') {
|
||||
if (jobs[i].type === 'upscale' || jobs[i].type === 'swapFace') {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75'
|
||||
} else {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75'
|
||||
}
|
||||
}
|
||||
|
||||
if (jobs[i].type === 'image' || jobs[i].type === 'variation') {
|
||||
jobs[i]['can_opt'] = true
|
||||
}
|
||||
}
|
||||
finishedJobs.value = jobs
|
||||
}).catch(e => {
|
||||
@@ -666,7 +744,7 @@ const changeModel = (item) => {
|
||||
}
|
||||
|
||||
// 图片上传
|
||||
const afterRead = (file) => {
|
||||
const uploadImg = (file) => {
|
||||
// 压缩图片并上传
|
||||
new Compressor(file.file, {
|
||||
quality: 0.6,
|
||||
@@ -675,7 +753,7 @@ const afterRead = (file) => {
|
||||
formData.append('file', result, result.name);
|
||||
// 执行上传操作
|
||||
httpPost('/api/upload', formData).then((res) => {
|
||||
params.value.img = res.data.url
|
||||
imgList.value.push(res.data.url)
|
||||
ElMessage.success('上传成功')
|
||||
}).catch((e) => {
|
||||
ElMessage.error('上传失败:' + e.message)
|
||||
@@ -686,17 +764,22 @@ const afterRead = (file) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 创建绘图任务
|
||||
const promptRef = ref(null)
|
||||
const generate = () => {
|
||||
if (params.value.prompt === '') {
|
||||
if (params.value.prompt === '' && params.value.task_type === "image") {
|
||||
promptRef.value.focus()
|
||||
return ElMessage.error("请输入绘画提示词!")
|
||||
}
|
||||
if (params.value.model.indexOf("niji") !== -1 && params.value.raw) {
|
||||
return ElMessage.error("动漫模型不允许启用原始模式")
|
||||
}
|
||||
if (imgList.value.length !== 2 && params.value.task_type === "swapFace") {
|
||||
return ElMessage.error("换脸操作需要上传两张图片")
|
||||
}
|
||||
params.value.session_id = getSessionId()
|
||||
params.value.img_arr = imgList.value
|
||||
httpPost("/api/mj/image", params.value).then(() => {
|
||||
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
||||
imgCalls.value -= 1
|
||||
@@ -707,7 +790,6 @@ const generate = () => {
|
||||
|
||||
// 图片放大任务
|
||||
const upscale = (index, item) => {
|
||||
console.log(item)
|
||||
send('/api/mj/upscale', index, item)
|
||||
}
|
||||
|
||||
@@ -765,6 +847,16 @@ const publishImage = (item, action) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 切换菜单
|
||||
const tabChange = (tab) => {
|
||||
params.value.task_type = tab
|
||||
}
|
||||
|
||||
// 删除已上传图片
|
||||
const removeUploadImage = (url) => {
|
||||
imgList.value = removeArrayItem(imgList.value, url)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
||||
@@ -600,7 +600,8 @@ onMounted(() => {
|
||||
if (jobs[i].progress === -1) {
|
||||
ElNotification({
|
||||
title: '任务执行失败',
|
||||
message: "任务ID:" + jobs[i]['task_id'],
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `任务ID:${jobs[i]['task_id']}<br />原因:${jobs[i]['err_msg']}`,
|
||||
type: 'error',
|
||||
})
|
||||
imgCalls.value += 1
|
||||
|
||||
@@ -309,7 +309,7 @@ const handleSelectionChange = function (rows) {
|
||||
const resetPass = (row) => {
|
||||
showResetPassDialog.value = true
|
||||
pass.value.id = row.id
|
||||
pass.value.username = row.mobile
|
||||
pass.value.username = row.username
|
||||
}
|
||||
|
||||
const doResetPass = () => {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<div class="app-background">
|
||||
<div class="mobile-setting container">
|
||||
<div class="title" style="padding-top: 50px">应用广场</div>
|
||||
<div class="title" style="padding-top: 200px">视频生成器即将开启……敬请期待</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@import "@/assets/css/mobile/apps.styl"
|
||||
</style>
|
||||
@@ -3,39 +3,8 @@
|
||||
<van-nav-bar :title="title"/>
|
||||
|
||||
<div class="content">
|
||||
<van-form @submit="save" v-model="form">
|
||||
<van-cell-group inset>
|
||||
<van-field
|
||||
v-model="form.chat_config.api_keys.OpenAI"
|
||||
label="OpenAI KEY"
|
||||
placeholder="OpenAI API KEY"
|
||||
/>
|
||||
<van-field
|
||||
v-model="form.chat_config.api_keys.Azure"
|
||||
label="Azure KEY"
|
||||
placeholder="Azure API KEY"
|
||||
/>
|
||||
<van-field
|
||||
v-model="form.chat_config.api_keys.ChatGLM"
|
||||
label="ChatGLM KEY"
|
||||
placeholder="ChatGLM API KEY"
|
||||
/>
|
||||
</van-cell-group>
|
||||
<div style="margin: 16px;">
|
||||
<van-button round block type="primary" native-type="submit">
|
||||
提交
|
||||
</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
<van-empty description="功能正在开发中"/>
|
||||
</div>
|
||||
|
||||
<van-popup v-model:show="showPicker" round position="bottom">
|
||||
<van-picker
|
||||
:columns="models"
|
||||
@cancel="showPicker = false"
|
||||
@confirm="selectModel"
|
||||
/>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -45,7 +14,7 @@ import {httpGet, httpPost} from "@/utils/http";
|
||||
import {showFailToast, showSuccessToast} from "vant";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const title = ref('聊天设置')
|
||||
const title = ref('图片创作广场')
|
||||
const form = ref({
|
||||
chat_config: {
|
||||
api_keys: {OpenAI: "", Azure: "", ChatGLM: ""}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user