优化显示逻辑

This commit is contained in:
王佑琳 2026-03-10 02:32:14 +08:00
parent 6c2ac44b33
commit e6290b53e5
10 changed files with 290 additions and 179 deletions

View File

@ -2,9 +2,11 @@
<Transition name="slide-up"> <Transition name="slide-up">
<div class="input-container" :class="{ generate : !props.isGenerate }" @click="handleContainerClick"> <div class="input-container" :class="{ generate : !props.isGenerate }" @click="handleContainerClick">
<div v-if="!props.isGenerate" class="title">AI绘画2026</div> <div v-if="!props.isGenerate" class="title">AI绘画2026</div>
<div v-if="useDisplay.Sender_variant === 'default'" class="scroll-to-bottom-btn" @click.stop="handleScrollToBottom"> <div v-if="useDisplay.Sender_variant === 'default'" class="scroll-to-bottom-btn" @click.stop="handleScrollToBottom">
<div class="scroll-to-bottom-text">回到底部</div> <div class="scroll-to-bottom-text">回到底部</div>
</div> </div>
<Sender :key="useDisplay.Sender_variant" v-model="prompt" :variant="useDisplay.Sender_variant" :placeholder="promptPlaceholder" :submit-btn-disabled="isgerenate.value" :auto-size="autoSizeConfig"> <Sender :key="useDisplay.Sender_variant" v-model="prompt" :variant="useDisplay.Sender_variant" :placeholder="promptPlaceholder" :submit-btn-disabled="isgerenate.value" :auto-size="autoSizeConfig">
<template #prefix> <template #prefix>
<div v-show="useDisplay.Sender_variant !== 'default'" class="prefix-self-wrap"> <div v-show="useDisplay.Sender_variant !== 'default'" class="prefix-self-wrap">
@ -18,7 +20,7 @@
</div> </div>
<Model v-model="model" /> <Model v-model="model" />
<Proportion v-model="proportion" /> <Proportion v-model="proportion" v-model:resolution="resolution" />
<Quantity v-model="quantity" /> <Quantity v-model="quantity" />
</div> </div>
</template> </template>
@ -82,9 +84,10 @@ const uploadRef = ref(null)
const model = ref('flux') const model = ref('flux')
const proportion = ref('9:16') const proportion = ref('9:16')
const quantity = ref(1) const quantity = ref(1)
const resolution = ref('1k')
const promptPlaceholder = '描述你想生成的画面和动作。' const promptPlaceholder = '描述你想生成的画面和动作。'
const prompt = ref('') const prompt = ref('一个女孩在树下吃苹果')
const imageurl = ref('') const imageurl = ref('')
const imageurlShow = ref('') const imageurlShow = ref('')
const isgerenate = ref(false) const isgerenate = ref(false)
@ -164,11 +167,16 @@ const handleStart = async () => {
isgerenate.value = true isgerenate.value = true
console.log('生成开始', isgerenate.value) console.log('生成开始', isgerenate.value)
const data = { const data = {
videoImg: imageurl.value, AIGC: 'Painting',
text: prompt.value, platform: 'runninghub',
file_type: 'video', file_type: 'image',
modelName: model.value,
prompt: prompt.value,
quantity: quantity.value,
aspect_ratio: proportion.value,
resolution: resolution.value,
} }
await generate(data, 1, 1) await generate('text', data)
console.log('生成中', isgerenate.value) console.log('生成中', isgerenate.value)
} }

View File

@ -63,16 +63,25 @@ const props = defineProps({
modelValue: { modelValue: {
type: String, type: String,
default: '1:1' default: '1:1'
},
resolution: {
type: String,
default: '2k'
} }
}) })
const emit = defineEmits(['update:modelValue', 'update:width', 'update:height']) const emit = defineEmits(['update:modelValue', 'update:resolution', 'update:width', 'update:height'])
const proportion = computed({ const proportion = computed({
get: () => props.modelValue, get: () => props.modelValue,
set: (value) => emit('update:modelValue', value) set: (value) => emit('update:modelValue', value)
}) })
const resolution = computed({
get: () => props.resolution,
set: (value) => emit('update:resolution', value)
})
const proportionOptions = [ const proportionOptions = [
{ value: '智能', label: '智能' }, { value: '智能', label: '智能' },
{ value: '21:9', label: '21:9' }, { value: '21:9', label: '21:9' },
@ -91,7 +100,6 @@ const resolutionOptions = [
{ value: '4k', label: '超清 4K' } { value: '4k', label: '超清 4K' }
] ]
const resolution = ref('2k')
const width = ref(2048) const width = ref(2048)
const height = ref(2048) const height = ref(2048)
@ -199,8 +207,9 @@ const getProportionStyle = (value) => {
} }
} }
// watch(() => [props.modelValue, props.resolution], () => {
updateDimensionsByResolution(resolution.value) updateDimensionsByResolution(resolution.value)
}, { immediate: true })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

4
src/config/index.js Normal file
View File

@ -0,0 +1,4 @@
import * as runninghub from './runninghub/index.js'
// import * as suno from './suno.js'
export default { runninghub }

View File

@ -0,0 +1,81 @@
export function Playload(data, type) {
data = getWidthHeight(data)
console.log('宽与高', data)
if(data.modelName === 'flux'){
return flux(data)
}
}
export function result(result) {
if (result.code === 0 && result.msg === 'success') {
return { type: true, url: result.data[0].fileUrl }
}
return { type: false, message: result.data.exception_message }
}
function getWidthHeight(data) {
// 去除分辨率字符串中的'p'并转换为数字
// const resolution = 720
const resolution = Number.parseInt(data.resolution.replace('p', '')) || Number.parseInt(data.resolution)
// 解析宽高比
const aspectRatioParts = data.aspect_ratio.split(':') || data.aspect_ratio.split('')
const widthRatio = Number.parseInt(aspectRatioParts[0])
const heightRatio = Number.parseInt(aspectRatioParts[1])
if (widthRatio > heightRatio) {
data.height = resolution
data.width = Math.round(resolution * widthRatio / heightRatio)
} else {
data.width = resolution
data.height = Math.round(resolution * heightRatio / widthRatio)
}
console.log(data.width, data.height)
return data
}
function LTX2(data) {
const nodeInfoList = [
{ nodeId: '9', fieldName: 'text', fieldValue: data.prompt },
{ nodeId: '40', fieldName: 'text', fieldValue: data.aspect_ratio },
{ nodeId: '39', fieldName: 'text', fieldValue: data.resolution },
{ nodeId: '29', fieldName: 'index', fieldValue: '' }
]
switch (index){
case 1:
nodeInfoList[3].fieldValue = '1'
break
case 2:
nodeInfoList[3].fieldValue = '2'
break
case 3:
nodeInfoList[3].fieldValue = '3'
break
case 4:
nodeInfoList[3].fieldValue = '4'
break
}
if (data.image1) nodeInfoList.push({ nodeId: '2', fieldName: 'image', fieldValue: data.image1 })
if (data.image2) nodeInfoList.push({ nodeId: '3', fieldName: 'image', fieldValue: data.image2 })
if (data.image3) nodeInfoList.push({ nodeId: '4', fieldName: 'image', fieldValue: data.image3 })
if (data.image4) nodeInfoList.push({ nodeId: '13', fieldName: 'image', fieldValue: data.image4 })
if (data.image5) nodeInfoList.push({ nodeId: '14', fieldName: 'image', fieldValue: data.image5 })
return {
workflowId: '2031032712240304130',
nodeInfoList
}
}
function flux(data) {
const nodeInfoList = [
{ nodeId: '23', fieldName: 'text', fieldValue: data.prompt },
{ nodeId: '129', fieldName: 'aspect_ratio', fieldValue: data.aspect_ratio },
{ nodeId: '101', fieldName: 'control_after_generate', fieldValue: 'randomize' }
]
return {
workflowId: '2011689651156819970',
nodeInfoList
}
}

View File

@ -29,7 +29,7 @@ const router = createRouter({
routes routes
}) })
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from) => {
if(to.query.token){ if(to.query.token){
setToken(to.query.token) setToken(to.query.token)
} else { } else {
@ -37,7 +37,7 @@ router.beforeEach(async (to, from, next) => {
const token = getToken() const token = getToken()
if (!token) { if (!token) {
// 没有 token重定向到登录页 // 没有 token重定向到登录页
return next('/login') return '/login'
} }
} }
@ -47,7 +47,7 @@ router.beforeEach(async (to, from, next) => {
const userStore = useUserStore() const userStore = useUserStore()
// 如果访问的是白名单路径,直接放行 // 如果访问的是白名单路径,直接放行
if (whiteList.includes(to.path)) { if (whiteList.includes(to.path)) {
return next() return true
} }
// 检查 token 是否有效 // 检查 token 是否有效
@ -60,15 +60,15 @@ router.beforeEach(async (to, from, next) => {
// 如果用户信息不存在,则从服务器获取 // 如果用户信息不存在,则从服务器获取
await userStore.getInfo() await userStore.getInfo()
} }
next() return true
} else { } else {
// token 无效,重定向到登录页 // token 无效,重定向到登录页
next('/login') return '/login'
} }
} catch (error) { } catch (error) {
// 验证过程中出错,重定向到登录页 // 验证过程中出错,重定向到登录页
console.error('验证 token 时出错:', error) console.error('验证 token 时出错:', error)
next('/login') return '/login'
} }
}) })

View File

@ -1,53 +1,71 @@
const DisplayStoreSetup = () => { const DisplayStoreSetup = () => {
const Sender_variant = ref('updown') const Sender_variant = ref('updown')
const scrollerRef = ref(null) const scrollerRef = ref(null)
const tempList = ref([])
const scrollToBottom = () => { const addGeneratingItem = (item) => {
console.log('store - 尝试滚动到底部') const newItem = {
id: item.taskId || crypto.randomUUID(),
status: 'generate',
text: item.text || '生成中...',
name: item.name || '生成中...',
type: item.type || 'image',
time: item.time || new Date().toLocaleString(),
files: [],
...item
}
tempList.value.push(newItem)
return newItem
}
const updateItemToSuccess = (taskId, fileUrl) => {
const index = tempList.value.findIndex(item => item.id === taskId)
if (index !== -1) {
tempList.value[index].status = 'success'
tempList.value[index].files = [fileUrl]
}
}
const initHistoryList = (historyList) => {
tempList.value = historyList
}
const scrollToBottom = async () => {
console.log('store - 滚动到底部')
const refValue = scrollerRef.value const refValue = scrollerRef.value
console.log('store - scrollerRef.value:', refValue)
if (refValue) { if (!refValue) {
console.log('store - scrollerRef.value.$el:', refValue.$el)
try {
if (typeof refValue.scrollToItem === 'function') {
console.log('store - 使用 scrollToItem 方法')
refValue.scrollToItem(refValue.items?.length - 1)
} else {
console.log('store - scrollToItem 方法不存在')
const scrollerEl = refValue.$el
if (!scrollerEl) {
console.log('store - scrollerEl 不存在')
return
}
console.log('store - scrollerEl:', scrollerEl)
const viewport = scrollerEl.querySelector('.vue-recycle-scroller__viewport')
if (viewport) {
console.log('store - 找到 viewport 元素')
viewport.scrollTop = viewport.scrollHeight
console.log('store - viewport.scrollTop:', viewport.scrollTop)
console.log('store - viewport.scrollHeight:', viewport.scrollHeight)
setTimeout(() => {
viewport.scrollTop = viewport.scrollHeight
}, 50)
}
}
} catch (error) {
console.error('store - 滚动出错:', error)
}
} else {
console.log('store - scrollerRef 不存在') console.log('store - scrollerRef 不存在')
return
}
try {
const scrollerEl = refValue.$el
if (scrollerEl) {
const viewport = scrollerEl.querySelector('.vue-recycle-scroller__viewport')
if (viewport) {
console.log('store - 原生滚动, scrollHeight:', viewport.scrollHeight)
viewport.scrollTop = viewport.scrollHeight
}
}
if (typeof refValue.scrollToItem === 'function' && tempList.value && tempList.value.length > 0) {
console.log('store - scrollToItem, index:', tempList.value.length - 1)
await nextTick()
refValue.scrollToItem(tempList.value.length - 1)
}
} catch (error) {
console.error('store - 滚动出错:', error)
} }
} }
return { return {
Sender_variant, Sender_variant,
scrollerRef, scrollerRef,
tempList,
addGeneratingItem,
updateItemToSuccess,
initHistoryList,
scrollToBottom scrollToBottom
} }
} }

View File

@ -1,74 +1,23 @@
import { useParamStore } from '@/stores' import outPlatform from '@/config/index'
export async function getFormattedTime(date = new Date()) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0') // 月份从0开始需要+1
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
}
// 处理音频生成任务的数据并返回 // 处理音频生成任务的数据并返回
export async function createTask(taskType = 1, params, title = '模特展示图') { export async function createTask(data, type, taskId, token) {
const paramStore = useParamStore() console.log(data, type)
const data = { const payload = await outPlatform[data.platform].Playload(data)
taskId: params.taskId,
taskRootId: params.taskRootId || paramStore.taskRootId, return {
parentTaskId: params.parentTaskId || '0', AIGC: data.AIGC,
AIGC: 'huanda', platform: data.platform,
platform: 'runninghub', prompt: data.prompt,
taskType, taskType: type === 'text' ? 1 : 2,
modelName: 'Flux', modelName: data.modelName,
title, payload,
file_type: params.file_type, taskId,
payload: {}, token
createTime: params.time,
parentCreateTime: params.parentCreateTime || '',
parentIndex: params.parentIndex || '',
token: params.token
} }
if (taskType === 1) {
data.payload = workflows.huanda
data.payload.nodeInfoList[0].fieldValue = paramStore.params.clothes
data.payload.nodeInfoList[1].fieldValue = paramStore.params.model
data.payload.nodeInfoList[2].fieldValue = paramStore.params.pose
data.payload.nodeInfoList[3].fieldValue = paramStore.params.background
data.payload.nodeInfoList[4].fieldValue = paramStore.params.model ? 0 : 1
data.payload.nodeInfoList[5].fieldValue = paramStore.params.pose ? 0 : 1
data.payload.nodeInfoList[6].fieldValue = paramStore.params.background ? 0 : 1
data.payload.nodeInfoList[7].fieldValue = params.prompt
data.payload.nodeInfoList[7].fieldValue = params.aspectRatio
} else if (taskType === 2) { // 对话修改
data.parentTaskId = params.parentTaskId
data.payload = workflows.talk
data.payload.nodeInfoList[0].fieldValue = params.text
data.payload.nodeInfoList[1].fieldValue = params.talkImg
} else if (taskType === 3) { // 生成视频
data.parentTaskId = params.parentTaskId
data.payload = workflows.video
data.payload.nodeInfoList[0].fieldValue = params.text
data.payload.nodeInfoList[1].fieldValue = params.videoImg
} else if (taskType === 4) { // AI生成模特
data.payload = workflows.model
data.payload.nodeInfoList[0].fieldValue = params.text
data.payload.nodeInfoList[1].fieldValue = params.aspectRatio
} else if (taskType === 5 || taskType === 6) { // AI生成服装背景
// data.parentTaskId = params.parentTaskId
data.payload = workflows.background_pose
data.payload.nodeInfoList[0].fieldValue = params.text
data.payload.nodeInfoList[1].fieldValue = params.aspectRatio
}
console.log('data:', data)
return data
} }
// 获取音频结果 // 获取结果
export async function getTask(result) { export async function getTask(result) {
if (result.code === 0 && result.msg === 'success') { if (result.code === 0 && result.msg === 'success') {
return { type: true, url: result.data[0].fileUrl } return { type: true, url: result.data[0].fileUrl }

View File

@ -1,8 +1,8 @@
import { ElNotification } from 'element-plus' import { ElNotification } from 'element-plus'
import { h, ref } from 'vue' import { h, ref } from 'vue'
import { useDisplayStore, useParamStore, useUserStore } from '@/stores' import { useDisplayStore } from '@/stores'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import { createTask, getFormattedTime, getTask } from '@/utils/createTask' import { createTask, getTask } from '@/utils/createTask'
import { userError } from '@/utils/tokenError' import { userError } from '@/utils/tokenError'
export function websocketError(code, msg) { export function websocketError(code, msg) {
@ -47,14 +47,17 @@ export function websocketSuccess() {
}) })
} }
export async function generate(taskType, data, type) { export async function generate(type, data) {
const progress_text = ref('') const progress_text = ref('')
const message = ref('') const message = ref('')
const previewUrl = ref('')
const useDisplay = useDisplayStore() const useDisplay = useDisplayStore()
const token = getToken() const token = getToken()
const taskId = crypto.randomUUID() const taskId = crypto.randomUUID()
let currentTaskId = null
const result = await createTask(taskType, { text: data.prompt, aspect_ratio: data.aspectRatio, token }) const result = await createTask(data, type, taskId, token)
console.log(result)
// const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjY0NDEwODAyMjk1OTgzNzIzMCwicm5TdHIiOiJiWkVwS2JLWFJyZmRIaFFHWXZKTkdzOGdGM0JSRmxQOCJ9.5eQ2GtVdrDntQDe2tnF8vl_DhTfd2uW-KNqzvl1imc0' // const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjY0NDEwODAyMjk1OTgzNzIzMCwicm5TdHIiOiJiWkVwS2JLWFJyZmRIaFFHWXZKTkdzOGdGM0JSRmxQOCJ9.5eQ2GtVdrDntQDe2tnF8vl_DhTfd2uW-KNqzvl1imc0'
const wsURL = `${import.meta.env.VITE_API_WORKFLOW_WS}/?token=${token}` const wsURL = `${import.meta.env.VITE_API_WORKFLOW_WS}/?token=${token}`
const socket = new WebSocket(wsURL) const socket = new WebSocket(wsURL)
@ -73,7 +76,7 @@ export async function generate(taskType, data, type) {
return return
} else if (event.data === 'please give me taskId') { } else if (event.data === 'please give me taskId') {
socket.send(`setTaskId:${taskId}`) socket.send(`setTaskId:${taskId}`)
progerss_text.value = '信息提交中...' progress_text.value = '信息提交中...'
return return
} else if (event.data === 'OK! Please continue. ') { } else if (event.data === 'OK! Please continue. ') {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
@ -82,12 +85,16 @@ export async function generate(taskType, data, type) {
})) }))
return return
} else if (event.data === '任务提交成功,正在排队中...') { } else if (event.data === '任务提交成功,正在排队中...') {
progerss_text.value = '视频生成中...' progress_text.value = '视频生成中...'
// 启动进度条更新 currentTaskId = taskId
startTime.value = Date.now()
progressPercent.value = 0 useDisplay.addGeneratingItem({
progressInterval.value = window.setInterval(updateProgress, 1000) taskId: taskId,
progress.value = true text: data.prompt || '生成中...',
name: data.prompt || '生成中...',
type: type
})
return return
} }
message.value = event.data message.value = event.data
@ -114,7 +121,6 @@ export async function generate(taskType, data, type) {
// 处理链接关闭 // 处理链接关闭
socket.onclose = async (event) => { socket.onclose = async (event) => {
console.log('WebSocket已关闭:', event) console.log('WebSocket已关闭:', event)
isGenerating.value = false
// 清理心跳定时器 // 清理心跳定时器
if (heartbeatInterval) { if (heartbeatInterval) {
@ -126,9 +132,14 @@ export async function generate(taskType, data, type) {
userError() userError()
} else if (event.code === 1000 && event.reason === 'success') { } else if (event.code === 1000 && event.reason === 'success') {
console.log('收到服务器消息:', res) console.log('收到服务器消息:', res)
const result = await getVideo(tempPlatform.value, res) const result = await getTask(res)
if (result.type) { if (result.type) {
previewUrl.value = result.url previewUrl.value = result.url
if (currentTaskId) {
useDisplay.updateItemToSuccess(currentTaskId, result.url)
}
websocketSuccess() websocketSuccess()
} else { } else {
websocketError(4403, res.message) websocketError(4403, res.message)
@ -136,10 +147,7 @@ export async function generate(taskType, data, type) {
} else { } else {
websocketError(event.code, event.reason) websocketError(event.code, event.reason)
} }
progress.value = false // clearInterval(progressInterval.value)
clearInterval(progressInterval.value)
progressInterval.value = null
isGenerating.value = false
// 清理心跳定时器 // 清理心跳定时器
if (heartbeatInterval) { if (heartbeatInterval) {
clearInterval(heartbeatInterval) clearInterval(heartbeatInterval)

View File

@ -27,7 +27,7 @@ defineProps({
left: 0; left: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: rgba(255, 255, 255, 0.9); background-color: rgb(255, 255, 255);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -66,6 +66,7 @@
<script setup> <script setup>
import { useDisplayStore, useParamStore, useUserStore } from '@/stores' import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
import { storeToRefs } from 'pinia'
import Set from './components/set.vue' import Set from './components/set.vue'
import RefreshOverlay from './components/RefreshOverlay.vue' import RefreshOverlay from './components/RefreshOverlay.vue'
import Select from '@/components/Select/index.vue' import Select from '@/components/Select/index.vue'
@ -91,6 +92,7 @@ const refreshing = ref(false)
const scrollerRef = ref(null) const scrollerRef = ref(null)
const isLoadingMore = ref(false) const isLoadingMore = ref(false)
const activeTab = ref('all') const activeTab = ref('all')
const isInitializing = ref(true)
let total = 0 let total = 0
const timeOptions = [ const timeOptions = [
@ -107,27 +109,30 @@ const favoriteOptions = [
const selectedTime = ref('all') const selectedTime = ref('all')
const selectedFavorite = ref('all') const selectedFavorite = ref('all')
const tempList = ref([]) const { tempList } = storeToRefs(useDisplay)
// const tempList = ref([ // const tempList = ref([
// { id: 0, type: 'image', status: 'none', name: '', time: '2025-12-01 18:26', files: [] }, // { id: 0, type: 'image', status: 'none', name: '', time: '2025-12-01 18:26', files: [] },
// { id: 1, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] }, // { id: 1, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] },
// { id: 2, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] }, // { id: 2, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] },
// { id: 3, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] }, // { id: 3, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] },
// { id: 4, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] } // { id: 4, type: 'image', status: 'success', name: '', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] }
// ]) // ])
const list = ref() const activeFilter = ref('all')
const list = computed(() => {
const data = tempList.value || []
if (activeFilter.value === 'all') {
return data
}
return data.filter((item) => item.type === activeFilter.value)
})
const page = ref(1) const page = ref(1)
list.value = tempList.value
// //
const toggleDisplay = (newValue, oldValue) => { const toggleDisplay = (newValue, oldValue) => {
if ((newValue === 'image' || newValue === 'video') && oldValue === 'all') { activeFilter.value = newValue
list.value = tempList.value.filter((item) => item.type === newValue)
} else if ((newValue === 'image' || newValue === 'video') && (oldValue === 'image' || oldValue === 'video')) {
list.value = tempList.value.filter((item) => item.type === newValue)
} else {
list.value = tempList.value
}
console.log(list.value)
} }
// //
@ -149,8 +154,12 @@ const conversion = (newlist) => {
// //
const fetchHistory= async (isScrollTopLoad = false) => { const fetchHistory= async (isScrollTopLoad = false) => {
try { try {
if (isScrollTopLoad) {
return
}
const result = await getGenerateHistoryList({ userId: userStore.userInfo.id, chargeType: 1 }) const result = await getGenerateHistoryList({ userId: userStore.userInfo.id, chargeType: 1 })
total = result.data.length total = result.data ? result.data.length : 0
if (total === 0) { if (total === 0) {
useDisplay.Sender_variant = 'updown' useDisplay.Sender_variant = 'updown'
router.push({ name: 'home' }) router.push({ name: 'home' })
@ -175,19 +184,48 @@ const fetchHistory= async (isScrollTopLoad = false) => {
}) })
if (!isScrollTopLoad && adaptedList.length > 0) { if (!isScrollTopLoad && adaptedList.length > 0) {
tempList.value = [adaptedList[adaptedList.length - 1]] useDisplay.initHistoryList(adaptedList)
list.value = tempList.value
await nextTick()
const scrollToBottomDirect = (force = false) => {
if (scrollerRef.value) {
const el = scrollerRef.value.$el
if (el) {
const viewport = el.querySelector('.vue-recycle-scroller__viewport')
if (viewport) {
console.log('直接滚动 - scrollHeight:', viewport.scrollHeight, 'force:', force)
if (force) {
viewport.scrollTop = viewport.scrollHeight + 1000
} else {
viewport.scrollTop = viewport.scrollHeight
}
}
}
if (typeof scrollerRef.value.scrollToItem === 'function') {
console.log('直接 scrollToItem')
scrollerRef.value.scrollToItem(list.value.length - 1)
}
}
}
for (let i = 0; i < 20; i++) {
setTimeout(() => {
scrollToBottomDirect(i >= 15)
}, 60 * i)
}
setTimeout(() => { setTimeout(() => {
const remainingItems = adaptedList.slice(0, -1) scrollToBottomDirect(true)
tempList.value = [...remainingItems, ...tempList.value] setTimeout(() => {
list.value = tempList.value refreshing.value = false
console.log('添加剩余数据后列表长度:', tempList.value.length) isInitializing.value = false
}, 1000) useDisplay.scrollToBottom()
await nextTick() }, 600)
}, 1500)
} else { } else {
tempList.value = adaptedList useDisplay.initHistoryList(adaptedList)
list.value = tempList.value
} }
} catch (error) { } catch (error) {
console.error('获取历史失败:', error) console.error('获取历史失败:', error)
@ -201,25 +239,28 @@ const fetchHistory= async (isScrollTopLoad = false) => {
// //
const getList = async () => { const getList = async () => {
if (isLoadingMore.value) return if (isLoadingMore.value) return
isLoadingMore.value = true isLoadingMore.value = true
try {
await fetchHistory(true) await fetchHistory(true)
isLoadingMore.value = false } finally {
isLoadingMore.value = false
}
} }
// //
const handleScroll = (event) => { const handleScroll = (event) => {
if (isInitializing.value) return
const { scrollTop, scrollHeight, clientHeight } = event.target const { scrollTop, scrollHeight, clientHeight } = event.target
const distanceToBottom = scrollHeight - scrollTop - clientHeight const distanceToBottom = scrollHeight - scrollTop - clientHeight
if (scrollTop <= 50 && !isLoadingMore.value) { //
getList() // if (scrollTop <= 50 && !isLoadingMore.value) {
} // getList()
// }
if (distanceToBottom <= 50) { if (distanceToBottom <= 50) {
useDisplay.Sender_variant = 'updown' useDisplay.Sender_variant = 'updown'
refreshing.value = false
} else if (distanceToBottom >= 350) { } else if (distanceToBottom >= 350) {
useDisplay.Sender_variant = 'default' useDisplay.Sender_variant = 'default'
} }
@ -229,21 +270,14 @@ onMounted(() => {
console.log('display 组件已挂载') console.log('display 组件已挂载')
if (!props.loading) return if (!props.loading) return
refreshing.value = true refreshing.value = true
fetchHistory()
nextTick(() => { nextTick(() => {
console.log('设置 scrollerRef 到 store') console.log('设置 scrollerRef 到 store')
useDisplay.scrollerRef = scrollerRef.value useDisplay.scrollerRef = scrollerRef.value
useDisplay.scrollToBottom() fetchHistory()
}) })
setTimeout(() => { page.value++
console.log('setTimeout 后尝试滚动')
useDisplay.scrollToBottom()
setTimeout(() => {
refreshing.value = false
}, 1000)
}, 1000)
}) })
</script> </script>