feat: the power log page is ready

This commit is contained in:
RockYang 2024-03-20 14:14:30 +08:00
parent ca83f139f7
commit e11c0a3633
19 changed files with 443 additions and 212 deletions

View File

@ -56,6 +56,7 @@ type ChatSession struct {
type ChatModel struct {
Id uint `json:"id"`
Platform Platform `json:"platform"`
Name string `json:"name"`
Value string `json:"value"`
Power int `json:"power"`
MaxTokens int `json:"max_tokens"` // 最大响应长度
@ -86,6 +87,21 @@ const (
PowerReward = PowerType(5) // 众筹
)
func (t PowerType) String() string {
switch t {
case PowerRecharge:
return "充值"
case PowerConsume:
return "消费"
case PowerRefund:
return "退款"
case PowerReward:
return "众筹"
}
return "其他"
}
type PowerMark int
const (

View File

@ -22,6 +22,7 @@ func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler {
// List 获取用户聊天应用列表
func (h *ChatRoleHandler) List(c *gin.Context) {
all := h.GetBool(c, "all")
userId := h.GetLoginUserId(c)
var roles []model.ChatRole
res := h.DB.Where("enable", true).Order("sort_num ASC").Find(&roles)
@ -31,7 +32,7 @@ func (h *ChatRoleHandler) List(c *gin.Context) {
}
// 获取所有角色
if userId == 0 {
if userId == 0 || all {
// 转成 vo
var roleVos = make([]vo.ChatRole, 0)
for _, r := range roles {

View File

@ -105,6 +105,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
session.ChatId = chatId
session.Model = types.ChatModel{
Id: chatModel.Id,
Name: chatModel.Name,
Value: chatModel.Value,
Power: chatModel.Power,
MaxTokens: chatModel.MaxTokens,
@ -535,7 +536,7 @@ func (h *ChatHandler) subUserPower(userVo vo.User, session *types.ChatSession, p
Mark: types.PowerSub,
Balance: userVo.Power - power,
Model: session.Model.Value,
Remark: fmt.Sprintf("提问长度:%d回复长度%d", promptTokens, replyTokens),
Remark: fmt.Sprintf("模型名称:%s, 提问长度:%d回复长度%d", session.Model.Name, promptTokens, replyTokens),
CreatedAt: time.Now(),
})
}

View File

@ -0,0 +1,65 @@
package handler
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/store/model"
"chatplus/store/vo"
"chatplus/utils"
"chatplus/utils/resp"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type PowerLogHandler struct {
BaseHandler
}
func NewPowerLogHandler(app *core.AppServer, db *gorm.DB) *PowerLogHandler {
return &PowerLogHandler{BaseHandler: BaseHandler{App: app, DB: db}}
}
func (h *PowerLogHandler) List(c *gin.Context) {
var data struct {
Model string `json:"model"`
Date []string `json:"date"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
session := h.DB.Session(&gorm.Session{})
if data.Model != "" {
session = session.Where("model", data.Model)
}
if len(data.Date) == 2 {
start := data.Date[0] + " 00:00:00"
end := data.Date[1] + " 00:00:00"
session = session.Where("created_at >= ? AND created_at <= ?", start, end)
}
var total int64
session.Model(&model.PowerLog{}).Count(&total)
var items []model.PowerLog
var list = make([]vo.PowerLog, 0)
offset := (data.Page - 1) * data.PageSize
res := session.Debug().Order("id DESC").Offset(offset).Limit(data.PageSize).Find(&items)
if res.Error == nil {
for _, item := range items {
var log vo.PowerLog
err := utils.CopyObject(item, &log)
if err != nil {
continue
}
log.Id = item.Id
log.CreatedAt = item.CreatedAt.Unix()
log.TypeStr = item.Type.String()
list = append(list, log)
}
}
resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, list))
}

View File

@ -108,6 +108,20 @@ func (h *UserHandler) Register(c *gin.Context) {
h.DB.Model(&model.InviteCode{}).Where("code = ?", data.InviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
if h.App.SysConfig.InvitePower > 0 {
h.DB.Model(&model.User{}).Where("id = ?", inviteCode.UserId).UpdateColumn("power", gorm.Expr("power + ?", h.App.SysConfig.InvitePower))
// 记录邀请算力充值日志
var inviter model.User
h.DB.Where("id", inviteCode.UserId).First(&inviter)
h.DB.Create(&model.PowerLog{
UserId: inviter.Id,
Username: inviter.Username,
Type: types.PowerInvite,
Amount: h.App.SysConfig.InvitePower,
Balance: inviter.Power,
Mark: types.PowerAdd,
Model: "",
Remark: fmt.Sprintf("邀请用户注册奖励,金额:%d邀请码%s新用户%s", h.App.SysConfig.InvitePower, inviteCode.Code, user.Username),
CreatedAt: time.Now(),
})
}
// 添加邀请记录

View File

@ -126,6 +126,7 @@ func main() {
fx.Provide(handler.NewOrderHandler),
fx.Provide(handler.NewProductHandler),
fx.Provide(handler.NewConfigHandler),
fx.Provide(handler.NewPowerLogHandler),
fx.Provide(admin.NewConfigHandler),
fx.Provide(admin.NewAdminHandler),
@ -401,8 +402,9 @@ func main() {
group.GET("remove", h.RemoveChat)
group.GET("message/remove", h.RemoveMessage)
}),
fx.Provide(handler.NewTestHandler),
fx.Invoke(func(s *core.AppServer, h *handler.TestHandler) {
fx.Invoke(func(s *core.AppServer, h *handler.PowerLogHandler) {
group := s.Engine.Group("/api/powerLog/")
group.POST("list", h.List)
}),
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
err := s.Run(db)

View File

@ -6,9 +6,10 @@ type PowerLog struct {
Id uint `json:"id"`
UserId uint `json:"user_id"`
Username string `json:"username"`
Type types.PowerType `json:"name"`
Type types.PowerType `json:"type"`
TypeStr string `json:"type_str"`
Amount int `json:"amount"`
Mark types.PowerMark `json:"fund_type"`
Mark types.PowerMark `json:"mark"`
Balance int `json:"balance"`
Model string `json:"model"`
Remark string `json:"remark"`

View File

@ -1,5 +1,6 @@
.page-mj {
background-color: #282c34;
height: 100vh;
}
.page-mj .inner {
display: flex;
@ -396,6 +397,10 @@
width: auto;
height: auto;
}
.page-mj .inner .task-list-box .no-more-data {
text-align: center;
padding: 20px;
}
.mj-list-item-prompt .el-icon {
margin-left: 10px;
cursor: pointer;

View File

@ -1,5 +1,6 @@
.page-mj {
background-color: #282c34;
height 100vh
.inner {
display: flex;

View File

@ -281,6 +281,10 @@
width: auto;
height: auto;
}
.page-sd .inner .task-list-box .no-more-data {
text-align: center;
padding: 20px;
}
.page-sd .el-overlay-dialog .el-dialog {
background-color: #1a1b1e;
}

View File

@ -2,137 +2,114 @@ html,
body,
#app,
.wrapper {
width: 100%;
height: 100%;
overflow: hidden;
width: 100%;
height: 100%;
overflow: hidden;
}
body {
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
.admin-home a {
text-decoration: none;
text-decoration: none;
}
.admin-home .content-box {
position: absolute;
left: 250px;
right: 0;
top: 0;
bottom: 0;
/*padding-bottom: 30px;*/
-webkit-transition: left 0.3s ease-in-out;
transition: left 0.3s ease-in-out;
background: #f0f0f0;
overflow-y: scroll;
position: absolute;
left: 250px;
right: 0;
top: 0;
bottom: 0;
padding-bottom: 30px;
-webkit-transition: left 0.3s ease-in-out;
transition: left 0.3s ease-in-out;
background: #f0f0f0;
}
.admin-home .content-box .content {
width: auto;
padding: 10px;
box-sizing: border-box;
/*BaseForm*/
width: auto;
height: 100vh;
overflow-y: scroll;
box-sizing: border-box;
/*BaseForm*/
}
.admin-home .content-box .content .container {
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
}
.admin-home .content-box .content .container .handle-box {
margin-bottom: 20px;
margin-bottom: 20px;
}
.admin-home .content-box .content .crumbs {
margin: 10px 0;
margin: 10px 0;
}
.admin-home .content-box .content .el-table th {
background-color: #f5f7fa !important;
background-color: #f5f7fa !important;
}
.admin-home .content-box .content .pagination {
margin: 20px 0;
display: flex;
justify-content: center;
width: 100%;
margin: 20px 0;
display: flex;
justify-content: center;
width: 100%;
}
.admin-home .content-box .content .plugins-tips {
padding: 20px 10px;
margin-bottom: 20px;
padding: 20px 10px;
margin-bottom: 20px;
}
.admin-home .content-box .content .el-button + .el-tooltip {
margin-left: 10px;
margin-left: 10px;
}
.admin-home .content-box .content .el-table tr:hover {
background: #f6faff;
background: #f6faff;
}
.admin-home .content-box .content .mgb20 {
margin-bottom: 20px;
margin-bottom: 20px;
}
.admin-home .content-box .content .move-enter-active,
.admin-home .content-box .content .move-leave-active {
transition: opacity 0.1s ease;
transition: opacity 0.1s ease;
}
.admin-home .content-box .content .move-enter-from,
.admin-home .content-box .content .move-leave-to {
opacity: 0;
opacity: 0;
}
.admin-home .content-box .content .form-box {
width: 600px;
width: 600px;
}
.admin-home .content-box .content .form-box .line {
text-align: center;
text-align: center;
}
.admin-home .content-box .content .el-time-panel__content::after,
.admin-home .content-box .content .el-time-panel__content::before {
margin-top: -7px;
margin-top: -7px;
}
.admin-home .content-box .content .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
padding-bottom: 0;
padding-bottom: 0;
}
.admin-home .content-box .content [class*=" el-icon-"],
.admin-home .content-box .content [class^=el-icon-] {
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.admin-home .content-box .content .el-sub-menu [class^=el-icon-] {
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
font-size: 18px;
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
font-size: 18px;
}
.admin-home .content-box .content [hidden] {
display: none !important;
display: none !important;
}
.admin-home .content-collapse {
left: 65px;
left: 65px;
}

View File

@ -36,8 +36,7 @@ body {
.content {
width: auto;
height: 100%;
padding: 10px;
height: 100vh;
overflow-y: scroll;
box-sizing: border-box;

View File

@ -292,4 +292,9 @@
}
}
}
.no-more-data {
text-align center
padding 20px
}
}

View File

@ -69,6 +69,7 @@
<el-input placeholder="手机号码"
size="large"
v-model="data.username"
maxlength="11"
autocomplete="off">
<template #prefix>
<el-icon>

View File

@ -57,7 +57,6 @@ const changeNav = (item) => {
@import '@/assets/iconfont/iconfont.css';
.home {
display: flex;
background-color: #25272D;
height 100vh
width 100%
@ -67,6 +66,7 @@ const changeNav = (item) => {
width 70px
padding 10px 6px
border-right: 1px solid #3c3c3c
background-color: #25272D
.logo {
display flex

View File

@ -391,88 +391,93 @@
</div>
<h2>创作记录</h2>
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.7)">
<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">
正在加载图片
</div>
</template>
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)">
<div v-if="finishedJobs.length > 0">
<ItemList :items="finishedJobs" :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">
正在加载图片
</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>
<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['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">
<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">
<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-mj"
: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-mj"
: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>
<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>
</template>
</ItemList>
<div class="no-more-data" v-if="isOver">
<span>没有更多数据了</span>
<i class="iconfont icon-face"></i>
</div>
</div>
<el-empty :image-size="100" v-else/>
</div> <!-- end finish job list-->
</div>
@ -594,7 +599,7 @@ const rewritePrompt = () => {
showLoginDialog.value = true
return
}
translating.value = true
httpPost("/api/prompt/rewrite", {"prompt": params.value.prompt}).then(res => {
params.value.prompt = res.data

View File

@ -350,43 +350,50 @@
<el-empty :image-size="100" v-else/>
</div>
<h2>创作记录</h2>
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.7)">
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0" :width="240" :gap="16">
<template #default="scope">
<div class="job-item animate" @click="showTask(scope.item)">
<el-image
:src="scope.item['img_url']+'?imageView2/1/w/240/h/240/q/75'"
fit="cover"
loading="lazy">
<template #placeholder>
<div class="image-slot">
正在加载图片
</div>
</template>
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)">
<div v-if="finishedJobs.length > 0">
<ItemList :items="finishedJobs" :width="240" :gap="16">
<template #default="scope">
<div class="job-item animate" @click="showTask(scope.item)">
<el-image
:src="scope.item['img_url']+'?imageView2/1/w/240/h/240/q/75'"
fit="cover"
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>
<template #error>
<div class="image-slot">
<el-icon>
<Picture/>
</el-icon>
</div>
</template>
</el-image>
<div class="remove">
<el-button type="danger" :icon="Delete" @click="removeImage($event,scope.item)" circle/>
<el-button type="warning" v-if="scope.item.publish"
@click="publishImage($event,scope.item, false)"
circle>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage($event,scope.item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<div class="remove">
<el-button type="danger" :icon="Delete" @click="removeImage($event,scope.item)" circle/>
<el-button type="warning" v-if="scope.item.publish"
@click="publishImage($event,scope.item, false)"
circle>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage($event,scope.item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
</div>
</div>
</div>
</template>
</ItemList>
</template>
</ItemList>
<div class="no-more-data" v-if="isOver">
<span>没有更多数据了</span>
<i class="iconfont icon-face"></i>
</div>
</div>
<el-empty :image-size="100" v-else/>
</div> <!-- end finish job list-->
</div>

View File

@ -1,31 +1,149 @@
<template>
<div class="power-log" :style="{ height: winHeight + 'px' }">
<div class="power-log" v-loading="loading">
<div class="inner">
<h2>消费日志</h2>
<div class="list-box" :style="{height: listBoxHeight + 'px'}">
<div class="handle-box">
<el-input v-model="query.model" placeholder="模型" class="handle-input mr10" clearable></el-input>
<el-date-picker
v-model="query.date"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="margin: 0 10px;width: 200px; position: relative;top:3px;"
/>
<el-button type="primary" :icon="Search" @click="fetchData">搜索</el-button>
</div>
<el-row v-if="items.length > 0">
<el-table :data="items" :row-key="row => row.id" table-layout="auto" border>
<el-table-column prop="username" label="用户"/>
<el-table-column prop="model" label="模型"/>
<el-table-column prop="type" label="类型">
<template #default="scope">
<el-tag size="small" :type="tagColors[scope.row.type]">{{ scope.row.type_str }}</el-tag>
</template>
</el-table-column>
<el-table-column label="数额">
<template #default="scope">
<div>
<el-text type="success" v-if="scope.row.mark === 1">+{{ scope.row.amount }}</el-text>
<el-text type="danger" v-if="scope.row.mark === 0">-{{ scope.row.amount }}</el-text>
</div>
</template>
</el-table-column>
<el-table-column prop="balance" label="余额"/>
<el-table-column label="发生时间">
<template #default="scope">
<span>{{ dateFormat(scope.row['created_at']) }}</span>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注"/>
</el-table>
<div class="pagination">
<el-pagination v-if="total > 0" background
layout="total,prev, pager, next"
:hide-on-single-page="true"
v-model:current-page="page"
v-model:page-size="pageSize"
@current-change="fetchData()"
:total="total"/>
</div>
</el-row>
<el-empty :image-size="100" v-else/>
</div>
</div>
</div>
</template>
<script setup>
import {ref} from "vue"
import {onMounted, ref} from "vue"
import {dateFormat} from "@/utils/libs";
import {Back, DocumentCopy, Search} from "@element-plus/icons-vue";
import Clipboard from "clipboard";
import {ElMessage} from "element-plus";
import {httpPost} from "@/utils/http";
const items = ref([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(20)
const loading = ref(false)
const listBoxHeight = window.innerHeight - 117
const query = ref({
model: "",
date: []
})
const tagColors = ref(["", "success", "primary", "danger", "info", "warning"])
onMounted(() => {
fetchData()
const clipboard = new Clipboard('.copy-order-no');
clipboard.on('success', () => {
ElMessage.success("复制成功!");
})
clipboard.on('error', () => {
ElMessage.error('复制失败!');
})
})
//
const fetchData = () => {
loading.value = true
httpPost('/api/powerLog/list', {
model: query.value.model,
date: query.value.date,
page: page.value,
page_size: pageSize.value
}).then((res) => {
if (res.data) {
items.value = res.data.items
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.page_size
}
loading.value = false
}).catch(e => {
loading.value = false
ElMessage.error("获取数据失败:" + e.message);
})
}
const winHeight = ref(window.innerHeight)
</script>
<style lang="stylus" scoped>
.power-log {
display: flex;
justify-content: center;
background-color: #282c34;
color #ffffff
background-color #ffffff
.inner {
display flex
justify-content left
width 100%
padding 0 20px 20px 20px
}
.list-box {
.handle-box {
padding 20px 0
.el-input {
max-width 150px
}
.el-date-editor {
max-width 200px;
}
}
.pagination {
display flex
justify-content center
width 100%
padding 20px
}
}
}
}
</style>

View File

@ -44,6 +44,15 @@
</div>
</div>
</el-form-item>
<el-form-item label="MidJourney算力" prop="mj_power">
<el-input v-model.number="system['mj_power']" placeholder="使用MidJourney画一张图消耗算力"/>
</el-form-item>
<el-form-item label="Stable-Diffusion算力" prop="sd_power">
<el-input v-model.number="system['sd_power']" placeholder="使用Stable-Diffusion画一张图消耗算力"/>
</el-form-item>
<el-form-item label="DALL-E-3算力" prop="dall_power">
<el-input v-model.number="system['dall_power']" placeholder="使用DALL-E-3画一张图消耗算力"/>
</el-form-item>
<el-form-item label="开放注册" prop="enabled_register">
<el-switch v-model="system['enabled_register']"/>
<el-tooltip