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",
|
"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",
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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 = ''
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user