1. 初步接入 comfy UI 出图,设定工作流,导出API,直接添加到软件,选中对应的工作流调用出图
2. 文案处理模型改为选择,同步通用设置的模型列表和令牌,添加测试链接功能
3. 添加缓存区图片删除功能
4. 添加垫图/MJ提示词过滤,提示无效的垫图文件(在出图的时候和添加垫图链接的时候,主要过滤飞书链接)
5. 优化聚合推文选图逻辑,没有出过图也能拖拽
6. 优化处理软件所有的加载状态
7. 修复MJ设置初始化问题
This commit is contained in:
2025-03-22 10:38:23 +08:00
parent 77ee38d302
commit 6336b36ead
51 changed files with 1900 additions and 133 deletions
+39 -20
View File
@@ -1,25 +1,42 @@
<template>
<n-spin style="z-index: 1000 !important" :show="softwareStore.spin.spinning">
<template #description> {{ softwareStore.spin.tip }} </template>
<n-config-provider
:hljs="hljs"
:theme="softwareStore.globalSetting.theme == 'dark' ? darkTheme : null"
>
<n-message-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>
<n-config-provider
:hljs="hljs"
:theme="softwareStore.globalSetting.theme == 'dark' ? darkTheme : null"
>
<n-message-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-modal
v-model:show="softwareStore.spin.spinning"
:mask-closable="false"
:close-on-esc="true"
transform-origin="center"
:show-icon="false"
style="background: transparent; box-shadow: none; width: auto"
content-style="padding: 0; background: transparent;"
:bordered="false"
:mask="false"
>
<n-spin :color="'#18a058'">
<template #description>
<span :style="{ color: '#18a058', fontSize: '16px' }">
{{ softwareStore.spin.tip }}
</span>
</template>
</n-spin>
</n-modal>
</template>
<script>
import { defineComponent, ref, onMounted } from 'vue'
import { defineComponent, onMounted } from 'vue'
import hljs from 'highlight.js/lib/core'
import javascript from 'highlight.js/lib/languages/javascript'
import {
@@ -29,7 +46,8 @@ import {
NModalProvider,
darkTheme,
NNotificationProvider,
NSpin
NSpin,
NModal
} from 'naive-ui'
import { useSoftwareStore } from '../../stores/software'
import { SoftColor } from '../../define/enum/softwareEnum'
@@ -42,7 +60,8 @@ export default defineComponent({
NMessageProvider,
NNotificationProvider,
NModalProvider,
NSpin
NSpin,
NModal
},
setup() {
let softwareStore = useSoftwareStore()
+71 -3
View File
@@ -2,6 +2,7 @@
import { OptionKeyName, OptionType } from '@/define/enum/option'
import { useOptionStore } from '@/stores/option'
import SDFluxCommon from './SDFluxCommon'
import { OptionModel } from '@/model/option/option'
/**
* 初始化特殊字符数据,用于文案的相关处理
@@ -44,7 +45,6 @@ async function InitSpecialCharacters() {
* @returns
*/
async function InitTTSGlobalSetting() {
debugger
let optionStore = useOptionStore()
let initData = {
selectModel: "edge-tts",
@@ -96,7 +96,6 @@ async function InitTTSGlobalSetting() {
* @returns
*/
async function InitFluxModelList(isMust: boolean = false): Promise<Array<{ label: string, value: string }>> {
debugger
let initData = []
if (!isMust) {
let FLUX_APIModelListData = await window.options.GetOptionByKey(OptionKeyName.FLUX_APIModelList);
@@ -136,9 +135,78 @@ async function InitFluxModelList(isMust: boolean = false): Promise<Array<{ label
}
}
/**
* 初始化 ComfyUI 的一些基础设置
* @returns
*/
async function InitComfyUISetting() {
let optionStore = useOptionStore()
let initSimpleSetting: OptionModel.ComfyUI_SimpleSettingModel = {
requestUrl: "",
selectedWorkflow: null,
negativePrompt: ""
};
let initWorkFlowSetting: Array<OptionModel.ComfyUI_WorkFlowSettingModel> = [];
// 初始化基础设置
let ComfyUI_SimpleSetting = await window.options.GetOptionByKey(
OptionKeyName.ComfyUI_SimpleSetting
)
if (ComfyUI_SimpleSetting.code == 0) {
// 获取失败
window.api.showGlobalMessageDialog(ComfyUI_SimpleSetting)
return
}
if (ComfyUI_SimpleSetting.data == null) {
// 没有数据初始化
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.ComfyUI_SimpleSetting,
JSON.stringify(initSimpleSetting),
OptionType.JOSN
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog(saveRes)
return
} else {
optionStore.ComfyUI_SimpleSetting = initSimpleSetting
}
} else {
optionStore.ComfyUI_SimpleSetting = JSON.parse(ComfyUI_SimpleSetting.data.value)
}
// 初始化工作流设置
let ComfyUI_WorkFlowSetting = await window.options.GetOptionByKey(
OptionKeyName.ComfyUI_WorkFlowSetting
)
if (ComfyUI_WorkFlowSetting.code == 0) {
// 获取失败
window.api.showGlobalMessageDialog(ComfyUI_WorkFlowSetting)
return
}
if (ComfyUI_WorkFlowSetting.data == null) {
// 没有数据初始化
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.ComfyUI_WorkFlowSetting,
JSON.stringify(initWorkFlowSetting),
OptionType.JOSN
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog(saveRes)
return
} else {
optionStore.ComfyUI_WorkFlowSetting = initWorkFlowSetting
}
} else {
optionStore.ComfyUI_WorkFlowSetting = JSON.parse(ComfyUI_WorkFlowSetting.data.value)
}
}
let InitCommon = {
InitSpecialCharacters,
InitTTSGlobalSetting,
InitFluxModelList
InitFluxModelList,
InitComfyUISetting
}
export default InitCommon
@@ -26,8 +26,10 @@
v-for="(image, index) in item.imagePaths"
:key="image.id"
:id="image.id"
:bookname="item.bookTaskName"
class="image-container"
@click="SelectImage(image, index)"
@contextmenu="handleContextMenu"
>
<n-image
:width="150"
@@ -43,6 +45,16 @@
</n-image-group>
</n-collapse-item>
</n-collapse>
<n-dropdown
placement="bottom-start"
trigger="manual"
:x="x"
:y="y"
:options="options"
:show="showDropdown"
:on-clickoutside="onClickoutside"
@select="handleSelect"
/>
<div style="margin-top: 15px; display: flex; justify-content: flex-end">
<n-button type="info" @click="SaveImageToData">选择完成</n-button>
</div>
@@ -51,12 +63,23 @@
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { NImageGroup, NImage, NSpin, NButton, useMessage, NCollapse, NCollapseItem } from 'naive-ui'
import { onMounted, ref, nextTick, h } from 'vue'
import {
NImageGroup,
NImage,
NSpin,
NButton,
useMessage,
NCollapse,
NCollapseItem,
NDropdown,
NIcon
} 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'
import { TrashBinOutline } from '@vicons/ionicons5'
let reverseManageStore = useReverseManageStore()
let props = defineProps({
@@ -71,6 +94,94 @@ let show = ref(true)
let defaultExpandedNames = ref([])
let selectImageIds = ref([])
let deleteImage = ref(null)
let showDropdown = ref(false)
let x = ref(0)
let y = ref(0)
const options = [
{
label: () => h('span', { style: { color: 'red' } }, `删除`),
key: 'delete',
icon: () => h(NIcon, { color: 'red' }, { default: () => h(TrashBinOutline) })
}
]
async function handleSelect(key) {
showDropdown.value = false
if (key == 'delete') {
if (deleteImage.value == null) {
message.error('未找到对应的被删除的图片数据,请检查!!')
return
}
// 开始删除
let res = await window.book.DeleteCacheImage(
deleteImage.value.booktaskId,
deleteImage.value.imagePath
)
if (res.code != 1) {
message.error(res.message)
return
}
// 删除成功
// 动态的修改数据
let currentBookIndex = data.value.findIndex((x) => x.bookTaskId == deleteImage.value.booktaskId)
if (currentBookIndex != -1) {
let currentBooks = data.value[currentBookIndex]
let findIndex = currentBooks.imagePaths.findIndex((x) => x.id == deleteImage.value.id)
if (findIndex != -1) {
currentBooks.imagePaths.splice(findIndex, 1)
}
if (currentBooks.imagePaths <= 0) {
data.value.splice(currentBookIndex, 1)
}
}
message.success(res.message)
} else {
message.error('未找到对应的操作,请检查')
}
}
function handleContextMenu(e) {
e.preventDefault()
showDropdown.value = false
debugger
let bookname = e.target.getAttribute('bookname')
if (bookname == null) {
message.error('未找到对应的批次数据名称,请检查')
return
}
let imageId = e.target.id
if (imageId == null) {
message.error('未找到对应的图片ID,请检查')
return
}
let currentBooks = data.value.find((x) => x.bookTaskName == bookname)
if (currentBooks == null) {
message.error('未找到对应的批次数据,请检查')
return
}
let cacheImageList = currentBooks.imagePaths
if (cacheImageList.length <= 0) {
message.error('未找到对应的图片数据,请检查')
return
}
let imageIndex = cacheImageList.findIndex((x) => x.id == imageId)
if (imageIndex == -1) {
message.error('未找到对应的图片数据,请检查')
return
}
deleteImage.value = { ...cacheImageList[imageIndex], booktaskId: currentBooks.bookTaskId }
nextTick().then(() => {
showDropdown.value = true
x.value = e.clientX
y.value = e.clientY
})
}
function onClickoutside() {
showDropdown.value = false
}
onMounted(async () => {
try {
@@ -80,7 +191,6 @@ onMounted(async () => {
message.error(res.message)
return
}
console.log('GetAllBookTaskImageCache', res.data)
// 将指定ID的数据放在最前面
const specifiedId = reverseManageStore.selectBookTask.id // 替换为你的指定ID
res.data.sort((a, b) =>
@@ -215,7 +325,6 @@ async function SaveImageToData() {
selectImagePaths[0],
props.type
)
console.log('SaveCacheImageToData', res)
if (res.code == 0) {
message.error(res.message)
return
@@ -201,6 +201,21 @@ export default defineComponent({
? characterData.value.show_image.split('?t')[0]
: null
// 开始保存
if (characterData.value.image_url != null && !isEmpty(characterData.value.image_url)) {
//
// 删除前面的空格
let trimmedUrl = characterData.value.image_url.trim()
if (trimmedUrl.startsWith('--cref')) {
message.error('风格垫图地址不能以 --cref 开头,请删除!!!')
return
}
if (trimmedUrl.includes('feishu.cn')) {
message.error('垫图链接不能包含 feishu.cn 飞书的链接,请正确的复制图片链接!!!')
return
}
}
await window.mj.SaveTagPropertyData(
[JSON.stringify(characterData.value), 'character_tags'],
(value) => {
@@ -36,7 +36,10 @@
</n-tooltip>
</div>
</n-form-item>
<div style="color: red;">注意风格提示词描述英文 才是实际合并的提示词数据请和出图的提示词语言保持一致中文或英文</div>
<div style="color: red">
注意风格提示词描述英文
才是实际合并的提示词数据请和出图的提示词语言保持一致中文或英文
</div>
<n-form-item label="风格提示词描述(英文)" path="prompt">
<n-input
type="textarea"
@@ -89,7 +92,7 @@
</template>
<script>
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
import { ref, onMounted, defineComponent, toRaw, watch } from 'vue'
import {
NImage,
useMessage,
@@ -145,15 +148,32 @@ export default defineComponent({
async function SaveStyleTag(e) {
e.preventDefault()
formRef.value?.validate(async (errors) => {
debugger
if (errors) {
message.error('请检查必填字段')
return
}
styleData.value['type'] = 'style_main'
styleData.value['show_image'] = styleData.value.show_image
? styleData.value.show_image.split('?t')[0]
: null
// 直接保存
// 判断链接是不是有效的
if (styleData.value.image_url != null && !isEmpty(styleData.value.image_url)) {
//
// 删除前面的空格
let trimmedUrl = styleData.value.image_url.trim()
if (trimmedUrl.startsWith('--sref')) {
message.error('风格垫图地址不能以 --sref 开头,请删除!!!')
return
}
if (trimmedUrl.includes('feishu.cn')) {
message.error('垫图链接不能包含 feishu.cn 飞书的链接,请正确的复制图片链接!!!')
return
}
}
// 开始保存
await window.mj.SaveTagPropertyData(
[JSON.stringify(styleData.value), 'style_tags'],
@@ -248,7 +268,6 @@ export default defineComponent({
}
imageLoading.value = true
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
if (value.code == 0) {
message.error(value.message)
imageLoading.value = false
@@ -292,12 +292,8 @@ async function imageDragStart(e) {
// 图片拖拽结束事件
async function imageDragDrop(e) {
debugger
console.log('end', e.target, dragTarget)
// 判断原始数据是不是为空
// if (dragTarget == null) {
// message.error('只能拖拽到当前行的图片上')
// return
// }
// 判断结束位置是不是主图片
if (e.target.getAttribute('mainImage') != '1') {
message.error('图片只能拖拽到主图片上')
@@ -307,19 +303,33 @@ async function imageDragDrop(e) {
.split('?')[0]
.replace(/^file:\/\/\//, '')
.replace(/^\//, '')
let target_img = e.target.src
.split('?')[0]
.replace(/^file:\/\/\//, '')
.replace(/^\//, '')
// let target_img = e.target.src
// .split('?')[0]
// .replace(/^file:\/\/\//, '')
// .replace(/^\//, '')
// 修改主图片
await window.api.DownloadImageFile([source_img, 'replace', target_img], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
data.value.outImagePath =
e.target.src.split('?')[0].replaceAll('\\', '/') + '?time=' + new Date().getTime()
})
let res = await window.book.MoveImageToMainImage(
data.value.bookTaskId,
data.value.id,
decodeURI(source_img)
)
if (res.code == 0) {
message.error(res.message)
return
}
data.value.outImagePath = res.data
message.success(res.message)
// await window.api.DownloadImageFile([source_img, 'replace', target_img], (value) => {
// if (value.code == 0) {
// message.error(value.message)
// return
// }
// data.value.outImagePath =
// e.target.src.split('?')[0].replaceAll('\\', '/') + '?time=' + new Date().getTime()
// })
}
// 图片拖拽进入事件
@@ -542,6 +552,7 @@ function renderIcon(icon) {
)
}
}
let mainImageOptions = []
function GetMainImageOptions() {
let t = [
@@ -137,7 +137,7 @@ export default defineComponent({
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
type = 'sd_merge'
} else {
type = 'mj_merge'
type = 'sd_merge'
}
}
// 开始进行合并,调用不同的合并模式
@@ -82,7 +82,7 @@ async function MergePrompt(key) {
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
type = 'sd_merge'
} else {
type = 'mj_merge'
type = 'sd_merge'
}
}
// 开始进行合并,调用不同的合并模式
@@ -108,6 +108,7 @@ async function MergePrompt(key) {
// 单个出图
async function SingleGenerateImage() {
message.info('1')
let res = undefined
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
res = await window.mj.AddMJGenerateImageTask(
@@ -125,6 +126,16 @@ async function SingleGenerateImage() {
data.value.id,
DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.ComfyUI) {
// 直接添加SD出图任务
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
BookBackTaskType.ComfyUI_IMAGE,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
data.value.id,
DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
@@ -226,6 +237,20 @@ async function UnderGenerateImage() {
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.ComfyUI) {
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasksList.push({
bookId: reverseManageStore.selectBook.id,
type: BookBackTaskType.ComfyUI_IMAGE,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
@@ -806,6 +806,9 @@ async function AddFluxImageAll(type, cover) {
} else if (type == BookImageCategory.D3) {
messageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.D3_IMAGE
} else if (type == BookImageCategory.ComfyUI) {
messageName = DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.ComfyUI_IMAGE
} else if (type == BookImageCategory.SD) {
messageName = DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.SD_IMAGE
@@ -899,6 +902,8 @@ async function GenerateImageAll(cover = true) {
await AddFluxImageAll(BookImageCategory.FLUX_API, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
await AddFluxImageAll(BookImageCategory.D3, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.ComfyUI) {
await AddFluxImageAll(BookImageCategory.ComfyUI, cover)
} else {
message.error('不支持的生图类型')
return
@@ -29,7 +29,7 @@
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NTabPane, NTabs, NCard } from 'naive-ui'
import MJSettingHome from '../../Setting/MJSetting/MJSettingHome.vue'
import SDSetting from '../../Setting/SDSetting.vue'
import SDSetting from '../../Setting/SDSetting/SDSetting.vue'
import VideoGenerateSetting from '../../Setting/VideoGenerateSetting.vue'
import WatermarkSetting from '../../Setting/WatermarkSetting.vue'
import SubtitleSetting from '../../Setting/SubtitleSetting.vue'
@@ -73,6 +73,11 @@ onMounted(async () => {
OriginalSDImageResponseReturn(value)
})
// 监听ComfyUI出图返回的数据
window.api.setEventListen([DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN], (value) => {
OriginalSDImageResponseReturn(value)
})
// 监听FLUX API 出图返回的数据
window.api.setEventListen([DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN], (value) =>
OriginalSDImageResponseReturn(value)
@@ -178,6 +183,7 @@ onUnmounted(() => {
window.api.removeEventListen(DEFINE_STRING.BOOK.REVERSE_PROMPT_TRANSLATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN)
})
@@ -130,7 +130,7 @@ async function MergePrompt(type = null) {
let image_generate_category = reverseManageStore.selectBookTask.imageCategory
let mergeType = 'mj_merge'
if (!type) {
if (image_generate_category == 'sd') {
if (image_generate_category == 'sd' || image_generate_category == 'comfyui') {
mergeType = 'sd_merge'
} else if (image_generate_category == 'mj') {
mergeType = 'mj_merge'
@@ -137,6 +137,16 @@ async function SingleGenerateImage() {
row.value.id,
DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.ComfyUI) {
// 直接添加SD出图任务
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
BookBackTaskType.ComfyUI_IMAGE,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
row.value.id,
DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN
)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
@@ -238,6 +248,20 @@ async function NextGenerateImage() {
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.ComfyUI) {
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
const element = bookTaskDetails[i]
tasksList.push({
bookId: reverseManageStore.selectBook.id,
type: BookBackTaskType.ComfyUI_IMAGE,
executeType: TaskExecuteType.AUTO,
bookTaskId: reverseManageStore.selectBookTask.id,
bookTaskDetailId: element.id,
messageName: DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN
})
}
res = await window.task.AddMultiBookBackTask(tasksList)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
let tasksList = []
for (let i = 0; i < bookTaskDetails.length; i++) {
@@ -387,6 +387,9 @@ async function AddFluxImageAll(type, cover) {
} else if (type == BookImageCategory.FLUX_API) {
messageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.FLUX_API_IMAGE
} else if (type == BookImageCategory.ComfyUI) {
messageName = DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.ComfyUI_IMAGE
} else if (type == BookImageCategory.D3) {
messageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
taskType = BookBackTaskType.D3_IMAGE
@@ -473,6 +476,8 @@ async function GenerateImageAll(cover = true) {
await AddFluxImageAll(BookImageCategory.FLUX_API, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
await AddFluxImageAll(BookImageCategory.D3, cover)
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.ComfyUI) {
await AddFluxImageAll(BookImageCategory.ComfyUI, cover)
} else {
message.error('不支持的生图类型')
return
@@ -78,7 +78,7 @@ function OriginalMJImageResponseReturn(value) {
// 原创SD生图返回的数据进行修改
function OriginalSDImageResponseReturn(value) {
console.log('OriginalSDImageResponseReturn', value)
console.log('OriginalSDImageResponseReturn111111', value)
// 开始修改
if (value.data) {
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
@@ -193,6 +193,10 @@ onMounted(async () => {
OriginalSDImageResponseReturn(value)
})
window.api.setEventListen([DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN], (value) => {
OriginalSDImageResponseReturn(value)
})
window.api.setEventListen([DEFINE_STRING.BOOK.RUNWAY_IMAGE_TO_VIDEO_RETURN], (value) => {
VideoImageToVideoReturn(value)
})
@@ -205,6 +209,7 @@ onUnmounted(() => {
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.ComfyUI_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.RUNWAY_IMAGE_TO_VIDEO_RETURN)
})
</script>
@@ -1,42 +1,136 @@
<template>
<n-space vertical>
<n-card title="LAI API 设置">
<div style="display: flex">
<n-input
v-model:value="optionStore.CW_AISetting.laiapi.gpt_url"
style="margin-right: 10px"
type="text"
placeholder="请输入GPT URL"
/>
<n-input
v-model:value="optionStore.CW_AISetting.laiapi.api_key"
style="margin-right: 10px"
type="text"
placeholder="请输入API KEY"
/>
<n-input
v-model:value="optionStore.CW_AISetting.laiapi.model"
type="text"
placeholder="请输入Model"
/>
<n-spin :show="loading">
<n-card title="LAI API 设置">
<div style="display: flex">
<n-input
v-model:value="optionStore.CW_AISetting.laiapi.gpt_url"
style="margin-right: 10px; flex: 1"
type="text"
placeholder="请输入GPT URL"
/>
<n-input
v-model:value="optionStore.CW_AISetting.laiapi.api_key"
style="margin-right: 10px; flex: 1"
type="text"
placeholder="请输入API KEY"
/>
<n-select
v-model:value="optionStore.CW_AISetting.laiapi.model"
:options="gpt_model_options"
placeholder="请选择Model"
style="flex: 1"
/>
<n-button
type="info"
style="margin-left: 10px; white-space: nowrap"
@click="testConnection"
>
测试连接
</n-button>
</div>
</n-card>
<div style="display: flex; justify-content: flex-end; margin: 20px">
<n-button
type="info"
style="margin-right: 20px; white-space: nowrap"
@click="syncGeneralSettings"
>
同步通用设置Key
</n-button>
<n-button type="info" @click="SaveAISetting">保存</n-button>
</div>
</n-card>
<div style="display: flex; justify-content: flex-end; margin: 20px">
<n-button type="primary" @click="SaveAISetting">保存</n-button>
</div>
</n-spin>
</n-space>
</template>
<script setup>
import { useMessage, useDialog, NCard, NSpace, NInput, NSelect, NButton } from 'naive-ui'
import { onMounted, ref } from 'vue'
import { useMessage, useDialog, NCard, NSpace, NInput, NSelect, NButton, NSpin } from 'naive-ui'
import { isEmpty } from 'lodash'
import { useOptionStore } from '@/stores/option'
import { OptionKeyName, OptionType } from '@/define/enum/option'
let gpt_model_options = ref([])
let loading = ref(false)
let optionStore = useOptionStore()
let message = useMessage()
let dialog = useDialog()
onMounted(async () => {
await window.api.getGptModelOption('all', (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
gpt_model_options.value = value.data
console.log(gpt_model_options.value)
})
})
/**
* 测试GPT链接是不是可以成功
*/
async function testConnection() {
loading.value = true
// 测试链接
await window.api.TestGPTConnection(
JSON.stringify({
gpt_business: optionStore.CW_AISetting.laiapi.gpt_url + '/v1/chat/completions',
gpt_key: optionStore.CW_AISetting.laiapi.api_key,
gpt_model: optionStore.CW_AISetting.laiapi.model
}),
(value) => {
loading.value = false
if (value.code == 0) {
window.api.showGlobalMessageDialog({
code: 0,
message: 'GPT链接失败,错误信息: ' + value.message
})
return
}
window.api.showGlobalMessageDialog({
code: 1,
message: 'gpt链接测试成功,请保存数据后使用!!!'
})
}
)
}
/**
* 同步通用设置中的数据信息
*/
async function syncGeneralSettings() {
// Create confirmation dialog before sync
const syncDialog = dialog.warning({
title: '同步确认',
content: '确认要同步通用设置中的API Key和Model吗?这将覆盖当前设置。',
positiveText: '确认',
negativeText: '取消',
onNegativeClick: () => {
message.info('已取消同步')
return true
},
onPositiveClick: async () => {
syncDialog.destroy()
// Get global settings
let globalSetting = window.config
if (!globalSetting) {
message.error('未找到全局通用设置,请检查通用设置!')
return
}
// Sync settings
optionStore.CW_AISetting.laiapi.api_key = globalSetting.gpt_key
optionStore.CW_AISetting.laiapi.model = globalSetting.gpt_model
window.api.showGlobalMessageDialog({
code: 1,
message: '已同步通用设置,请测试成功后保存后使用!'
})
}
})
}
async function SaveAISetting() {
let da = dialog.warning({
title: '提示',
@@ -110,6 +110,7 @@ export default defineComponent({
* 单句生图
*/
async function SingleGenerateImage() {
message.info("3")
await window.api.OriginalSDImageGenerate([JSON.stringify([row]), true, false], (value) => {
if (value.code == 0) {
message.error(value.message)
@@ -112,6 +112,7 @@ export default defineComponent({
*/
async function SingleGenerateImage() {
// 检查当前数据是不是有被锁定
message.info("5")
if (row.value.imageLock) {
message.error('当前数据已经被锁定,无法生图')
return
@@ -160,6 +160,7 @@ import { v4 as uuidv4 } from 'uuid'
import MJDefine from '@/main/Service/MJ/mjDefine'
import { OptionKeyName, OptionType } from '@/define/enum/option'
import { ValidateJson } from '@/define/Tools/validate'
import { useOptionStore } from '@/stores/option'
export default defineComponent({
components: {
@@ -196,6 +197,7 @@ export default defineComponent({
})
let request_model_options = ref([])
let mj_speed_options = ref([])
let optionStore = useOptionStore()
/**
* 初始化GPT的option
@@ -238,6 +240,7 @@ export default defineComponent({
* 初始化MJ的option
*/
async function InitMjOptions() {
debugger
let mjSettingData = await window.options.GetOptionByKey(OptionKeyName.MJ_GlobalSetting)
if (mjSettingData.code == 0) {
throw new Error('加载MJ设置失败,失败原因如下:' + mjSettingData.message)
@@ -250,6 +253,7 @@ export default defineComponent({
}
mj_globalSetting = JSON.parse(mjSettingData.data.value)
mj_globalSetting = Object.assign(optionStore.MJ_GlobalSetting, mj_globalSetting)
let mjSimpleSetting = mj_globalSetting.mj_simpleSetting
mjSetting.value = Object.assign(mjSetting.value, mjSimpleSetting)
@@ -319,8 +323,6 @@ export default defineComponent({
throw new Error(value.message)
}
})
debugger
// 保存MJ的基础设置
let saveMjRes = await window.options.ModifyOptionByKey(
OptionKeyName.MJ_GlobalSetting,
@@ -66,7 +66,6 @@ async function InitMJSettingData() {
optionStore.MJ_GlobalSetting = JSON.parse(mjRes.data.value)
message.success('加载MJSetting信息成功')
}
await TimeDelay(1000)
} catch (error) {
window.api.showGlobalMessageDialog({
@@ -82,8 +81,6 @@ async function InitMJSettingData() {
* 保存MJ配置
*/
async function SaveMjSetting() {
// 在保存的时候要进行数据保存
debugger
// 通过选择的请求模式判断哪些是要校验的
let request_model = optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel
if (request_model == MJImageType.API_MJ) {
@@ -0,0 +1,250 @@
<template>
<div class="add-comfy-ui-workflow">
<n-form
ref="formRef"
:model="formValue"
:rules="rules"
label-placement="left"
label-width="100"
require-mark-placement="right-hanging"
>
<n-form-item label="名称" path="name">
<n-input v-model:value="formValue.name" placeholder="输入工作流名称" />
</n-form-item>
<n-form-item label="工作流文件" path="jsonFile">
<n-input v-model:value="formValue.workflowPath" placeholder="选择工作流API文件" readonly />
<n-button @click="triggerFileSelect" type="primary" style="margin-left: 20px">
选择文件
</n-button>
</n-form-item>
</n-form>
<div class="button-group">
<n-space justify="end" align="center">
<n-button @click="checkWorkflowFile" :disabled="!jsonContent" secondary>
检查工作流文件
</n-button>
<n-button
type="primary"
@click="saveWorkflow"
:disabled="!formValue.name || !formValue.workflowPath"
>
{{ isEdit ? '更新' : '保存' }}
</n-button>
</n-space>
</div>
<input
type="file"
ref="fileInput"
accept=".json"
style="display: none"
@change="handleFileUpload"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { NForm, NFormItem, NInput, NButton, NSpace, useMessage } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
import { OptionKeyName, OptionType } from '@/define/enum/option'
const fileInput = ref(null)
const jsonContent = ref(null)
const formRef = ref(null)
const message = useMessage()
const optionStore = useOptionStore()
const formValue = ref({
id: null,
name: '',
workflowPath: null
})
const rules = {
name: {
required: true,
message: '请输入工作流名称',
trigger: 'blur'
},
jsonFile: {
required: true,
message: '请选择选择工作流API文件',
trigger: 'change'
}
}
// 接收props - 如果是编辑模式会传入workflowData
const props = defineProps({
workflowData: {
type: Object,
default: null
}
})
const isEdit = ref(false)
// 初始化表单数据
onMounted(() => {
if (props.workflowData) {
isEdit.value = true
formValue.value = {
id: props.workflowData.id,
name: props.workflowData.name,
workflowPath: props.workflowData.workflowPath
}
}
})
async function saveWorkflow() {
try {
// 检查名称是否重复(编辑模式下需要排除自身)
const isDuplicate = optionStore.ComfyUI_WorkFlowSetting.some(
(item) =>
item.name === formValue.value.name && (!isEdit.value || item.id !== formValue.value.id)
)
if (isDuplicate) {
message.error('工作流名称已存在,请重新输入')
return
}
if (isEdit.value) {
// 更新现有工作流
const index = optionStore.ComfyUI_WorkFlowSetting.findIndex(
(item) => item.id === formValue.value.id
)
if (index !== -1) {
optionStore.ComfyUI_WorkFlowSetting[index] = {
id: formValue.value.id,
name: formValue.value.name,
workflowPath: formValue.value.workflowPath
}
}
} else {
// 添加新工作流
optionStore.ComfyUI_WorkFlowSetting.push({
id: crypto.randomUUID(),
name: formValue.value.name,
workflowPath: formValue.value.workflowPath
})
}
// 保存到数据库
let res = await window.options.ModifyOptionByKey(
OptionKeyName.ComfyUI_WorkFlowSetting,
JSON.stringify(optionStore.ComfyUI_WorkFlowSetting),
OptionType.JOSN
)
if (res.code == 1) {
message.success(isEdit.value ? '更新成功' : '保存成功')
// 重置表单
if (!isEdit.value) {
formValue.value = {
name: '',
workflowPath: null,
id: null
}
jsonContent.value = null
}
} else {
message.error((isEdit.value ? '更新' : '保存') + '失败,失败原因:' + res.message)
}
} catch (error) {
message.error((isEdit.value ? '更新' : '保存') + '失败,失败原因:' + error.message)
}
}
const triggerFileSelect = () => {
fileInput.value.click()
}
/**
* 选择并读取json文件
* @param event
*/
const handleFileUpload = (event) => {
const file = event.target.files[0]
if (!file) return
formValue.value.workflowPath = file.path
const reader = new FileReader()
reader.onload = (e) => {
try {
jsonContent.value = JSON.parse(e.target.result)
} catch (error) {
console.error('解析JSON失败:', error)
message.error('无法解析JSON文件,请确保文件格式正确')
jsonContent.value = null
formValue.value.jsonFile = null
}
}
reader.readAsText(file)
}
/**
* 检查工作流文件是不是正确
*/
async function checkWorkflowFile() {
if (formValue.value.workflowPath == null) {
message.error('请先选择工作流文件')
return
}
if (!jsonContent.value) {
message.error('工作流文件内容为空,请选择工作流文件')
return
}
console.log(jsonContent.value)
let hasPositivePrompt = false
let hasNegativePrompt = false
// 检查是不是有正向提示词和反向提示词
let elements = []
// 处理不同格式的ComfyUI工作流
if (Array.isArray(jsonContent.value)) {
message.error('工作流文件的格式不正确,请检查工作流文件')
} else if (typeof jsonContent.value === 'object') {
// ComfyUI工作流通常将节点保存在nodes属性中
if (jsonContent.value.nodes) {
elements = Object.values(jsonContent.value.nodes)
} else {
elements = Object.values(jsonContent.value)
}
}
for (const element of elements) {
if (element && element.class_type === 'CLIPTextEncode') {
if (element._meta?.title === '正向提示词') {
hasPositivePrompt = true
}
if (element._meta?.title === '反向提示词') {
hasNegativePrompt = true
}
}
}
if (!hasPositivePrompt || !hasNegativePrompt) {
message.error(
'工作流文件缺少正向提示词或反向提示词,请检查工作流文件,把对应的文本编码模块的标题改为正向提示词和反向提示词!!'
)
return
} else {
message.success('工作流文件检查成功通过')
}
}
</script>
<style scoped>
.add-comfy-ui-workflow {
padding: 20px;
max-width: 600px;
}
</style>
@@ -0,0 +1,313 @@
<template>
<div class="comfy-ui-setting">
<div class="form-section">
<h3 class="section-title">ComfyUI 基础设置</h3>
<n-form inline :model="optionStore.ComfyUI_SimpleSetting" class="inline-form">
<n-form-item label="请求地址" path="requestUrl">
<n-input
v-model:value="optionStore.ComfyUI_SimpleSetting.requestUrl"
placeholder="输入请求地址"
/>
</n-form-item>
<n-form-item label="当前工作流" path="selectedWorkflow">
<n-select
placeholder="选择工作流"
v-model:value="optionStore.ComfyUI_SimpleSetting.selectedWorkflow"
:options="
optionStore.ComfyUI_WorkFlowSetting.map((workflow) => ({
label: workflow.name,
value: workflow.id
}))
"
style="width: 200px"
/>
</n-form-item>
<n-form-item label="反向提示词" path="negativePrompt">
<n-input
v-model:value="optionStore.ComfyUI_SimpleSetting.negativePrompt"
style="width: 300px"
placeholder="输入反向提示词"
/>
</n-form-item>
<n-form-item>
<n-button type="info" style="margin-left: 8px" @click="SaveComfyUISimpleSetting">
保存设置
</n-button>
</n-form-item>
</n-form>
</div>
<div style="color: red">
<p>注意</p>
<p>1 Comfy UI的工作流中正向提示词和反向提示必须为 <strong>Clip文本编码</strong> 节点</p>
<p>2 标题必须对应 <strong>正向提示词和反向提示词</strong></p>
<p>
3 图像输出节点必须是 <strong>保存图像</strong> 节点<strong
>采样器只支持简单 K采样器和K采样器高级</strong
>
</p>
</div>
<div class="action-bar">
<n-button type="primary" @click="handleAdd">添加</n-button>
</div>
<div ref="tableContainer" class="table-container">
<n-data-table
:columns="columns"
:data="optionStore.ComfyUI_WorkFlowSetting"
:bordered="true"
:single-line="false"
:max-height="tableHeight"
/>
</div>
</div>
</template>
<script setup>
import { ref, h, onMounted, onUnmounted, nextTick } from 'vue'
import {
NDataTable,
NButton,
useDialog,
NForm,
NFormItem,
NInput,
useMessage,
NSelect,
NSpace,
NPopconfirm,
NIcon
} from 'naive-ui'
// 导入图标
import { useSoftwareStore } from '../../../../../stores/software'
import { useOptionStore } from '../../../../../stores/option'
import InitCommon from '../../../common/initCommon'
import AddComfyUIWorkflow from './AddComfyUIWorkflow.vue'
import { OptionKeyName, OptionType } from '@/define/enum/option'
const softwareStore = useSoftwareStore()
const optionStore = useOptionStore()
const dialog = useDialog()
let message = useMessage()
onMounted(async () => {
try {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '加载设置中...'
await InitCommon.InitComfyUISetting()
message.success('加载 ComfyUI 设置成功')
} catch (error) {
message.error('加载设置失败,失败原因:' + error.message)
} finally {
softwareStore.spin.spinning = false
}
// 初始化表格高度并监听窗口大小变化
updateTableHeight()
window.addEventListener('resize', updateTableHeight)
})
onUnmounted(() => {
// 移除事件监听器
window.removeEventListener('resize', updateTableHeight)
})
async function SaveComfyUISimpleSetting() {
try {
// 判断请求地址是不是 http 或者是 https 开头
if (
!optionStore.ComfyUI_SimpleSetting.requestUrl.startsWith('http://') &&
!optionStore.ComfyUI_SimpleSetting.requestUrl.startsWith('https://')
) {
message.error('请求地址必须以 http 或者 https 开头')
return
}
// 判断当前选中的工作流ID是不是在下面的表格数据中存在
if (
!optionStore.ComfyUI_WorkFlowSetting.some(
(workflow) => workflow.id === optionStore.ComfyUI_SimpleSetting.selectedWorkflow
)
) {
message.error('当前选中的工作流不存在,请重新选择')
return
}
// 开始保存
let res = await window.options.ModifyOptionByKey(
OptionKeyName.ComfyUI_SimpleSetting,
JSON.stringify(optionStore.ComfyUI_SimpleSetting),
OptionType.JOSN
)
if (res.code == 1) {
message.success('保存设置成功')
} else {
message.error('保存设置失败,失败原因:' + res.message)
}
} catch (error) {
message.error('保存ComfyUI通用设置失败,失败原因:' + error.message)
}
}
// 表格高度自适应相关
const tableContainer = ref(null)
const tableHeight = ref(300) // 默认高度
const minTableHeight = 200 // 最小高度
// 更新表格高度的函数
const updateTableHeight = () => {
nextTick(() => {
if (!tableContainer.value) return
// 获取容器在视口中的位置信息
const containerRect = tableContainer.value.getBoundingClientRect()
// 考虑容器已经存在的内容(表单和说明等)的高度
const existingContentHeight = containerRect.top
// 获取视口高度
const viewportHeight = window.innerHeight
// 计算表格可用的高度
const bottomPadding = 60 // 底部预留间距
const availableHeight = viewportHeight - existingContentHeight - bottomPadding
// 设置表格高度,不小于最小高度
tableHeight.value = Math.max(availableHeight, minTableHeight)
})
}
// Table columns
const columns = [
{
title: '名称',
key: 'name'
},
{
title: '工作流路径',
key: 'workflowPath'
},
{
title: '操作',
key: 'actions',
render(row) {
return h(
'div',
{},
{
default: () => [
h(
NButton,
{
size: 'small',
type: 'info',
secondary: true,
onClick: () => handleEdit(row)
},
{
default: () => '编辑'
}
),
h(
NButton,
{
size: 'small',
type: 'error',
secondary: true,
onClick: () => handleRemove(row),
style: { marginLeft: '16px' }
},
{
default: () => '删除'
}
)
]
}
)
}
}
]
const handleAdd = () => {
dialog.info({
title: '添加工作流',
showIcon: false,
maskClosable: false,
style: { width: '600px' },
content: () => h(AddComfyUIWorkflow)
})
}
// 编辑工作流
const handleEdit = (row) => {
dialog.info({
title: '编辑工作流',
showIcon: false,
maskClosable: false,
style: { width: '600px' },
content: () => h(AddComfyUIWorkflow, { workflowData: row })
})
}
// 删除工作流
const handleRemove = (row) => {
dialog.warning({
title: '确认删除',
content: `确定要删除工作流"${row.name}"吗?`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
try {
debugger
// 从工作流列表中删除该项
const index = optionStore.ComfyUI_WorkFlowSetting.findIndex((item) => item.id === row.id)
if (index == -1) {
message.error('删除失败: 未找到该工作流')
return
}
optionStore.ComfyUI_WorkFlowSetting.splice(index, 1)
// 如果删除的是当前选中的工作流,清空选择
if (optionStore.ComfyUI_SimpleSetting.selectedWorkflow === row.id) {
optionStore.ComfyUI_SimpleSetting.selectedWorkflow = ''
}
// 保存到数据库
let res = await window.options.ModifyOptionByKey(
OptionKeyName.ComfyUI_WorkFlowSetting,
JSON.stringify(optionStore.ComfyUI_WorkFlowSetting),
OptionType.JOSN
)
if (res.code == 0) {
message.error('删除失败,失败原因:' + res.message)
return
}
res = await window.options.ModifyOptionByKey(
OptionKeyName.ComfyUI_SimpleSetting,
JSON.stringify(optionStore.ComfyUI_SimpleSetting),
OptionType.JOSN
)
if (res.code == 1) {
message.success('删除成功')
} else {
message.error('删除失败,失败原因:' + res.message)
}
} catch (error) {
message.error('删除失败: ' + error.message)
}
}
})
}
</script>
<style scoped>
.description {
margin-bottom: 16px;
font-size: large;
color: red;
}
.action-bar {
margin: 16px 0;
}
.table-container {
width: 100%;
}
</style>
@@ -62,24 +62,18 @@
v-model:value="formValue.denoising_strength"
placeholder="输入重绘幅度"
/>
<n-checkbox
style="margin-left: 30px"
size="large"
v-model:checked="formValue.adetailer"
label="是否开启修脸/修手"
/>
</n-form-item>
</div>
<n-form-item label="采样方式" path="sampler_name">
<n-select
style="width: 250px"
v-model:value="formValue.sampler_name"
:options="samplers_options"
placeholder="选择采样方式"
/>
</n-form-item>
<div style="display: flex">
<n-form-item label="迭代步数" path="steps">
<n-form-item label="采样方式" path="sampler_name">
<n-select
style="width: 250px"
v-model:value="formValue.sampler_name"
:options="samplers_options"
placeholder="选择采样方式"
/>
</n-form-item>
<n-form-item label="迭代步数" path="steps" style="margin-left: 10px">
<n-input-number
style="width: 150px"
v-model:value="formValue.steps"
@@ -98,19 +92,37 @@
/>
</n-form-item>
</div>
<n-form-item label="图片分辨率" path="resolution">
<n-input-number
:show-button="false"
v-model:value="formValue.width"
placeholder="输入迭代步数"
/>
<span style="margin: 0 10px"> * </span>
<n-input-number
:show-button="false"
v-model:value="formValue.height"
placeholder="输入迭代步数"
/>
</n-form-item>
<div style="display: flex">
<n-form-item label="图片分辨率" path="resolution">
<n-input-number
:show-button="false"
v-model:value="formValue.width"
placeholder="宽"
style="width: 100px"
/>
<span style="margin: 0 10px"> * </span>
<n-input-number
:show-button="false"
v-model:value="formValue.height"
placeholder="高"
style="width: 100px"
/>
</n-form-item>
<n-form-item>
<n-checkbox
style="margin-left: 30px"
size="large"
v-model:checked="formValue.adetailer"
label="是否开启修脸/修手"
/>
<n-button style="margin-left: 10px" type="primary" @click="AdetailerSetting"
>修手/脸设置</n-button
>
</n-form-item>
</div>
<div style="display: flex">
<n-form-item
style="width: 300px"
@@ -151,14 +163,14 @@
</n-form-item>
</n-form>
</n-tab-pane>
<n-tab-pane name="ADetailer setting" tab="ADetailer 设置">
<SDADetailerSetting />
<n-tab-pane name="ComfyUI API Setting" tab="ComfyUI 设置">
<ComfyUISetting />
</n-tab-pane>
</n-tabs>
</template>
<script setup>
import { defineComponent, ref, h, onMounted, toRaw } from 'vue'
import { ref, h, onMounted, toRaw } from 'vue'
import {
NForm,
NFormItem,
@@ -169,10 +181,13 @@ import {
NSelect,
NTabs,
NTabPane,
NCheckbox
NCheckbox,
useDialog
} from 'naive-ui'
import SDADetailerSetting from '../Components/SDADetailerSetting.vue'
import InitCommon from '../../common/initCommon'
import SDADetailerSetting from './SDADetailerSetting.vue'
import InitCommon from '../../../common/initCommon'
import ComfyUISetting from './ComfyUISetting.vue'
let dialog = useDialog()
let flux_model_options = ref([])
@@ -332,4 +347,14 @@ async function LoadSDServiceData() {
message.success('加载成功')
})
}
async function AdetailerSetting() {
dialog.info({
title: 'ADetailer模型设置',
showIcon: false,
maskClosable: false,
style: { width: '600px' },
content: () => h(SDADetailerSetting)
})
}
</script>
+1 -1
View File
@@ -61,7 +61,7 @@ const routes = [
{
path: '/sd_setting',
name: 'sd_setting',
component: () => import('./components/Setting/SDSetting.vue')
component: () => import('./components/Setting/SDSetting/SDSetting.vue')
},
{
path: '/copywriting',