4.0.3 (2025.09.26)

1. 新增 海螺生成视频(文生视频,图转视频,首尾帧视频)
2. 修复MJ出图的部分问题
3. 优化原创的加载速度,分批次渲染,加快界面显示速度
This commit is contained in:
lq1405 2025-09-26 12:11:02 +08:00
parent 3d5307c8e4
commit 2b70e511d2
6 changed files with 172 additions and 16 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "laitool-pro", "name": "laitool-pro",
"productName": "LaiToolPro", "productName": "LaiToolPro",
"version": "v4.0.2", "version": "v4.0.3",
"description": "来推 Pro - 一款集音频处理、文案生成、图片生成、视频生成等功能于一体的多合一AI工具软件。", "description": "来推 Pro - 一款集音频处理、文案生成、图片生成、视频生成等功能于一体的多合一AI工具软件。",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "xiangbei", "author": "xiangbei",

View File

@ -30,8 +30,8 @@ interface ISoftwareData {
} }
export const SoftwareData: ISoftwareData = { export const SoftwareData: ISoftwareData = {
version: 'V4.0.2', version: 'V4.0.3',
date: '2025-09-23', date: '2025-09-26',
systemInfo: { systemInfo: {
documentationUrl: 'https://rvgyir5wk1c.feishu.cn/wiki/WdaWwAfDdiLOnjkywIgcaQoKnog', documentationUrl: 'https://rvgyir5wk1c.feishu.cn/wiki/WdaWwAfDdiLOnjkywIgcaQoKnog',
updateUrl: 'https://pvwu1oahp5m.feishu.cn/docx/CAjGdTDlboJ3nVx0cQccOuNHnvd', updateUrl: 'https://pvwu1oahp5m.feishu.cn/docx/CAjGdTDlboJ3nVx0cQccOuNHnvd',

View File

@ -318,7 +318,7 @@ export class MJApiService extends MJBasic {
imagePath: resData.imageUrl, imagePath: resData.imageUrl,
imageUrls: resData.imageUrls imageUrls: resData.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) .map((item) => item.url)
: [], : [],
messageId: taskId, messageId: taskId,

View File

@ -3,7 +3,8 @@ import {
BookTaskStatus, BookTaskStatus,
BookType, BookType,
DialogType, DialogType,
OperateBookType OperateBookType,
PromptMergeType
} from '@/define/enum/bookEnum' } from '@/define/enum/bookEnum'
import { MJBasic } from './mjBasic' import { MJBasic } from './mjBasic'
import { GeneralResponse } from '@/define/model/generalResponse' import { GeneralResponse } from '@/define/model/generalResponse'
@ -155,7 +156,7 @@ export class MJServiceHandle extends MJBasic {
sceneString = await this.presetBasicService.GetScenePresetStringByIds(sceneIds) sceneString = await this.presetBasicService.GetScenePresetStringByIds(sceneIds)
} }
if (characterIds && characterIds.length > 0) { 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 characterString = res.characterString
characterUrl = res.characterUrl characterUrl = res.characterUrl
} }
@ -517,7 +518,7 @@ export class MJServiceHandle extends MJBasic {
bookTaskDetail.name as string, bookTaskDetail.name as string,
path.join( path.join(
bookTask.imageFolder as string, bookTask.imageFolder as string,
`subImage\\${bookTaskDetail.name}\\${new Date().getTime()}.png` `subImage\\${bookTaskDetail.name}`
) )
) )
if (imageArray && imageArray.length < 4) { if (imageArray && imageArray.length < 4) {

View File

@ -40,7 +40,8 @@ export class PresetBasicService extends PresetBasic {
*/ */
async GetCharacterPresetStringByIds( async GetCharacterPresetStringByIds(
ids: string[], ids: string[],
type: PromptMergeType = PromptMergeType.MJ_MERGE type: PromptMergeType = PromptMergeType.MJ_MERGE,
mjModel: string = ''
): Promise<{ characterString: string; characterUrl: string }> { ): Promise<{ characterString: string; characterUrl: string }> {
await this.InitPresetBasic() await this.InitPresetBasic()
let characterString = '' let characterString = ''
@ -56,11 +57,19 @@ export class PresetBasicService extends PresetBasic {
crefCw = (element.crefCw ?? 20).toString() crefCw = (element.crefCw ?? 20).toString()
} }
} }
// 判断是不是v7
if (mjModel == '0d33ae62-e0a8-4429-89e4-304bfd20cd6f') {
//这边坐下合并
if (characterUrl != '') {
characterUrl = ` --oref ${characterUrl} --ow ${crefCw}`
}
} else {
// 其他
//这边坐下合并 //这边坐下合并
if (characterUrl != '') { if (characterUrl != '') {
characterUrl = ` --cref ${characterUrl} --cw ${crefCw}` characterUrl = ` --cref ${characterUrl} --cw ${crefCw}`
} }
}
return { characterString, characterUrl } return { characterString, characterUrl }
} else if (type == PromptMergeType.SD_MERGE) { } else if (type == PromptMergeType.SD_MERGE) {
let result = '' let result = ''

View File

@ -4,17 +4,28 @@
<n-data-table <n-data-table
ref="tableRef" ref="tableRef"
:columns="columns" :columns="columns"
:data="bookStore.selectBookTaskDetail" :data="displayData"
:loading="loading" :loading="loading"
:single-line="false" :single-line="false"
:row-key="rowKey" :row-key="rowKey"
:scroll-x="bookStore.selectBookTask.openVideoGenerate ? 1700 : 1600" :scroll-x="bookStore.selectBookTask.openVideoGenerate ? 1700 : 1600"
:max-height="tableHeight" :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> </div>
</template> </template>
<script setup> <script setup>
import { computed, ref, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { useBookStore, useSoftwareStore } from '@/renderer/src/stores' import { useBookStore, useSoftwareStore } from '@/renderer/src/stores'
import DatatableAfterGpt from './DatatableAfterGpt.vue' import DatatableAfterGpt from './DatatableAfterGpt.vue'
import DatatableCharacterAndSceneAndStyle from './DatatableCharacterAndSceneAndStyle.vue' import DatatableCharacterAndSceneAndStyle from './DatatableCharacterAndSceneAndStyle.vue'
@ -40,9 +51,83 @@ const tableRef = ref(null)
const tableHeight = ref(500) // const tableHeight = ref(500) //
const loading = ref(false) 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 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 // maxHegiht
async function getMaxHeight() { async function getMaxHeight() {
let height = window.innerHeight let height = window.innerHeight
@ -338,17 +423,78 @@ function scrollToTableRow(event) {
message.error(t('未找到指定ID的分镜数据')) message.error(t('未找到指定ID的分镜数据'))
return 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) { if (tableRef.value) {
// 使Naive UI
let doc = document.querySelectorAll('.n-data-table-tr') let doc = document.querySelectorAll('.n-data-table-tr')
let sk = doc[findIndex + 1] let sk = doc[findIndex + 1]
if (sk) {
tableRef.value.scrollTo({ el: sk, behavior: 'smooth' }) tableRef.value.scrollTo({ el: sk, behavior: 'smooth' })
} }
} }
}
</script> </script>
<style scoped> <style scoped>
.empty-margin { .empty-margin {
padding: 0px 5px !important; 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> </style>