add back-to-top component for all list page

This commit is contained in:
RockYang 2024-07-29 11:00:53 +08:00
parent abdf5298fe
commit 5132d52a44
9 changed files with 137 additions and 53 deletions

View File

@ -456,15 +456,44 @@ func (h *MidJourneyHandler) Remove(c *gin.Context) {
resp.ERROR(c, "记录不存在") resp.ERROR(c, "记录不存在")
return return
} }
// remove job recode // remove job recode
res := h.DB.Delete(&job) tx := h.DB.Begin()
if res.Error != nil { if err := tx.Delete(&job).Error; err != nil {
resp.ERROR(c, res.Error.Error()) tx.Rollback()
resp.ERROR(c, err.Error())
return return
} }
// refund power
err := tx.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("power", gorm.Expr("power + ?", job.Power)).Error
if err != nil {
tx.Rollback()
resp.ERROR(c, err.Error())
return
}
var user model.User
h.DB.Where("id = ?", job.UserId).First(&user)
err = tx.Create(&model.PowerLog{
UserId: user.Id,
Username: user.Username,
Type: types.PowerConsume,
Amount: job.Power,
Balance: user.Power + job.Power,
Mark: types.PowerAdd,
Model: "mid-journey",
Remark: fmt.Sprintf("绘画任务失败退回算力。任务ID%s", job.TaskId),
CreatedAt: time.Now(),
}).Error
if err != nil {
tx.Rollback()
resp.ERROR(c, err.Error())
return
}
tx.Commit()
// remove image // remove image
err := h.uploader.GetUploadHandler().Delete(job.ImgURL) err = h.uploader.GetUploadHandler().Delete(job.ImgURL)
if err != nil { if err != nil {
logger.Error("remove image failed: ", err) logger.Error("remove image failed: ", err)
} }

View File

@ -8,7 +8,6 @@ package mj
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import ( import (
"fmt"
"geekai/core/types" "geekai/core/types"
logger2 "geekai/logger" logger2 "geekai/logger"
"geekai/service" "geekai/service"
@ -188,28 +187,6 @@ func (p *ServicePool) SyncTaskProgress() {
} }
for _, job := range jobs { for _, job := range jobs {
// 失败或者 30 分钟还没完成的任务删除并退回算力
if time.Now().Sub(job.CreatedAt) > time.Minute*30 {
p.db.Delete(&job)
// 退回算力
tx := p.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("power", gorm.Expr("power + ?", job.Power))
if tx.Error == nil && tx.RowsAffected > 0 {
var user model.User
p.db.Where("id = ?", job.UserId).First(&user)
p.db.Create(&model.PowerLog{
UserId: user.Id,
Username: user.Username,
Type: types.PowerConsume,
Amount: job.Power,
Balance: user.Power + job.Power,
Mark: types.PowerAdd,
Model: "mid-journey",
Remark: fmt.Sprintf("绘画任务失败退回算力。任务ID%s", job.TaskId),
CreatedAt: time.Now(),
})
}
continue
}
if servicePlus := p.getService(job.ChannelId); servicePlus != nil { if servicePlus := p.getService(job.ChannelId); servicePlus != nil {
_ = servicePlus.Notify(job) _ = servicePlus.Notify(job)
} }

View File

@ -79,7 +79,7 @@
background-color #383838 background-color #383838
border 1px solid #454545 border 1px solid #454545
border-radius 5px border-radius 5px
padding 10px padding 5px
margin-bottom 10px margin-bottom 10px
display flex display flex
flex-flow column flex-flow column
@ -91,12 +91,13 @@
} }
.el-image { .el-image {
height 60px height 30px
width 100% width 100%
} }
.text { .text {
margin-top 6px margin-top 4px
font-size 12px
} }
} }

View File

@ -0,0 +1,77 @@
<template>
<button v-if="showButton" @click="scrollToTop" class="scroll-to-top" :style="{bottom: bottom + 'px', right: right + 'px', backgroundColor: bgColor}">
<el-icon><ArrowUpBold /></el-icon>
</button>
</template>
<script>
import {ArrowUpBold} from "@element-plus/icons-vue";
export default {
name: 'BackTop',
components: {ArrowUpBold},
props: {
bottom: {
type: Number,
default: 30
},
right: {
type: Number,
default: 30
},
bgColor: {
type: String,
default: '#007bff'
}
},
data() {
return {
showButton: false
};
},
mounted() {
this.checkScroll();
window.addEventListener('resize', this.checkScroll);
this.$el.parentElement.addEventListener('scroll', this.checkScroll);
},
beforeUnmount() {
window.removeEventListener('resize', this.checkScroll);
this.$el.parentElement.removeEventListener('scroll', this.checkScroll);
},
methods: {
scrollToTop() {
const container = this.$el.parentElement;
container.scrollTo({
top: 0,
behavior: 'smooth'
});
},
checkScroll() {
const container = this.$el.parentElement;
this.showButton = container.scrollTop > 50;
}
}
}
</script>
<style scoped lang="stylus">
.scroll-to-top {
position: fixed;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
outline: none;
transition: opacity 0.3s;
width 40px
height 40px
display flex
justify-content center
align-items center
font-size 20px
&:hover {
opacity: 0.6;
}
}
</style>

View File

@ -118,6 +118,8 @@
v-if="item.type==='prompt'" :data="item" :list-style="listStyle"/> v-if="item.type==='prompt'" :data="item" :list-style="listStyle"/>
<chat-reply v-else-if="item.type==='reply'" :data="item" @regen="reGenerate" :read-only="false" :list-style="listStyle"/> <chat-reply v-else-if="item.type==='reply'" :data="item" @regen="reGenerate" :read-only="false" :list-style="listStyle"/>
</div> </div>
<back-top :right="30" :bottom="100" bg-color="#19C27D"/>
</div><!-- end chat box --> </div><!-- end chat box -->
<div class="input-box"> <div class="input-box">
@ -220,6 +222,8 @@ import FileSelect from "@/components/FileSelect.vue";
import FileList from "@/components/FileList.vue"; import FileList from "@/components/FileList.vue";
import ChatSetting from "@/components/ChatSetting.vue"; import ChatSetting from "@/components/ChatSetting.vue";
import axios from "axios"; import axios from "axios";
import BackTop from "@/components/BackTop.vue";
import {showMessageError} from "@/utils/dialog";
const title = ref('ChatGPT-智能助手'); const title = ref('ChatGPT-智能助手');
const models = ref([]) const models = ref([])
@ -603,18 +607,6 @@ const connect = function (chat_id, role_id) {
} }
} }
//
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.value}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`); const _socket = new WebSocket(host + `/api/chat/new?session_id=${sessionId.value}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`);
_socket.addEventListener('open', () => { _socket.addEventListener('open', () => {
chatData.value = []; // chatData.value = []; //
@ -636,8 +628,6 @@ const connect = function (chat_id, role_id) {
} else { // } else { //
loadChatHistory(chat_id); loadChatHistory(chat_id);
} }
//
sendHeartbeat()
}); });
_socket.addEventListener('message', event => { _socket.addEventListener('message', event => {
@ -648,7 +638,7 @@ const connect = function (chat_id, role_id) {
reader.onload = () => { reader.onload = () => {
const data = JSON.parse(String(reader.result)); const data = JSON.parse(String(reader.result));
if (data.type === 'start') { if (data.type === 'start') {
const prePrompt = chatData.value[chatData.value.length-1].content const prePrompt = chatData.value[chatData.value.length-1]?.content
chatData.value.push({ chatData.value.push({
type: "reply", type: "reply",
id: randString(32), id: randString(32),
@ -689,9 +679,11 @@ const connect = function (chat_id, role_id) {
} else { } else {
lineBuffer.value += data.content; lineBuffer.value += data.content;
const reply = chatData.value[chatData.value.length - 1] const reply = chatData.value[chatData.value.length - 1]
if (reply) {
reply['orgContent'] = lineBuffer.value; reply['orgContent'] = lineBuffer.value;
reply['content'] = md.render(processContent(lineBuffer.value)); reply['content'] = md.render(processContent(lineBuffer.value));
} }
}
// //
nextTick(() => { nextTick(() => {
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight) document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
@ -716,7 +708,7 @@ const connect = function (chat_id, role_id) {
connect(chat_id, role_id) connect(chat_id, role_id)
}).catch(() => { }).catch(() => {
loading.value = true loading.value = true
setTimeout(() => connect(chat_id, role_id), 3000) showMessageError("会话已断开,刷新页面...")
}); });
}); });

View File

@ -174,7 +174,7 @@
</div> <!-- end finish job list--> </div> <!-- end finish job list-->
</div> </div>
</div> </div>
<back-top :right="30" :bottom="30" bg-color="#0f7a71"/>
</div><!-- end task list box --> </div><!-- end task list box -->
</div> </div>
@ -193,6 +193,7 @@ import Clipboard from "clipboard";
import {checkSession} from "@/action/session"; import {checkSession} from "@/action/session";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue"; import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue";
const listBoxHeight = ref(0) const listBoxHeight = ref(0)
// const paramBoxHeight = ref(0) // const paramBoxHeight = ref(0)

View File

@ -593,6 +593,7 @@
</div> <!-- end finish job list--> </div> <!-- end finish job list-->
</div> </div>
</div> </div>
<back-top :right="30" :bottom="30" bg-color="#0f7a71"/>
</div><!-- end task list box --> </div><!-- end task list box -->
</div> </div>
@ -613,6 +614,7 @@ import {getSessionId} from "@/store/session";
import {copyObj, removeArrayItem} from "@/utils/libs"; import {copyObj, removeArrayItem} from "@/utils/libs";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue"; import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue";
const listBoxHeight = ref(0) const listBoxHeight = ref(0)
const paramBoxHeight = ref(0) const paramBoxHeight = ref(0)
@ -1014,7 +1016,7 @@ const publishImage = (item, action) => {
item.publish = action item.publish = action
page.value = 0 page.value = 0
isOver.value = false isOver.value = false
fetchFinishJobs() item.publish = action
}).catch(e => { }).catch(e => {
ElMessage.error(text + "失败:" + e.message) ElMessage.error(text + "失败:" + e.message)
}) })

View File

@ -345,7 +345,7 @@
</div> <!-- end finish job list--> </div> <!-- end finish job list-->
</div> </div>
</div> </div>
<back-top :right="30" :bottom="30" bg-color="#0f7a71"/>
</div><!-- end task list box --> </div><!-- end task list box -->
</div> </div>
@ -476,6 +476,7 @@ import {useRouter} from "vue-router";
import {getSessionId} from "@/store/session"; import {getSessionId} from "@/store/session";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue"; import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue";
const listBoxHeight = ref(0) const listBoxHeight = ref(0)
// const paramBoxHeight = ref(0) // const paramBoxHeight = ref(0)
@ -755,7 +756,7 @@ const publishImage = (event, item, action) => {
item.publish = action item.publish = action
page.value = 0 page.value = 0
isOver.value = false isOver.value = false
fetchFinishJobs() item.publish = action
}).catch(e => { }).catch(e => {
ElMessage.error(text + "失败:" + e.message) ElMessage.error(text + "失败:" + e.message)
}) })

View File

@ -163,7 +163,10 @@
<i class="iconfont icon-face"></i> <i class="iconfont icon-face"></i>
</div> </div>
</div> <back-top :right="30" :bottom="30" bg-color="#0f7a71"/>
</div><!-- end of waterfall -->
</div> </div>
<!-- 任务详情弹框 --> <!-- 任务详情弹框 -->
<el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true"> <el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true">
@ -301,6 +304,7 @@ import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import Clipboard from "clipboard"; import Clipboard from "clipboard";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import BackTop from "@/components/BackTop.vue";
const data = ref({ const data = ref({
"mj": [], "mj": [],