V 3.2.1 2024.11.9

1. (聚合推文)修复原创默认出图方式设置默认值
2. (聚合推文)优化出图显示
3. (聚合推文)添加图片上传功能,包括主图和选图区域
4. (聚合推文)新增图片缓存区,上传缓存图片(主图和选图区的图片),可以直接在当前小说所有的批次中的分镜中调用(下载到主图和选图区)
5. (聚合推文)添加一键修脸开关
6. (聚合推文)原创添加一键锁定
7. (聚合推文)新增小说批次任务显示当前所属小说
8. (聚合推文)小说批次任务状态显示优化
9. (聚合推文)优化后台任务状态显示
10. (聚合推文)原创,反推 一键生图 修复
This commit is contained in:
lq1405 2024-11-09 16:46:06 +08:00
parent 51deef0c09
commit 6fa58e4d94
69 changed files with 1865 additions and 274 deletions

13
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "laitool", "name": "laitool",
"version": "3.2.0", "version": "3.2.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "laitool", "name": "laitool",
"version": "3.2.0", "version": "3.2.1",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@alicloud/alimt20181012": "^1.2.0", "@alicloud/alimt20181012": "^1.2.0",
@ -30,7 +30,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment-timezone": "^0.5.45", "moment-timezone": "^0.5.45",
"music-metadata": "^7.14.0", "music-metadata": "^7.14.0",
"node-edge-tts": "^1.2.3", "node-edge-tts": "^1.2.4",
"node-machine-id": "^1.1.12", "node-machine-id": "^1.1.12",
"npm": "^10.7.0", "npm": "^10.7.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
@ -6274,9 +6274,10 @@
} }
}, },
"node_modules/node-edge-tts": { "node_modules/node-edge-tts": {
"version": "1.2.3", "version": "1.2.4",
"resolved": "https://registry.npmmirror.com/node-edge-tts/-/node-edge-tts-1.2.3.tgz", "resolved": "https://registry.npmmirror.com/node-edge-tts/-/node-edge-tts-1.2.4.tgz",
"integrity": "sha512-ug2osAv7qihpbcuuhWWrnDbqDZ0q7AcIHtRumQ/PJaXV6QR2sI/1bBlUWLNk0tTF6Y6CWCYuBoHYYBQztRterg==", "integrity": "sha512-6IvNVJz+pFmgMuGGAew0MlhfexgakXGH11pXZtqfMR/l+afhK0XxxUIEOf3MEJQ8vhR3jeXEECOSW9w4LjX7Fw==",
"license": "MIT",
"dependencies": { "dependencies": {
"https-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.1",
"ws": "^8.13.0", "ws": "^8.13.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "laitool", "name": "laitool",
"version": "3.2.0", "version": "3.2.1",
"description": "An AI tool for image processing, video processing, and other functions.", "description": "An AI tool for image processing, video processing, and other functions.",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "laitool.cn", "author": "laitool.cn",
@ -38,7 +38,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment-timezone": "^0.5.45", "moment-timezone": "^0.5.45",
"music-metadata": "^7.14.0", "music-metadata": "^7.14.0",
"node-edge-tts": "^1.2.3", "node-edge-tts": "^1.2.4",
"node-machine-id": "^1.1.12", "node-machine-id": "^1.1.12",
"npm": "^10.7.0", "npm": "^10.7.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 1011 KiB

Binary file not shown.

View File

@ -11,9 +11,9 @@ const fspromises = fs.promises
* @param {*} path * @param {*} path
* @returns true表示存在false表示不存在 * @returns true表示存在false表示不存在
*/ */
export async function CheckFileOrDirExist(path) { export async function CheckFileOrDirExist(filePath) {
try { try {
await fspromises.access(path) await fspromises.access(filePath)
return true // 文件或目录存在 return true // 文件或目录存在
} catch (error) { } catch (error) {
return false // 文件或目录不存在 return false // 文件或目录不存在

View File

@ -49,7 +49,8 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
backgroundMusic: string | null // 背景音乐ID backgroundMusic: string | null // 背景音乐ID
friendlyReminder: string | null // 友情提示 friendlyReminder: string | null // 友情提示
imageFolder: string | null imageFolder: string | null
imageStyle: string[] | null // 软件内置的样式 imageStyle: string | null // 软件内置的样式
cacheImageList: string[] | null // 缓存的图片列表
autoAnalyzeCharacter: string | null // 自动分析角色设置 autoAnalyzeCharacter: string | null // 自动分析角色设置
customizeImageStyle: string[] | null // 自定义的样式 customizeImageStyle: string[] | null // 自定义的样式
videoConfig: string | null // 合成视频设置 videoConfig: string | null // 合成视频设置
@ -80,6 +81,7 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
friendlyReminder: 'string?', friendlyReminder: 'string?',
imageFolder: 'string?', imageFolder: 'string?',
subImageFolder: "string?[]", subImageFolder: "string?[]",
cacheImageList: 'string?[]',
imageStyle: 'string?[]', imageStyle: 'string?[]',
autoAnalyzeCharacter: 'string?', autoAnalyzeCharacter: 'string?',
customizeImageStyle: 'string?[]', customizeImageStyle: 'string?[]',

View File

@ -9,7 +9,7 @@ import { BaseRealmService } from './bookBasic'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { OtherData } from '../../../enum/softwareEnum.js' import { OtherData } from '../../../enum/softwareEnum.js'
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
import { Book } from '../../../../model/book.js' import { Book } from '../../../../model/book/book.js'
import { GeneralResponse } from '../../../../model/generalResponse.js' import { GeneralResponse } from '../../../../model/generalResponse.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')

View File

@ -197,6 +197,13 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
newBookTask[i].draftDepend = '' newBookTask[i].draftDepend = ''
} }
} }
if (oldRealm.schemaVersion < 29) {
const oldBookTask = oldRealm.objects('BookTask')
const newBookTask = newRealm.objects('BookTask')
for (let i = 0; i < oldBookTask.length; i++) {
newBookTask[i].cacheImageList = undefined
}
}
} }
export class BaseRealmService extends BaseService { export class BaseRealmService extends BaseService {
@ -238,7 +245,7 @@ export class BaseRealmService extends BaseService {
BookTaskDetailModel BookTaskDetailModel
], ],
path: this.dbpath, path: this.dbpath,
schemaVersion: 27, schemaVersion: 29,
migration: migration migration: migration
} }
this.realm = await Realm.open(config) this.realm = await Realm.open(config)

View File

@ -10,7 +10,7 @@ import { BaseRealmService } from './bookBasic.js'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js' import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js'
import { version } from '../../../../../package.json' import { version } from '../../../../../package.json'
import { Book } from '../../../../model/book.js' import { Book } from '../../../../model/book/book.js'
import { GeneralResponse } from '../../../../model/generalResponse.js' import { GeneralResponse } from '../../../../model/generalResponse.js'
export class BookService extends BaseRealmService { export class BookService extends BaseRealmService {
@ -205,7 +205,7 @@ export class BookService extends BaseRealmService {
} else if (book.type == BookType.MJ_REVERSE) { } else if (book.type == BookType.MJ_REVERSE) {
imageCategory = BookImageCategory.MJ imageCategory = BookImageCategory.MJ
} else if (book.type == BookType.ORIGINAL) { } else if (book.type == BookType.ORIGINAL) {
imageCategory = global.config.defaultImageMode imageCategory = global.config.defaultImageMode ?? BookImageCategory.MJ
} else { } else {
throw new Error('未知的小说类型') throw new Error('未知的小说类型')
} }

View File

@ -8,7 +8,7 @@ import { cloneDeep, isEmpty } from 'lodash'
import { JoinPath } from '../../../Tools/file' import { JoinPath } from '../../../Tools/file'
import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js' import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
import { Book } from "../../../../model/book" import { Book } from "../../../../model/book/book"
import { GeneralResponse } from '../../../../model/generalResponse.js' import { GeneralResponse } from '../../../../model/generalResponse.js'
let dbPath = path.resolve(define.db_path, 'book.realm') let dbPath = path.resolve(define.db_path, 'book.realm')

View File

@ -8,7 +8,7 @@ import { BaseRealmService } from './bookBasic'
import { JoinPath } from '../../../Tools/file' import { JoinPath } from '../../../Tools/file'
import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js' import { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid')
import { Book } from '../../../../model/book' import { Book } from '../../../../model/book/book'
import { TagDefine } from '../../../tagDefine.js' import { TagDefine } from '../../../tagDefine.js'
import { ImageStyleDefine } from "../../../../define/iamgeStyleDefine" import { ImageStyleDefine } from "../../../../define/iamgeStyleDefine"
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
@ -89,6 +89,7 @@ export class BookTaskService extends BaseRealmService {
srtPath: JoinPath(define.project_path, bookTask.srtPath), srtPath: JoinPath(define.project_path, bookTask.srtPath),
audioPath: JoinPath(define.project_path, bookTask.audioPath), audioPath: JoinPath(define.project_path, bookTask.audioPath),
imageFolder: JoinPath(define.project_path, bookTask.imageFolder), imageFolder: JoinPath(define.project_path, bookTask.imageFolder),
cacheImageList: bookTask.cacheImageList ? Array.from(bookTask.cacheImageList).map(item => JoinPath(define.project_path, item)) : [],
imageCategory: bookTask.imageCategory ? bookTask.imageCategory : BookImageCategory.MJ, // 默认使用MJ出图 imageCategory: bookTask.imageCategory ? bookTask.imageCategory : BookImageCategory.MJ, // 默认使用MJ出图
} as Book.SelectBookTask; } as Book.SelectBookTask;
}) })

View File

@ -150,7 +150,9 @@ define['hkServerUrl'] = 'https://laitool.net/'
define['bakServerUrl'] = 'https://laitool.net/' define['bakServerUrl'] = 'https://laitool.net/'
define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0' define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0'
define['lms'] = // define['lms'] =
process.env.NODE_ENV == 'development' ? 'https://localhost:44362' : 'https://lms.laitool.cn' // process.env.NODE_ENV == 'development' ? 'https://localhost:44362' : 'https://lms.laitool.cn'
define['lms'] = 'https://lms.laitool.cn'
export { define } export { define }

View File

@ -107,6 +107,18 @@ const BOOK = {
/** 添加一键生图后台任务 */ /** 添加一键生图后台任务 */
GENERATE_ALL_TASK_IMAGE: "GENERATE_ALL_TASK_IMAGE", GENERATE_ALL_TASK_IMAGE: "GENERATE_ALL_TASK_IMAGE",
/** 上传图片到小说中,并修改小说信息 */
UPLOAD_IMAGE_TO_BOOK_AND_UPDATE_MESSAGE: "UPLOAD_IMAGE_TO_BOOK_AND_UPDATE_MESSAGE",
/** 上传图片到缓存 */
UPLOAD_IMAGE_TO_CACHE: "UPLOAD_IMAGE_TO_CACHE",
/** 获取当前小说中的所有的批次中的缓存图片 */
GET_ALL_BOOK_TASK_IMAGE_CACHE: "GET_ALL_BOOK_TASK_IMAGE_CACHE",
/** 保存缓存区的屠图片到小说主图或者是选图区 */
SAVE_CACHE_IMAGE_TO_DATA: "SAVE_CACHE_IMAGE_TO_DATA",
//#endregion //#endregion
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD', COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',

View File

@ -10,5 +10,11 @@ export const SYSTEM = {
/** 检查机器码状态 */ /** 检查机器码状态 */
CHECK_MACHINE_STATUS: "CHECK_MACHINE_STATUS", CHECK_MACHINE_STATUS: "CHECK_MACHINE_STATUS",
/** 获取软件版本信息,更新信息,首页内容,公告等 */ /** 获取软件版本信息,更新信息,首页内容,公告等 */
GET_REMOTE_SYSTEM_INFORMATION: 'GET_REMOTE_SYSTEM_INFORMATION' GET_REMOTE_SYSTEM_INFORMATION: 'GET_REMOTE_SYSTEM_INFORMATION',
/** 选择单个指定文件后缀的文件 */
SELECT_SINGLE_FILE: "SELECT_SINGLE_FILE",
/** 选择多个指定文件后缀的文件 */
SELECT_MULTIPLE_FILE: "SELECT_MULTIPLE_FILE",
} }

View File

@ -1,3 +1,5 @@
import { TaskModal } from "@/model/task";
export enum BookType { export enum BookType {
// 原创 // 原创
ORIGINAL = 'original', ORIGINAL = 'original',
@ -333,70 +335,194 @@ export function GetBookBackTaskTypeLabel(key: string) {
* @param key * @param key
* @returns * @returns
*/ */
export function GetBookTaskDetailStatusLabel(key: string) { export function GetBookTaskDetailStatusLabel(key: string): TaskModal.TaskStatus {
switch (key) { switch (key) {
case BookTaskStatus.WAIT: case BookTaskStatus.WAIT:
return '等待'; return {
status: BookTaskStatus.WAIT,
label: '等待',
type: 'warning'
};
case BookTaskStatus.STORYBOARD: case BookTaskStatus.STORYBOARD:
return '分镜计算中'; return {
status: BookTaskStatus.STORYBOARD,
label: '分镜计算中',
type: 'info'
};
case BookTaskStatus.STORYBOARD_FAIL: case BookTaskStatus.STORYBOARD_FAIL:
return '分镜计算失败'; return {
status: BookTaskStatus.STORYBOARD_FAIL,
label: '分镜计算失败',
type: 'error'
};
case BookTaskStatus.STORYBOARD_DONE: case BookTaskStatus.STORYBOARD_DONE:
return '分镜计算完成'; return {
status: BookTaskStatus.STORYBOARD_DONE,
label: '分镜计算完成',
type: 'success'
};
case BookTaskStatus.SPLIT: case BookTaskStatus.SPLIT:
return '分割视频中'; return {
status: BookTaskStatus.SPLIT,
label: '分割视频中',
type: 'info'
};
case BookTaskStatus.SPLIT_FAIL: case BookTaskStatus.SPLIT_FAIL:
return '分割视频失败'; return {
status: BookTaskStatus.SPLIT_FAIL,
label: '分割视频失败',
type: 'error'
};
case BookTaskStatus.SPLIT_DONE: case BookTaskStatus.SPLIT_DONE:
return '分割视频完成'; return {
status: BookTaskStatus.SPLIT_DONE,
label: '分割视频完成',
type: 'success'
};
case BookTaskStatus.AUDIO: case BookTaskStatus.AUDIO:
return '提取音频中'; return {
status: BookTaskStatus.AUDIO,
label: '提取音频中',
type: 'info'
};
case BookTaskStatus.AUDIO_FAIL: case BookTaskStatus.AUDIO_FAIL:
return '提取音频失败'; return {
status: BookTaskStatus.AUDIO_FAIL,
label: '提取音频失败',
type: 'error'
};
case BookTaskStatus.AUDIO_DONE: case BookTaskStatus.AUDIO_DONE:
return '提取音频完成'; return {
status: BookTaskStatus.AUDIO_DONE,
label: '提取音频完成',
type: 'success'
};
case BookTaskStatus.RECOGNIZE: case BookTaskStatus.RECOGNIZE:
return '识别字幕中'; return {
status: BookTaskStatus.RECOGNIZE,
label: '识别字幕中',
type: 'info'
};
case BookTaskStatus.RECOGNIZE_FAIL: case BookTaskStatus.RECOGNIZE_FAIL:
return '识别字幕失败'; return {
status: BookTaskStatus.RECOGNIZE_FAIL,
label: '识别字幕失败',
type: 'error'
};
case BookTaskStatus.RECOGNIZE_DONE: case BookTaskStatus.RECOGNIZE_DONE:
return '识别字幕完成'; return {
status: BookTaskStatus.RECOGNIZE_DONE,
label: '识别字幕完成',
type: 'success'
};
case BookTaskStatus.FRAME: case BookTaskStatus.FRAME:
return '抽帧中'; return {
status: BookTaskStatus.FRAME,
label: '抽帧中',
type: 'info'
};
case BookTaskStatus.FRAME_FAIL: case BookTaskStatus.FRAME_FAIL:
return '抽帧失败'; return {
status: BookTaskStatus.FRAME_FAIL,
label: '抽帧失败',
type: 'error'
};
case BookTaskStatus.FRAME_DONE: case BookTaskStatus.FRAME_DONE:
return '抽帧完成'; return {
status: BookTaskStatus.FRAME_DONE,
label: '抽帧完成',
type: 'success'
};
case BookTaskStatus.REVERSE: case BookTaskStatus.REVERSE:
return '反推中'; return {
status: BookTaskStatus.REVERSE,
label: '反推中',
type: 'info'
};
case BookTaskStatus.REVERSE_FAIL: case BookTaskStatus.REVERSE_FAIL:
return '反推失败'; return {
status: BookTaskStatus.REVERSE_FAIL,
label: '反推失败',
type: 'error'
};
case BookTaskStatus.REVERSE_DONE: case BookTaskStatus.REVERSE_DONE:
return '反推完成'; return {
status: BookTaskStatus.REVERSE_DONE,
label: '反推完成',
type: 'success'
};
case BookTaskStatus.IMAGE: case BookTaskStatus.IMAGE:
return '生成图片中'; return {
status: BookTaskStatus.IMAGE,
label: '生成图片中',
type: 'info'
};
case BookTaskStatus.IMAGE_FAIL: case BookTaskStatus.IMAGE_FAIL:
return '生成图片失败'; return {
status: BookTaskStatus.IMAGE_FAIL,
label: '生成图片失败',
type: 'error'
};
case BookTaskStatus.IMAGE_DONE: case BookTaskStatus.IMAGE_DONE:
return '生成图片完成'; return {
status: BookTaskStatus.IMAGE_DONE,
label: '生成图片完成',
type: 'success'
};
case BookTaskStatus.HD: case BookTaskStatus.HD:
return '高清中'; return {
status: BookTaskStatus.HD,
label: '高清中',
type: 'info'
};
case BookTaskStatus.HD_FAIL: case BookTaskStatus.HD_FAIL:
return '高清失败'; return {
status: BookTaskStatus.HD_FAIL,
label: '高清失败',
type: 'error'
};
case BookTaskStatus.HD_DONE: case BookTaskStatus.HD_DONE:
return '高清完成'; return {
status: BookTaskStatus.HD_DONE,
label: '高清完成',
type: 'success'
};
case BookTaskStatus.COMPOSING: case BookTaskStatus.COMPOSING:
return '合成视频中'; return {
status: BookTaskStatus.COMPOSING,
label: '合成视频中',
type: 'info'
};
case BookTaskStatus.COMPOSING_FAIL: case BookTaskStatus.COMPOSING_FAIL:
return '合成视频失败'; return {
status: BookTaskStatus.COMPOSING_FAIL,
label: '合成视频失败',
type: 'error'
};
case BookTaskStatus.COMPOSING_DONE: case BookTaskStatus.COMPOSING_DONE:
return '合成视频完成'; return {
status: BookTaskStatus.COMPOSING_DONE,
label: '合成视频完成',
type: 'success'
};
case BookTaskStatus.DRAFT_DONE: case BookTaskStatus.DRAFT_DONE:
return '添加草稿完成'; return {
status: BookTaskStatus.DRAFT_DONE,
label: '添加草稿完成',
type: 'success'
};
case BookTaskStatus.DRAFT_FAIL: case BookTaskStatus.DRAFT_FAIL:
return '添加草稿失败'; return {
status: BookTaskStatus.DRAFT_FAIL,
label: '添加草稿失败',
type: 'error'
};
default: default:
return key; return {
status: "UNKNOWN",
label: "UNKNOWN",
type: 'warning'
};
} }
} }
@ -405,21 +531,50 @@ export function GetBookTaskDetailStatusLabel(key: string) {
* @param key * @param key
* @returns * @returns
*/ */
export function GetBookBackTaskStatusLabel(key: string) { export function GetBookBackTaskStatusLabel(key: string): TaskModal.TaskStatus {
switch (key) { switch (key) {
case BookBackTaskStatus.WAIT: case BookBackTaskStatus.WAIT:
return '等待'; return {
status: BookBackTaskStatus.WAIT,
label: '等待',
type: 'warning'
};
case BookBackTaskStatus.RUNNING: case BookBackTaskStatus.RUNNING:
return '运行中'; return {
status: BookBackTaskStatus.RUNNING,
label: '运行中',
type: 'info'
};
case BookBackTaskStatus.PAUSE: case BookBackTaskStatus.PAUSE:
return '暂停'; return {
status: BookBackTaskStatus.PAUSE,
label: '暂停',
type: 'warning'
};
case BookBackTaskStatus.DONE: case BookBackTaskStatus.DONE:
return '完成'; return {
status: BookBackTaskStatus.DONE,
label: '完成',
type: 'success'
};
case BookBackTaskStatus.FAIL: case BookBackTaskStatus.FAIL:
return '失败'; return {
status: BookBackTaskStatus.FAIL,
label: '失败',
type: 'error'
};
case BookBackTaskStatus.RECONNECT: case BookBackTaskStatus.RECONNECT:
return '重连'; return {
status: BookBackTaskStatus.RECONNECT,
label: '重连',
type: 'warning'
};
default: default:
return key; return {
status: "UNKNOWN",
label: "UNKNOWN",
type: 'warning'
};
} }
} }

View File

@ -280,6 +280,23 @@ export function BookIpc() {
async (event, ids: string[], operateBookType: OperateBookType) => await bookImage.GenerateAllTaskImage(ids, operateBookType) async (event, ids: string[], operateBookType: OperateBookType) => await bookImage.GenerateAllTaskImage(ids, operateBookType)
) )
/**上传图片到小说中,并修改小说信息 */
ipcMain.handle(
DEFINE_STRING.BOOK.UPLOAD_IMAGE_TO_BOOK_AND_UPDATE_MESSAGE,
async (event, bookTaskDetailId: string, imageFile: string | string[], option: string) => await bookImage.UpLoadImageToBookAndUpdateMessage(bookTaskDetailId, imageFile, option)
)
/** 上传图片到缓存 */
ipcMain.handle(DEFINE_STRING.BOOK.UPLOAD_IMAGE_TO_CACHE,
async (event, bookTaskId: string, imageFile: string | string[]) => await bookImage.UpLoadImageToCache(bookTaskId, imageFile)
)
/** 获取当前小说中的所有的批次中的缓存图片 */
ipcMain.handle(DEFINE_STRING.BOOK.GET_ALL_BOOK_TASK_IMAGE_CACHE, async (event, bookTaskId: string) => await bookImage.GetAllBookTaskImageCache(bookTaskId))
/** 保存缓存区的屠图片到小说主图或者是选图区 */
ipcMain.handle(DEFINE_STRING.BOOK.SAVE_CACHE_IMAGE_TO_DATA, async (event, bookTaskDetailId: string, imageFile: string | string[], option: string) => await bookImage.SaveCacheImageToData(bookTaskDetailId, imageFile, option))
//#endregion //#endregion

View File

@ -1,7 +1,7 @@
import { ipcMain } from 'electron' import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string' import { DEFINE_STRING } from '../../define/define_string'
import { errorMessage, successMessage } from '../Public/generalTools' import { errorMessage, successMessage } from '../Public/generalTools'
import { Book } from '../../model/book' import { Book } from '../../model/book/book'
import { BookTaskService } from '../../define/db/service/Book/bookTaskService' import { BookTaskService } from '../../define/db/service/Book/bookTaskService'
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService' import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
import { BookService } from '../../define/db/service/Book/bookService' import { BookService } from '../../define/db/service/Book/bookService'

View File

@ -28,5 +28,11 @@ function SystemIpc() {
ipcMain.handle(DEFINE_STRING.SYSTEM.CHECK_MACHINE_STATUS, async (event, value: string) => await systemInfo.CheckMachineStatus(value)) ipcMain.handle(DEFINE_STRING.SYSTEM.CHECK_MACHINE_STATUS, async (event, value: string) => await systemInfo.CheckMachineStatus(value))
/** 获取软件版本信息,更新信息,首页内容,公告等 */ /** 获取软件版本信息,更新信息,首页内容,公告等 */
ipcMain.handle(DEFINE_STRING.SYSTEM.GET_REMOTE_SYSTEM_INFORMATION, async (event) => await systemInfo.GetRemoteSystemInformation()) ipcMain.handle(DEFINE_STRING.SYSTEM.GET_REMOTE_SYSTEM_INFORMATION, async (event) => await systemInfo.GetRemoteSystemInformation())
/** 选择单个指定文件后缀的文件 */
ipcMain.handle(DEFINE_STRING.SYSTEM.SELECT_SINGLE_FILE, async (event, value: string[]) => await electronInterface.SelectSingleFile(value))
/** 选择多个指定文件后缀的文件 */
ipcMain.handle(DEFINE_STRING.SYSTEM.SELECT_MULTIPLE_FILE, async (event, value: string[]) => await electronInterface.SelectMultipleFile(value))
} }
export { SystemIpc } export { SystemIpc }

View File

@ -7,7 +7,7 @@ import { GeneralResponse } from '../../../model/generalResponse'
import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic' import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'
import { BookTask } from './bookTask' import { BookTask } from './bookTask'
import fs from 'fs' import fs from 'fs'
import { Book } from '../../../model/book' import { Book } from '../../../model/book/book'
export class BookBasic { export class BookBasic {
bookServiceBasic: BookServiceBasic bookServiceBasic: BookServiceBasic

View File

@ -9,7 +9,7 @@ import path from 'path'
import { BasicReverse } from './basicReverse' import { BasicReverse } from './basicReverse'
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService' import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
import { LogScheduler } from "../task/logScheduler" import { LogScheduler } from "../task/logScheduler"
import { Book } from '../../../model/book' import { Book } from '../../../model/book/book'
import { LoggerStatus, OtherData, ResponseMessageType } from '../../../define/enum/softwareEnum' import { LoggerStatus, OtherData, ResponseMessageType } from '../../../define/enum/softwareEnum'
import { GeneralResponse } from '../../../model/generalResponse' import { GeneralResponse } from '../../../model/generalResponse'
import { Subtitle } from '../Subtitle/subtitle' import { Subtitle } from '../Subtitle/subtitle'

View File

@ -21,7 +21,7 @@ import {
BookType, BookType,
TaskExecuteType TaskExecuteType
} from '../../../define/enum/bookEnum' } from '../../../define/enum/bookEnum'
import { Book } from '../../../model/book' import { Book } from '../../../model/book/book'
import { GeneralResponse } from '../../../model/generalResponse' import { GeneralResponse } from '../../../model/generalResponse'
const fspromises = fs.promises const fspromises = fs.promises

View File

@ -6,7 +6,7 @@ import path from 'path';
import { FfmpegOptions } from "../ffmpegOptions"; import { FfmpegOptions } from "../ffmpegOptions";
import { CheckFileOrDirExist, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file"; import { CheckFileOrDirExist, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
import fs from 'fs'; import fs from 'fs';
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { LogScheduler } from '../task/logScheduler'; import { LogScheduler } from '../task/logScheduler';
import { BookBasic } from "./BooKBasic"; import { BookBasic } from "./BooKBasic";
import { LoggerStatus, OtherData } from "../../../define/enum/softwareEnum"; import { LoggerStatus, OtherData } from "../../../define/enum/softwareEnum";

View File

@ -1,6 +1,6 @@
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { BookRepalceDataType } from "../../../define/enum/bookEnum"; import { BookRepalceDataType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";

View File

@ -1,7 +1,7 @@
import { BookBackTaskType, BookImageCategory, BookType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum"; import { BookBackTaskType, BookImageCategory, BookType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import path from 'path' import path from 'path'
import { Tools } from "../../../main/tools" import { Tools } from "../../../main/tools"
import { ImageSplit } from "../../../define/Tools/image"; import { ImageSplit } from "../../../define/Tools/image";
@ -263,7 +263,7 @@ export class BookImage {
//#endregion //#endregion
//#region 将指定的批次任务,添加里面的所有的分镜到出图任务中
/** /**
* *
* @param ids ID * @param ids ID
@ -344,6 +344,9 @@ export class BookImage {
} }
} }
//#endregion
//#region 生成所有的图片,这个方法主要是分流,根据批次生成的方式,添加对应的数据
/** /**
* *
* @param bookTaskId * @param bookTaskId
@ -369,6 +372,8 @@ export class BookImage {
} }
} }
//#endregion
//#region 对图片进行锁定或者是解锁操作 //#region 对图片进行锁定或者是解锁操作
/** /**
* *
@ -583,4 +588,208 @@ export class BookImage {
} }
} }
//#endregion //#endregion
//#region 上传图片到小说中,并修改小说信息
/**
*
* @param bookTaskDetailId
* @param imageFile
* @param option
*/
private async UploadOne(bookTaskDetailId: string, imageFile: string, option: string): Promise<string> {
console.log('开始上传图片', bookTaskDetailId, imageFile, option)
if (!await CheckFileOrDirExist(path.resolve(imageFile))) {
throw new Error(`图片文件 ${imageFile} 不存在,请检查`)
}
// 修改数据库数据
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(bookTaskDetailId);
// 将图片复制到对应的文件夹中
// 生成一个0-10的随机数
let random = Math.floor(Math.random() * 10);
let newImagePath = path.join(define.project_path, `${bookTaskDetail.bookId}/data/Upload/${bookTaskDetail.name}_${Date.now()}_${random}.png`);
await CheckFolderExistsOrCreate(path.dirname(newImagePath));
await CopyFileOrFolder(imageFile, newImagePath);
if (option == "outImagePath") {
let outImagePath = bookTaskDetail.outImagePath;
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskDetail.bookTaskId);
if (isEmpty(bookTaskDetail.outImagePath)) {
outImagePath = path.join(define.project_path, `${bookTaskDetail.bookId}/tmp/${bookTask.name}/${bookTaskDetail.name}.png`);
}
await CheckFolderExistsOrCreate(path.dirname(outImagePath));
await CopyFileOrFolder(newImagePath, outImagePath);
await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetailId, {
outImagePath: path.relative(define.project_path, outImagePath)
})
await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(bookTaskDetailId, {
progress: 100,
status: 'success',
category: MJImageType.IMPORT,
messageId: '',
action: MJAction.IMAGINE,
})
return outImagePath + '?t=' + new Date().getTime()
} else if (option == "subImagePath") {
// 修改数据库数据
let subImagePath = bookTaskDetail.subImagePath ?? [];
subImagePath.push(newImagePath);
await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetailId, {
subImagePath: subImagePath.map((item) => path.relative(define.project_path, item))
})
return newImagePath + '?t=' + new Date().getTime()
} else {
throw new Error("无效的操作类型,请检查")
}
}
/**
*
* @param bookTaskDetailId ID
* @param imageFile
* @param option "outImagePath" | "subImagePath"
*/
public async UpLoadImageToBookAndUpdateMessage(bookTaskDetailId: string, imageFile: string | string[], option: string): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
if (option == "outImagePath") {
let res = await this.UploadOne(bookTaskDetailId, imageFile as string, option)
return successMessage(res, '上传图片,并修改小说信息成功', 'BookImage_UpLoadImageToBookAndUpdateMessage')
} else if (option == "subImagePath") {
let subImage = [] as string[]
for (let i = 0; i < (imageFile as string[]).length; i++) {
let res = await this.UploadOne(bookTaskDetailId, imageFile[i], option);
subImage.push(res);
}
return successMessage(subImage as string[], '上传图片,并修改小说信息成功', 'BookImage_UpLoadImageToBookAndUpdateMessage')
} else {
throw new Error("无效的操作类型,请检查")
}
} catch (error) {
return errorMessage('上传图片到小说中,并修改小说信息失败,错误信息如下:' + error.message, 'BookImage_UpLoadImageToBookAndUpdateMessage')
}
}
//#endregion
//#region 上传图片到缓存区
/**
*
* @param bookTaskId ID
* @param imageFile
*/
async UpLoadOneImageToCache(bookTaskId: string, imageFile: string): Promise<void> {
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
let cacheImagePath = path.join(define.project_path, `${bookTask.bookId}/data/Cache/${Date.now()}_${Math.floor(Math.random() * 10)}.png`);
imageFile = imageFile.split("?t=")[0];
imageFile = imageFile.split("?time=")[0];
if (imageFile.startsWith("file:/")) {
imageFile = imageFile.replace("file:///", "");
imageFile = imageFile.replace("file://", "");
imageFile = imageFile.replace("file:/", "");
}
if (!await CheckFileOrDirExist(imageFile)) {
throw new Error(`图片文件 ${imageFile} 不存在,请检查`)
}
await CheckFolderExistsOrCreate(path.dirname(cacheImagePath));
await CopyFileOrFolder(imageFile, cacheImagePath);
// 修改缓存区数据
let cacheImageList = bookTask.cacheImageList ?? [];
cacheImageList.push(cacheImagePath);
await this.bookServiceBasic.UpdetedBookTaskData(bookTaskId, {
cacheImageList: cacheImageList.map((item) => path.relative(define.project_path, item))
})
}
/**
*
* @param bookTaskId
* @param imageFile
*/
public async UpLoadImageToCache(bookTaskId: string, imageFile: string | string[]) {
try {
// 判断是不是一个数组
if (imageFile instanceof Array) {
for (let i = 0; i < imageFile.length; i++) {
let image = imageFile[i];
await this.UpLoadOneImageToCache(bookTaskId, image)
}
} else if (typeof imageFile === 'string') {
await this.UpLoadOneImageToCache(bookTaskId, imageFile)
} else {
throw new Error('未知的数据类型,请检查')
}
return successMessage(null, '上传图片到缓存中成功', 'BookImage_UpLoadImageToCache')
} catch (error) {
return errorMessage('上传图片到缓存中失败,错误信息如下:' + error.message, 'BookImage_UpLoadImageToCache')
}
}
//#endregion
//#region 获取当前小说中的所有的批次中的缓存图片
/**
*
* @param bookTaskId ID
* @returns
*/
public async GetAllBookTaskImageCache(bookTaskId: string) {
try {
if (isEmpty(bookTaskId)) {
throw new Error('没有找到对应的批次任务,请检查')
}
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskId);
let bookTasks = (await this.bookServiceBasic.GetBookTaskData({
bookId: bookTask.bookId
})).bookTasks;
if (bookTasks.length <= 0) {
throw new Error('没有找到对应的批次任务,请检查')
}
let result = [] as BookTask.BookTaskImageCacheImageList[]
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i];
let cacheImageList = element.cacheImageList ?? [];
if (cacheImageList.length > 0) {
// 检查图片文件是不是存在,不存在移除
let cacheImageListTemp = [] as string[]
for (let i = 0; i < cacheImageList.length; i++) {
const element = cacheImageList[i];
if (await CheckFileOrDirExist(element)) {
cacheImageListTemp.push(element)
}
}
result.push({
bookTaskName: element.name,
bookId: element.bookId,
bookTaskId: element.id,
imagePaths: cacheImageListTemp
})
}
}
return successMessage(result, '获取当前小说中的所有的批次中的缓存图片成功', 'BookImage_GetAllBookTaskImageCache')
} catch (error) {
return errorMessage('获取当前小说中的所有的批次中的缓存图片失败,错误信息如下:' + error.message, 'BookImage_GetAllBookTaskImageCache')
}
}
//#endregion
//#region 保存缓存区的屠图片到小说主图或者是选图区
/**
*
* @param bookTaskDetailId ID
* @param imageFile
* @param option "outImagePath" (imageFile为字符串) | "subImagePathimageFile为字符串数组"
* @returns
*/
async SaveCacheImageToData(bookTaskDetailId: string, imageFile: string | string[], option: string) {
return await this.UpLoadImageToBookAndUpdateMessage(bookTaskDetailId, imageFile, option)
}
//#endregion
} }

View File

@ -1,6 +1,6 @@
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { BookType, OperateBookType } from "../../../define/enum/bookEnum"; import { BookType, OperateBookType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, SendReturnMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, SendReturnMessage, successMessage } from "../../Public/generalTools";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";

View File

@ -1,7 +1,7 @@
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file"; import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
import { AddBookTaskCopyData, BookImageCategory, BookTaskStatus, BookType, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum"; import { AddBookTaskCopyData, BookImageCategory, BookTaskStatus, BookType, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import path from 'path' import path from 'path'
import { add, cloneDeep, isEmpty } from "lodash"; import { add, cloneDeep, isEmpty } from "lodash";
import { define } from '../../../define/define' import { define } from '../../../define/define'
@ -122,7 +122,7 @@ export class BookTask {
async CopyBookTaskBaseData(bookTask: Book.SelectBookTask, addNewBookTask: Book.AddBookTask, no: number): Promise<Book.SelectBookTask> { async CopyBookTaskBaseData(bookTask: Book.SelectBookTask, addNewBookTask: Book.AddBookTask, no: number): Promise<Book.SelectBookTask> {
let name = 'output_' + no.toString().padStart(5, '0'); let name = 'output_' + no.toString().padStart(5, '0');
let imageFolder = path.join(define.project_path, `${bookTask.bookId}/tmp/${name}`); let imageFolder = path.join(define.project_path, `${bookTask.bookId}/tmp/${name}`);
let imageCategory = global.config.defaultImageMode; let imageCategory = global.config.defaultImageMode ?? BookImageCategory.MJ;
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId) let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId)
if (!isEmpty(bookTask.imageCategory)) { if (!isEmpty(bookTask.imageCategory)) {
imageCategory = bookTask.imageCategory; imageCategory = bookTask.imageCategory;
@ -132,7 +132,7 @@ export class BookTask {
} else if (book.type == BookType.SD_REVERSE) { } else if (book.type == BookType.SD_REVERSE) {
imageCategory = BookImageCategory.SD imageCategory = BookImageCategory.SD
} else if (book.type == BookType.ORIGINAL) { } else if (book.type == BookType.ORIGINAL) {
imageCategory = global.config.defaultImageMode imageCategory = global.config.defaultImageMode ?? BookImageCategory.MJ
} }
} }

View File

@ -1,5 +1,5 @@
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum"; import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { Setting } from '../../../main/setting/setting' import { Setting } from '../../../main/setting/setting'

View File

@ -1,6 +1,6 @@
import { TagDefineType } from "../../../define/enum/bookEnum"; import { TagDefineType } from "../../../define/enum/bookEnum";
import { ImageStyleDefine } from "../../../define/iamgeStyleDefine"; import { ImageStyleDefine } from "../../../define/iamgeStyleDefine";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { TagCustomize } from "../../Original/TagCustomize"; import { TagCustomize } from "../../Original/TagCustomize";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";

View File

@ -2,7 +2,7 @@ import { isEmpty } from "lodash";
import { gptDefine } from "../../../define/gptDefine"; import { gptDefine } from "../../../define/gptDefine";
import axios from "axios"; import axios from "axios";
import { RetryWithBackoff } from "../../../define/Tools/common"; import { RetryWithBackoff } from "../../../define/Tools/common";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
/** /**
* GPT相关的服务都在这边 * GPT相关的服务都在这边

View File

@ -1,5 +1,5 @@
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { checkStringValueAddPrefix, checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools"; import { checkStringValueAddPrefix, checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../define/Tools/file"; import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../define/Tools/file";
import { define } from "../../../define/define" import { define } from "../../../define/define"

View File

@ -1,4 +1,4 @@
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools"; import { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
import { define } from '../../../define/define' import { define } from '../../../define/define'

View File

@ -1,6 +1,6 @@
import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "../../../define/enum/bookEnum"; import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "../../../define/enum/bookEnum";
import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService"; import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { TaskModal } from "@/model/task"; import { TaskModal } from "@/model/task";
import { cloneDeep, isEmpty } from "lodash"; import { cloneDeep, isEmpty } from "lodash";

View File

@ -1,5 +1,5 @@
import { BookService } from "../../../define/db/service/Book/bookService"; import { BookService } from "../../../define/db/service/Book/bookService";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
export class BookBasic { export class BookBasic {
bookService: BookService bookService: BookService

View File

@ -1,7 +1,7 @@
import { DEFINE_STRING } from "../../../define/define_string"; import { DEFINE_STRING } from "../../../define/define_string";
import { GeneralResponse } from "../../../model/generalResponse"; import { GeneralResponse } from "../../../model/generalResponse";
import { BookService } from "../../../define/db/service/Book/bookService"; import { BookService } from "../../../define/db/service/Book/bookService";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, TaskExecuteType } from "../../../define/enum/bookEnum"; import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, TaskExecuteType } from "../../../define/enum/bookEnum";
import BookBackTaskServiceBasic from "./bookBackTaskServiceBasic"; import BookBackTaskServiceBasic from "./bookBackTaskServiceBasic";
import { BookBasic } from "./bookBasic"; import { BookBasic } from "./bookBasic";

View File

@ -1,6 +1,6 @@
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService"; import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
import { BookTaskStatus } from "../../../define/enum/bookEnum"; import { BookTaskStatus } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
export default class BookTaskDetailServiceBasic { export default class BookTaskDetailServiceBasic {

View File

@ -1,5 +1,5 @@
import { BookTaskService } from "../../../define/db/service/Book/bookTaskService"; import { BookTaskService } from "../../../define/db/service/Book/bookTaskService";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { BookService } from "../../../define/db/service/Book/bookService"; import { BookService } from "../../../define/db/service/Book/bookService";
export default class BookTaskServiceBasic { export default class BookTaskServiceBasic {

View File

@ -11,7 +11,7 @@ import {
GetFilesWithExtensions GetFilesWithExtensions
} from '../../../define/Tools/file' } from '../../../define/Tools/file'
import { shell } from 'electron' import { shell } from 'electron'
import { Book } from '../../../model/book' import { Book } from '../../../model/book/book'
import fs from 'fs' import fs from 'fs'
import { GeneralResponse } from '../../../model/generalResponse' import { GeneralResponse } from '../../../model/generalResponse'
import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic' import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic'

View File

@ -13,7 +13,7 @@ import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { Subtitle } from "./subtitle"; import { Subtitle } from "./subtitle";
import { LogScheduler } from "../task/logScheduler"; import { LogScheduler } from "../task/logScheduler";
import { BookTaskStatus, BookType, OperateBookType } from "../../../define/enum/bookEnum"; import { BookTaskStatus, BookType, OperateBookType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { TimeStringToMilliseconds } from "../../../define/Tools/time"; import { TimeStringToMilliseconds } from "../../../define/Tools/time";
export class SubtitleService { export class SubtitleService {

View File

@ -4,7 +4,7 @@ import { errorMessage, successMessage } from "../../Public/generalTools";
import { Translate } from "./Translate"; import { Translate } from "./Translate";
import { DEFINE_STRING } from "../../../define/define_string" import { DEFINE_STRING } from "../../../define/define_string"
import { TranslateAPIType, TranslateType } from "../../../define/enum/translate"; import { TranslateAPIType, TranslateType } from "../../../define/enum/translate";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book/book";
import { ResponseMessageType } from "../../../define/enum/softwareEnum"; import { ResponseMessageType } from "../../../define/enum/softwareEnum";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { ValidateJson } from "../../../define/Tools/validate"; import { ValidateJson } from "../../../define/Tools/validate";

View File

@ -1,7 +1,8 @@
import { shell } from "electron"; import { dialog, shell } from "electron";
import { CheckFileOrDirExist } from "../../../define/Tools/file"; import { CheckFileOrDirExist } from "../../../define/Tools/file";
import { errorMessage, successMessage } from "../../Public/generalTools"; import { errorMessage, successMessage } from "../../Public/generalTools";
import path from 'path'; import path from 'path';
import { GeneralResponse } from "@/model/generalResponse";
/** 打开指定的文件夹的方法 */ /** 打开指定的文件夹的方法 */
@ -29,6 +30,7 @@ export default class ElectronInterface {
} }
await shell.openPath(value) await shell.openPath(value)
} }
/** /**
* *
* @param params * @param params
@ -57,4 +59,47 @@ export default class ElectronInterface {
return errorMessage('打开文件夹错误,错误信息如下:' + error.message, 'SystemIpc_OPEN_FOLDER') return errorMessage('打开文件夹错误,错误信息如下:' + error.message, 'SystemIpc_OPEN_FOLDER')
} }
} }
/**
*
* @param value
*/
public async SelectSingleFile(value: string[]): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
let { filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'fileName', extensions: value }]
})
if (filePaths.length === 0) {
throw new Error('没有选择的文件');
}
return successMessage(filePaths[0], '选择文件成功', 'SystemIpc_SelectSingleFile')
} catch (error) {
return errorMessage('选择文件错误,错误信息如下:' + error.message, 'SystemIpc_SelectSingleFile');
}
}
/**
*
* @param value
* @returns
*/
public async SelectMultipleFile(value: string[]): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
try {
const { filePaths } = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
filters: [{ name: 'fileName', extensions: value }]
});
if (filePaths.length === 0) {
throw new Error('没有选择的文件');
}
return successMessage(filePaths, '选择文件成功', 'SystemIpc_SelectMultipleFile');
} catch (error) {
console.error('选择文件错误:', error); // 记录错误日志
return errorMessage('选择文件错误,错误信息如下:' + error.message, 'SystemIpc_SelectMultipleFile');
}
}
} }

View File

@ -140,10 +140,10 @@ export default class SystemInfo {
throw new Error(res.data.message) throw new Error(res.data.message)
} }
global.endTime = res.data.endTime global.endTime = res.data.data.deactivationTime
global.permissions = res.data.permissions global.permissions = res.data.data.permissions
global.CheckMachineId = true global.CheckMachineId = true
return successMessage(res.data, '获取机器码状态成功') return successMessage(res.data.data, '获取机器码状态成功')
} catch (error) { } catch (error) {
return errorMessage('获取机器码状态错误,错误信息如下:' + error.message, 'SystemIpc_CHECK_MACHINE_STATUS') return errorMessage('获取机器码状态错误,错误信息如下:' + error.message, 'SystemIpc_CHECK_MACHINE_STATUS')
} }

View File

@ -203,7 +203,8 @@ export class TTS {
saveSubtitles: true, saveSubtitles: true,
pitch: `${edgeTTS.pitch}%`, pitch: `${edgeTTS.pitch}%`,
rate: `${edgeTTS.rate}%`, rate: `${edgeTTS.rate}%`,
volume: `${edgeTTS.volumn}%` volume: `${edgeTTS.volumn}%`,
timeout : 10000
}) })
let ttsRes = await tts.ttsPromise(text, mp3Path) let ttsRes = await tts.ttsPromise(text, mp3Path)
console.log(ttsRes) console.log(ttsRes)

View File

@ -22,7 +22,7 @@ import { BookService } from '../../define/db/service/Book/bookService';
import { OperateBookType } from '../../define/enum/bookEnum'; import { OperateBookType } from '../../define/enum/bookEnum';
import { GeneralResponse } from '../../model/generalResponse'; import { GeneralResponse } from '../../model/generalResponse';
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'; import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService';
import { Book } from '../../model/book'; import { Book } from '../../model/book/book';
import { DEFINE_STRING } from '../../define/define_string'; import { DEFINE_STRING } from '../../define/define_string';
import { BookTaskService } from '../../define/db/service/Book/bookTaskService'; import { BookTaskService } from '../../define/db/service/Book/bookTaskService';

View File

@ -1,9 +1,8 @@
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType, BookRepalceDataType, BookImageCategory } from "../define/enum/bookEnum" import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType, BookRepalceDataType, BookImageCategory } from "../../define/enum/bookEnum"
import { MJAction } from "../define/enum/bookEnum" import { MJAction } from "../../define/enum/bookEnum"
import { MJImageType } from "../define/enum/mjEnum" import { MJImageType } from "../../define/enum/mjEnum"
declare namespace Book { declare namespace Book {
type SelectBook = { type SelectBook = {
id?: string, id?: string,
no?: number, no?: number,
@ -61,6 +60,7 @@ declare namespace Book {
friendlyReminder?: string | null // 友情提示 friendlyReminder?: string | null // 友情提示
imageFolder?: string, imageFolder?: string,
customizeImageStyle?: string[], customizeImageStyle?: string[],
cacheImageList?: string[],
prefixPrompt?: string, prefixPrompt?: string,
suffixPrompt?: string, suffixPrompt?: string,
styleList?: BookStyle[] | DefineBookStyle[] // 样式列表, styleList?: BookStyle[] | DefineBookStyle[] // 样式列表,

8
src/model/book/bookTask.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
declare namespace BookTask {
type BookTaskImageCacheImageList = {
bookTaskName: string;
bookTaskId: string;
bookId: string;
imagePaths: string[];
}
}

10
src/model/task.d.ts vendored
View File

@ -1,4 +1,4 @@
import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "@/define/enum/bookEnum" import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, TaskExecuteType } from "@/define/enum/bookEnum"
declare namespace TaskModal { declare namespace TaskModal {
interface Task { interface Task {
@ -45,4 +45,12 @@ declare namespace TaskModal {
count: number, count: number,
data: BackTaskCollection[] data: BackTaskCollection[]
} }
type TaskStatus = {
status: BookBackTaskStatus | "UNKNOWN" | BookTaskStatus;
label: string;
type?: string;
color?: string;
} & ({ type: string } | { color: string })
} }

View File

@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { DEFINE_STRING } from '../define/define_string' import { DEFINE_STRING } from '../define/define_string'
import { Book } from '../model/book' import { Book } from '../model/book/book'
import { SubtitleModel } from '../model/subtitle' import { SubtitleModel } from '../model/subtitle'
import { BookType, OperateBookType } from '../define/enum/bookEnum' import { BookType, OperateBookType } from '../define/enum/bookEnum'
@ -182,6 +182,16 @@ const book = {
/** 添加一键生图后台任务 */ /** 添加一键生图后台任务 */
GenerateAllTaskImage: async (ids: string[], operateBookType: OperateBookType) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GENERATE_ALL_TASK_IMAGE, ids, operateBookType), GenerateAllTaskImage: async (ids: string[], operateBookType: OperateBookType) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GENERATE_ALL_TASK_IMAGE, ids, operateBookType),
/** 上传图片到小说中,并修改小说信息 */
UpLoadImageToBookAndUpdateMessage: async (bookTaskDetailId: string, imageFile: string | string[], option: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.UPLOAD_IMAGE_TO_BOOK_AND_UPDATE_MESSAGE, bookTaskDetailId, imageFile, option),
/** 上传图片到缓存 */
UpLoadImageToCache: async (bookTaskId: string, imageFile: string | string[]) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.UPLOAD_IMAGE_TO_CACHE, bookTaskId, imageFile),
/** 获取当前小说中的所有的批次中的缓存图片 */
GetAllBookTaskImageCache: async (bookTaskId: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_ALL_BOOK_TASK_IMAGE_CACHE, bookTaskId),
/** 保存缓存区的屠图片到小说主图或者是选图区 */
SaveCacheImageToData: async (bookTaskDetailId: string, imageFile: string | string[], option: string) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.SAVE_CACHE_IMAGE_TO_DATA, bookTaskDetailId, imageFile, option),
//#endregion //#endregion
//#region 一键反推的单个任务 //#region 一键反推的单个任务

View File

@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { DEFINE_STRING } from '../define/define_string' import { DEFINE_STRING } from '../define/define_string'
import { Book } from '../model/book' import { Book } from '../model/book/book'
const db = { const db = {
//#region 小说相关的修改 //#region 小说相关的修改

View File

@ -14,7 +14,13 @@ const system = {
/** 检查机器码状态 */ /** 检查机器码状态 */
CheckMachineStatus: (value: string) => ipcRenderer.invoke(DEFINE_STRING.SYSTEM.CHECK_MACHINE_STATUS, value), CheckMachineStatus: (value: string) => ipcRenderer.invoke(DEFINE_STRING.SYSTEM.CHECK_MACHINE_STATUS, value),
/** 获取软件版本信息,更新信息,首页内容,公告等 */ /** 获取软件版本信息,更新信息,首页内容,公告等 */
GetRemoteSystemInformation : () => ipcRenderer.invoke(DEFINE_STRING.SYSTEM.GET_REMOTE_SYSTEM_INFORMATION) GetRemoteSystemInformation: () => ipcRenderer.invoke(DEFINE_STRING.SYSTEM.GET_REMOTE_SYSTEM_INFORMATION),
/** 选择单个指定文件后缀的文件 */
SelectSingleFile: (value: string[]) => ipcRenderer.invoke(DEFINE_STRING.SYSTEM.SELECT_SINGLE_FILE, value),
/** 选择多个指定文件后缀的文件 */
SelectMultipleFile: (value: string[]) => ipcRenderer.invoke(DEFINE_STRING.SYSTEM.SELECT_MULTIPLE_FILE, value),
} }
export { system } export { system }

View File

@ -6,11 +6,13 @@
:theme="softwareStore.globalSetting.theme == 'dark' ? darkTheme : null" :theme="softwareStore.globalSetting.theme == 'dark' ? darkTheme : null"
> >
<n-message-provider> <n-message-provider>
<n-dialog-provider> <n-modal-provider>
<n-notification-provider> <n-dialog-provider>
<RouterView></RouterView> <n-notification-provider>
</n-notification-provider> <RouterView></RouterView>
</n-dialog-provider> </n-notification-provider>
</n-dialog-provider>
</n-modal-provider>
</n-message-provider> </n-message-provider>
</n-config-provider> </n-config-provider>
</n-spin> </n-spin>
@ -24,6 +26,7 @@ import {
NMessageProvider, NMessageProvider,
NDialogProvider, NDialogProvider,
NConfigProvider, NConfigProvider,
NModalProvider,
darkTheme, darkTheme,
NNotificationProvider, NNotificationProvider,
NSpin NSpin
@ -38,13 +41,13 @@ export default defineComponent({
NDialogProvider, NDialogProvider,
NMessageProvider, NMessageProvider,
NNotificationProvider, NNotificationProvider,
NModalProvider,
NSpin NSpin
}, },
setup() { setup() {
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
onMounted(async () => { onMounted(async () => {
softwareStore.SoftColor = SoftColor softwareStore.SoftColor = SoftColor
window.api.getSettingDafultData(async (value) => { window.api.getSettingDafultData(async (value) => {
await window.darkMode.toggle(value.theme) await window.darkMode.toggle(value.theme)
@ -80,8 +83,8 @@ export default defineComponent({
/* Customize the scrollbar */ /* Customize the scrollbar */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 2px; width: 4px;
height: 4px; height: 6px;
} }
/* Track */ /* Track */

View File

@ -103,6 +103,7 @@ import {
NFormItem, NFormItem,
NInput, NInput,
NButton, NButton,
NTag,
NGrid, NGrid,
NGridItem, NGridItem,
NSelect NSelect
@ -144,7 +145,10 @@ let columns = [
{ {
title: '状态', title: '状态',
key: 'status', key: 'status',
minWidth: 100 minWidth: 100,
render: (row) => {
return renderStatus(row)
}
}, },
{ {
title: '失败原因', title: '失败原因',
@ -156,6 +160,14 @@ let columns = [
} }
] ]
function renderStatus(row) {
return h(
NTag,
{ bordered: false, type: GetBookBackTaskStatusLabel(row.status).type },
{ default: GetBookBackTaskStatusLabel(row.status).label }
)
}
async function ResetQuery() { async function ResetQuery() {
softwareStore.ResetBackTaskQueryData() softwareStore.ResetBackTaskQueryData()
await query(1, pagination.value.pageSize) await query(1, pagination.value.pageSize)
@ -237,7 +249,7 @@ let GeBookBackTaskStatus = () => {
let options = [] let options = []
for (const key in BookBackTaskStatus) { for (const key in BookBackTaskStatus) {
options.push({ options.push({
label: GetBookBackTaskStatusLabel(BookBackTaskStatus[key]), label: GetBookBackTaskStatusLabel(BookBackTaskStatus[key]).label,
value: BookBackTaskStatus[key] value: BookBackTaskStatus[key]
}) })
} }

View File

@ -0,0 +1,300 @@
<template>
<n-spin :show="show">
<n-collapse
id="l-collapse"
v-model:expanded-names="defaultExpandedNames"
@item-header-click="handleItemHeaderClick"
>
<n-collapse-item
v-for="(item, index) in data"
:title="item.bookTaskName"
:name="item.bookTaskId"
:key="item.bookTaskId"
>
<n-image-group>
<div
style="
display: flex;
flex-wrap: wrap;
align-items: center;
padding-left: 10px;
min-width: 50px;
overflow: auto;
"
>
<div
v-for="(image, index) in item.imagePaths"
:key="image.id"
:id="image.id"
class="image-container"
@click="SelectImage(image, index)"
>
<n-image
:width="150"
style="margin: 5px"
:src="image.imagePath"
alt="图片描述"
preview-disabled
lazy
>
</n-image>
</div>
</div>
</n-image-group>
</n-collapse-item>
</n-collapse>
<div style="margin-top: 15px; display: flex; justify-content: flex-end">
<n-button type="info" @click="SaveImageToData">选择完成</n-button>
</div>
<template #description> 加载数据中 </template>
</n-spin>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { NImageGroup, NImage, NSpin, NButton, useMessage, NCollapse, NCollapseItem } from 'naive-ui'
import { useReverseManageStore } from '@/stores/reverseManage'
import { v4 as uuidv4 } from 'uuid'
import { MJImageType } from '@/define/enum/mjEnum'
import { MJAction } from '@/define/enum/bookEnum'
let reverseManageStore = useReverseManageStore()
let props = defineProps({
type: undefined,
bookTaskDetailId: undefined,
bookTaskId: undefined
})
let message = useMessage()
let data = ref([])
let show = ref(true)
let defaultExpandedNames = ref([])
let selectImageIds = ref([])
onMounted(async () => {
try {
//
let res = await window.book.GetAllBookTaskImageCache(props.bookTaskId)
if (res.code == 0) {
message.error(res.message)
return
}
console.log('GetAllBookTaskImageCache', res.data)
// ID
const specifiedId = reverseManageStore.selectBookTask.id // ID
res.data.sort((a, b) =>
a.bookTaskId === specifiedId ? -1 : b.bookTaskId === specifiedId ? 1 : 0
)
res.data.forEach((item) => {
let images = []
for (let i = 0; i < item.imagePaths.length; i++) {
const element = item.imagePaths[i]
images.push({
id: uuidv4(),
imagePath: element
})
}
data.value.push({
...item,
imagePaths: images
})
})
defaultExpandedNames.value = res.data.map((item) => {
if (item.bookTaskId == specifiedId) {
return item.bookTaskId
}
})
message.success('加载数据成功')
let lCollapse = document.getElementById('l-collapse')
if (lCollapse) {
lCollapse.style.height = window.innerHeight - 150 + 'px'
lCollapse.style.overflow = 'auto'
}
} catch (error) {
message.error(error.message)
} finally {
show.value = false
}
})
async function handleItemHeaderClick({ name, expanded }) {
setTimeout(() => {
if (expanded) {
//
let bookTasks = data.value.filter((item) => item.bookTaskId == name)
for (let i = 0; i < bookTasks.length; i++) {
const element = bookTasks[i]
for (let i = 0; i < element.imagePaths.length; i++) {
const image = element.imagePaths[i]
if (selectImageIds.value.some((x) => x.id == image.id)) {
let el = document.getElementById(image.id)
if (el) {
el.classList.add('selected')
}
}
}
}
}
}, 0)
}
async function SelectImage(image, index) {
if (props.type == 'outImagePath') {
//
let tempSelectImageIds = selectImageIds.value
selectImageIds.value = []
for (let i = 0; i < tempSelectImageIds.length; i++) {
const element = tempSelectImageIds[i]
let el = document.getElementById(element.id)
if (el) {
el.classList.remove('selected')
}
}
selectImageIds.value.push(image)
for (let i = 0; i < selectImageIds.value.length; i++) {
const element = selectImageIds.value[i]
let el = document.getElementById(element.id)
if (el) {
el.classList.add('selected')
}
}
} else if (props.type == 'subImagePath') {
//
//
let findIndex = selectImageIds.value.findIndex((x) => x.id == image.id)
if (findIndex == -1) {
selectImageIds.value.push(image)
for (let i = 0; i < selectImageIds.value.length; i++) {
const element = selectImageIds.value[i]
let el = document.getElementById(element.id)
if (el) {
el.classList.add('selected')
}
}
} else {
//
//
selectImageIds.value.splice(findIndex, 1)
let el = document.getElementById(image.id)
if (el) {
el.classList.remove('selected')
}
}
}
}
function RemoveImageSelect() {
for (let i = 0; i < selectImageIds.value.length; i++) {
const element = selectImageIds.value[i]
let el = document.getElementById(element.id)
if (el) {
el.classList.remove('selected')
}
}
}
async function SaveImageToData() {
//
if (selectImageIds.value.length <= 0) {
message.error('请选择图片')
return
}
let selectImagePaths = selectImageIds.value.map((x) => x.imagePath)
if (selectImagePaths.length <= 0) {
message.error('请选择图片')
return
}
if (props.type == 'outImagePath') {
//
//
let res = await window.book.SaveCacheImageToData(
props.bookTaskDetailId,
selectImagePaths[0],
props.type
)
console.log('SaveCacheImageToData', res)
if (res.code == 0) {
message.error(res.message)
return
}
//
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(x) => x.id == props.bookTaskDetailId
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].outImagePath = res.data
reverseManageStore.selectBookTaskDetail[findIndex].mjMessage = {
progress: 100,
status: 'success',
category: MJImageType.IMPORT,
messageId: '',
action: MJAction.IMAGINE
}
}
RemoveImageSelect()
selectImageIds.value = []
message.success(res.message)
} else if (props.type == 'subImagePath') {
let res = await window.book.SaveCacheImageToData(
props.bookTaskDetailId,
selectImagePaths,
props.type
)
console.log('SaveCacheImageToData', res)
if (res.code == 0) {
message.error(res.message)
return
}
//
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == props.bookTaskDetailId
)
if (findIndex != -1) {
let subImagePath = reverseManageStore.selectBookTaskDetail[findIndex].subImagePath
subImagePath.push(...res.data)
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath = subImagePath.map(
(item) => item.split('?t=')[0] + `?t=${new Date().getTime()}`
)
}
RemoveImageSelect()
selectImageIds.value = []
message.success(res.message)
}
}
</script>
<style scoped>
.image-container {
position: relative;
cursor: pointer;
margin: 5px 5px;
transition:
transform 0.3s,
box-shadow 0.3s;
}
.image-container.selected {
transform: scale(1.05);
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.3);
border: 2px solid #1890ff;
}
.image-container::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 0.3s;
}
.image-container.selected::after {
opacity: 1;
}
</style>

View File

@ -0,0 +1,119 @@
<template>
<div>
<n-image-group>
<div
style="
display: flex;
flex-wrap: wrap;
align-items: center;
padding-left: 10px;
min-width: 50px;
overflow: auto;
"
>
<div
v-for="(image, index) in data"
:key="image.id"
:id="image.id"
class="image-container"
:class="{ selected: image.selected }"
@click="SelectImage(image, index)"
>
<n-image
:width="150"
style="margin: 5px"
:src="image.imagePath"
alt="图片描述"
preview-disabled
lazy
>
</n-image>
</div>
</div>
</n-image-group>
<div>
<n-button type="info" @click="UpdateImageToCache">选择完成</n-button>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { NImage, NImageGroup, NButton, useMessage } from 'naive-ui'
import { v4 as uuidv4 } from 'uuid'
import { useReverseManageStore } from '@/stores/reverseManage'
let props = defineProps({
imageList: undefined
})
let data = ref([])
let message = useMessage()
let reverseManageStore = useReverseManageStore()
onMounted(() => {
for (let i = 0; i < props.imageList.length; i++) {
const element = props.imageList[i]
data.value.push({
id: uuidv4(),
imagePath: element,
selected: false
})
}
})
async function SelectImage(image, index) {
data.value[index].selected = !data.value[index].selected
}
async function UpdateImageToCache() {
let tempData = data.value.filter((item) => item.selected)
if (tempData.length <= 0) {
message.error('请选择图片')
return
}
let images = tempData.map((item) => item.imagePath)
let res = await window.book.UpLoadImageToCache(reverseManageStore.selectBookTask.id, images)
if (res.code == 1) {
//
for (let i = 0; i < data.value.length; i++) {
const element = data.value[i]
if (element.selected) {
element.selected = false
}
}
}
window.api.showGlobalMessage(res)
}
</script>
<style scoped>
.image-container {
position: relative;
cursor: pointer;
margin: 5px 5px;
transition:
transform 0.3s,
box-shadow 0.3s;
}
.image-container.selected {
transform: scale(1.05);
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.3);
border: 2px solid #1890ff;
}
.image-container::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 0.3s;
}
.image-container.selected::after {
opacity: 1;
}
</style>

View File

@ -0,0 +1,101 @@
<template>
<div style="display: flex; align-items: center">
<span>参数</span>
<n-dropdown
trigger="hover"
:options="options"
@select="handleSelect"
:render-option="renderOption"
>
<n-button text :style="style">
<n-icon> <MenuOpenRound /> </n-icon
></n-button>
</n-dropdown>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { NDropdown, NButton, NIcon, useMessage } from 'naive-ui'
import MenuOpenRound from '@/renderer/src/components/Icon/MenuOpenRound.vue'
import { useReverseManageStore } from '@/stores/reverseManage'
import { BookImageCategory } from '@/define/enum/bookEnum'
import { useSoftwareStore } from '@/stores/software'
import { TimeDelay } from '@/define/Tools/time'
let props = defineProps({
style: undefined
})
let style = ref(props.style)
let message = useMessage()
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let options = [
{ label: '启用修脸', key: 'openFaceRepair' },
{ label: '关闭修脸', key: 'closeFaceRepair' }
]
async function ModifyFaceRepair(key) {
//adetailer
let adetailer = true
if (key == 'openFaceRepair') {
adetailer = true
} else if (key == 'closeFaceRepair') {
adetailer = false
} else {
message.error('ModifyFaceRepair' + '_' + '未知操作')
}
//
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
let res = await window.db.UpdateBookTaskDetailData(element.id, {
adetailer: adetailer
})
if (res.code == 1) {
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
reverseManageStore.selectBookTaskDetail[findIndex].adetailer = adetailer
} else {
throw new Error(`修改${element.name}失败`)
}
}
await TimeDelay(1000)
}
async function handleSelect(key) {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在修改所有的修脸参数'
try {
switch (key) {
case 'openFaceRepair':
await ModifyFaceRepair(key)
break
case 'closeFaceRepair':
await ModifyFaceRepair(key)
break
default:
message.error('未知操作')
break
}
} catch (error) {
message.error('操作失败,' + error.message)
} finally {
softwareStore.spin.spinning = false
}
}
function renderOption({ node, option }) {
if (option.key === 'openFaceRepair' || option.key === 'closeFaceRepair') {
if (
reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD ||
reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE
) {
return node
}
} else {
return node
}
}
</script>

View File

@ -50,7 +50,21 @@
@drop="imageDragDrop" @drop="imageDragDrop"
@dragover="imageDragOver" @dragover="imageDragOver"
> >
<div style="display: flex; align-items: center; position: relative"> <div
style="display: flex; align-items: center; position: relative"
@contextmenu="handleContextMenu"
>
<n-button
text
:type="data.imageLock ? 'error' : 'primary'"
style="font-size: 32px; position: absolute; left: 0px; top: 0px"
@click="ModifyLock"
>
<n-icon>
<LockClosed v-if="data.imageLock" />
<LockOpen v-else />
</n-icon>
</n-button>
<n-image <n-image
class="g-image-class generate-image-show" class="g-image-class generate-image-show"
:src="data.outImagePath ? data.outImagePath : softwareStore.globalSetting.space_image" :src="data.outImagePath ? data.outImagePath : softwareStore.globalSetting.space_image"
@ -59,33 +73,69 @@
:height="120" :height="120"
lazy lazy
/> />
<n-button
text
:type="data.imageLock ? 'error' : 'primary'"
style="font-size: 24px; position: absolute; left: -12px; top: -5px"
@click="ModifyLock"
>
<n-icon>
<LockClosed v-if="data.imageLock" />
<LockOpen v-else />
</n-icon>
</n-button>
<n-button
text
type="info"
style="font-size: 24px; position: absolute; left: 8px; top: -5px"
@click="DownloadImage"
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"
>
<n-icon>
<Download />
</n-icon>
</n-button>
</div> </div>
<n-dropdown
:options="mainImageOptions"
placement="bottom-start"
trigger="manual"
:x="x"
:y="y"
:show="showDropdown"
:on-clickoutside="onClickoutside"
@select="handleSelect"
>
</n-dropdown>
<div> <div>
<n-divider vertical style="height: 100%" /> <n-divider vertical style="height: 100%" />
</div> </div>
<div style="display: flex; align-items: center"> <div style="display: flex">
<div style="width: 24px">
<n-tooltip>
<template #trigger>
<n-button
style="font-size: 24px"
text
color="#ee7959"
@click="UpLoadImageToSelecImageRegion"
>
<n-icon>
<UploadRound />
</n-icon>
</n-button>
</template>
上传本地图片到选图区
</n-tooltip>
<n-tooltip>
<template #trigger>
<n-button
style="font-size: 24px"
text
color="#e5a84b"
@click="UpLoadImageRegionToCache"
>
<n-icon>
<OpenInBrowserRound />
</n-icon>
</n-button>
</template>
上传选图区图片到缓存区
</n-tooltip>
<n-tooltip>
<template #trigger>
<n-button
style="font-size: 24px"
text
color="#45465e"
@click="DownloadCacheImageToSelecImageRegion"
>
<n-icon>
<DownloadRound />
</n-icon>
</n-button>
</template>
从缓存区下载图片到选图区
</n-tooltip>
</div>
<n-image-group> <n-image-group>
<div <div
style=" style="
@ -100,7 +150,7 @@
subImage="true" subImage="true"
class="g-image-class" class="g-image-class"
draggable="true" draggable="true"
:width="56" :width="55"
style="margin: 1px" style="margin: 1px"
v-for="(image, index) in data.subImagePath" v-for="(image, index) in data.subImagePath"
:key="image.id" :key="image.id"
@ -113,11 +163,21 @@
</n-image-group> </n-image-group>
</div> </div>
</div> </div>
<n-drawer v-model:show="showDrawer" :width="drawerWidth" placement="left">
<n-drawer-content :title="drawerTitle">
<BookTaskImageCache
:type="drawertype"
:bookTaskDetailId="data.id"
:bookTaskId="reverseManageStore.selectBookTask.id"
/>
</n-drawer-content>
</n-drawer>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, toRaw, watch } from 'vue' import { ref, onMounted, nextTick, watch, h } from 'vue'
import { import {
NImage, NImage,
useDialog, useDialog,
@ -127,14 +187,25 @@ import {
NButton, NButton,
NPopover, NPopover,
NTag, NTag,
useMessage NDropdown,
NDrawer,
NTooltip,
NDrawerContent,
useMessage,
useModal
} from 'naive-ui' } from 'naive-ui'
import { LockClosed, LockOpen, Download } from '@vicons/ionicons5' import { LockClosed, LockOpen, Download, FolderOpen } from '@vicons/ionicons5'
import UploadRound from '@/renderer/src/components/Icon/UploadRound.vue'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useImageStore } from '../../../../../stores/image' import { useImageStore } from '../../../../../stores/image'
import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum' import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
import { isEmpty } from 'lodash' import OpenInBrowserRound from '@/renderer/src/components/Icon/OpenInBrowserRound.vue'
import BookTaskImageCache from '@/renderer/src/components/Book/Components/Image/BookTaskImageCache.vue'
import DownloadRound from '@/renderer/src/components/Icon/DownloadRound.vue'
import SelectRegionImage from '@/renderer/src/components/Book/Components/Image/SelectRegionImage.vue'
import { isEmpty, reverse } from 'lodash'
import { MJAction, MJImageType } from '@/define/enum/mjEnum'
let props = defineProps({ let props = defineProps({
initData: undefined, initData: undefined,
index: undefined index: undefined
@ -142,6 +213,7 @@ let props = defineProps({
let imageStore = useImageStore() let imageStore = useImageStore()
let message = useMessage() let message = useMessage()
let dialog = useDialog() let dialog = useDialog()
let modal = useModal()
let data = ref(props.initData) let data = ref(props.initData)
let images = ref(props.initData.subImagePath) let images = ref(props.initData.subImagePath)
let outImagePath = ref(props.initData.outImagePath) let outImagePath = ref(props.initData.outImagePath)
@ -149,6 +221,12 @@ let width = ref(null)
let dragTarget = null let dragTarget = null
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let showDropdown = ref(false)
let showDrawer = ref(false)
let drawertype = ref('outImagePath')
let drawerTitle = ref(
`选择缓存区文件__主图__${reverseManageStore.selectBookTask.name}__${props.initData.name}`
)
// props.initData.subImagePath // props.initData.subImagePath
watch( watch(
@ -165,7 +243,7 @@ watch(
() => props.initData.outImagePath, () => props.initData.outImagePath,
(value) => { (value) => {
if (value) { if (value) {
outImagePath.value = value + `?t=${new Date().getTime()}` outImagePath.value = value.split('?t=')[0] + `?t=${new Date().getTime()}`
} }
} }
) )
@ -250,7 +328,6 @@ async function imageDragOver(e) {
} }
async function ModifyLock() { async function ModifyLock() {
if (!data.value.imageLock) { if (!data.value.imageLock) {
// //
if (!outImagePath.value) { if (!outImagePath.value) {
@ -335,4 +412,237 @@ async function DownloadImage() {
} }
}) })
} }
/** 上传图片信息到小说出图中 */
async function UpLoadImageToBookAndUpdateMessage() {
dialog.warning({
title: '上传图片提示',
content:
'注意上传的图片png到主图并且会覆盖原有的图片不可恢复上传到图片只能是裁剪过的该操作不会自动裁剪上传是否继续',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
//
let imageFiles = await window.system.SelectSingleFile(['png'])
if (imageFiles.code == 0) {
message.error(imageFiles.message)
return
}
let res = await window.book.UpLoadImageToBookAndUpdateMessage(
data.value.id,
imageFiles.data,
'outImagePath'
)
if (res.code == 1) {
//
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == data.value.id
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].outImagePath = res.data
reverseManageStore.selectBookTaskDetail[findIndex].mjMessage = {
progress: 100,
status: 'success',
category: MJImageType.IMPORT,
messageId: '',
action: MJAction.IMAGINE
}
}
}
window.api.showGlobalMessage(res)
}
})
}
/** 大小当前小说批次的出图文件夹 */
async function OpenBookTaskDetailImageFolder() {
let imageFolder = reverseManageStore.selectBookTask.imageFolder
let res = await window.system.OpenFolder({
folderPath: imageFolder,
baseProject: false
})
if (res.code == 0) {
message.error(res.message)
} else {
message.success('打开文件夹成功')
}
}
/** 将图片上传到选图区域 */
async function UpLoadImageToSelecImageRegion() {
let da = dialog.warning({
title: '上传图片提示',
content:
'注意将选中的图片png上传到选图区域回叠加到后面上传到图片只能是裁剪过的该操作不会自动裁剪上传是否继续',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
//
let imageFiles = await window.system.SelectMultipleFile(['png'])
if (imageFiles.code == 0) {
message.error(imageFiles.message)
return
}
let res = await window.book.UpLoadImageToBookAndUpdateMessage(
data.value.id,
imageFiles.data,
'subImagePath'
)
if (res.code == 1) {
//
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == data.value.id
)
debugger
if (findIndex != -1) {
let subImagePath = reverseManageStore.selectBookTaskDetail[findIndex].subImagePath
subImagePath.push(...res.data)
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath = subImagePath.map(
(item) => item.split('?t=')[0] + `?t=${new Date().getTime()}`
)
}
}
window.api.showGlobalMessage(res)
}
})
}
async function UpLoadImageRegionToCache() {
if (data.value.subImagePath.length <= 0) {
message.error('没有选图区图片,不能上传到缓存区')
return
}
dialog.create({
title: '上传图片到缓存区',
showIcon: false,
style: 'width: 800px',
content: () =>
h(SelectRegionImage, {
imageList: data.value.subImagePath
})
})
}
async function DownloadCacheImageToSelecImageRegion() {
drawertype.value = 'subImagePath'
drawerTitle.value = `选择缓存区文件__选图区__${reverseManageStore.selectBookTask.name}__${data.value.name}`
showDrawer.value = true
}
function renderIcon(icon) {
return () => {
return h(
NIcon,
{
size: 18
},
{
default: () => h(icon)
}
)
}
}
let mainImageOptions = []
function GetMainImageOptions() {
let t = [
{
label: data.value.imageLock ? '解锁' : '锁定',
key: 'lock',
icon: data.value.imageLock ? renderIcon(LockClosed) : renderIcon(LockOpen)
},
{
label: '下载图片',
key: 'download',
icon: renderIcon(Download)
},
{
label: '上传图片到小说',
key: 'upload',
icon: renderIcon(UploadRound)
},
{
label: '上传图片到缓存区',
key: 'uploadToCache',
icon: renderIcon(OpenInBrowserRound)
},
{
label: '从缓存区下载',
key: 'downloadFromCache',
icon: renderIcon(DownloadRound)
},
{
label: '打开文件夹',
key: 'open',
icon: renderIcon(FolderOpen)
}
]
if (reverseManageStore.selectBookTask.imageCategory != BookImageCategory.MJ) {
return t.filter((item) => item.key != 'download')
} else {
return t
}
}
let x = ref(0)
let y = ref(0)
function handleContextMenu(e) {
e.preventDefault()
showDropdown.value = false
nextTick().then(() => {
mainImageOptions = GetMainImageOptions()
showDropdown.value = true
x.value = e.clientX
y.value = e.clientY
})
}
function onClickoutside() {
showDropdown.value = false
}
async function UpLoadImageToCache() {
if (isEmpty(data.value.outImagePath)) {
message.error('没有生成图片,不能上传到缓存区')
return
}
let da = dialog.warning({
title: '上传缓存区提示',
content: '会将当前主题上传到缓存区,缓存区的图片可以在其他地方使用,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.UpLoadImageToCache(
reverseManageStore.selectBookTask.id,
data.value.outImagePath
)
window.api.showGlobalMessage(res)
da.destroy()
}
})
}
let drawerWidth = ref(window.innerWidth * 0.8)
async function DownloadImageFromCache() {
drawertype.value = 'outImagePath'
showDrawer.value = true
}
async function handleSelect(key) {
showDropdown.value = false
if (key == 'lock') {
await ModifyLock()
} else if (key == 'download') {
await DownloadImage()
} else if (key == 'upload') {
await UpLoadImageToBookAndUpdateMessage()
} else if (key == 'open') {
await OpenBookTaskDetailImageFolder()
} else if (key == 'uploadToCache') {
await UpLoadImageToCache()
} else if (key == 'downloadFromCache') {
await DownloadImageFromCache()
} else {
message.error('未知操作')
}
}
</script> </script>

View File

@ -43,7 +43,7 @@ import { NSelect, NButton, useMessage, useDialog, NPopover, NIcon } from 'naive-
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { Download } from '@vicons/ionicons5' import { Download } from '@vicons/ionicons5'
import { OperateBookType } from '../../../../../define/enum/bookEnum' import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let message = useMessage() let message = useMessage()
let dialog = useDialog() let dialog = useDialog()
@ -53,7 +53,7 @@ let image_options = ref([])
let select = computed(() => let select = computed(() =>
reverseManageStore.selectBookTask.imageCategory reverseManageStore.selectBookTask.imageCategory
? reverseManageStore.selectBookTask.imageCategory ? reverseManageStore.selectBookTask.imageCategory
: softwareStore.globalSetting.defaultImageMode : softwareStore.globalSetting.defaultImageMode ?? BookImageCategory.MJ
) )
onMounted(async () => { onMounted(async () => {

View File

@ -8,132 +8,188 @@
@update:value="handleUpdateValue" @update:value="handleUpdateValue"
:options="options" :options="options"
/> />
<n-dropdown trigger="hover" :options="blockOptions" @select="ImageLockOperation"> <n-dropdown
<n-button trigger="hover"
:color="softwareStore.SoftColor.ZHUYANTUO" :options="blockOptions"
@click="ImageLockOperation('lock')" @select="dropdownSelectHandle"
size="tiny" :render-option="renderOptionHandle"
style="margin-left: 5px"
>一键锁定</n-button
>
</n-dropdown>
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
@click="OneToFourBookTask"
size="tiny"
style="margin-left: 5px"
>一拆四</n-button
> >
<n-button text :style="style">
<n-icon> <MenuOpenRound /> </n-icon>
</n-button>
</n-dropdown>
</div> </div>
</template> </template>
<script> <script setup>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, computed } from 'vue' import { ref, onMounted, computed, render } from 'vue'
import { useMessage, useDialog, NSelect, NButton, NDropdown } from 'naive-ui' import { useMessage, useDialog, NIcon, NSelect, NButton, NDropdown } from 'naive-ui'
import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum' import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
import MenuOpenRound from '../../Icon/MenuOpenRound.vue'
export default defineComponent({ let props = defineProps({
components: { NSelect, NButton, NDropdown }, style: undefined
})
let style = ref(props.style)
setup() { let message = useMessage()
let message = useMessage() let dialog = useDialog()
let dialog = useDialog() let softwareStore = useSoftwareStore()
let softwareStore = useSoftwareStore() let reverseManageStore = useReverseManageStore()
let reverseManageStore = useReverseManageStore() let select = computed(() =>
let select = computed(() => reverseManageStore.selectBookTask.imageCategory
reverseManageStore.selectBookTask.imageCategory ? reverseManageStore.selectBookTask.imageCategory
? reverseManageStore.selectBookTask.imageCategory : softwareStore.globalSetting.defaultImageMode ?? BookImageCategory.MJ
: softwareStore.globalSetting.defaultImageMode )
) let options = ref([])
let options = ref([])
onMounted(async () => { onMounted(async () => {
debugger //
// await window.api.GetImageGenerateCategory((value) => {
await window.api.GetImageGenerateCategory((value) => { if (value.code == 0) {
if (value.code == 0) { message.error(value.message)
message.error(value.message) return
return
}
options.value = value.data
})
})
async function handleUpdateValue(value) {
//
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
imageCategory: value
})
if (res.code == 0) {
message.error(res.message)
return
}
reverseManageStore.selectBookTask.imageCategory = value
message.success('修改成功')
} }
options.value = value.data
})
})
/** async function handleUpdateValue(value) {
* 土拍你 //
* @param value let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
*/ imageCategory: value
async function ImageLockOperation(value) { })
let res = await window.book.ImageLockOperation( if (res.code == 0) {
message.error(res.message)
return
}
reverseManageStore.selectBookTask.imageCategory = value
message.success('修改成功')
}
async function ImageLockOperation(value) {
let res = await window.book.ImageLockOperation(
reverseManageStore.selectBookTask.id,
value,
OperateBookType.BOOKTASK
)
if (res.code == 0) {
message.error(res.message)
return
}
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == element.id)
if (index != -1) {
reverseManageStore.selectBookTaskDetail[index].imageLock = element.imageLock
}
}
message.success(res.message)
}
async function dropdownSelectHandle(key) {
switch (key) {
case 'lock':
case 'unlock':
await ImageLockOperation(key)
break
case 'downloadMJImage':
await DownloadAllImage()
break
case 'oneToFour':
await OneToFourBookTask()
break
default:
message.error('未知操作')
break
}
}
async function DownloadAllImage() {
let da = dialog.warning({
title: '采集所有图片提示',
content:
'即将开始采集所有的MJ生图图片满足一下条件的分镜才会被采集状态不为 error有生图信息有消息ID没有生成图片图片的有效期为24小时是否继续',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
da?.destroy()
let res = await window.book.GetImageUrlAndDownload(
reverseManageStore.selectBookTask.id, reverseManageStore.selectBookTask.id,
value, OperateBookType.BOOKTASK,
OperateBookType.BOOKTASK false
) )
if (res.code == 1) {
//
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].outImagePath =
element.data.outImagePath
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath =
element.data.subImagePath
reverseManageStore.selectBookTaskDetail[findIndex].mjMessage = element.data.mjMessage
}
}
message.success(res.message)
} else {
message.error(res.message)
}
}
})
}
//
async function OneToFourBookTask() {
dialog.warning({
title: '一拆四提示',
content:
'是否一拆四(一拆四是个泛指,根据出的子图数量最少的),会自动生成多个批次任务并设置对应的图片!',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.OneToFourBookTask(reverseManageStore.selectBookTask.id)
if (res.code == 0) { if (res.code == 0) {
message.error(res.message) message.error(res.message)
return return
} }
for (let i = 0; i < res.data.length; i++) {
const element = res.data[i]
let index = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
if (index != -1) {
reverseManageStore.selectBookTaskDetail[index].imageLock = element.imageLock
}
}
message.success(res.message) message.success(res.message)
} }
})
}
// function renderOptionHandle({ node, option }) {
async function OneToFourBookTask() { console.log('renderOptionHandle', node, option)
dialog.warning({ if (option.key == 'downloadMJImage') {
title: '一拆四提示', if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
content: return node
'是否一拆四(一拆四是个泛指,根据出的子图数量最少的),会自动生成多个批次任务并设置对应的图片!',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
let res = await window.book.OneToFourBookTask(reverseManageStore.selectBookTask.id)
if (res.code == 0) {
message.error(res.message)
return
}
message.success(res.message)
}
})
}
return {
reverseManageStore,
handleUpdateValue,
OneToFourBookTask,
softwareStore,
ImageLockOperation,
select,
blockOptions: [
{
label: '一键解锁',
key: 'unlock'
}
],
options
} }
} else {
return node
} }
}) }
let blockOptions = [
{
label: '一键锁定',
key: 'lock'
},
{
label: '一键解锁',
key: 'unlock'
},
{
label: 'MJ采集全部图片',
key: 'downloadMJImage'
},
{
label: '一拆四',
key: 'oneToFour'
}
]
</script> </script>

View File

@ -84,7 +84,7 @@
:color="softwareStore.SoftColor.ORANGE" :color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px" style="margin-left: 5px"
size="small" size="small"
@click="GenerateImageAll" @click="GenerateImageAll(true)"
> >
一键生图 一键生图
</n-button> </n-button>
@ -138,7 +138,13 @@ import VideoCanvas from '../../VideoSubtitle/VideoCanvas.vue'
import { SubtitleSavePositionType } from '../../../../../define/enum/waterMarkAndSubtitle.ts' import { SubtitleSavePositionType } from '../../../../../define/enum/waterMarkAndSubtitle.ts'
import { DEFINE_STRING } from '../../../../../define/define_string/index.ts' import { DEFINE_STRING } from '../../../../../define/define_string/index.ts'
import Setting from './Setting.vue' import Setting from './Setting.vue'
import { BookType, OperateBookType } from '../../../../../define/enum/bookEnum.ts' import {
BookBackTaskType,
BookImageCategory,
BookType,
OperateBookType,
TaskExecuteType
} from '../../../../../define/enum/bookEnum.ts'
import TranslateSetting from '../../Setting/TranslateSetting.vue' import TranslateSetting from '../../Setting/TranslateSetting.vue'
import { TranslateType } from '../../../../../define/enum/translate.ts' import { TranslateType } from '../../../../../define/enum/translate.ts'
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common.ts' import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common.ts'
@ -464,6 +470,10 @@ async function handleSelect(key) {
await Translate('translate_english') await Translate('translate_english')
break break
case 'generate_space_image': //
await GenerateImageAll(false)
break
case 'translate_setting': // case 'translate_setting': //
await Translate('translate_setting') await Translate('translate_setting')
break break
@ -780,18 +790,124 @@ async function TranslateService(from, to) {
} }
/** /**
* 生成所有图片 * 根据指定类型为所有书籍任务生成图像
* @param {String} type - 小说图像类别
* @param {Boolean} cover - 是否覆盖现有图像
*/ */
async function GenerateImageAll() { async function AddFluxImageAll(type, cover) {
let res = await window.book.GenerateImageAll( let messageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
reverseManageStore.selectBookTask.id, let taskType = BookBackTaskType.FLUX_API_IMAGE
reverseManageStore.selectBookTask.imageCategory if (type == BookImageCategory.FLUX_FORGE) {
) messageName = DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
if (res.code == 0) { taskType = BookBackTaskType.FLUX_FORGE_IMAGE
message.error(res.message) } else if (type == BookImageCategory.FLUX_API) {
messageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.FLUX_API_IMAGE
} else if (type == BookImageCategory.D3) {
messageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.D3_IMAGE
} else if (type == BookImageCategory.SD) {
messageName = DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.SD_IMAGE
} else {
message.error('未知的图像类别')
return return
} }
message.success(res.message) //
let bookTaskDetails = reverseManageStore.selectBookTaskDetail.filter((item) => !item.imageLock)
if (cover) {
//
let tasks = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasks.push({
bookId: reverseManageStore.selectBook.id,
type: taskType,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: messageName
})
}
if (tasks.length <= 0) {
message.error('没有需要生成的分镜')
return
}
let res = await await window.task.AddMultiBookBackTask(tasks)
window.api.showGlobalMessage(res)
} else {
//
let tasks = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
if (!element.mjMessage) {
tasks.push({
bookId: reverseManageStore.selectBook.id,
type: taskType,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: messageName
})
}
}
if (tasks.length == 0) {
message.error('没有未生成的分镜,或者都被锁定了')
return
}
let res = await await window.task.AddMultiBookBackTask(tasks)
window.api.showGlobalMessage(res)
}
return
}
/**
* 生成所有图片
*/
async function GenerateImageAll(cover = true) {
let da = dialog.warning({
title: '生成所有图片提示',
content: `即将开始生成 ${cover ? '所有的' : '从未生成过的分镜'} 图片,当前的生图模式为 ${
reverseManageStore.selectBookTask.imageCategory
}开始执行会有1-20秒的延迟除了被锁定的图片其余的图片都会重新生成是否继续`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
try {
da?.destroy()
debugger
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
let res = await window.mj.AddMJGenerateImageTask(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN,
cover
)
if (res?.code == 1) {
message.success(res.message)
} else {
message.error(res.message)
}
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
await AddFluxImageAll(BookImageCategory.SD, cover)
} else if (
reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE
) {
await AddFluxImageAll(BookImageCategory.FLUX_FORGE, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
await AddFluxImageAll(BookImageCategory.FLUX_API, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
await AddFluxImageAll(BookImageCategory.D3, cover)
} else {
message.error('不支持的生图类型')
return
}
} catch (error) {
message.error(error.message)
}
}
})
} }
/** /**
@ -973,6 +1089,7 @@ let translateOptions = [
} }
] ]
let generateOptions = [ let generateOptions = [
{ label: '生成未出图分镜', key: 'generate_space_image' },
{ {
label: '停止生图', label: '停止生图',
key: 'stop_image_generate' key: 'stop_image_generate'

View File

@ -27,6 +27,7 @@ import DatatableAfterGpt from './DatatableAfterGpt.vue'
import DatatableGenerateImage from './DatatableGenerateImage.vue' import DatatableGenerateImage from './DatatableGenerateImage.vue'
import ODataTableAction from '../Original/ODataTableAction.vue' import ODataTableAction from '../Original/ODataTableAction.vue'
import SDReversePrompt from './SDReversePrompt.vue' import SDReversePrompt from './SDReversePrompt.vue'
import BookTaskDetailOptions from '../Components/ManageBook/BookTaskDetailOptions.vue'
let softwareStore = useSoftwareStore() let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
@ -117,7 +118,9 @@ const columns = [
}, },
{ {
// //
title: '参数', title(row) {
return h(BookTaskDetailOptions, { style: 'font-size: 24px;margin-left: 5px' })
},
key: 'parameter', key: 'parameter',
width: '110', width: '110',
render(row, index) { render(row, index) {
@ -126,7 +129,7 @@ const columns = [
}, },
{ {
title() { title() {
return h(DatatableHeaderImage) return h(DatatableHeaderImage, { style: 'font-size: 24px; margin-left: 5px' })
}, },
width: 300, width: 300,
className: 'empty-margin', className: 'empty-margin',

View File

@ -1,6 +1,14 @@
<template> <template>
<div style="margin-bottom: 5px; margin-left: 5px; display: flex; align-items: center"> <div style="margin-bottom: 5px; margin-left: 5px; display: flex; align-items: center">
<n-button :render-icon="renderIcon" size="small" type="info" @click="AddBookDialog()" <n-button size="small" strong secondary type="default">{{
reverseManageStore.selectBook.name
}}</n-button>
<n-button
style="margin-left: 5px"
:render-icon="renderIcon"
size="small"
type="info"
@click="AddBookDialog()"
>新增</n-button >新增</n-button
> >
<n-button style="margin-left: 5px" size="small" type="info" @click="GenerateAllTaskImage()" <n-button style="margin-left: 5px" size="small" type="info" @click="GenerateAllTaskImage()"
@ -54,14 +62,15 @@ import {
NDataTable, NDataTable,
NIcon, NIcon,
NDropdown, NDropdown,
NPerformantEllipsis NPerformantEllipsis,
NTag
} from 'naive-ui' } from 'naive-ui'
import { useReverseManageStore } from '../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../stores/software' import { useSoftwareStore } from '../../../../stores/software'
import { AddSharp, Open } from '@vicons/ionicons5' import { AddSharp, Open } from '@vicons/ionicons5'
import BookTaskListAction from './Components/ManageBook/BookTaskListAction.vue' import BookTaskListAction from './Components/ManageBook/BookTaskListAction.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { OperateBookType } from '../../../../define/enum/bookEnum' import { GetBookTaskDetailStatusLabel, OperateBookType } from '../../../../define/enum/bookEnum'
import { DEFINE_STRING } from '../../../../define/define_string' import { DEFINE_STRING } from '../../../../define/define_string'
import { ResponseMessageType } from '../../../../define/enum/softwareEnum' import { ResponseMessageType } from '../../../../define/enum/softwareEnum'
import AddBookTask from './Components/ManageBook/AddBookTask.vue' import AddBookTask from './Components/ManageBook/AddBookTask.vue'
@ -137,7 +146,10 @@ function createColumns() {
{ {
title: '状态', title: '状态',
key: 'status', key: 'status',
width: 100 width: 100,
render(row) {
return renderStatus(row)
}
}, },
{ {
title: '失败原因', title: '失败原因',
@ -185,6 +197,14 @@ function createOptions(row) {
] ]
} }
function renderStatus(row) {
return h(
NTag,
{ bordered: false, type: GetBookTaskDetailStatusLabel(row.status).type },
{ default: GetBookTaskDetailStatusLabel(row.status).label }
)
}
async function HDImage(value) { async function HDImage(value) {
softwareStore.spin.spinning = true softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。' softwareStore.spin.tip = '正在进行高清检查。。。'
@ -397,7 +417,7 @@ async function GenerateAllTaskImage() {
[...checkedRowKeysRef.value], [...checkedRowKeysRef.value],
OperateBookType.ASSIGNBOOKTASK OperateBookType.ASSIGNBOOKTASK
) )
window.api.showGlobalMessageDialog(res); window.api.showGlobalMessageDialog(res)
softwareStore.spin.spinning = false softwareStore.spin.spinning = false
} }
}) })

View File

@ -35,7 +35,6 @@
</n-popover> </n-popover>
<n-input <n-input
:status="input_status"
size="tiny" size="tiny"
placeholder="请生成出图命令或是提示词" placeholder="请生成出图命令或是提示词"
type="textarea" type="textarea"
@ -178,7 +177,6 @@ async function SingleGenerateImage() {
// //
async function NextGenerateImage() { async function NextGenerateImage() {
let res = undefined let res = undefined
let bookTaskDetails = reverseManageStore.selectBookTaskDetail.filter( let bookTaskDetails = reverseManageStore.selectBookTaskDetail.filter(
(item, index) => index >= props.index && !item.imageLock (item, index) => index >= props.index && !item.imageLock

View File

@ -382,6 +382,9 @@ async function AddFluxImageAll(type, cover) {
if (type == BookImageCategory.FLUX_FORGE) { if (type == BookImageCategory.FLUX_FORGE) {
messageName = DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN messageName = DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.FLUX_FORGE_IMAGE taskType = BookBackTaskType.FLUX_FORGE_IMAGE
} else if (type == BookImageCategory.FLUX_API) {
messageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.FLUX_API_IMAGE
} else if (type == BookImageCategory.D3) { } else if (type == BookImageCategory.D3) {
messageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN messageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.D3_IMAGE taskType = BookBackTaskType.D3_IMAGE
@ -465,7 +468,7 @@ async function GenerateImageAll(cover = true) {
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) { } else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) {
await AddFluxImageAll(BookImageCategory.FLUX_FORGE, cover) await AddFluxImageAll(BookImageCategory.FLUX_FORGE, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) { } else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
await AddFluxImageAll(BookImageCategory.FLUX_FORGE, cover) await AddFluxImageAll(BookImageCategory.FLUX_API, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) { } else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
await AddFluxImageAll(BookImageCategory.D3, cover) await AddFluxImageAll(BookImageCategory.D3, cover)
} else { } else {

View File

@ -6,13 +6,11 @@
classMame="cd-22223" classMame="cd-22223"
:max-height="maxHeight" :max-height="maxHeight"
:loading="softwareStore.loading.originDatatableDataLoading" :loading="softwareStore.loading.originDatatableDataLoading"
:row-key="rowKey"
:columns="createColumns({})" :columns="createColumns({})"
:data="reverseManageStore.selectBookTaskDetail" :data="reverseManageStore.selectBookTaskDetail"
:pagination="false" :pagination="false"
:bordered="false" :bordered="false"
:scroll-x="0" :scroll-x="0"
@update:checked-row-keys="handleCheck"
/> />
</div> </div>
</template> </template>
@ -30,7 +28,9 @@ import ODataTableHeaderPrompt from './ODatatableHeaderPrompt.vue'
import ODatatablePrompt from './ODatatablePrompt.vue' import ODatatablePrompt from './ODatatablePrompt.vue'
import ODataTableAction from './ODataTableAction.vue' import ODataTableAction from './ODataTableAction.vue'
import DatatableHeaderGenerateImage from '../MJReverse/DatatableHeaderGenerateImage.vue' import DatatableHeaderGenerateImage from '../MJReverse/DatatableHeaderGenerateImage.vue'
import DatatableHeaderImage from '../MJReverse/DatatableHeaderImage.vue'
import DatatableGenerateImage from '../MJReverse/DatatableGenerateImage.vue' import DatatableGenerateImage from '../MJReverse/DatatableGenerateImage.vue'
import BookTaskDetailOptions from '../Components/ManageBook/BookTaskDetailOptions.vue'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../stores/software'
let reverseManageStore = useReverseManageStore() let reverseManageStore = useReverseManageStore()
@ -141,7 +141,9 @@ const createColumns = ({}) => {
}, },
{ {
// //
title: '参数', title(row) {
return h(BookTaskDetailOptions, { style: 'font-size: 24px;margin-left: 5px' })
},
key: 'parameter', key: 'parameter',
width: '130', width: '130',
render(row, index) { render(row, index) {
@ -151,7 +153,8 @@ const createColumns = ({}) => {
{ {
// //
title(row) { title(row) {
return h(DatatableHeaderGenerateImage) // return h(DatatableHeaderGenerateImage)
return h(DatatableHeaderImage, { style: 'font-size: 24px; margin-left: 5px' })
}, },
key: 'options', key: 'options',
className: 'empty-margin', className: 'empty-margin',

View File

@ -0,0 +1,12 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
d="M16.59 9H15V4c0-.55-.45-1-1-1h-4c-.55 0-1 .45-1 1v5H7.41c-.89 0-1.34 1.08-.71 1.71l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59c.63-.63.19-1.71-.7-1.71zM5 19c0 .55.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1H6c-.55 0-1 .45-1 1z"
fill="currentColor"
></path>
</svg>
</template>

View File

@ -0,0 +1,12 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
d="M4 18h11c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1zm0-5h8c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1zM3 7c0 .55.45 1 1 1h11c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1zm17.3 7.88L17.42 12l2.88-2.88a.996.996 0 1 0-1.41-1.41L15.3 11.3a.996.996 0 0 0 0 1.41l3.59 3.59c.39.39 1.02.39 1.41 0c.38-.39.39-1.03 0-1.42z"
fill="currentColor"
></path>
</svg>
</template>

View File

@ -0,0 +1,12 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
d="M19 4H5a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h3c.55 0 1-.45 1-1s-.45-1-1-1H5V8h14v10h-3c-.55 0-1 .45-1 1s.45 1 1 1h3c1.1 0 2-.9 2-2V6a2 2 0 0 0-2-2zm-7.35 6.35l-2.79 2.79c-.32.32-.1.86.35.86H11v5c0 .55.45 1 1 1s1-.45 1-1v-5h1.79c.45 0 .67-.54.35-.85l-2.79-2.79c-.19-.2-.51-.2-.7-.01z"
fill="currentColor"
></path>
</svg>
</template>

View File

@ -0,0 +1,14 @@
<template>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
>
<path
d="M10 16h4c.55 0 1-.45 1-1v-5h1.59c.89 0 1.34-1.08.71-1.71L12.71 3.7a.996.996 0 0 0-1.41 0L6.71 8.29c-.63.63-.19 1.71.7 1.71H9v5c0 .55.45 1 1 1zm-4 2h12c.55 0 1 .45 1 1s-.45 1-1 1H6c-.55 0-1-.45-1-1s.45-1 1-1z"
fill="currentColor"
></path>
</svg>
</div>
</template>

View File

@ -2,7 +2,7 @@ import { messageDark, useMessage } from 'naive-ui'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { errorMessage, successMessage } from '../main/Public/generalTools' import { errorMessage, successMessage } from '../main/Public/generalTools'
import { BookTaskStatus } from '../define/enum/bookEnum' import { BookTaskStatus } from '../define/enum/bookEnum'
import { Book } from '../model/book' import { Book } from '../model/book/book'
import { GeneralResponse } from '../model/generalResponse' import { GeneralResponse } from '../model/generalResponse'
import { PresetModel } from '../model/preset' import { PresetModel } from '../model/preset'