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

View File

@ -1,6 +1,6 @@
{
"name": "laitool",
"version": "3.2.0",
"version": "3.2.1",
"description": "An AI tool for image processing, video processing, and other functions.",
"main": "./out/main/index.js",
"author": "laitool.cn",
@ -38,7 +38,7 @@
"lodash": "^4.17.21",
"moment-timezone": "^0.5.45",
"music-metadata": "^7.14.0",
"node-edge-tts": "^1.2.3",
"node-edge-tts": "^1.2.4",
"node-machine-id": "^1.1.12",
"npm": "^10.7.0",
"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
* @returns true表示存在false表示不存在
*/
export async function CheckFileOrDirExist(path) {
export async function CheckFileOrDirExist(filePath) {
try {
await fspromises.access(path)
await fspromises.access(filePath)
return true // 文件或目录存在
} catch (error) {
return false // 文件或目录不存在

View File

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

View File

@ -9,7 +9,7 @@ import { BaseRealmService } from './bookBasic'
import { isEmpty } from 'lodash'
import { OtherData } from '../../../enum/softwareEnum.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'
const { v4: uuidv4 } = require('uuid')

View File

@ -197,6 +197,13 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
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 {
@ -238,7 +245,7 @@ export class BaseRealmService extends BaseService {
BookTaskDetailModel
],
path: this.dbpath,
schemaVersion: 27,
schemaVersion: 29,
migration: migration
}
this.realm = await Realm.open(config)

View File

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

View File

@ -8,7 +8,7 @@ import { cloneDeep, isEmpty } from 'lodash'
import { JoinPath } from '../../../Tools/file'
import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js'
const { v4: uuidv4 } = require('uuid')
import { Book } from "../../../../model/book"
import { Book } from "../../../../model/book/book"
import { GeneralResponse } from '../../../../model/generalResponse.js'
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 { BookBackTaskList } from '../../model/Book/BookBackTaskListModel.js'
const { v4: uuidv4 } = require('uuid')
import { Book } from '../../../../model/book'
import { Book } from '../../../../model/book/book'
import { TagDefine } from '../../../tagDefine.js'
import { ImageStyleDefine } from "../../../../define/iamgeStyleDefine"
import { cloneDeep } from 'lodash'
@ -89,6 +89,7 @@ export class BookTaskService extends BaseRealmService {
srtPath: JoinPath(define.project_path, bookTask.srtPath),
audioPath: JoinPath(define.project_path, bookTask.audioPath),
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出图
} as Book.SelectBookTask;
})

View File

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

View File

@ -107,6 +107,18 @@ const BOOK = {
/** 添加一键生图后台任务 */
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
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',

View File

@ -10,5 +10,11 @@ export const SYSTEM = {
/** 检查机器码状态 */
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 {
// 原创
ORIGINAL = 'original',
@ -333,70 +335,194 @@ export function GetBookBackTaskTypeLabel(key: string) {
* @param key
* @returns
*/
export function GetBookTaskDetailStatusLabel(key: string) {
export function GetBookTaskDetailStatusLabel(key: string): TaskModal.TaskStatus {
switch (key) {
case BookTaskStatus.WAIT:
return '等待';
return {
status: BookTaskStatus.WAIT,
label: '等待',
type: 'warning'
};
case BookTaskStatus.STORYBOARD:
return '分镜计算中';
return {
status: BookTaskStatus.STORYBOARD,
label: '分镜计算中',
type: 'info'
};
case BookTaskStatus.STORYBOARD_FAIL:
return '分镜计算失败';
return {
status: BookTaskStatus.STORYBOARD_FAIL,
label: '分镜计算失败',
type: 'error'
};
case BookTaskStatus.STORYBOARD_DONE:
return '分镜计算完成';
return {
status: BookTaskStatus.STORYBOARD_DONE,
label: '分镜计算完成',
type: 'success'
};
case BookTaskStatus.SPLIT:
return '分割视频中';
return {
status: BookTaskStatus.SPLIT,
label: '分割视频中',
type: 'info'
};
case BookTaskStatus.SPLIT_FAIL:
return '分割视频失败';
return {
status: BookTaskStatus.SPLIT_FAIL,
label: '分割视频失败',
type: 'error'
};
case BookTaskStatus.SPLIT_DONE:
return '分割视频完成';
return {
status: BookTaskStatus.SPLIT_DONE,
label: '分割视频完成',
type: 'success'
};
case BookTaskStatus.AUDIO:
return '提取音频中';
return {
status: BookTaskStatus.AUDIO,
label: '提取音频中',
type: 'info'
};
case BookTaskStatus.AUDIO_FAIL:
return '提取音频失败';
return {
status: BookTaskStatus.AUDIO_FAIL,
label: '提取音频失败',
type: 'error'
};
case BookTaskStatus.AUDIO_DONE:
return '提取音频完成';
return {
status: BookTaskStatus.AUDIO_DONE,
label: '提取音频完成',
type: 'success'
};
case BookTaskStatus.RECOGNIZE:
return '识别字幕中';
return {
status: BookTaskStatus.RECOGNIZE,
label: '识别字幕中',
type: 'info'
};
case BookTaskStatus.RECOGNIZE_FAIL:
return '识别字幕失败';
return {
status: BookTaskStatus.RECOGNIZE_FAIL,
label: '识别字幕失败',
type: 'error'
};
case BookTaskStatus.RECOGNIZE_DONE:
return '识别字幕完成';
return {
status: BookTaskStatus.RECOGNIZE_DONE,
label: '识别字幕完成',
type: 'success'
};
case BookTaskStatus.FRAME:
return '抽帧中';
return {
status: BookTaskStatus.FRAME,
label: '抽帧中',
type: 'info'
};
case BookTaskStatus.FRAME_FAIL:
return '抽帧失败';
return {
status: BookTaskStatus.FRAME_FAIL,
label: '抽帧失败',
type: 'error'
};
case BookTaskStatus.FRAME_DONE:
return '抽帧完成';
return {
status: BookTaskStatus.FRAME_DONE,
label: '抽帧完成',
type: 'success'
};
case BookTaskStatus.REVERSE:
return '反推中';
return {
status: BookTaskStatus.REVERSE,
label: '反推中',
type: 'info'
};
case BookTaskStatus.REVERSE_FAIL:
return '反推失败';
return {
status: BookTaskStatus.REVERSE_FAIL,
label: '反推失败',
type: 'error'
};
case BookTaskStatus.REVERSE_DONE:
return '反推完成';
return {
status: BookTaskStatus.REVERSE_DONE,
label: '反推完成',
type: 'success'
};
case BookTaskStatus.IMAGE:
return '生成图片中';
return {
status: BookTaskStatus.IMAGE,
label: '生成图片中',
type: 'info'
};
case BookTaskStatus.IMAGE_FAIL:
return '生成图片失败';
return {
status: BookTaskStatus.IMAGE_FAIL,
label: '生成图片失败',
type: 'error'
};
case BookTaskStatus.IMAGE_DONE:
return '生成图片完成';
return {
status: BookTaskStatus.IMAGE_DONE,
label: '生成图片完成',
type: 'success'
};
case BookTaskStatus.HD:
return '高清中';
return {
status: BookTaskStatus.HD,
label: '高清中',
type: 'info'
};
case BookTaskStatus.HD_FAIL:
return '高清失败';
return {
status: BookTaskStatus.HD_FAIL,
label: '高清失败',
type: 'error'
};
case BookTaskStatus.HD_DONE:
return '高清完成';
return {
status: BookTaskStatus.HD_DONE,
label: '高清完成',
type: 'success'
};
case BookTaskStatus.COMPOSING:
return '合成视频中';
return {
status: BookTaskStatus.COMPOSING,
label: '合成视频中',
type: 'info'
};
case BookTaskStatus.COMPOSING_FAIL:
return '合成视频失败';
return {
status: BookTaskStatus.COMPOSING_FAIL,
label: '合成视频失败',
type: 'error'
};
case BookTaskStatus.COMPOSING_DONE:
return '合成视频完成';
return {
status: BookTaskStatus.COMPOSING_DONE,
label: '合成视频完成',
type: 'success'
};
case BookTaskStatus.DRAFT_DONE:
return '添加草稿完成';
return {
status: BookTaskStatus.DRAFT_DONE,
label: '添加草稿完成',
type: 'success'
};
case BookTaskStatus.DRAFT_FAIL:
return '添加草稿失败';
return {
status: BookTaskStatus.DRAFT_FAIL,
label: '添加草稿失败',
type: 'error'
};
default:
return key;
return {
status: "UNKNOWN",
label: "UNKNOWN",
type: 'warning'
};
}
}
@ -405,21 +531,50 @@ export function GetBookTaskDetailStatusLabel(key: string) {
* @param key
* @returns
*/
export function GetBookBackTaskStatusLabel(key: string) {
export function GetBookBackTaskStatusLabel(key: string): TaskModal.TaskStatus {
switch (key) {
case BookBackTaskStatus.WAIT:
return '等待';
return {
status: BookBackTaskStatus.WAIT,
label: '等待',
type: 'warning'
};
case BookBackTaskStatus.RUNNING:
return '运行中';
return {
status: BookBackTaskStatus.RUNNING,
label: '运行中',
type: 'info'
};
case BookBackTaskStatus.PAUSE:
return '暂停';
return {
status: BookBackTaskStatus.PAUSE,
label: '暂停',
type: 'warning'
};
case BookBackTaskStatus.DONE:
return '完成';
return {
status: BookBackTaskStatus.DONE,
label: '完成',
type: 'success'
};
case BookBackTaskStatus.FAIL:
return '失败';
return {
status: BookBackTaskStatus.FAIL,
label: '失败',
type: 'error'
};
case BookBackTaskStatus.RECONNECT:
return '重连';
return {
status: BookBackTaskStatus.RECONNECT,
label: '重连',
type: 'warning'
};
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)
)
/**上传图片到小说中,并修改小说信息 */
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

View File

@ -1,7 +1,7 @@
import { ipcMain } from 'electron'
import { DEFINE_STRING } from '../../define/define_string'
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 { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
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.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 }

View File

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

View File

@ -9,7 +9,7 @@ import path from 'path'
import { BasicReverse } from './basicReverse'
import { BookTaskDetailService } from '../../../define/db/service/Book/bookTaskDetailService'
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 { GeneralResponse } from '../../../model/generalResponse'
import { Subtitle } from '../Subtitle/subtitle'

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import { BookBackTaskType, BookImageCategory, BookType, MJAction, OperateBookType, TaskExecuteType } from "../../../define/enum/bookEnum";
import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { Book } from "../../../model/book";
import { Book } from "../../../model/book/book";
import path from 'path'
import { Tools } from "../../../main/tools"
import { ImageSplit } from "../../../define/Tools/image";
@ -263,7 +263,7 @@ export class BookImage {
//#endregion
//#region 将指定的批次任务,添加里面的所有的分镜到出图任务中
/**
*
* @param ids ID
@ -344,6 +344,9 @@ export class BookImage {
}
}
//#endregion
//#region 生成所有的图片,这个方法主要是分流,根据批次生成的方式,添加对应的数据
/**
*
* @param bookTaskId
@ -369,6 +372,8 @@ export class BookImage {
}
}
//#endregion
//#region 对图片进行锁定或者是解锁操作
/**
*
@ -583,4 +588,208 @@ export class BookImage {
}
}
//#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 { BookType, OperateBookType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book";
import { Book } from "../../../model/book/book";
import { GeneralResponse } from "../../../model/generalResponse";
import { errorMessage, SendReturnMessage, successMessage } from "../../Public/generalTools";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";

View File

@ -1,7 +1,7 @@
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFolderAllFile } from "../../../define/Tools/file";
import { AddBookTaskCopyData, BookImageCategory, BookTaskStatus, BookType, CopyImageType, OperateBookType } from "../../../define/enum/bookEnum";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { Book } from "../../../model/book";
import { Book } from "../../../model/book/book";
import path from 'path'
import { add, cloneDeep, isEmpty } from "lodash";
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> {
let name = 'output_' + no.toString().padStart(5, '0');
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)
if (!isEmpty(bookTask.imageCategory)) {
imageCategory = bookTask.imageCategory;
@ -132,7 +132,7 @@ export class BookTask {
} else if (book.type == BookType.SD_REVERSE) {
imageCategory = BookImageCategory.SD
} 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 { Book } from "../../../model/book";
import { Book } from "../../../model/book/book";
import { errorMessage, successMessage } from "../../Public/generalTools";
import { GeneralResponse } from "../../../model/generalResponse";
import { Setting } from '../../../main/setting/setting'

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { isEmpty } from "lodash";
import { Book } from "../../../model/book";
import { Book } from "../../../model/book/book";
import { checkStringValueAddPrefix, checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
import { CheckFolderExistsOrCreate, CopyFileOrFolder, JoinPath } from "../../../define/Tools/file";
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 { checkStringValueAddSuffix, errorMessage, successMessage } from "../../Public/generalTools";
import { define } from '../../../define/define'

View File

@ -1,6 +1,6 @@
import { BookBackTaskStatus, BookBackTaskType, TaskExecuteType } from "../../../define/enum/bookEnum";
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 { cloneDeep, isEmpty } from "lodash";

View File

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

View File

@ -1,7 +1,7 @@
import { DEFINE_STRING } from "../../../define/define_string";
import { GeneralResponse } from "../../../model/generalResponse";
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 BookBackTaskServiceBasic from "./bookBackTaskServiceBasic";
import { BookBasic } from "./bookBasic";

View File

@ -1,6 +1,6 @@
import { BookTaskDetailService } from "../../../define/db/service/Book/bookTaskDetailService";
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";
export default class BookTaskDetailServiceBasic {

View File

@ -1,5 +1,5 @@
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";
export default class BookTaskServiceBasic {

View File

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

View File

@ -13,7 +13,7 @@ import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { Subtitle } from "./subtitle";
import { LogScheduler } from "../task/logScheduler";
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";
export class SubtitleService {

View File

@ -4,7 +4,7 @@ import { errorMessage, successMessage } from "../../Public/generalTools";
import { Translate } from "./Translate";
import { DEFINE_STRING } from "../../../define/define_string"
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 { isEmpty } from "lodash";
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 { errorMessage, successMessage } from "../../Public/generalTools";
import path from 'path';
import { GeneralResponse } from "@/model/generalResponse";
/** 打开指定的文件夹的方法 */
@ -29,6 +30,7 @@ export default class ElectronInterface {
}
await shell.openPath(value)
}
/**
*
* @param params
@ -57,4 +59,47 @@ export default class ElectronInterface {
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)
}
global.endTime = res.data.endTime
global.permissions = res.data.permissions
global.endTime = res.data.data.deactivationTime
global.permissions = res.data.data.permissions
global.CheckMachineId = true
return successMessage(res.data, '获取机器码状态成功')
return successMessage(res.data.data, '获取机器码状态成功')
} catch (error) {
return errorMessage('获取机器码状态错误,错误信息如下:' + error.message, 'SystemIpc_CHECK_MACHINE_STATUS')
}

View File

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

View File

@ -22,7 +22,7 @@ import { BookService } from '../../define/db/service/Book/bookService';
import { OperateBookType } from '../../define/enum/bookEnum';
import { GeneralResponse } from '../../model/generalResponse';
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 { 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 { MJAction } from "../define/enum/bookEnum"
import { MJImageType } from "../define/enum/mjEnum"
import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, TaskExecuteType, BookRepalceDataType, BookImageCategory } from "../../define/enum/bookEnum"
import { MJAction } from "../../define/enum/bookEnum"
import { MJImageType } from "../../define/enum/mjEnum"
declare namespace Book {
type SelectBook = {
id?: string,
no?: number,
@ -61,6 +60,7 @@ declare namespace Book {
friendlyReminder?: string | null // 友情提示
imageFolder?: string,
customizeImageStyle?: string[],
cacheImageList?: string[],
prefixPrompt?: string,
suffixPrompt?: string,
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 {
interface Task {
@ -45,4 +45,12 @@ declare namespace TaskModal {
count: number,
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 { DEFINE_STRING } from '../define/define_string'
import { Book } from '../model/book'
import { Book } from '../model/book/book'
import { SubtitleModel } from '../model/subtitle'
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),
/** 上传图片到小说中,并修改小说信息 */
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
//#region 一键反推的单个任务

View File

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

View File

@ -14,7 +14,13 @@ const system = {
/** 检查机器码状态 */
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 }

View File

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

View File

@ -103,6 +103,7 @@ import {
NFormItem,
NInput,
NButton,
NTag,
NGrid,
NGridItem,
NSelect
@ -144,7 +145,10 @@ let columns = [
{
title: '状态',
key: 'status',
minWidth: 100
minWidth: 100,
render: (row) => {
return renderStatus(row)
}
},
{
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() {
softwareStore.ResetBackTaskQueryData()
await query(1, pagination.value.pageSize)
@ -237,7 +249,7 @@ let GeBookBackTaskStatus = () => {
let options = []
for (const key in BookBackTaskStatus) {
options.push({
label: GetBookBackTaskStatusLabel(BookBackTaskStatus[key]),
label: GetBookBackTaskStatusLabel(BookBackTaskStatus[key]).label,
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"
@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
class="g-image-class generate-image-show"
:src="data.outImagePath ? data.outImagePath : softwareStore.globalSetting.space_image"
@ -59,33 +73,69 @@
:height="120"
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>
<n-dropdown
:options="mainImageOptions"
placement="bottom-start"
trigger="manual"
:x="x"
:y="y"
:show="showDropdown"
:on-clickoutside="onClickoutside"
@select="handleSelect"
>
</n-dropdown>
<div>
<n-divider vertical style="height: 100%" />
</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>
<div
style="
@ -100,7 +150,7 @@
subImage="true"
class="g-image-class"
draggable="true"
:width="56"
:width="55"
style="margin: 1px"
v-for="(image, index) in data.subImagePath"
:key="image.id"
@ -113,11 +163,21 @@
</n-image-group>
</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>
</template>
<script setup>
import { ref, onMounted, toRaw, watch } from 'vue'
import { ref, onMounted, nextTick, watch, h } from 'vue'
import {
NImage,
useDialog,
@ -127,14 +187,25 @@ import {
NButton,
NPopover,
NTag,
useMessage
NDropdown,
NDrawer,
NTooltip,
NDrawerContent,
useMessage,
useModal
} 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 { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useImageStore } from '../../../../../stores/image'
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({
initData: undefined,
index: undefined
@ -142,6 +213,7 @@ let props = defineProps({
let imageStore = useImageStore()
let message = useMessage()
let dialog = useDialog()
let modal = useModal()
let data = ref(props.initData)
let images = ref(props.initData.subImagePath)
let outImagePath = ref(props.initData.outImagePath)
@ -149,6 +221,12 @@ let width = ref(null)
let dragTarget = null
let reverseManageStore = useReverseManageStore()
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
watch(
@ -165,7 +243,7 @@ watch(
() => props.initData.outImagePath,
(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() {
if (!data.value.imageLock) {
//
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>

View File

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

View File

@ -8,132 +8,188 @@
@update:value="handleUpdateValue"
:options="options"
/>
<n-dropdown trigger="hover" :options="blockOptions" @select="ImageLockOperation">
<n-button
:color="softwareStore.SoftColor.ZHUYANTUO"
@click="ImageLockOperation('lock')"
size="tiny"
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-dropdown
trigger="hover"
:options="blockOptions"
@select="dropdownSelectHandle"
:render-option="renderOptionHandle"
>
<n-button text :style="style">
<n-icon> <MenuOpenRound /> </n-icon>
</n-button>
</n-dropdown>
</div>
</template>
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch, computed } from 'vue'
import { useMessage, useDialog, NSelect, NButton, NDropdown } from 'naive-ui'
<script setup>
import { ref, onMounted, computed, render } from 'vue'
import { useMessage, useDialog, NIcon, NSelect, NButton, NDropdown } from 'naive-ui'
import { BookImageCategory, OperateBookType } from '../../../../../define/enum/bookEnum'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../../stores/software'
import MenuOpenRound from '../../Icon/MenuOpenRound.vue'
export default defineComponent({
components: { NSelect, NButton, NDropdown },
let props = defineProps({
style: undefined
})
let style = ref(props.style)
setup() {
let message = useMessage()
let dialog = useDialog()
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let select = computed(() =>
reverseManageStore.selectBookTask.imageCategory
? reverseManageStore.selectBookTask.imageCategory
: softwareStore.globalSetting.defaultImageMode
)
let options = ref([])
let message = useMessage()
let dialog = useDialog()
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let select = computed(() =>
reverseManageStore.selectBookTask.imageCategory
? reverseManageStore.selectBookTask.imageCategory
: softwareStore.globalSetting.defaultImageMode ?? BookImageCategory.MJ
)
let options = ref([])
onMounted(async () => {
debugger
//
await window.api.GetImageGenerateCategory((value) => {
if (value.code == 0) {
message.error(value.message)
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('修改成功')
onMounted(async () => {
//
await window.api.GetImageGenerateCategory((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
options.value = value.data
})
})
/**
* 土拍你
* @param value
*/
async function ImageLockOperation(value) {
let res = await window.book.ImageLockOperation(
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('修改成功')
}
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,
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) {
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 OneToFourBookTask() {
dialog.warning({
title: '一拆四提示',
content:
'是否一拆四(一拆四是个泛指,根据出的子图数量最少的),会自动生成多个批次任务并设置对应的图片!',
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
function renderOptionHandle({ node, option }) {
console.log('renderOptionHandle', node, option)
if (option.key == 'downloadMJImage') {
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
return node
}
} else {
return node
}
})
}
let blockOptions = [
{
label: '一键锁定',
key: 'lock'
},
{
label: '一键解锁',
key: 'unlock'
},
{
label: 'MJ采集全部图片',
key: 'downloadMJImage'
},
{
label: '一拆四',
key: 'oneToFour'
}
]
</script>

View File

@ -84,7 +84,7 @@
:color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px"
size="small"
@click="GenerateImageAll"
@click="GenerateImageAll(true)"
>
一键生图
</n-button>
@ -138,7 +138,13 @@ import VideoCanvas from '../../VideoSubtitle/VideoCanvas.vue'
import { SubtitleSavePositionType } from '../../../../../define/enum/waterMarkAndSubtitle.ts'
import { DEFINE_STRING } from '../../../../../define/define_string/index.ts'
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 { TranslateType } from '../../../../../define/enum/translate.ts'
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common.ts'
@ -464,6 +470,10 @@ async function handleSelect(key) {
await Translate('translate_english')
break
case 'generate_space_image': //
await GenerateImageAll(false)
break
case 'translate_setting': //
await Translate('translate_setting')
break
@ -780,18 +790,124 @@ async function TranslateService(from, to) {
}
/**
* 生成所有图片
* 根据指定类型为所有书籍任务生成图像
* @param {String} type - 小说图像类别
* @param {Boolean} cover - 是否覆盖现有图像
*/
async function GenerateImageAll() {
let res = await window.book.GenerateImageAll(
reverseManageStore.selectBookTask.id,
reverseManageStore.selectBookTask.imageCategory
)
if (res.code == 0) {
message.error(res.message)
async function AddFluxImageAll(type, cover) {
let messageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
let taskType = BookBackTaskType.FLUX_API_IMAGE
if (type == BookImageCategory.FLUX_FORGE) {
messageName = DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
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) {
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
}
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 = [
{ label: '生成未出图分镜', key: 'generate_space_image' },
{
label: '停止生图',
key: 'stop_image_generate'

View File

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

View File

@ -1,6 +1,14 @@
<template>
<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 style="margin-left: 5px" size="small" type="info" @click="GenerateAllTaskImage()"
@ -54,14 +62,15 @@ import {
NDataTable,
NIcon,
NDropdown,
NPerformantEllipsis
NPerformantEllipsis,
NTag
} from 'naive-ui'
import { useReverseManageStore } from '../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../stores/software'
import { AddSharp, Open } from '@vicons/ionicons5'
import BookTaskListAction from './Components/ManageBook/BookTaskListAction.vue'
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 { ResponseMessageType } from '../../../../define/enum/softwareEnum'
import AddBookTask from './Components/ManageBook/AddBookTask.vue'
@ -137,7 +146,10 @@ function createColumns() {
{
title: '状态',
key: 'status',
width: 100
width: 100,
render(row) {
return renderStatus(row)
}
},
{
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) {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在进行高清检查。。。'
@ -397,7 +417,7 @@ async function GenerateAllTaskImage() {
[...checkedRowKeysRef.value],
OperateBookType.ASSIGNBOOKTASK
)
window.api.showGlobalMessageDialog(res);
window.api.showGlobalMessageDialog(res)
softwareStore.spin.spinning = false
}
})

View File

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

View File

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

View File

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