4.0.3 (2025.09.26)
1. 新增 海螺生成视频(文生视频,图转视频,首尾帧视频) 2. 修复MJ出图的部分问题 3. 优化原创的加载速度,分批次渲染,加快界面显示速度
This commit is contained in:
parent
3d5307c8e4
commit
2b70e511d2
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "laitool-pro",
|
||||
"productName": "LaiToolPro",
|
||||
"version": "v4.0.2",
|
||||
"version": "v4.0.3",
|
||||
"description": "来推 Pro - 一款集音频处理、文案生成、图片生成、视频生成等功能于一体的多合一AI工具软件。",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "xiangbei",
|
||||
|
||||
@ -30,8 +30,8 @@ interface ISoftwareData {
|
||||
}
|
||||
|
||||
export const SoftwareData: ISoftwareData = {
|
||||
version: 'V4.0.2',
|
||||
date: '2025-09-23',
|
||||
version: 'V4.0.3',
|
||||
date: '2025-09-26',
|
||||
systemInfo: {
|
||||
documentationUrl: 'https://rvgyir5wk1c.feishu.cn/wiki/WdaWwAfDdiLOnjkywIgcaQoKnog',
|
||||
updateUrl: 'https://pvwu1oahp5m.feishu.cn/docx/CAjGdTDlboJ3nVx0cQccOuNHnvd',
|
||||
|
||||
@ -318,7 +318,7 @@ export class MJApiService extends MJBasic {
|
||||
imagePath: resData.imageUrl,
|
||||
imageUrls: resData.imageUrls
|
||||
? resData.imageUrls
|
||||
.filter((item) => item.url != null && !isEmpty(item.url))
|
||||
.filter((item) => item.url != null && !isEmpty(item.url) && !item.url.startsWith("https://cdn.midjourney.com"))
|
||||
.map((item) => item.url)
|
||||
: [],
|
||||
messageId: taskId,
|
||||
|
||||
@ -3,7 +3,8 @@ import {
|
||||
BookTaskStatus,
|
||||
BookType,
|
||||
DialogType,
|
||||
OperateBookType
|
||||
OperateBookType,
|
||||
PromptMergeType
|
||||
} from '@/define/enum/bookEnum'
|
||||
import { MJBasic } from './mjBasic'
|
||||
import { GeneralResponse } from '@/define/model/generalResponse'
|
||||
@ -155,7 +156,7 @@ export class MJServiceHandle extends MJBasic {
|
||||
sceneString = await this.presetBasicService.GetScenePresetStringByIds(sceneIds)
|
||||
}
|
||||
if (characterIds && characterIds.length > 0) {
|
||||
let res = await this.presetBasicService.GetCharacterPresetStringByIds(characterIds)
|
||||
let res = await this.presetBasicService.GetCharacterPresetStringByIds(characterIds, PromptMergeType.MJ_MERGE, this.mjGeneralSetting?.model as string)
|
||||
characterString = res.characterString
|
||||
characterUrl = res.characterUrl
|
||||
}
|
||||
@ -517,7 +518,7 @@ export class MJServiceHandle extends MJBasic {
|
||||
bookTaskDetail.name as string,
|
||||
path.join(
|
||||
bookTask.imageFolder as string,
|
||||
`subImage\\${bookTaskDetail.name}\\${new Date().getTime()}.png`
|
||||
`subImage\\${bookTaskDetail.name}`
|
||||
)
|
||||
)
|
||||
if (imageArray && imageArray.length < 4) {
|
||||
|
||||
@ -40,7 +40,8 @@ export class PresetBasicService extends PresetBasic {
|
||||
*/
|
||||
async GetCharacterPresetStringByIds(
|
||||
ids: string[],
|
||||
type: PromptMergeType = PromptMergeType.MJ_MERGE
|
||||
type: PromptMergeType = PromptMergeType.MJ_MERGE,
|
||||
mjModel: string = ''
|
||||
): Promise<{ characterString: string; characterUrl: string }> {
|
||||
await this.InitPresetBasic()
|
||||
let characterString = ''
|
||||
@ -56,10 +57,18 @@ export class PresetBasicService extends PresetBasic {
|
||||
crefCw = (element.crefCw ?? 20).toString()
|
||||
}
|
||||
}
|
||||
|
||||
//这边坐下合并
|
||||
if (characterUrl != '') {
|
||||
characterUrl = ` --cref ${characterUrl} --cw ${crefCw}`
|
||||
// 判断是不是v7
|
||||
if (mjModel == '0d33ae62-e0a8-4429-89e4-304bfd20cd6f') {
|
||||
//这边坐下合并
|
||||
if (characterUrl != '') {
|
||||
characterUrl = ` --oref ${characterUrl} --ow ${crefCw}`
|
||||
}
|
||||
} else {
|
||||
// 其他
|
||||
//这边坐下合并
|
||||
if (characterUrl != '') {
|
||||
characterUrl = ` --cref ${characterUrl} --cw ${crefCw}`
|
||||
}
|
||||
}
|
||||
return { characterString, characterUrl }
|
||||
} else if (type == PromptMergeType.SD_MERGE) {
|
||||
|
||||
@ -4,17 +4,28 @@
|
||||
<n-data-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:data="bookStore.selectBookTaskDetail"
|
||||
:data="displayData"
|
||||
:loading="loading"
|
||||
:single-line="false"
|
||||
:row-key="rowKey"
|
||||
:scroll-x="bookStore.selectBookTask.openVideoGenerate ? 1700 : 1600"
|
||||
:max-height="tableHeight"
|
||||
@scroll="handleTableScroll"
|
||||
/>
|
||||
|
||||
<!-- 加载更多指示器 - 浮动在表格上方 -->
|
||||
<div v-if="hasMoreData" class="load-more-indicator-floating">
|
||||
<n-spin :show="isLoadingMore" size="small">
|
||||
<div class="load-more-content">
|
||||
{{ isLoadingMore ? t('正在加载更多...') : t('滚动到底部加载更多') }}
|
||||
</div>
|
||||
</n-spin>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useBookStore, useSoftwareStore } from '@/renderer/src/stores'
|
||||
import DatatableAfterGpt from './DatatableAfterGpt.vue'
|
||||
import DatatableCharacterAndSceneAndStyle from './DatatableCharacterAndSceneAndStyle.vue'
|
||||
@ -40,9 +51,83 @@ const tableRef = ref(null)
|
||||
const tableHeight = ref(500) // 默认高度
|
||||
const loading = ref(false)
|
||||
|
||||
// 分页渲染相关变量
|
||||
const pageSize = ref(20) // 每次渲染的数据条数,可以根据性能调整
|
||||
const currentPage = ref(1) // 当前渲染到第几页
|
||||
const isLoadingMore = ref(false) // 是否正在加载更多数据
|
||||
const scrollThreshold = ref(100) // 距离底部多少像素时开始加载下一页
|
||||
|
||||
// 根据数据总量动态调整页面大小(可选优化)
|
||||
const adaptivePageSize = computed(() => {
|
||||
const totalData = bookStore.selectBookTaskDetail.length
|
||||
if (totalData > 1000) return pageSize.value * 3 // 数据很多时,每页多显示一些
|
||||
if (totalData > 500) return pageSize.value * 2
|
||||
if (totalData > 100) return pageSize.value * 1.5
|
||||
return pageSize.value // 数据较少时,每页少显示一些,响应更快
|
||||
})
|
||||
|
||||
// 计算当前显示的数据
|
||||
const displayData = computed(() => {
|
||||
const currentPageSize = adaptivePageSize.value
|
||||
const endIndex = currentPage.value * currentPageSize
|
||||
return bookStore.selectBookTaskDetail.slice(0, endIndex)
|
||||
})
|
||||
|
||||
// 是否还有更多数据
|
||||
const hasMoreData = computed(() => {
|
||||
return displayData.value.length < bookStore.selectBookTaskDetail.length
|
||||
})
|
||||
|
||||
// 行数据的唯一标识
|
||||
const rowKey = (row) => row.id
|
||||
|
||||
// 处理表格滚动事件
|
||||
const handleTableScroll = (e) => {
|
||||
if (isLoadingMore.value || !hasMoreData.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||
const distanceToBottom = scrollHeight - scrollTop - clientHeight
|
||||
|
||||
// 当距离底部小于阈值时,加载更多数据
|
||||
if (distanceToBottom <= scrollThreshold.value) {
|
||||
loadMoreData()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多数据
|
||||
const loadMoreData = async () => {
|
||||
if (isLoadingMore.value || !hasMoreData.value) {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadingMore.value = true
|
||||
|
||||
// 模拟异步加载延迟,让渲染更平滑
|
||||
await new Promise((resolve) => setTimeout(resolve, 300))
|
||||
|
||||
currentPage.value++
|
||||
isLoadingMore.value = false
|
||||
}
|
||||
|
||||
// 重置分页状态(当数据源变化时调用)
|
||||
const resetPagination = () => {
|
||||
currentPage.value = 1
|
||||
isLoadingMore.value = false
|
||||
}
|
||||
|
||||
// 监听数据源变化,重置分页
|
||||
watch(
|
||||
() => bookStore.selectBookTaskDetail.length,
|
||||
(newLength, oldLength) => {
|
||||
// 如果数据长度变化,重置分页状态
|
||||
if (newLength !== oldLength) {
|
||||
resetPagination()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 计算当前程序界面的高度,减去上下的高度,设置maxHegiht
|
||||
async function getMaxHeight() {
|
||||
let height = window.innerHeight
|
||||
@ -338,12 +423,32 @@ function scrollToTableRow(event) {
|
||||
message.error(t('未找到指定ID的分镜数据'))
|
||||
return
|
||||
}
|
||||
// 如果表格有ref
|
||||
|
||||
// 检查目标行是否在当前显示的数据范围内
|
||||
const currentDisplayCount = displayData.value.length
|
||||
if (findIndex >= currentDisplayCount) {
|
||||
// 如果目标行还没有渲染,先加载到该行
|
||||
const targetPage = Math.ceil((findIndex + 1) / adaptivePageSize.value)
|
||||
currentPage.value = targetPage
|
||||
|
||||
// 等待数据更新后再滚动
|
||||
nextTick(() => {
|
||||
scrollToRow(findIndex)
|
||||
})
|
||||
} else {
|
||||
// 如果已经在显示范围内,直接滚动
|
||||
scrollToRow(findIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动到指定行的实际执行函数
|
||||
function scrollToRow(findIndex) {
|
||||
if (tableRef.value) {
|
||||
// 使用Naive UI的内置方法滚动到指定行
|
||||
let doc = document.querySelectorAll('.n-data-table-tr')
|
||||
let sk = doc[findIndex + 1]
|
||||
tableRef.value.scrollTo({ el: sk, behavior: 'smooth' })
|
||||
if (sk) {
|
||||
tableRef.value.scrollTo({ el: sk, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -351,4 +456,45 @@ function scrollToTableRow(event) {
|
||||
.empty-margin {
|
||||
padding: 0px 5px !important;
|
||||
}
|
||||
|
||||
.book-task-detail-table {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 浮动加载指示器 */
|
||||
.load-more-indicator-floating {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1000;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(8px);
|
||||
pointer-events: none; /* 防止阻挡表格操作 */
|
||||
}
|
||||
|
||||
.load-more-content {
|
||||
padding: 10px 24px;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 深色模式适配 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.load-more-indicator-floating {
|
||||
background: rgba(24, 24, 28, 0.95);
|
||||
border-color: #444;
|
||||
}
|
||||
|
||||
.load-more-content {
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user