V 3.3.9(2025.05.29)

1. 生图包适配,修复部分任务无法获取图片的问题。
2. 新增聚合推文原创重置功能,直接重置GPT提示词、合并提示词、出图等。
3. 优化加载界面,提升用户体验。
4. 优化主页显示,优化UI界面,提升用户体验。
This commit is contained in:
2025-05-29 14:44:37 +08:00
parent 50900d3465
commit 7a774c48da
42 changed files with 3256 additions and 2359 deletions
+10
View File
@@ -0,0 +1,10 @@
export const SoftwareData = {
"version": "V3.3.9",
"date": "2025-05-29",
"notes": [
"修复了音频处理的错误。",
"改进了视频处理的性能。",
"更新了依赖库以提高安全性。"
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
import Realm, { UpdateMode } from 'realm'
import { BookModel } from '../../model/Book/book.js'
import path from 'path'
import { define } from '../../../define.js'
import { define } from '../../../define'
import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum.js'
import { successMessage } from '../../../../main/Public/generalTools'
import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file'
-163
View File
@@ -1,163 +0,0 @@
let define = {}
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'
if (!isBrowser) {
const path = require('path')
const { app } = require('electron')
if (!app.isPackaged) {
define = {
discordScript: path.join(__dirname, '../../src/main/discord/discordScript.js'),
zhanwei_image: path.join(__dirname, '../../resources/image/zhanwei.png'),
config_path: path.join(__dirname, '../../resources/config/global_setting.json'),
clip_setting: path.join(__dirname, '../../resources/config/clip_setting.json'),
sd_setting: path.join(__dirname, '../../resources/config/sd_config.json'),
dynamic_setting: path.join(__dirname, '../../resources/config/dynamic_setting.json'),
tag_setting: path.join(__dirname, '../../resources/config/tag_setting.json'),
img_base: path.join(__dirname, '../../resources/config/img_base.json'),
video_config: path.join(__dirname, '../../resources/config/video_config.json'),
system_config: path.join(__dirname, '../../resources/config/system_config.json'),
scripts_path: path.join(__dirname, '../../resources/scripts'),
db_path: path.join(__dirname, '../../resources/scripts/db'),
project_path: path.join(__dirname, '../../project'),
tts_path: path.join(__dirname, '../../tts'),
logger_path: path.join(__dirname, '../../resources/logger'),
package_path: path.join(__dirname, '../../resources/package'),
image_path: path.join(__dirname, '../../resources/image'),
temp_sd_image: path.join(__dirname, '../../resources/image/TempSDImage'),
draft_temp_path: path.join(__dirname, '../../resources/tmp/temp.zip'),
init_config_path: path.join(__dirname, '../../resources/tmp/config'),
clip_speed_temp_path: path.join(__dirname, '../../resources/tmp/Clip/speeds_tmp.json'),
add_canvases_temp_path: path.join(__dirname, '../../resources/tmp/Clip/canvases_tmp.json'),
add_sound_channel_mappings_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/sound_channel_mappings_tmp.json'
),
add_vocal_separations_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/vocal_separations_tmp.json'
),
add_material_video_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/videoMaterialTemp.json'
),
add_tracks_segments_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/tracks_segments_tmp.json'
),
add_tracks_type_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/tracks_type_tmp.json'
),
add_material_animations_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/material_animations_tmp.json'
),
add_material_text_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/material_text_temp.json'
),
add_track_text_segments_temp_path: path.join(
__dirname,
'../../resources/tmp/Clip/track_text_segments_temp.json'
),
add_materials_beats_tmp_path: path.join(
__dirname,
'../../resources/tmp/Clip/materials_beats_tmp.json'
),
add_materials_audios_tmp_path: path.join(
__dirname,
'../../resources/tmp/Clip/materials_audios_tmp.json'
),
add_tracks_audio_segments_tmp_path: path.join(
__dirname,
'../../resources/tmp/Clip/tracks_audio_segments_tmp.json'
),
add_keyframe_tmp_path: path.join(__dirname, '../../resources/tmp/Clip/keyframe_tmp.json')
}
} else {
define = {
zhanwei_image: path.join(__dirname, '../../../resources/image/zhanwei.png'),
config_path: path.join(__dirname, '../../../resources/config/global_setting.json'),
clip_setting: path.join(__dirname, '../../../resources/config/clip_setting.json'),
sd_setting: path.join(__dirname, '../../../resources/config/sd_config.json'),
dynamic_setting: path.join(__dirname, '../../../resources/config/dynamic_setting.json'),
tag_setting: path.join(__dirname, '../../../resources/config/tag_setting.json'),
video_config: path.join(__dirname, '../../../resources/config/video_config.json'),
system_config: path.join(__dirname, '../../../resources/config/system_config.json'),
img_base: path.join(__dirname, '../../../resources/config/img_base.json'),
scripts_path: path.join(__dirname, '../../../resources/scripts'),
db_path: path.join(__dirname, '../../../resources/scripts/db'),
project_path: path.join(__dirname, '../../../project'),
tts_path: path.join(__dirname, '../../../tts'),
logger_path: path.join(__dirname, '../../../resources/logger'),
package_path: path.join(__dirname, '../../../resources/package'),
discordScript: path.join(__dirname, '../../../resources/scripts/discordScript.js'),
image_path: path.join(__dirname, '../../../resources/image'),
temp_sd_image: path.join(__dirname, '../../../resources/image/TempSDImage'),
draft_temp_path: path.join(__dirname, '../../../resources/tmp/temp.zip'),
init_config_path: path.join(__dirname, '../../../resources/tmp/config'),
clip_speed_temp_path: path.join(__dirname, '../../../resources/tmp/Clip/speeds_tmp.json'),
add_canvases_temp_path: path.join(__dirname, '../../../resources/tmp/Clip/canvases_tmp.json'),
add_sound_channel_mappings_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/sound_channel_mappings_tmp.json'
),
add_vocal_separations_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/vocal_separations_tmp.json'
),
add_material_video_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/videoMaterialTemp.json'
),
add_tracks_segments_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/tracks_segments_tmp.json'
),
add_tracks_type_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/tracks_type_tmp.json'
),
add_material_animations_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/material_animations_tmp.json'
),
add_material_text_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/material_text_temp.json'
),
add_track_text_segments_temp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/track_text_segments_temp.json'
),
add_materials_beats_tmp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/materials_beats_tmp.json'
),
add_materials_audios_tmp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/materials_audios_tmp.json'
),
add_tracks_audio_segments_tmp_path: path.join(
__dirname,
'../../../resources/tmp/Clip/tracks_audio_segments_tmp.json'
),
add_keyframe_tmp_path: path.join(__dirname, '../../../resources/tmp/Clip/keyframe_tmp.json')
}
}
}
define['remotemj_api'] = 'https://api.laitool.net/'
define['serverUrl'] = 'http://lms.laitool.cn'
// define['serverUrl'] = 'https://localhost:44362'
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'] = 'https://lms.laitool.cn'
export { define }
+178
View File
@@ -0,0 +1,178 @@
// 检测当前环境
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'
// 浏览器环境的路径处理函数
function joinPath(...segments: string[]): string {
return segments.join('/').replace(/\/+/g, '/');
}
// 统一的路径处理函数
const pathJoin = isBrowser ? joinPath : require('path').join;
// 定义类型接口
interface DefineConfig {
// 脚本和可执行文件路径
discordScript: string | null;
// 图片资源路径
zhanwei_image: string;
image_path: string;
temp_sd_image: string;
// 配置文件路径
config_path: string | null;
clip_setting: string | null;
sd_setting: string | null;
dynamic_setting: string | null;
tag_setting: string | null;
img_base: string | null;
video_config: string | null;
system_config: string | null;
// 目录路径
scripts_path: string | null;
db_path: string | null;
project_path: string | null;
tts_path: string | null;
logger_path: string | null;
package_path: string | null;
// 临时文件路径
draft_temp_path: string | null;
init_config_path: string | null;
// Clip 相关临时文件路径
clip_speed_temp_path: string | null;
add_canvases_temp_path: string | null;
add_sound_channel_mappings_temp_path: string | null;
add_vocal_separations_temp_path: string | null;
add_material_video_temp_path: string | null;
add_tracks_segments_temp_path: string | null;
add_tracks_type_temp_path: string | null;
add_material_animations_temp_path: string | null;
add_material_text_temp_path: string | null;
add_track_text_segments_temp_path: string | null;
add_materials_beats_tmp_path: string | null;
add_materials_audios_tmp_path: string | null;
add_tracks_audio_segments_tmp_path: string | null;
add_keyframe_tmp_path: string | null;
// API 配置
remotemj_api: string;
serverUrl: string;
hkServerUrl: string;
bakServerUrl: string;
API: string;
lms: string;
}
let define: DefineConfig = {} as DefineConfig;
if (!isBrowser) {
const path = require('path')
const { app } = require('electron')
// 根据打包状态确定基础路径
const basePath = !app.isPackaged ?
path.join(__dirname, '../../') :
path.join(__dirname, '../../../');
define = {
discordScript: !app.isPackaged ? pathJoin(__dirname, '../../src/main/discord/discordScript.js') : pathJoin(basePath, 'resources/scripts/discordScript.js'),
zhanwei_image: pathJoin(basePath, 'resources/image/zhanwei.png'),
config_path: pathJoin(basePath, 'resources/config/global_setting.json'),
clip_setting: pathJoin(basePath, 'resources/config/clip_setting.json'),
sd_setting: pathJoin(basePath, 'resources/config/sd_config.json'),
dynamic_setting: pathJoin(basePath, 'resources/config/dynamic_setting.json'),
tag_setting: pathJoin(basePath, 'resources/config/tag_setting.json'),
img_base: pathJoin(basePath, 'resources/config/img_base.json'),
video_config: pathJoin(basePath, 'resources/config/video_config.json'),
system_config: pathJoin(basePath, 'resources/config/system_config.json'),
scripts_path: pathJoin(basePath, 'resources/scripts'),
db_path: pathJoin(basePath, 'resources/scripts/db'),
project_path: pathJoin(basePath, 'project'),
tts_path: pathJoin(basePath, 'tts'),
logger_path: pathJoin(basePath, 'resources/logger'),
package_path: pathJoin(basePath, 'resources/package'),
image_path: pathJoin(basePath, 'resources/image'),
temp_sd_image: pathJoin(basePath, 'resources/image/TempSDImage'),
draft_temp_path: pathJoin(basePath, 'resources/tmp/temp.zip'),
init_config_path: pathJoin(basePath, 'resources/tmp/config'),
clip_speed_temp_path: pathJoin(basePath, 'resources/tmp/Clip/speeds_tmp.json'),
add_canvases_temp_path: pathJoin(basePath, 'resources/tmp/Clip/canvases_tmp.json'),
add_sound_channel_mappings_temp_path: pathJoin(basePath, 'resources/tmp/Clip/sound_channel_mappings_tmp.json'),
add_vocal_separations_temp_path: pathJoin(basePath, 'resources/tmp/Clip/vocal_separations_tmp.json'),
add_material_video_temp_path: pathJoin(basePath, 'resources/tmp/Clip/videoMaterialTemp.json'),
add_tracks_segments_temp_path: pathJoin(basePath, 'resources/tmp/Clip/tracks_segments_tmp.json'),
add_tracks_type_temp_path: pathJoin(basePath, 'resources/tmp/Clip/tracks_type_tmp.json'),
add_material_animations_temp_path: pathJoin(basePath, 'resources/tmp/Clip/material_animations_tmp.json'),
add_material_text_temp_path: pathJoin(basePath, 'resources/tmp/Clip/material_text_temp.json'),
add_track_text_segments_temp_path: pathJoin(basePath, 'resources/tmp/Clip/track_text_segments_temp.json'),
add_materials_beats_tmp_path: pathJoin(basePath, 'resources/tmp/Clip/materials_beats_tmp.json'),
add_materials_audios_tmp_path: pathJoin(basePath, 'resources/tmp/Clip/materials_audios_tmp.json'),
add_tracks_audio_segments_tmp_path: pathJoin(basePath, 'resources/tmp/Clip/tracks_audio_segments_tmp.json'),
add_keyframe_tmp_path: pathJoin(basePath, 'resources/tmp/Clip/keyframe_tmp.json'),
// API 配置
remotemj_api: 'https://api.laitool.net/',
serverUrl: 'http://lms.laitool.cn',
hkServerUrl: 'https://laitool.net/',
bakServerUrl: 'https://laitool.net/',
API: 'f85d39ed5a40fd09966f13f12b6cf0f0',
lms: 'https://lms.laitool.cn'
};
} else {
// 浏览器环境
const basePath = './';
define = {
discordScript: null,
zhanwei_image: pathJoin(basePath, 'resources/image/zhanwei.png'),
config_path: null,
clip_setting: null,
sd_setting: null,
dynamic_setting: null,
tag_setting: null,
img_base: null,
video_config: null,
system_config: null,
scripts_path: null,
db_path: null,
project_path: null,
tts_path: null,
logger_path: null,
package_path: null,
image_path: pathJoin(basePath, 'resources/image'),
temp_sd_image: pathJoin(basePath, 'resources/image/TempSDImage'),
draft_temp_path: null,
init_config_path: null,
clip_speed_temp_path: null,
add_canvases_temp_path: null,
add_sound_channel_mappings_temp_path: null,
add_vocal_separations_temp_path: null,
add_material_video_temp_path: null,
add_tracks_segments_temp_path: null,
add_tracks_type_temp_path: null,
add_material_animations_temp_path: null,
add_material_text_temp_path: null,
add_track_text_segments_temp_path: null,
add_materials_beats_tmp_path: null,
add_materials_audios_tmp_path: null,
add_tracks_audio_segments_tmp_path: null,
add_keyframe_tmp_path: null,
// API 配置
remotemj_api: 'https://api.laitool.net/',
serverUrl: 'http://lms.laitool.cn',
hkServerUrl: 'https://laitool.net/',
bakServerUrl: 'https://laitool.net/',
API: 'f85d39ed5a40fd09966f13f12b6cf0f0',
lms: 'https://lms.laitool.cn'
};
}
// 浏览器环境下挂载到window
if (isBrowser && typeof window !== 'undefined') {
(window as any).define = define;
}
// 导出类型和实例
export type { DefineConfig };
export { define };
@@ -0,0 +1,12 @@
const AXIOS = {
/** http的get请求 */
HTTP_GET: 'http:get',
/** http的post请求 */
HTTP_POST: 'http:post',
/** http的put请求 */
HTTP_PUT: 'http:put',
/** http的delete请求 */
HTTP_DELETE: 'http:delete'
}
export default AXIOS
+2
View File
@@ -6,9 +6,11 @@ import BOOK from "./bookDefineString"
import WRITE from "./writeDefineString"
import DB from "./dbDefineString"
import OPTIONS from "./optionsDefineString"
import AXIOS from "./axiosDefineString"
export const DEFINE_STRING = {
SYSTEM: SYSTEM,
AXIOS : AXIOS,
TASK: TASK,
TTS: TTS,
BOOK: BOOK,
+85
View File
@@ -0,0 +1,85 @@
import { ipcMain } from 'electron'
import axios from 'axios'
import { DEFINE_STRING } from '../../define/define_string'
function AxiosIpc() {
// 通用 GET 请求
ipcMain.handle(DEFINE_STRING.AXIOS.HTTP_GET, async (_, url, config = {}) => {
try {
const response = await axios.get(url, config)
return {
success: true,
data: response.data,
status: response.status
}
} catch (error: any) {
return {
success: false,
error: error.message,
message: error.message,
status: error.response?.status,
data: error.response?.data
}
}
})
// 通用 POST 请求
ipcMain.handle(DEFINE_STRING.AXIOS.HTTP_POST, async (_, url, data = {}, config = {}) => {
try {
const response = await axios.post(url, data, config)
return {
success: true,
data: response.data,
status: response.status
}
} catch (error: any) {
return {
success: false,
error: error.message,
message: error.message,
status: error.response?.status,
data: error.response?.data
}
}
})
// 通用 PUT 请求
ipcMain.handle(DEFINE_STRING.AXIOS.HTTP_PUT, async (_, url, data = {}, config = {}) => {
try {
const response = await axios.put(url, data, config)
return {
success: true,
data: response.data,
status: response.status
}
} catch (error: any) {
return {
success: false,
error: error.message,
status: error.response?.status,
data: error.response?.data
}
}
})
// 通用 DELETE 请求
ipcMain.handle(DEFINE_STRING.AXIOS.HTTP_DELETE, async (_, url, config = {}) => {
try {
const response = await axios.delete(url, config)
return {
success: true,
data: response.data,
status: response.status
}
} catch (error: any) {
return {
success: false,
error: error.message,
status: error.response?.status,
data: error.response?.data
}
}
})
}
export default AxiosIpc
@@ -18,6 +18,7 @@ import { DBIpc } from './dbIpc'
import { PresetIpc } from './presetIpc'
import { TaskIpc } from './taskIpc'
import { OptionsIpc } from './optionsIpc'
import AxiosIpc from './axiosIpc'
export async function RegisterIpc(createWindow) {
PromptIpc()
@@ -40,4 +41,5 @@ export async function RegisterIpc(createWindow) {
BookIpc()
TTSIpc()
OptionsIpc()
AxiosIpc()
}
+1 -1
View File
@@ -619,7 +619,7 @@ export class MJOpt {
// }
// 判断是不是有批量的图片
if (task_res.subImagePath.length > 0) {
if (task_res.subImagePath && task_res.subImagePath.length > 0) {
batchImages = [...task_res.subImagePath]
}
+9 -13
View File
@@ -75,7 +75,10 @@ export default class SystemInfo {
nextDate.setDate(currentDate.getDate() + 1);
if (systemConfig && systemConfig.gpu && nextDate > new Date()) {
global.gpu = systemConfig.gpu
return successMessage(version + ' ' + (global.gpu?.name ? global.gpu.name : ''), '获取成功')
return successMessage({
version: version,
gpu: global.gpu
}, '获取版本信息和显卡信息成功', 'SystemIpc_GET_VISION_AND_GPU_MESSAGE');
}
}
@@ -103,7 +106,10 @@ export default class SystemInfo {
systemConfig.gpu = global.gpu
systemConfig.gpu.updateTime = new Date()
await fs.promises.writeFile(systemConfigPath, JSON.stringify(systemConfig));
return successMessage(version + ' ' + (global.gpu?.name ? global.gpu.name : ''), '获取成功')
return successMessage({
version: version,
gpu: global.gpu
}, '获取版本信息和显卡信息成功', 'SystemIpc_GET_VISION_AND_GPU_MESSAGE');
} catch (error) {
return errorMessage('获取版本信息和显卡信息错误,错误信息如下:' + error.message, 'SystemIpc_GET_VISION_AND_GPU_MESSAGE')
}
@@ -116,12 +122,7 @@ export default class SystemInfo {
*/
public async CheckMachineStatus(value: string) {
try {
// 判断机器码是不是存在
// let res = await axios.post('http://api.yu-zhile.com/GetMachineStatus', {
// machineId: value
// })
// /lms/Machine/GetMachineStatus/{machineId}
//
let res = await axios.get('https://lms.laitool.cn/lms/Machine/GetMachineStatus/' + value);
if (res.status != 200) {
throw new Error('请求错误')
@@ -145,11 +146,6 @@ export default class SystemInfo {
public async GetMachineId() {
try {
let baseId = await machineId(true);
let checkRes = await this.CheckMachineStatus(baseId);
if (checkRes.code == 1) {
global.machineId = baseId;
return successMessage(baseId, '获取机器码成功')
}
let hardwareInfo = '';
try {
if (process.platform === 'win32') {
+6 -7
View File
@@ -19,7 +19,7 @@ import { SoftWareServiceBasic } from './Service/ServiceBasic/softwareServiceBasi
// ipc
import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js'
import { Logger } from './logger.js'
import { RegisterIpc } from './IPCEvent/index.js'
import { RegisterIpc } from './IPCEvent/index'
import { InitRemoteMjSettingType } from './initFunc'
@@ -47,13 +47,12 @@ function removeIpcHandler(hash) {
}
}
async function createWindow(hash = 'ShowMessage', data, url = null) {
async function createWindow(hash = 'mainHome', data, url = null) {
// Create the browser window.
await InitData(global)
global.currentHash = hash
// 判断当前是不是有设置的宽高,用的话记忆
let isRe =
global.config.window_wh_bm_remember && hash == 'ShowMessage' && global.config.window_wh_bm
let isRe = global.config.window_wh_bm_remember && hash == 'mainHome' && global.config.window_wh_bm
const ses = session.fromPartition('persist:my-session')
let mainWindow = new BrowserWindow({
width: isRe ? global.config.window_wh_bm.width : 900,
@@ -112,7 +111,7 @@ async function createWindow(hash = 'ShowMessage', data, url = null) {
removeIpcHandler(hash)
global.newWindow = global.newWindow.filter((item) => item.id != mainWindow.id)
// 判断当前的是不是开启了记录功能
if (global.config.window_wh_bm_remember && hash == 'ShowMessage') {
if (global.config.window_wh_bm_remember && hash == 'mainHome') {
let window_wh_bm = mainWindow.getBounds()
// 记录到文件中
await setting.ModifySampleSetting(JSON.stringify({ window_wh_bm: window_wh_bm }))
@@ -201,7 +200,7 @@ app.whenReady().then(async () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
mainWindow = createWindow('ShowMessage', null)
mainWindow = createWindow('mainHome', null)
}
})
@@ -210,7 +209,7 @@ app.whenReady().then(async () => {
await AutoSync()
global.newWindow = []
mainWindow = await createWindow('ShowMessage', null)
mainWindow = await createWindow('mainHome', null)
global.requestQuene = new AsyncQueue(global, global.config.task_number)
global.fileQueue = new AsyncQueue(global, 1)
+57
View File
@@ -13,4 +13,61 @@ declare namespace SystemConfig {
updateTime?: Date;
}
//#region 更新信息和版本信息
/**
* 更新变更类型
*/
export type ChangeType = 'bugfix' | 'add' | 'improvement' | 'remove' | 'security' | 'performance'
/**
* 单个变更项
*/
export interface ChangeItem {
/** 变更类型 */
type: ChangeType
/** 变更描述 */
description: string
}
/**
* 版本更新信息
*/
export interface SubUpdateInfo {
/** 版本号 */
version: string
/** 更新日期 */
updateDate: string
/** 变更列表 */
changes: ChangeItem[]
}
/**
* 完整的更新信息结构
*/
export interface UpdateInfo {
/** 最新版本号 */
latestVersion: string
/** 最新更新日期 */
updateDate: string
/** 所有版本的更新信息 */
updateInfo: SubUpdateInfo[]
}
/**
* 系统更新信息
*/
interface VersionInfo {
/** 当前版本 */
currentVersion: string
/** 最新版本 */
latestVersion: string
/** 更新信息 */
updateInfo: UpdateInfo
/** 是否可以更新 */
canUpdate: boolean
}
//#endregion
}
+61
View File
@@ -0,0 +1,61 @@
import { DEFINE_STRING } from '../define/define_string'
import { ipcRenderer } from 'electron'
// 定义请求配置接口
interface RequestConfig {
headers?: Record<string, string>
params?: Record<string, any>
timeout?: number
[key: string]: any
}
// 定义响应接口
interface HttpResponse<T = any> {
success: boolean
data?: T
error?: string
status?: number
}
// HTTP 客户端接口
const axiosPrelod = {
/**
* 发送 GET 请求
* @param url 请求地址
* @param config 请求配置
*/
get: async <T = any>(url: string, config?: RequestConfig): Promise<HttpResponse<T>> =>
await ipcRenderer.invoke(DEFINE_STRING.AXIOS.HTTP_GET, url, config),
/**
* 发送 POST 请求
* @param url 请求地址
* @param data 请求数据
* @param config 请求配置
*/
post: async <T = any>(
url: string,
data?: any,
config?: RequestConfig
): Promise<HttpResponse<T>> =>
await ipcRenderer.invoke(DEFINE_STRING.AXIOS.HTTP_POST, url, data, config),
/**
* 发送 PUT 请求
* @param url 请求地址
* @param data 请求数据
* @param config 请求配置
*/
put: async <T = any>(url: string, data?: any, config?: RequestConfig): Promise<HttpResponse<T>> =>
await ipcRenderer.invoke(DEFINE_STRING.AXIOS.HTTP_PUT, url, data, config),
/**
* 发送 DELETE 请求
* @param url 请求地址
* @param config 请求配置
*/
delete: async <T = any>(url: string, config?: RequestConfig): Promise<HttpResponse<T>> =>
await ipcRenderer.invoke(DEFINE_STRING.AXIOS.HTTP_DELETE, url, config)
}
export { axiosPrelod }
+3
View File
@@ -17,6 +17,7 @@ import { translate } from './translate'
import { preset } from './preset'
import { task } from './task'
import { options } from './options'
import { axiosPrelod } from './axios'
// Custom APIs for renderer
let events = []
@@ -480,6 +481,7 @@ if (process.contextIsolated) {
contextBridge.exposeInMainWorld('preset', preset)
contextBridge.exposeInMainWorld('task', task)
contextBridge.exposeInMainWorld('options', options)
contextBridge.exposeInMainWorld('axios', axiosPrelod)
contextBridge.exposeInMainWorld('darkMode', {
toggle: (value) => ipcRenderer.invoke('dark-mode:toggle', value)
})
@@ -505,4 +507,5 @@ if (process.contextIsolated) {
window.task = task
window.translate = translate
window.options = options
window.axios = axiosPrelod
}
-6
View File
@@ -13,9 +13,7 @@ declare module 'vue' {
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCode: typeof import('naive-ui')['NCode']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDataTable: typeof import('naive-ui')['NDataTable']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDivider: typeof import('naive-ui')['NDivider']
NDropdown: typeof import('naive-ui')['NDropdown']
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
@@ -32,10 +30,6 @@ declare module 'vue' {
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NLog: typeof import('naive-ui')['NLog']
NMenu: typeof import('naive-ui')['NMenu']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NModalProvider: typeof import('naive-ui')['NModalProvider']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopover: typeof import('naive-ui')['NPopover']
NProgress: typeof import('naive-ui')['NProgress']
NSelect: typeof import('naive-ui')['NSelect']
+31 -40
View File
@@ -7,7 +7,9 @@
<n-modal-provider>
<n-dialog-provider>
<n-notification-provider>
<RouterView></RouterView>
<LoadingComponent v-if="loading" @loading-complete="onComplete" fullScreen>
</LoadingComponent>
<RouterView v-else></RouterView>
</n-notification-provider>
</n-dialog-provider>
</n-modal-provider>
@@ -35,7 +37,7 @@
</n-modal>
</template>
<script>
<script setup>
import { defineComponent, onMounted } from 'vue'
import hljs from 'highlight.js/lib/core'
import javascript from 'highlight.js/lib/languages/javascript'
@@ -51,49 +53,38 @@ import {
} from 'naive-ui'
import { useSoftwareStore } from '../../stores/software'
import { SoftColor } from '../../define/enum/softwareEnum'
import LoadingComponent from './components/Home/LoadingComponent.vue'
const loading = ref(true)
const onComplete = () => {
loading.value = false
}
hljs.registerLanguage('javascript', javascript)
export default defineComponent({
components: {
NConfigProvider,
NDialogProvider,
NMessageProvider,
NNotificationProvider,
NModalProvider,
NSpin,
NModal
},
setup() {
let softwareStore = useSoftwareStore()
onMounted(async () => {
softwareStore.SoftColor = SoftColor
window.api.getSettingDafultData(async (value) => {
await window.darkMode.toggle(value.theme)
softwareStore.globalSetting = value
})
let software = await window.setting.GetSoftwareSetting()
if (software.code == 0) {
throw new Error('初始化信息错误: ' + software.message)
}
if (software.data == null) {
throw new Error('初始化信息错误: 未获取到数据')
}
if (software.data.length == 0) {
throw new Error('初始化信息错误: 未获取到数据')
}
softwareStore.softWare = software.data
console.log(softwareStore.softWare)
await window.darkMode.toggle(softwareStore.softWare.theme)
})
let softwareStore = useSoftwareStore()
return {
javascript,
hljs,
darkTheme,
softwareStore
}
onMounted(async () => {
loading.value = true
softwareStore.SoftColor = SoftColor
window.api.getSettingDafultData(async (value) => {
console.log(value.theme)
await window.darkMode.toggle(value.theme)
softwareStore.globalSetting = value
})
let software = await window.setting.GetSoftwareSetting()
if (software.code == 0) {
throw new Error('初始化信息错误: ' + software.message)
}
if (software.data == null) {
throw new Error('初始化信息错误: 未获取到数据')
}
if (software.data.length == 0) {
throw new Error('初始化信息错误: 未获取到数据')
}
softwareStore.softWare = software.data
console.log(softwareStore.softWare)
})
</script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 KiB

@@ -90,6 +90,7 @@ import {
import { useRouter } from 'vue-router'
import { DEFINE_STRING } from '../../../../../define/define_string'
import { isEmpty } from 'lodash'
import { TimeDelay } from '@/define/Tools/time'
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let dialog = useDialog()
@@ -357,6 +358,94 @@ async function ButtonSelect(key) {
}
}
//
async function resetALLData() {
let da = dialog.warning({
title: '重置所有数据提示',
content: `即将开始重置所有的数据,包括GPT提示词、合并提示词、生成图片等,当前的操作不可逆,是否继续?`,
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
try {
da?.destroy()
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在重置GPT提示词数据,请稍后。。。'
let res = await window.book.ResetGptReverseData(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
BookType.ORIGINAL
)
softwareStore.spin.spinning = false
if (res.code == 1) {
//
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
reverseManageStore.selectBookTaskDetail[i].gptPrompt = ''
}
}
message.success('重置GPT提示词成功')
await TimeDelay(500)
softwareStore.spin.tip = '正在重置合并提示词数据,请稍后。。。'
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i]
let res = await window.db.UpdateBookTaskDetailData(element.id, {
prompt: ''
})
if (res.code == 1) {
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
(item) => item.id == element.id
)
if (findIndex != -1) {
reverseManageStore.selectBookTaskDetail[findIndex].prompt = ''
}
} else {
message.error(res.message)
}
}
message.success('重置合并提示词成功')
await TimeDelay(500)
softwareStore.spin.tip = '正在重置生成图片数据,请稍后。。。'
res = await window.book.ResetGenerateImage(
reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK,
false
)
softwareStore.spin.spinning = 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].mjMessage = undefined
reverseManageStore.selectBookTaskDetail[findIndex].outImagePath = ''
reverseManageStore.selectBookTaskDetail[findIndex].subImagePath = []
}
}
} else {
message.error(res.message)
return
}
message.success('重置生成图片数据成功')
await TimeDelay(500)
softwareStore.spin.spinning = false
message.success('重置所有数据成功')
} catch (error) {
softwareStore.spin.spinning = false
message.error('重置所有数据失败,' + error.message)
} finally {
softwareStore.spin.spinning = false
}
}
})
}
// GPT
async function GetPromptAll() {
let da = dialog.warning({
+49 -164
View File
@@ -1,40 +1,38 @@
<template>
<n-spin :show="show">
<n-space vertical>
<n-layout has-sider style="height: 100vh; position: relative">
<n-layout-sider
bordered
collapse-mode="width"
:collapsed-width="64"
:width="220"
<n-space vertical>
<n-layout has-sider style="height: 100vh; position: relative">
<n-layout-sider
bordered
collapse-mode="width"
:collapsed-width="64"
:width="220"
:collapsed="collapsed"
show-trigger
@collapse="collapsed = true"
@expand="collapsed = false"
style="position: relative"
>
<n-menu
:collapsed="collapsed"
show-trigger
@collapse="collapsed = true"
@expand="collapsed = false"
style="position: relative"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="menuOptions"
:render-icon="renderMenuIcon"
:expand-icon="expandIcon"
default-value="mainHome"
>
<n-menu
:collapsed="collapsed"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="menuOptions"
:render-icon="renderMenuIcon"
:expand-icon="expandIcon"
>
</n-menu>
</n-layout-sider>
<n-layout-content content-style="padding: 5px 5px 5px 10px; height:100%">
<!-- <Setting></Setting> -->
<router-view></router-view>
</n-layout-content>
</n-layout>
</n-space>
<template #description> 正在加载软件授权信息 </template>
</n-spin>
</n-menu>
</n-layout-sider>
<n-layout-content content-style="padding: 5px 5px 5px 10px; height:100%">
<!-- <Setting></Setting> -->
<router-view></router-view>
</n-layout-content>
</n-layout>
</n-space>
</template>
<script setup>
import { ref, h, onMounted, defineComponent, toRaw, computed } from 'vue'
import { ref, h, onMounted, toRaw, computed } from 'vue'
import { RouterLink } from 'vue-router'
import {
useDialog,
@@ -45,12 +43,12 @@ import {
NLayoutContent,
NIcon,
useNotification,
useMessage,
NSpin
useMessage
} from 'naive-ui'
import {
CaretDownOutline,
HomeOutline,
PaperPlaneOutline,
SettingsOutline,
DuplicateOutline,
@@ -58,7 +56,7 @@ import {
RadioOutline,
BookOutline
} from '@vicons/ionicons5'
import CheckMachineId from '../Components/CheckMachineId.vue'
import { DEFINE_STRING } from '../../../../define/define_string'
import { MD5 } from 'crypto-js'
import InputDialogContent from '../Original/Components/InputDialogContent.vue'
@@ -66,16 +64,11 @@ import APIIcon from '../Icon/APIIcon.vue'
import BackTaskIcon from '../Icon/BackTaskIcon.vue'
import BackTask from '@/renderer/src/components/BackTask/BackTask.vue'
import { useSystemStore } from '../../../../stores/system'
import { TimeDelay } from '../../../../define/Tools/time'
import { BookBackTaskStatus } from '../../../../define/enum/bookEnum'
let collapsed = ref(false)
let dialog = useDialog()
let machineRef = ref()
let message = useMessage()
let notification = useNotification()
let show = ref(true)
const systemStore = useSystemStore()
let key_down_ref = ref(null)
let showMenu = ref(true)
@@ -87,6 +80,7 @@ function renderMenuIcon(option) {
if (option.key == 'sdoriginal') return h(NIcon, null, { default: () => h(PaperPlaneOutline) })
if (option.key == 'setting') return h(NIcon, null, { default: () => h(SettingsOutline) })
if (option.key == 'gptCopywriting') return h(NIcon, null, { default: () => h(BookOutline) })
if (option.key == 'mainHome') return h(NIcon, null, { default: () => h(HomeOutline) })
if (option.key == 'book_management') return h(NIcon, null, { default: () => h(GridOutline) })
if (option.key == 'lai_api') return h(NIcon, null, { default: () => h(APIIcon) })
if (option.key == 'backward_matrix') return h(NIcon, null, { default: () => h(DuplicateOutline) })
@@ -103,128 +97,7 @@ function renderMenuIcon(option) {
if (option.key == 'TTS_Services') return h(NIcon, null, { default: () => h(RadioOutline) })
}
//
async function StartBackTask(isGiveUp) {
let startRes = await window.task.StartBackTask(isGiveUp)
if (startRes.code == 0) {
message.error(startRes.message)
} else {
message.success(startRes.message)
}
}
/** 判断是不是还是后台任务,用户确认是不是丢弃 */
async function CheckWaitTask() {
await TimeDelay(1000)
let getWaitTaskCountRes = await window.task.GetAllStatusTaskCount([
BookBackTaskStatus.WAIT,
BookBackTaskStatus.RECONNECT
])
//
if (getWaitTaskCountRes.code == 0) {
message.error(getWaitTaskCountRes.message)
await StartBackTask(true)
return
}
if (getWaitTaskCountRes.data && getWaitTaskCountRes.data > 0) {
//
dialog.warning({
title: '后台任务',
content: `检测到后台未完成的任务,任务数量为 ${getWaitTaskCountRes.data},请选择操作!`,
positiveText: '继续执行',
negativeText: '丢弃所有',
onPositiveClick: async () => {
//
await StartBackTask(false)
},
onNegativeClick: async () => {
//
await StartBackTask(true)
}
})
} else {
//
await StartBackTask(true)
}
}
/** 校验机器码的逻辑 */
async function GetMachineStatus() {
try {
//
const machineRes = await window.system.GetMachineId()
if (machineRes.code == 0) {
message.error(machineRes.message)
return
}
systemStore.machineId = machineRes.data
const checkMachineSatatusRes = await window.system.CheckMachineStatus(machineRes.data)
if (checkMachineSatatusRes.code == 1) {
//
message.success('自动检测机器码成功')
//
await CheckWaitTask()
return
} else {
message.error('自动检测机器码失败,请手动确认')
}
//
//
const da = dialog.create({
showIcon: false,
content: () => h(CheckMachineId, { ref: machineRef }),
style: `width : 400px; height: 300px`,
maskClosable: false,
positiveText: '确定',
onClose: () => {
//
window.api.QuitApp()
return false
},
onEsc: () => {
//
window.api.QuitApp()
return false
},
onPositiveClick: async () => {
const checkMachineSatatusRes = await window.system.CheckMachineStatus(machineRes.data)
if (checkMachineSatatusRes.code == 1) {
//
message.success('机器码校验成功')
da.destroy()
//
await CheckWaitTask()
} else {
message.error(checkMachineSatatusRes.message)
return false
}
}
})
} catch (error) {
message.error('获取机器状态码失败,请联系管理员,错误信息如下:' + error.toString())
} finally {
show.value = false
}
}
/** 加载版本和GPU信息 */
async function GetViosionAndGpuMessage() {
//
let res = await window.system.GetViosionAndGpuMessage()
console.log(res)
if (res.code == 1) {
document.title = 'LAITool ' + res.data
} else {
message.success(res.message)
}
}
onMounted(async () => {
//
await GetViosionAndGpuMessage()
// ctrl + alt + l
window.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.altKey && e.key === 'l') {
@@ -306,7 +179,6 @@ onMounted(async () => {
window.api.setEventListen([DEFINE_STRING.SHOW_MAIN_MESSAGE], (value) => {
let mess = value.message
let type = 'success'
if (value.code == 0) {
message.error(mess)
} else if (value.code == 2) {
@@ -318,9 +190,6 @@ onMounted(async () => {
}
})
//
await GetMachineStatus()
window.api.getSettingDafultData(async (value) => {
window.config = value
if (!(window.config && window.config.showOriginal)) {
@@ -335,6 +204,22 @@ function expandIcon(value) {
const menuOptions = computed(() => {
let me = [
{
label: () =>
h(
RouterLink,
{
to: {
name: 'mainHome'
},
class: 'router-link-a'
},
{
default: () => '首页'
}
),
key: 'mainHome'
},
{
label: () =>
h(
@@ -0,0 +1,842 @@
<!-- LoadingComponent.vue -->
<template>
<div class="loading-container" :class="{ 'full-screen': fullScreen }">
<div class="loading-content">
<!-- 主要加载动画 -->
<div class="loading-spinner">
<div class="spinner-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<!-- 加载文本 -->
<div class="loading-text">
<h3>{{ title }}</h3>
<p>{{ currentStepText }}</p>
</div>
<!-- 进度条 -->
<div class="loading-progress">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
</div>
<span class="progress-text">{{ progress }}%</span>
</div>
<!-- 加载步骤 -->
<div class="loading-steps">
<div
v-for="(step, index) in loadingSteps"
:key="index"
class="step-item"
:class="{
active: index === currentStep,
completed: index < currentStep
}"
>
<div class="step-icon">
<n-icon v-if="index < currentStep" size="14">
<checkmark-outline />
</n-icon>
<div v-else-if="index === currentStep" class="step-loading"></div>
<span v-else class="step-number">{{ index + 1 }}</span>
</div>
<span class="step-text">{{ step.text }}</span>
</div>
</div>
</div>
<!-- 背景装饰 -->
<div class="loading-background">
<div class="bg-circle bg-circle-1"></div>
<div class="bg-circle bg-circle-2"></div>
<div class="bg-circle bg-circle-3"></div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, nextTick, readonly } from 'vue'
import { NIcon, useMessage, useDialog } from 'naive-ui'
import { CheckmarkOutline } from '@vicons/ionicons5'
import { useSystemStore } from '@/stores/system'
import CheckMachineId from '@/renderer/src/components/Components/CheckMachineId.vue'
import { TimeDelay } from '@/define/Tools/time'
import { BookBackTaskStatus } from '@/define/enum/bookEnum'
import { define } from '@/define/define'
import { ValidateJson } from '@/define/Tools/validate'
const message = useMessage()
const dialog = useDialog()
const systemStore = useSystemStore()
const props = defineProps({
//
fullScreen: {
type: Boolean,
default: false
},
//
title: {
type: String,
default: 'LAI Tool 启动中'
},
//
themeColor: {
type: String,
default: '#18a058'
},
//
autoStart: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['loading-complete'])
//
const progress = ref(0)
const currentStep = ref(0)
const isCompleted = ref(false)
const isLoading = ref(false) //
//
const loadingSteps = ref([
{
text: '初始化系统组件',
duration: 1000,
action: async () => {
//
await new Promise((resolve) => setTimeout(resolve, 800))
}
},
{
text: '获取设备硬件信息',
duration: 900,
action: async () => {
//
await GetViosionAndGpuMessage()
await new Promise((resolve) => setTimeout(resolve, 700))
}
},
{
text: '加载软件信息',
duration: 1200,
action: async () => {
//
await GetRemoteSystemInformation()
await new Promise((resolve) => setTimeout(resolve, 1000))
}
},
{
text: '检查软件授权状态',
duration: 800,
action: async () => {
await GetMachineStatus()
//
await new Promise((resolve) => setTimeout(resolve, 600))
}
},
{
text: '准备用户界面',
duration: 600,
action: async () => {
//
await new Promise((resolve) => setTimeout(resolve, 400))
}
}
])
//
async function GetRemoteSystemInformation() {
try {
//
let remoteVersionRes = await window.axios.get(
define.lms + `/lms/Options/GetOptionsByKey/0/2/LaitoolVersion`
)
console.log('remoteVersionRes', remoteVersionRes)
if (remoteVersionRes.success == false) {
throw new Error('获取远程版本信息失败: ' + remoteVersionRes.message)
}
if (remoteVersionRes.data && remoteVersionRes.data.code == 1) {
systemStore.remoteVersion = remoteVersionRes.data.data.value
} else {
throw new Error('获取远程版本信息失败: ' + remoteVersionRes.data.message)
}
//
let remoteUpdateJsonContentRes = await window.axios.get(
define.lms + `/lms/Options/GetOptionsByKey/0/2/LaitoolUpdateJsonContent`
)
console.log('remoteUpdateJsonContentRes', remoteUpdateJsonContentRes)
if (remoteUpdateJsonContentRes.success == false) {
throw new Error('获取远程更新信息失败: ' + remoteUpdateJsonContentRes.message)
}
if (
remoteUpdateJsonContentRes.data &&
remoteUpdateJsonContentRes.data.code == 1 &&
ValidateJson(remoteUpdateJsonContentRes.data.data.value)
) {
systemStore.versionInfo.updateInfo = JSON.parse(remoteUpdateJsonContentRes.data.data.value)
} else {
throw new Error('获取远程更新信息失败: ' + remoteUpdateJsonContentRes.data.message)
}
systemStore.versionInfo.currentVersion = 'v' + systemStore.version
systemStore.versionInfo.latestVersion = 'v' + systemStore.remoteVersion
if (systemStore.version < systemStore.remoteVersion) {
//
systemStore.versionInfo.canUpdate = true
} else {
systemStore.versionInfo.canUpdate = false
}
} catch (error) {
throw error
}
}
/** 加载版本和GPU信息 */
async function GetViosionAndGpuMessage() {
//
let res = await window.system.GetViosionAndGpuMessage()
console.log(res)
if (res.code == 1) {
document.title = 'LAITool v' + res.data.version
systemStore.version = res.data.version
systemStore.gpu = res.data.gpu
} else {
message.success(res.message)
}
}
const machineRef = ref(null)
/** 校验机器码的逻辑 */
async function GetMachineStatus() {
try {
//
const machineRes = await window.system.GetMachineId()
if (machineRes.code == 0) {
message.error(machineRes.message)
return
}
systemStore.machineId = machineRes.data
const checkMachineSatatusRes = await window.system.CheckMachineStatus(machineRes.data)
if (checkMachineSatatusRes.code == 1) {
//
message.success('自动检测机器码成功')
//
await CheckWaitTask()
return
} else {
message.error('自动检测机器码失败,请手动确认')
}
//
//
const da = dialog.create({
showIcon: false,
content: () => h(CheckMachineId, { ref: machineRef }),
style: `width : 400px; height: 300px`,
maskClosable: false,
positiveText: '确定',
onClose: () => {
//
window.api.QuitApp()
return false
},
onEsc: () => {
//
window.api.QuitApp()
return false
},
onPositiveClick: async () => {
const checkMachineSatatusRes = await window.system.CheckMachineStatus(machineRes.data)
if (checkMachineSatatusRes.code == 1) {
//
message.success('机器码校验成功')
da.destroy()
//
jumpToStep(4)
await nextTick() // DOM
continue_() // 3
//
await CheckWaitTask()
} else {
message.error(checkMachineSatatusRes.message)
return false
}
}
})
throw new Error('手动验证机器码')
} catch (error) {
message.error('获取机器状态码失败,请联系管理员,错误信息如下:' + error.toString())
throw error
} finally {
}
}
/** 判断是不是还是后台任务,用户确认是不是丢弃 */
async function CheckWaitTask() {
await TimeDelay(1000)
let getWaitTaskCountRes = await window.task.GetAllStatusTaskCount([
BookBackTaskStatus.WAIT,
BookBackTaskStatus.RECONNECT
])
//
if (getWaitTaskCountRes.code == 0) {
message.error(getWaitTaskCountRes.message)
await StartBackTask(true)
return
}
if (getWaitTaskCountRes.data && getWaitTaskCountRes.data > 0) {
//
dialog.warning({
title: '后台任务',
content: `检测到后台未完成的任务,任务数量为 ${getWaitTaskCountRes.data},请选择操作!`,
positiveText: '继续执行',
negativeText: '丢弃所有',
onPositiveClick: async () => {
//
await StartBackTask(false)
},
onNegativeClick: async () => {
//
await StartBackTask(true)
}
})
} else {
//
await StartBackTask(true)
}
}
//
async function StartBackTask(isGiveUp) {
let startRes = await window.task.StartBackTask(isGiveUp)
if (startRes.code == 0) {
message.error(startRes.message)
} else {
message.success(startRes.message)
}
}
//
const currentStepText = computed(() => {
if (isCompleted.value) {
return '加载完成,即将进入应用'
}
if (currentStep.value < loadingSteps.value.length) {
return loadingSteps.value[currentStep.value].text
}
return '正在准备...'
})
//
const startLoading = async () => {
if (isLoading.value) return //
isLoading.value = true
currentStep.value = 0
progress.value = 0
isCompleted.value = false
await executeStepsFrom(0)
}
//
const continueLoading = async () => {
if (isLoading.value) return //
if (isCompleted.value) return //
isLoading.value = true
await executeStepsFrom(currentStep.value)
}
//
const executeStepsFrom = async (startStep) => {
const totalSteps = loadingSteps.value.length
for (let i = startStep; i < totalSteps; i++) {
currentStep.value = i
const step = loadingSteps.value[i]
try {
//
const startTime = Date.now()
//
await Promise.all([
//
animateProgress(i, totalSteps),
//
step.action()
])
//
const elapsed = Date.now() - startTime
const minDuration = step.duration || 800
if (elapsed < minDuration) {
await new Promise((resolve) => setTimeout(resolve, minDuration - elapsed))
}
} catch (error) {
//
console.error(`步骤 ${i + 1} 执行失败:`, error)
isLoading.value = false
return //
}
}
//
currentStep.value = totalSteps
progress.value = 100
isCompleted.value = true
isLoading.value = false
//
setTimeout(() => {
emit('loading-complete')
}, 500)
}
//
const animateProgress = async (stepIndex, totalSteps) => {
const startProgress = Math.floor((stepIndex / totalSteps) * 100)
const endProgress = Math.floor(((stepIndex + 1) / totalSteps) * 100)
const duration = 300 //
const steps = 20 //
const stepDuration = duration / steps
const progressStep = (endProgress - startProgress) / steps
for (let i = 0; i <= steps; i++) {
progress.value = Math.min(startProgress + Math.floor(progressStep * i), endProgress)
await new Promise((resolve) => setTimeout(resolve, stepDuration))
}
}
//
const start = () => {
startLoading()
}
//
const continue_ = () => {
continueLoading()
}
//
const skipCurrentStep = () => {
if (isLoading.value || isCompleted.value) return
//
currentStep.value += 1
//
continueLoading()
}
//
const jumpToStep = (stepIndex) => {
if (isLoading.value || isCompleted.value) return
if (stepIndex < 0 || stepIndex >= loadingSteps.value.length) return
currentStep.value = stepIndex
//
const totalSteps = loadingSteps.value.length
progress.value = Math.floor((stepIndex / totalSteps) * 100)
}
//
const reset = () => {
isLoading.value = false
progress.value = 0
currentStep.value = 0
isCompleted.value = false
}
//
const pause = () => {
isLoading.value = false
}
//
const resume = () => {
if (!isCompleted.value) {
continueLoading()
}
}
//
defineExpose({
start, //
continue: continue_, //
skipCurrentStep, //
jumpToStep, //
reset, //
pause, //
resume, //
//
isLoading: readonly(isLoading),
isCompleted: readonly(isCompleted),
currentStep: readonly(currentStep),
progress: readonly(progress)
})
//
onMounted(() => {
if (props.autoStart) {
nextTick(() => {
startLoading()
})
}
})
</script>
<style scoped>
.loading-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
border-radius: 12px;
overflow: hidden;
}
.loading-container.full-screen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
min-height: 100vh;
max-height: 100vh;
border-radius: 0;
margin: 0;
padding: 0;
}
.loading-content {
position: relative;
text-align: center;
max-width: 400px;
padding: 20px;
width: 100%;
box-sizing: border-box;
}
/* 确保全屏时内容居中 */
.loading-container.full-screen .loading-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: auto;
max-width: 500px;
min-width: 400px;
}
/* 加载动画 */
.loading-spinner {
margin-bottom: 24px;
}
.spinner-ring {
display: inline-block;
position: relative;
width: 64px;
height: 64px;
}
.spinner-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 51px;
height: 51px;
margin: 6px;
border: 6px solid v-bind(themeColor);
border-radius: 50%;
animation: spinner-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: v-bind(themeColor) transparent transparent transparent;
}
.spinner-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.spinner-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.spinner-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes spinner-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 加载文本 */
.loading-text h3 {
margin: 0 0 8px 0;
font-size: 18px;
font-weight: 600;
}
.loading-text p {
margin: 0;
font-size: 14px;
line-height: 1.5;
min-height: 20px;
}
/* 进度条 */
.loading-progress {
margin: 24px 0;
}
.progress-bar {
width: 100%;
height: 6px;
background: #f0f0f0;
border-radius: 3px;
overflow: hidden;
margin-bottom: 8px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, v-bind(themeColor), #40a9ff);
border-radius: 3px;
transition: width 0.3s ease;
}
.progress-text {
font-size: 12px;
font-weight: 500;
}
/* 加载步骤 */
.loading-steps {
text-align: left;
max-width: 100%;
}
.step-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 0;
opacity: 0.5;
transition: all 0.3s ease;
}
.step-item.active {
opacity: 1;
color: v-bind(themeColor);
}
.step-item.completed {
opacity: 0.8;
color: #52c41a;
}
.step-icon {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: #f0f0f0;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
}
.step-item.active .step-icon {
background: v-bind(themeColor);
color: white;
}
.step-item.completed .step-icon {
background: #52c41a;
color: white;
}
.step-loading {
width: 12px;
height: 12px;
border: 2px solid white;
border-top: 2px solid transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.step-number {
}
.step-item.active .step-number {
}
.step-text {
font-size: 14px;
font-weight: 500;
flex: 1;
word-break: break-word;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 背景装饰 */
.loading-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: linear-gradient(45deg, rgba(24, 160, 88, 0.1), rgba(64, 169, 255, 0.1));
animation: float 6s ease-in-out infinite;
}
.bg-circle-1 {
width: 100px;
height: 100px;
top: 10%;
left: 10%;
animation-delay: 0s;
}
.bg-circle-2 {
width: 150px;
height: 150px;
top: 60%;
right: 10%;
animation-delay: 2s;
}
.bg-circle-3 {
width: 80px;
height: 80px;
bottom: 20%;
left: 60%;
animation-delay: 4s;
}
@keyframes float {
0%,
100% {
transform: translateY(0px) rotate(0deg);
}
50% {
transform: translateY(-20px) rotate(180deg);
}
}
/* 深色模式 */
.dark .loading-container {
background: rgba(26, 26, 26, 0.95);
}
.dark .loading-container.full-screen {
background: rgba(26, 26, 26, 0.98);
}
.dark .loading-text h3 {
color: #fff;
}
.dark .loading-text p {
color: #ccc;
}
.dark .progress-bar {
background: #3d3d3d;
}
.dark .progress-text {
color: #ccc;
}
.dark .step-icon {
background: #3d3d3d;
}
.dark .step-number {
color: #ccc;
}
.dark .step-text {
color: #fff;
}
.dark .step-item {
color: #ccc;
}
/* 响应式适配 */
@media (max-width: 768px) {
.loading-container.full-screen .loading-content {
min-width: 300px;
max-width: 90vw;
padding: 16px;
}
.loading-text h3 {
font-size: 16px;
}
.step-text {
font-size: 13px;
}
}
/* 确保覆盖所有可能的父级样式 */
.loading-container.full-screen {
position: fixed !important;
inset: 0 !important;
width: 100vw !important;
height: 100vh !important;
min-width: 100vw !important;
min-height: 100vh !important;
max-width: 100vw !important;
max-height: 100vh !important;
left: 0 !important;
top: 0 !important;
right: 0 !important;
bottom: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
border-radius: 0 !important;
box-sizing: border-box !important;
}
</style>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,238 @@
<template>
<div class="contact-developer">
<!-- 头部说明 -->
<div class="header-section">
<div class="title">👨💻 开发者微信</div>
<div class="description">扫描下方二维码添加开发者微信获取专业技术支持</div>
</div>
<!-- 二维码图片容器 -->
<div class="qrcode-container">
<img
src="../../assets//dev-user.jpg"
alt="开发者微信二维码"
class="qrcode-image"
@error="handleImageError"
/>
<div v-if="showPlaceholder" class="qrcode-placeholder">
<div class="placeholder-icon">📱</div>
<div class="placeholder-text">二维码图片</div>
<div class="placeholder-subtext">请联系管理员获取</div>
</div>
</div>
<!-- 联系说明 -->
<div class="contact-info">
<div class="contact-title">
<span class="contact-icon">💡</span>
联系须知
</div>
<ul class="contact-list">
<li>添加时请备注LAITool用户</li>
<li>工作时间周一至周五 9:00-18:00</li>
<li>支持技术咨询BUG反馈功能建议</li>
<li>请详细描述您遇到的问题以便快速解决</li>
</ul>
</div>
<!-- 底部提示 -->
<div class="footer-info">
<div>如无法扫码可复制微信号</div>
<div class="wechat-id" @click="copyWechatId">{{ wechatId }}</div>
<div>或发送邮件至{{ email }}</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useMessage } from 'naive-ui'
const props = defineProps({
wechatId: {
type: String,
default: 'xiangbie88'
},
email: {
type: String,
default: '2769838458@qq.com'
}
})
const emit = defineEmits(['copy-success', 'copy-error'])
const message = useMessage()
const showPlaceholder = ref(false)
const handleImageError = () => {
showPlaceholder.value = true
}
const copyWechatId = async () => {
try {
await navigator.clipboard.writeText(props.wechatId)
message.success('微信号已复制到剪贴板')
emit('copy-success', props.wechatId)
} catch (error) {
message.error(`复制失败,请手动复制:${props.wechatId}`)
emit('copy-error', error)
}
}
</script>
<style scoped>
.contact-developer {
text-align: center;
padding: 20px 0;
font-size: 14px;
line-height: 1.6;
}
.header-section {
margin-bottom: 24px;
}
.title {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
}
.description {
font-size: 14px;
line-height: 1.5;
}
.qrcode-container {
margin: 24px 0;
padding: 20px;
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
border-radius: 12px;
border: 2px dashed #1890ff;
position: relative;
}
.qrcode-image {
width: 200px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.qrcode-placeholder {
width: 200px;
height: 200px;
background: #f5f5f5;
border: 2px dashed #d9d9d9;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
margin: 0 auto;
}
.placeholder-icon {
font-size: 48px;
margin-bottom: 12px;
}
.placeholder-text {
font-size: 14px;
}
.placeholder-subtext {
font-size: 12px;
margin-top: 4px;
}
.contact-info {
margin-top: 24px;
padding: 16px;
background: #fff7e6;
border-radius: 8px;
border-left: 4px solid #fa8c16;
text-align: left;
}
.contact-title {
font-weight: 600;
color: #fa8c16;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.contact-icon {
margin-right: 8px;
}
.contact-list {
margin: 0;
padding-left: 20px;
color: #666;
font-size: 13px;
line-height: 1.6;
}
.contact-list li {
margin-bottom: 4px;
}
.footer-info {
margin-top: 16px;
font-size: 12px;
line-height: 1.4;
}
.wechat-id {
font-weight: 600;
color: #1890ff;
margin-top: 4px;
font-size: 20px;
cursor: pointer;
user-select: all;
padding: 4px 8px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
/* 深色模式适配 */
.dark .title,
.dark .contact-title {
color: #fff;
}
.dark .description {
color: #ccc;
}
.dark .qrcode-container {
background: linear-gradient(135deg, #1a2332 0%, #2a3441 100%);
border-color: #3a4a5c;
}
.dark .qrcode-placeholder {
background: #2d2d2d;
border-color: #4a4a4a;
color: #ccc;
}
.dark .contact-info {
background: #2a2418;
border-color: #5a4a2c;
}
.dark .contact-list {
color: #ccc;
}
.dark .footer-info {
color: #ccc;
}
.dark .wechat-id:hover {
background-color: #1a2332;
}
</style>
@@ -0,0 +1,164 @@
<template>
<div class="wechat-group">
<!-- 群信息卡片 -->
<div class="group-info">
<div class="group-title">🎯 加入VIP用户交流群</div>
<div class="group-description">{{ groupInfo }}</div>
</div>
<!-- 群内福利 -->
<div class="benefits-section">
<div class="benefits-title">💡 群内福利</div>
<ul class="benefits-list">
<li class="benefit-item">
<span class="benefit-icon">📢</span>
获取最新版本信息和更新通知
</li>
<li class="benefit-item">
<span class="benefit-icon">💬</span>
与其他用户交流使用技巧和经验
</li>
<li class="benefit-item">
<span class="benefit-icon">🐛</span>
快速反馈问题和建议
</li>
<li class="benefit-item">
<span class="benefit-icon">🛠</span>
获得开发团队的技术支持
</li>
</ul>
</div>
<!-- 联系方式 -->
<div class="contact-info">联系管理员邀请入群或者联系对应代理</div>
<!-- 群规提醒 -->
<div class="rules-notice">请勿在群内发布广告或无关内容保持良好交流氛围</div>
</div>
</template>
<script setup>
const props = defineProps({
groupInfo: {
type: String,
default: '仅限永久VIP用户可加入'
}
})
</script>
<style scoped>
.wechat-group {
line-height: 1.6;
font-size: 14px;
}
.group-info {
margin-bottom: 16px;
padding: 12px;
background: #f0f9ff;
border-radius: 8px;
border-left: 4px solid #1890ff;
}
.group-title {
font-weight: 600;
color: #1890ff;
margin-bottom: 4px;
}
.group-description {
font-size: 13px;
color: #1890ff;
}
.benefits-section {
margin-bottom: 16px;
}
.benefits-title {
margin-bottom: 12px;
font-weight: 600;
}
.benefits-list {
margin: 0;
padding-left: 20px;
}
.benefit-item {
margin-bottom: 8px;
padding: 4px 0;
display: flex;
align-items: flex-start;
gap: 8px;
}
.benefit-icon {
font-weight: 600;
flex-shrink: 0;
}
.benefit-item:nth-child(1) .benefit-icon {
color: #52c41a;
}
.benefit-item:nth-child(2) .benefit-icon {
color: #1890ff;
}
.benefit-item:nth-child(3) .benefit-icon {
color: #f0a020;
}
.benefit-item:nth-child(4) .benefit-icon {
color: #d03050;
}
.contact-info {
margin-top: 16px;
padding: 8px;
background: #fff7e6;
border-radius: 6px;
text-align: center;
color: #fa8c16;
font-size: 12px;
}
.rules-notice {
margin-top: 8px;
padding: 8px;
background: #fff7e6;
border-radius: 6px;
text-align: center;
color: #fa8c16;
font-size: 12px;
}
/* 深色模式适配 */
.dark .group-info {
background: #1a2332;
border-color: #3a4a5c;
}
.dark .group-title {
color: #40a9ff;
}
.dark .group-description {
color: #ccc;
}
.dark .benefits-title {
color: #fff;
}
.dark .benefits-list {
color: #ccc;
}
.dark .contact-info,
.dark .rules-notice {
background: #2a2418;
color: #ffa940;
}
</style>
+5
View File
@@ -13,6 +13,11 @@ const routes = [
path: '/',
component: () => import('./components/Home/Home.vue'),
children: [
{
path: '/mainHome',
name: 'mainHome',
component: () => import('./components/Home/SoftwareHome.vue')
},
{
path: '/gptCopywriting',
name: 'gptCopywriting',
+30 -1
View File
@@ -6,7 +6,36 @@ export type SystemStoreModel = {
export const useSystemStore = defineStore('system', {
state: () => ({
machineId: undefined
machineId: undefined,
version: '1.1.1',
remoteVersion: '1.1.1',
versionInfo: {
currentVersion: 'v1.0.0',
latestVersion: 'v1.0.0',
updateInfo: {
latestVersion: 'v1.0.0',
updateDate: '2023-10-01',
updateInfo: [
{
version: 'v1.0.0',
updateDate: '2023-09-01',
changes: [
{
type: 'improvement',
description: '改进了性能'
}
]
}
]
},
canUpdate: false
} as SystemConfig.VersionInfo,
gpu: {
name: void 0,
type: void 0,
vender: void 0,
updateTime: void 0
} as SystemConfig.GpuMessage
} as SystemStoreModel),
getters: {
},