mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-18 17:26:38 +08:00
image task list page for admin console is ready
This commit is contained in:
parent
52e40daf23
commit
ba20717a09
@ -1,4 +1,8 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
## v4.1.6
|
||||||
|
* 功能优化:优化MysQL容器配置文档,解决MysQL容器资源占用过高问题
|
||||||
|
* 功能新增:管理后台增加AI绘图任务管理,可在管理后台浏览和删除用户的绘图任务
|
||||||
|
|
||||||
## v4.1.5
|
## v4.1.5
|
||||||
* 功能优化:重构 websocket 组件,减少 websocket 连接数,全站共享一个 websocket 连接
|
* 功能优化:重构 websocket 组件,减少 websocket 连接数,全站共享一个 websocket 连接
|
||||||
* Bug修复:兼容手机端原生微信支付和支付宝支付渠道
|
* Bug修复:兼容手机端原生微信支付和支付宝支付渠道
|
||||||
|
174
api/handler/admin/image_handler.go
Normal file
174
api/handler/admin/image_handler.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
||||||
|
// * Use of this source code is governed by a Apache-2.0 license
|
||||||
|
// * that can be found in the LICENSE file.
|
||||||
|
// * @Author yangjian102621@163.com
|
||||||
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
import (
|
||||||
|
"geekai/core"
|
||||||
|
"geekai/core/types"
|
||||||
|
"geekai/handler"
|
||||||
|
"geekai/store/model"
|
||||||
|
"geekai/store/vo"
|
||||||
|
"geekai/utils"
|
||||||
|
"geekai/utils/resp"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageHandler struct {
|
||||||
|
handler.BaseHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImageHandler(app *core.AppServer, db *gorm.DB) *ImageHandler {
|
||||||
|
return &ImageHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type query struct {
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
CreatedAt []string `json:"created_time"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MjList Midjourney 任务列表
|
||||||
|
func (h *ImageHandler) MjList(c *gin.Context) {
|
||||||
|
var data query
|
||||||
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session := h.DB.Session(&gorm.Session{})
|
||||||
|
if data.Username != "" {
|
||||||
|
var user model.User
|
||||||
|
err := h.DB.Where("username", data.Username).First(&user).Error
|
||||||
|
if err == nil {
|
||||||
|
session = session.Where("user_id", user.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Prompt != "" {
|
||||||
|
session = session.Where("prompt LIKE ?", "%"+data.Prompt+"%")
|
||||||
|
}
|
||||||
|
if len(data.CreatedAt) == 2 {
|
||||||
|
start := utils.Str2stamp(data.CreatedAt[0] + " 00:00:00")
|
||||||
|
end := utils.Str2stamp(data.CreatedAt[1] + " 00:00:00")
|
||||||
|
session = session.Where("created_at >= ? AND created_at <= ?", start, end)
|
||||||
|
}
|
||||||
|
var total int64
|
||||||
|
session.Model(&model.MidJourneyJob{}).Count(&total)
|
||||||
|
var list []model.MidJourneyJob
|
||||||
|
var items = make([]vo.MidJourneyJob, 0)
|
||||||
|
offset := (data.Page - 1) * data.PageSize
|
||||||
|
err := session.Order("id DESC").Offset(offset).Limit(data.PageSize).Find(&list).Error
|
||||||
|
if err == nil {
|
||||||
|
// 填充数据
|
||||||
|
for _, item := range list {
|
||||||
|
var job vo.MidJourneyJob
|
||||||
|
err = utils.CopyObject(item, &job)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
job.CreatedAt = item.CreatedAt.Unix()
|
||||||
|
items = append(items, job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, items))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SdList Stable Diffusion 任务列表
|
||||||
|
func (h *ImageHandler) SdList(c *gin.Context) {
|
||||||
|
var data query
|
||||||
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session := h.DB.Session(&gorm.Session{})
|
||||||
|
if data.Username != "" {
|
||||||
|
var user model.User
|
||||||
|
err := h.DB.Where("username", data.Username).First(&user).Error
|
||||||
|
if err == nil {
|
||||||
|
session = session.Where("user_id", user.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Prompt != "" {
|
||||||
|
session = session.Where("prompt LIKE ?", "%"+data.Prompt+"%")
|
||||||
|
}
|
||||||
|
if len(data.CreatedAt) == 2 {
|
||||||
|
start := utils.Str2stamp(data.CreatedAt[0] + " 00:00:00")
|
||||||
|
end := utils.Str2stamp(data.CreatedAt[1] + " 00:00:00")
|
||||||
|
session = session.Where("created_at >= ? AND created_at <= ?", start, end)
|
||||||
|
}
|
||||||
|
var total int64
|
||||||
|
session.Model(&model.SdJob{}).Count(&total)
|
||||||
|
var list []model.SdJob
|
||||||
|
var items = make([]vo.SdJob, 0)
|
||||||
|
offset := (data.Page - 1) * data.PageSize
|
||||||
|
err := session.Order("id DESC").Offset(offset).Limit(data.PageSize).Find(&list).Error
|
||||||
|
if err == nil {
|
||||||
|
// 填充数据
|
||||||
|
for _, item := range list {
|
||||||
|
var job vo.SdJob
|
||||||
|
err = utils.CopyObject(item, &job)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
job.CreatedAt = item.CreatedAt.Unix()
|
||||||
|
items = append(items, job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, items))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DallList DALL-E 任务列表
|
||||||
|
func (h *ImageHandler) DallList(c *gin.Context) {
|
||||||
|
var data query
|
||||||
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session := h.DB.Session(&gorm.Session{})
|
||||||
|
if data.Username != "" {
|
||||||
|
var user model.User
|
||||||
|
err := h.DB.Where("username", data.Username).First(&user).Error
|
||||||
|
if err == nil {
|
||||||
|
session = session.Where("user_id", user.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Prompt != "" {
|
||||||
|
session = session.Where("prompt LIKE ?", "%"+data.Prompt+"%")
|
||||||
|
}
|
||||||
|
if len(data.CreatedAt) == 2 {
|
||||||
|
start := utils.Str2stamp(data.CreatedAt[0] + " 00:00:00")
|
||||||
|
end := utils.Str2stamp(data.CreatedAt[1] + " 00:00:00")
|
||||||
|
session = session.Where("created_at >= ? AND created_at <= ?", start, end)
|
||||||
|
}
|
||||||
|
var total int64
|
||||||
|
session.Model(&model.DallJob{}).Count(&total)
|
||||||
|
var list []model.DallJob
|
||||||
|
var items = make([]vo.DallJob, 0)
|
||||||
|
offset := (data.Page - 1) * data.PageSize
|
||||||
|
err := session.Order("id DESC").Offset(offset).Limit(data.PageSize).Find(&list).Error
|
||||||
|
if err == nil {
|
||||||
|
// 填充数据
|
||||||
|
for _, item := range list {
|
||||||
|
var job vo.DallJob
|
||||||
|
err = utils.CopyObject(item, &job)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
job.CreatedAt = item.CreatedAt.Unix()
|
||||||
|
items = append(items, job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, items))
|
||||||
|
}
|
@ -539,6 +539,13 @@ func main() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
fx.Provide(admin.NewImageHandler),
|
||||||
|
fx.Invoke(func(s *core.AppServer, h *admin.ImageHandler) {
|
||||||
|
group := s.Engine.Group("/api/admin/image")
|
||||||
|
group.POST("/list/mj", h.MjList)
|
||||||
|
group.POST("/list/sd", h.SdList)
|
||||||
|
group.POST("/list/dall", h.DallList)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
// 启动应用程序
|
// 启动应用程序
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -1,23 +1,21 @@
|
|||||||
package vo
|
package vo
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type MidJourneyJob struct {
|
type MidJourneyJob struct {
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
UserId int `json:"user_id"`
|
UserId int `json:"user_id"`
|
||||||
ChannelId string `json:"channel_id"`
|
ChannelId string `json:"channel_id"`
|
||||||
TaskId string `json:"task_id"`
|
TaskId string `json:"task_id"`
|
||||||
MessageId string `json:"message_id"`
|
MessageId string `json:"message_id"`
|
||||||
ReferenceId string `json:"reference_id"`
|
ReferenceId string `json:"reference_id"`
|
||||||
ImgURL string `json:"img_url"`
|
ImgURL string `json:"img_url"`
|
||||||
OrgURL string `json:"org_url"`
|
OrgURL string `json:"org_url"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Progress int `json:"progress"`
|
Progress int `json:"progress"`
|
||||||
Prompt string `json:"prompt"`
|
Prompt string `json:"prompt"`
|
||||||
UseProxy bool `json:"use_proxy"`
|
UseProxy bool `json:"use_proxy"`
|
||||||
Publish bool `json:"publish"`
|
Publish bool `json:"publish"`
|
||||||
ErrMsg string `json:"err_msg"`
|
ErrMsg string `json:"err_msg"`
|
||||||
Power int `json:"power"`
|
Power int `json:"power"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package vo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SdJob struct {
|
type SdJob struct {
|
||||||
@ -17,5 +16,5 @@ type SdJob struct {
|
|||||||
Publish bool `json:"publish"`
|
Publish bool `json:"publish"`
|
||||||
ErrMsg string `json:"err_msg"`
|
ErrMsg string `json:"err_msg"`
|
||||||
Power int `json:"power"`
|
Power int `json:"power"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
{{ threeItem.title }}
|
{{ threeItem.title }}
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<el-menu-item v-else :index="subItem.index">
|
<el-menu-item v-else :index="subItem.index" :key="subItem.index">
|
||||||
<i v-if="subItem.icon" :class="'iconfont icon-'+subItem.icon"></i>
|
<i v-if="subItem.icon" :class="'iconfont icon-'+subItem.icon"></i>
|
||||||
{{ subItem.title }}
|
{{ subItem.title }}
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
@ -64,8 +64,8 @@ const logo = ref('')
|
|||||||
|
|
||||||
// 加载系统配置
|
// 加载系统配置
|
||||||
httpGet('/api/admin/config/get?key=system').then(res => {
|
httpGet('/api/admin/config/get?key=system').then(res => {
|
||||||
title.value = res.data['admin_title']
|
title.value = res.data.admin_title
|
||||||
logo.value = res.data['logo']
|
logo.value = res.data.logo
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("加载系统配置失败: " + e.message)
|
ElMessage.error("加载系统配置失败: " + e.message)
|
||||||
})
|
})
|
||||||
@ -101,7 +101,7 @@ const items = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
icon: 'api-key',
|
icon: 'api-key',
|
||||||
index: '/admin/apikey',
|
index: '/admin/apikey',
|
||||||
@ -137,6 +137,11 @@ const items = [
|
|||||||
index: '/admin/chats',
|
index: '/admin/chats',
|
||||||
title: '对话管理',
|
title: '对话管理',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'image',
|
||||||
|
index: '/admin/images',
|
||||||
|
title: '绘图管理',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'role',
|
icon: 'role',
|
||||||
index: '/admin/manger',
|
index: '/admin/manger',
|
||||||
|
@ -233,6 +233,12 @@ const routes = [
|
|||||||
meta: {title: '对话管理'},
|
meta: {title: '对话管理'},
|
||||||
component: () => import('@/views/admin/ChatList.vue'),
|
component: () => import('@/views/admin/ChatList.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/admin/images',
|
||||||
|
name: 'admin-images',
|
||||||
|
meta: {title: '绘图管理'},
|
||||||
|
component: () => import('@/views/admin/ImageList.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/powerLog',
|
path: '/admin/powerLog',
|
||||||
name: 'admin-power-log',
|
name: 'admin-power-log',
|
||||||
|
510
web/src/views/admin/ImageList.vue
Normal file
510
web/src/views/admin/ImageList.vue
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container chat-list">
|
||||||
|
<el-tabs v-model="activeName" @tab-change="handleChange">
|
||||||
|
<el-tab-pane label="Midjourney" name="mj" v-loading="data.mj.loading">
|
||||||
|
<div class="handle-box">
|
||||||
|
<el-input v-model.number="data.mj.query.username" placeholder="用户名" class="handle-input mr10"
|
||||||
|
@keyup="search($event,'mj')"></el-input>
|
||||||
|
<el-input v-model="data.mj.query.prompt" placeholder="提示词" class="handle-input mr10"
|
||||||
|
@keyup="search($event,'mj')"></el-input>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="data.mj.query.created_at"
|
||||||
|
type="daterange"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="margin-right: 10px;width: 200px; position: relative;top:3px;"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" :icon="Search" @click="fetchMjData">搜索</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="data.mj.items.length > 0">
|
||||||
|
<el-row>
|
||||||
|
<el-table :data="data.mj.items" :row-key="row => row.id" table-layout="auto">
|
||||||
|
<el-table-column prop="user_id" label="用户ID"/>
|
||||||
|
<el-table-column label="任务类型">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button :color="taskTypeTheme[scope.row.type].color" size="small" plain>{{taskTypeTheme[scope.row.type].text}}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="progress" label="任务进度">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.progress <= 100">{{scope.row.progress}}%</span>
|
||||||
|
<el-tag v-else type="danger">已失败</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="power" label="消耗算力"/>
|
||||||
|
<el-table-column label="结果图片">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" type="success" @click="showImage(scope.row.img_url)" v-if="scope.row.img_url !== ''" plain>预览图片</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="提示词">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
title="绘画提示词"
|
||||||
|
:width="300"
|
||||||
|
trigger="hover"
|
||||||
|
:content="scope.row.prompt"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<span>{{ substr(scope.row.prompt, 20) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="创建时间">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ dateFormat(scope.row['created_at']) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="失败原因">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
title="失败原因"
|
||||||
|
:width="300"
|
||||||
|
trigger="hover"
|
||||||
|
:content="scope.row.err_msg"
|
||||||
|
v-if="scope.row.progress === 101"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-text type="danger">{{ substr(scope.row.err_msg, 20) }}</el-text>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else>无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popconfirm title="确定要删除当前记录吗?" @confirm="remove(scope.row, 'mj')">
|
||||||
|
<template #reference>
|
||||||
|
<el-button size="small" type="danger">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination v-if="data.mj.total > 0" background
|
||||||
|
layout="total,prev, pager, next"
|
||||||
|
:hide-on-single-page="true"
|
||||||
|
v-model:current-page="data.mj.page"
|
||||||
|
v-model:page-size="data.mj.pageSize"
|
||||||
|
@current-change="fetchMjData()"
|
||||||
|
:total="data.mj.total"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="Stable-Diffusion" name="sd" v-loading="data.sd.loading">
|
||||||
|
<div class="handle-box">
|
||||||
|
<el-input v-model.number="data.sd.query.username" placeholder="用户名" class="handle-input mr10"
|
||||||
|
@keyup="search($event, 'sd')"></el-input>
|
||||||
|
<el-input v-model="data.sd.query.prompt" placeholder="提示词" class="handle-input mr10"
|
||||||
|
@keyup="search($event, 'sd')"></el-input>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="data.sd.query.created_at"
|
||||||
|
type="daterange"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="margin-right: 10px;width: 200px; position: relative;top:3px;"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" :icon="Search" @click="fetchSdData">搜索</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="data.sd.items.length > 0">
|
||||||
|
<el-row>
|
||||||
|
<el-table :data="data.sd.items" :row-key="row => row.id" table-layout="auto">
|
||||||
|
<el-table-column prop="user_id" label="用户ID"/>
|
||||||
|
<el-table-column prop="progress" label="任务进度">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.progress <= 100">{{scope.row.progress}}%</span>
|
||||||
|
<el-tag v-else type="danger">已失败</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="power" label="消耗算力"/>
|
||||||
|
<el-table-column label="结果图片">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" type="success" @click="showImage(scope.row.img_url)" v-if="scope.row.img_url !== ''" plain>预览图片</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="提示词">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
title="绘画提示词"
|
||||||
|
:width="300"
|
||||||
|
trigger="hover"
|
||||||
|
:content="scope.row.prompt"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<span>{{ substr(scope.row.prompt, 20) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="创建时间">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ dateFormat(scope.row['created_at']) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="失败原因">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
title="失败原因"
|
||||||
|
:width="300"
|
||||||
|
trigger="hover"
|
||||||
|
:content="scope.row.err_msg"
|
||||||
|
v-if="scope.row.progress === 101"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-text type="danger">{{ substr(scope.row.err_msg, 20) }}</el-text>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else>无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popconfirm title="确定要删除当前记录吗?" @confirm="remove(scope.row, 'sd')">
|
||||||
|
<template #reference>
|
||||||
|
<el-button size="small" type="danger">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination v-if="data.sd.total > 0" background
|
||||||
|
layout="total,prev, pager, next"
|
||||||
|
:hide-on-single-page="true"
|
||||||
|
v-model:current-page="data.sd.page"
|
||||||
|
v-model:page-size="data.sd.pageSize"
|
||||||
|
@current-change="fetchSdData()"
|
||||||
|
:total="data.sd.total"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="DALL-E" name="dall">
|
||||||
|
<div class="handle-box">
|
||||||
|
<el-input v-model.number="data.dall.query.username" placeholder="用户名" class="handle-input mr10"
|
||||||
|
@keyup="search($event,'dall')"></el-input>
|
||||||
|
<el-input v-model="data.dall.query.prompt" placeholder="提示词" class="handle-input mr10"
|
||||||
|
@keyup="search($event, 'dall')"></el-input>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="data.dall.query.created_at"
|
||||||
|
type="daterange"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="margin-right: 10px;width: 200px; position: relative;top:3px;"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" :icon="Search" @click="fetchDallData">搜索</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="data.dall.items.length > 0">
|
||||||
|
<el-row>
|
||||||
|
<el-table :data="data.dall.items" :row-key="row => row.id" table-layout="auto">
|
||||||
|
<el-table-column prop="user_id" label="用户ID"/>
|
||||||
|
<el-table-column prop="progress" label="任务进度">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.progress <= 100">{{scope.row.progress}}%</span>
|
||||||
|
<el-tag v-else type="danger">已失败</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="power" label="消耗算力"/>
|
||||||
|
<el-table-column label="结果图片">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" type="success" @click="showImage(scope.row.img_url)" v-if="scope.row.img_url !== ''" plain>预览图片</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="提示词">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
title="绘画提示词"
|
||||||
|
:width="300"
|
||||||
|
trigger="hover"
|
||||||
|
:content="scope.row.prompt"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<span>{{ substr(scope.row.prompt, 20) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="创建时间">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ dateFormat(scope.row['created_at']) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="失败原因">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
title="失败原因"
|
||||||
|
:width="300"
|
||||||
|
trigger="hover"
|
||||||
|
:content="scope.row.err_msg"
|
||||||
|
v-if="scope.row.progress === 101"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-text type="danger">{{ substr(scope.row.err_msg, 20) }}</el-text>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else>无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popconfirm title="确定要删除当前记录吗?" @confirm="remove(scope.row, 'dall')">
|
||||||
|
<template #reference>
|
||||||
|
<el-button size="small" type="danger">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination v-if="data.dall.total > 0" background
|
||||||
|
layout="total,prev, pager, next"
|
||||||
|
:hide-on-single-page="true"
|
||||||
|
v-model:current-page="data.dall.page"
|
||||||
|
v-model:page-size="data.dall.pageSize"
|
||||||
|
@current-change="fetchDallData()"
|
||||||
|
:total="data.dall.total"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="showImageDialog"
|
||||||
|
title="图片预览"
|
||||||
|
>
|
||||||
|
<el-image
|
||||||
|
:src="imgURL"
|
||||||
|
:zoom-rate="1.2"
|
||||||
|
:max-scale="7"
|
||||||
|
:min-scale="0.2"
|
||||||
|
:preview-src-list="[imgURL]"
|
||||||
|
:initial-index="0"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import {dateFormat, substr} from "@/utils/libs";
|
||||||
|
import {Search} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
// 变量定义
|
||||||
|
const data = ref({
|
||||||
|
"mj": {
|
||||||
|
items: [],
|
||||||
|
query: {prompt: "", username: "", created_at: [], page: 1, page_size: 15},
|
||||||
|
total: 0,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
loading: true
|
||||||
|
},
|
||||||
|
"sd": {
|
||||||
|
items: [],
|
||||||
|
query: {prompt: "", username: "", created_at: [], page: 1, page_size: 15},
|
||||||
|
total: 0,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
loading: true
|
||||||
|
},
|
||||||
|
"dall": {
|
||||||
|
items: [],
|
||||||
|
query: {prompt: "", username: "", created_at: [], page: 1, page_size: 15},
|
||||||
|
total: 0,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
loading: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const activeName = ref("mj")
|
||||||
|
const taskTypeTheme = {
|
||||||
|
image: {text: "绘图", color: "#2185d0"},
|
||||||
|
upscale: {text: "放大", color: "#f2711c" },
|
||||||
|
variation: {text: "变换", color: "#00b5ad"},
|
||||||
|
blend: {text: "融图", color: "#21ba45"},
|
||||||
|
swapFace: {text: "换脸", color: "#a333c8"}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchMjData()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleChange = (tab) => {
|
||||||
|
switch (tab) {
|
||||||
|
case "mj":
|
||||||
|
fetchMjData()
|
||||||
|
break
|
||||||
|
case "sd":
|
||||||
|
fetchSdData()
|
||||||
|
break
|
||||||
|
case "dall":
|
||||||
|
fetchDallData()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索对话
|
||||||
|
const search = (evt,tab) => {
|
||||||
|
if (evt.keyCode === 13) {
|
||||||
|
handleChange(tab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
const fetchMjData = () => {
|
||||||
|
const d = data.value.mj
|
||||||
|
d.query.page = d.page
|
||||||
|
d.query.page_size = d.pageSize
|
||||||
|
httpPost('/api/admin/image/list/mj', d.query).then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
d.items = res.data.items
|
||||||
|
d.total = res.data.total
|
||||||
|
d.page = res.data.page
|
||||||
|
d.pageSize = res.data.page_size
|
||||||
|
}
|
||||||
|
d.loading = false
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("获取数据失败:" + e.message);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchSdData = () => {
|
||||||
|
const d = data.value.sd
|
||||||
|
d.query.page = d.page
|
||||||
|
d.query.page_size = d.pageSize
|
||||||
|
httpPost('/api/admin/image/list/sd', d.query).then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
d.items = res.data.items
|
||||||
|
d.total = res.data.total
|
||||||
|
d.page = res.data.page
|
||||||
|
d.pageSize = res.data.page_size
|
||||||
|
}
|
||||||
|
d.loading = false
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("获取数据失败:" + e.message);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchDallData = () => {
|
||||||
|
const d = data.value.dall
|
||||||
|
d.query.page = d.page
|
||||||
|
d.query.page_size = d.pageSize
|
||||||
|
httpPost('/api/admin/image/list/dall', d.query).then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
d.items = res.data.items
|
||||||
|
d.total = res.data.total
|
||||||
|
d.page = res.data.page
|
||||||
|
d.pageSize = res.data.page_size
|
||||||
|
}
|
||||||
|
d.loading = false
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("获取数据失败:" + e.message);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const remove = function (row,tab) {
|
||||||
|
httpGet('/api/admin/chat/remove?chat_id=' + row.chat_id).then(() => {
|
||||||
|
ElMessage.success("删除成功!")
|
||||||
|
}).catch((e) => {
|
||||||
|
ElMessage.error("删除失败:" + e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showImageDialog = ref(false)
|
||||||
|
const imgURL = ref('')
|
||||||
|
const showImage = (url) => {
|
||||||
|
showImageDialog.value = true
|
||||||
|
imgURL.value = url
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.chat-list {
|
||||||
|
.handle-box {
|
||||||
|
margin-bottom 20px
|
||||||
|
.handle-input {
|
||||||
|
max-width 150px;
|
||||||
|
margin-right 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opt-box {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
display flex;
|
||||||
|
justify-content flex-end
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
padding 20px 0
|
||||||
|
display flex
|
||||||
|
justify-content right
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-box {
|
||||||
|
overflow hidden
|
||||||
|
|
||||||
|
// 变量定义
|
||||||
|
--content-font-size: 16px;
|
||||||
|
--content-color: #c1c1c1;
|
||||||
|
|
||||||
|
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||||
|
|
||||||
|
.chat-line {
|
||||||
|
// 隐藏滚动条
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user