LaiTool V3.1.1.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n-spin style="z-index: 1000 !important;" :show="softwareStore.spin.spinning">
|
||||
<n-spin style="z-index: 1000 !important" :show="softwareStore.spin.spinning">
|
||||
<template #description> {{ softwareStore.spin.tip }} </template>
|
||||
<n-config-provider
|
||||
:hljs="hljs"
|
||||
@@ -47,6 +47,7 @@ export default defineComponent({
|
||||
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) {
|
||||
@@ -58,7 +59,7 @@ export default defineComponent({
|
||||
if (software.data.length == 0) {
|
||||
throw new Error('初始化信息错误: 未获取到数据')
|
||||
}
|
||||
softwareStore.softWare = software.data[0]
|
||||
softwareStore.softWare = software.data
|
||||
await window.darkMode.toggle(softwareStore.softWare.theme)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
<template>
|
||||
<div v-if="data.subValue && data.subValue.length != 0">
|
||||
<div v-for="(item, index) in data.subValue">
|
||||
<n-input
|
||||
v-model:value="item.srt_value"
|
||||
type="textarea"
|
||||
size="tiny"
|
||||
:autosize="{ minRows: 1, maxRows: 5 }"
|
||||
@input="InputDebounced"
|
||||
>
|
||||
</n-input>
|
||||
<div style="height: 135px; width : 100%;overflow: auto; display: flex; margin: auto">
|
||||
<div v-if="data.subValue && data.subValue.length != 0" style="width : 100%">
|
||||
<div v-for="(item, index) in data.subValue" style="width : 100%">
|
||||
<n-input
|
||||
v-model:value="item.srt_value"
|
||||
type="textarea"
|
||||
style="width : 100%"
|
||||
size="tiny"
|
||||
:autosize="{ minRows: 1, maxRows: 5 }"
|
||||
@input="InputDebounced"
|
||||
>
|
||||
</n-input>
|
||||
</div>
|
||||
</div>
|
||||
<n-input
|
||||
v-else
|
||||
v-model:value="data.afterGpt"
|
||||
type="textarea"
|
||||
size="tiny"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
@input="InputDebounced"
|
||||
>
|
||||
</n-input>
|
||||
</div>
|
||||
<n-input
|
||||
v-else
|
||||
v-model:value="data.afterGpt"
|
||||
type="textarea"
|
||||
size="tiny"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
@input="InputDebounced"
|
||||
>
|
||||
</n-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="height: 22px; display: flex">
|
||||
<div v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'" style="z-index: 100">
|
||||
进度:{{ data.mjMessage?.progress }}%
|
||||
</div>
|
||||
<n-tag
|
||||
v-if="data.mjMessage?.status != 'error'"
|
||||
type="success"
|
||||
style="height: 18px; margin: 2px 0 2px 5px"
|
||||
>{{
|
||||
data.mjMessage?.status != null && data.mjMessage?.status != ''
|
||||
? data.mjMessage?.status
|
||||
: 'wait'
|
||||
}}</n-tag
|
||||
>
|
||||
<div v-else style="margin-left: 5px">
|
||||
<n-popover style="padding: 0; margin: 0" trigger="hover">
|
||||
<template #trigger>
|
||||
<n-tag style="height: 18px; margin: 2px 0 2px 0" type="error">error</n-tag>
|
||||
</template>
|
||||
<span>{{ data.mjMessage?.message }}</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
<n-popover
|
||||
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"
|
||||
style="padding: 0; margin: 0"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #trigger>
|
||||
<div
|
||||
style="
|
||||
margin-left: 5px;
|
||||
width: 100px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
{{ data.mjMessage?.messageId }}
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ data.mjMessage?.messageId }}</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
<div></div>
|
||||
<div
|
||||
style="display: flex; margin-right: 10px; height: auto; min-height: 120px"
|
||||
@dragstart="imageDragStart"
|
||||
@drop="imageDragDrop"
|
||||
@dragover="imageDragOver"
|
||||
>
|
||||
<div style="display: flex; align-items: center; position: relative">
|
||||
<n-image
|
||||
class="g-image-class generate-image-show"
|
||||
:src="data.outImagePath ? data.outImagePath : softwareStore.globalSetting.space_image"
|
||||
fit="cover"
|
||||
:width="120"
|
||||
:height="120"
|
||||
lazy
|
||||
/>
|
||||
<n-button
|
||||
text
|
||||
:type="data.imageLock ? 'error' : 'primary'"
|
||||
style="font-size: 24px; position: absolute; left: -12px; top: -5px"
|
||||
@click="ModifyLock"
|
||||
>
|
||||
<n-icon>
|
||||
<LockClosed v-if="data.imageLock" />
|
||||
<LockOpen v-else />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
<n-button
|
||||
text
|
||||
type="info"
|
||||
style="font-size: 24px; position: absolute; left: 8px; top: -5px"
|
||||
@click="DownloadImage"
|
||||
>
|
||||
<n-icon>
|
||||
<Download />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</div>
|
||||
<div>
|
||||
<n-divider vertical style="height: 100%" />
|
||||
</div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<n-image-group>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
height: 122px;
|
||||
overflow: auto;
|
||||
"
|
||||
>
|
||||
<n-image
|
||||
subImage="true"
|
||||
class="g-image-class"
|
||||
draggable="true"
|
||||
:width="56"
|
||||
style="margin: 1px"
|
||||
v-for="(image, index) in data.subImagePath"
|
||||
:key="image.id"
|
||||
:src="image"
|
||||
alt="图片描述"
|
||||
lazy
|
||||
>
|
||||
</n-image>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useDialog,
|
||||
NImageGroup,
|
||||
NDivider,
|
||||
NIcon,
|
||||
NButton,
|
||||
NPopover,
|
||||
NTag,
|
||||
useMessage
|
||||
} from 'naive-ui'
|
||||
import { LockClosed, LockOpen, Download } from '@vicons/ionicons5'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { useImageStore } from '../../../../../stores/image'
|
||||
let props = defineProps({
|
||||
initData: undefined,
|
||||
index: undefined
|
||||
})
|
||||
let imageStore = useImageStore()
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let data = ref(props.initData)
|
||||
let images = ref(props.initData.subImagePath)
|
||||
let outImagePath = ref(props.initData.outImagePath)
|
||||
let width = ref(null)
|
||||
let dragTarget = null
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let softwareStore = useSoftwareStore()
|
||||
|
||||
// 监听 props.initData.subImagePath 修改
|
||||
watch(
|
||||
() => props.initData.subImagePath,
|
||||
(value) => {
|
||||
images.value = value
|
||||
width.value = 170 / 3
|
||||
setNodeAttribute()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 props.initData.outImagePath 修改
|
||||
watch(
|
||||
() => props.initData.outImagePath,
|
||||
(value) => {
|
||||
outImagePath.value = value + `?t=${new Date().getTime()}`
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 props.initData 修改
|
||||
watch(
|
||||
() => props.initData,
|
||||
(value) => {
|
||||
data.value = value
|
||||
setNodeAttribute()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
setNodeAttribute()
|
||||
outImagePath.value = outImagePath.value + `?t=${new Date().getTime()}`
|
||||
})
|
||||
|
||||
async function setNodeAttribute() {
|
||||
let ele = document.querySelectorAll('.g-image-class')
|
||||
ele.forEach((item) => {
|
||||
item.setAttribute('data-id', data.value.id)
|
||||
Array.from(item.getElementsByTagName('img')).forEach((img) => {
|
||||
img.setAttribute('data-id', data.value.id)
|
||||
})
|
||||
})
|
||||
let mainImage = document.querySelectorAll('.generate-image-show')
|
||||
mainImage.forEach((item) => {
|
||||
item.setAttribute('mainImage', '1')
|
||||
Array.from(item.getElementsByTagName('img')).forEach((img) => {
|
||||
img.setAttribute('mainImage', '1')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 图片拖拽开始事件
|
||||
async function imageDragStart(e) {
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
console.log('move', e.target)
|
||||
dragTarget = e.target
|
||||
imageStore.SetDragTarget(e.target)
|
||||
}
|
||||
|
||||
// 图片拖拽结束事件
|
||||
async function imageDragDrop(e) {
|
||||
console.log('end', e.target, dragTarget)
|
||||
// 判断原始数据是不是为空
|
||||
// if (dragTarget == null) {
|
||||
// message.error('只能拖拽到当前行的图片上')
|
||||
// return
|
||||
// }
|
||||
// 判断结束位置是不是主图片
|
||||
if (e.target.getAttribute('mainImage') != '1') {
|
||||
message.error('图片只能拖拽到主图片上')
|
||||
return
|
||||
}
|
||||
let source_img = imageStore.GetDragTarget.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()
|
||||
})
|
||||
}
|
||||
|
||||
// 图片拖拽进入事件
|
||||
async function imageDragOver(e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
async function ModifyLock() {
|
||||
// 锁定之前,判断是不是有
|
||||
if (!outImagePath.value) {
|
||||
message.error('没有生成图片,不能锁定')
|
||||
return
|
||||
}
|
||||
|
||||
let lock = !data.value.imageLock
|
||||
// 修改数据库数据
|
||||
let res = await window.db.UpdateBookTaskDetailData(data.value.id, {
|
||||
imageLock: lock
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
}
|
||||
message.success('图片锁定/解锁成功')
|
||||
reverseManageStore.selectBookTaskDetail[props.index].imageLock = data.value.imageLock
|
||||
data.value.imageLock = lock
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对应的分镜图片
|
||||
*/
|
||||
async function DownloadImage() {
|
||||
// 目前只能判断是不是有mj_message
|
||||
dialog.warning({
|
||||
title: '下载图片警告',
|
||||
content: '单个图片的下载,只支持 MJ API 生图,当前有图片的话会被覆盖掉,是否继续?',
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
if (data.value.mj_message) {
|
||||
debugger
|
||||
// 加判断
|
||||
// 只要状态不是success 和 error ,其他的都重新获取
|
||||
if (data.value.mj_message.status == 'error') {
|
||||
message.error('失败状态不能采集图片')
|
||||
return
|
||||
}
|
||||
if (isEmpty(data.value.mj_message.message_id)) {
|
||||
message.error('没有消息ID,不能采集图片')
|
||||
return
|
||||
}
|
||||
window.mj.GetGeneratedMJImageAndSplit(JSON.stringify([toRaw(data.value)]), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i]
|
||||
// 修改对应的数据
|
||||
data.value.outImagePath =
|
||||
'file://' +
|
||||
element.outImagePath.replaceAll('\\', '/') +
|
||||
'?time=' +
|
||||
new Date().getTime()
|
||||
|
||||
data.value.subImagePath = element.subImagePath.map(
|
||||
(item) => 'file://' + item.replaceAll('\\', '/') + '?time=' + new Date().getTime()
|
||||
)
|
||||
data.value.mj_message.image_click = element.result
|
||||
data.value.mj_message.image_path = element.image_path
|
||||
data.value.mj_message.progress = 100
|
||||
data.value.mj_message.status = 'success'
|
||||
}
|
||||
})
|
||||
} else if (data.value.mjMessage) {
|
||||
message.error('暂时不支持')
|
||||
return
|
||||
} else {
|
||||
message.error('没有生图数据')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -159,11 +159,12 @@ export default defineComponent({
|
||||
|
||||
// 处理批次的翻译
|
||||
function TranslateBatchParams(from, to) {
|
||||
debugger
|
||||
let translateData = []
|
||||
for (let i = index.value; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
// 这边判断GPT提示词是不是存在,存在的话翻译GPT提示词
|
||||
if (!isEmpty(data.value.gptPrompt)) {
|
||||
if (!isEmpty(element.gptPrompt)) {
|
||||
translateData.push({
|
||||
text: element.gptPrompt,
|
||||
from: from,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<span>出图</span>
|
||||
<n-select
|
||||
style="width: 100px; margin-left: 5px"
|
||||
size="tiny"
|
||||
:options="image_options"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
v-model:value="reverseManageStore.selectBookTask.imageCategory"
|
||||
placeholder="选择生图模式"
|
||||
></n-select>
|
||||
<n-button
|
||||
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"
|
||||
color="#7c461e"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MJGetImage"
|
||||
>MJ采集图片</n-button
|
||||
>
|
||||
<n-button
|
||||
v-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"
|
||||
color="#e18a3b"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="OneSplitFour"
|
||||
>1拆4</n-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { NSelect, NButton, useMessage } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
let softwareStore = useSoftwareStore()
|
||||
let message = useMessage()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let image_options = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取生图下拉列表的方式
|
||||
await window.api.GetImageGenerateCategory((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
image_options.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
async function UpdateImageGenerateCategory(value, options) {
|
||||
// 调用下保存
|
||||
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
|
||||
imageCategory: value
|
||||
})
|
||||
window.api.showGlobalMessage(res)
|
||||
}
|
||||
</script>
|
||||
@@ -95,7 +95,6 @@ export default defineComponent({
|
||||
|
||||
// 选择风格
|
||||
async function SelectStyle() {
|
||||
debugger
|
||||
// 先获取风格
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<div style="display: flex; align-items: center">
|
||||
<n-button color="#b6a014" size="small" @click="formateWrite">一键格式化</n-button>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button quaternary circle size="tiny" color="#b6a014" @click="AddSplitChar">
|
||||
<template #icon>
|
||||
<n-icon size="25"> <AddCircleOutline /> </n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>添加分割标识符</span>
|
||||
</n-popover>
|
||||
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-input-number
|
||||
style="margin-left: 10px; width: 100px"
|
||||
size="small"
|
||||
placeholder="输入合并的行数"
|
||||
:max="10000"
|
||||
:min="1"
|
||||
@update:value="mergeCountChange"
|
||||
:show-button="false"
|
||||
v-model:value="write_setting.merge_count"
|
||||
></n-input-number>
|
||||
</template>
|
||||
<span>设置将多少行进行合并</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-input
|
||||
style="margin-left: 10px; width: 80px"
|
||||
size="small"
|
||||
placeholder="输入中间拼接的字符"
|
||||
@update:value="mergeCountChange"
|
||||
:show-button="false"
|
||||
v-model:value="write_setting.merge_char"
|
||||
></n-input>
|
||||
</template>
|
||||
<span>设置中间拼接的字符</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-input
|
||||
style="margin-left: 10px; width: 80px"
|
||||
size="small"
|
||||
placeholder="输入结尾拼接的字符"
|
||||
@update:value="mergeCountChange"
|
||||
:show-button="false"
|
||||
v-model:value="write_setting.end_char"
|
||||
></n-input>
|
||||
</template>
|
||||
<span>设置结尾拼接的字符</span>
|
||||
</n-popover>
|
||||
|
||||
<n-button color="#80a492" size="small" style="margin-left: 10px" @click="mergeWord"
|
||||
>合并</n-button
|
||||
>
|
||||
<n-button color="#f3a694" size="small" style="margin-left: 10px" @click="CopyWord"
|
||||
>一键复制内容</n-button
|
||||
>
|
||||
</div>
|
||||
<n-input
|
||||
style="margin-top: 5px"
|
||||
v-model:value="word"
|
||||
:autosize="{ minRows: 30, maxRows: 30 }"
|
||||
type="textarea"
|
||||
placeholder="输入分镜文案"
|
||||
@update:value="ChangeWordInput"
|
||||
/>
|
||||
<div style="margin-top: 5px; display: flex">
|
||||
<div style="flex: 1">共 {{ rowCount }} 行分镜(当前数量不是最后数量,最后会删除空行)</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NIcon,
|
||||
NPopover,
|
||||
NInputNumber
|
||||
} from 'naive-ui'
|
||||
import { AddCircleOutline } from '@vicons/ionicons5'
|
||||
import InputDialogContent from '../../../Original/Components/InputDialogContent.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NIcon,
|
||||
AddCircleOutline,
|
||||
NPopover
|
||||
},
|
||||
props: ['initData'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let rowCount = ref(0)
|
||||
let data = ref(props.initData)
|
||||
let word = ref(null)
|
||||
let word_arr = ref([])
|
||||
let split_ref = ref(null)
|
||||
|
||||
let write_setting = ref({
|
||||
split_char: '。,“”‘’!?【】《》()…—:;.,\'\'""!?[]<>()...-:;',
|
||||
merge_count: 3,
|
||||
merge_char: ',',
|
||||
end_char: '。'
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// 拼接word
|
||||
let tmp_arr = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
tmp_arr.push(item.after_gpt)
|
||||
}
|
||||
word.value = tmp_arr.join('\n')
|
||||
ChangeWordInput()
|
||||
|
||||
// 加载初始配置数据
|
||||
await window.api.GetDefineConfigJsonByProperty(
|
||||
JSON.stringify(['clip_setting', 'write_setting', false, null]),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
if (value.data) {
|
||||
write_setting.value = value.data
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* 修改数据框时触发的事件
|
||||
*/
|
||||
async function ChangeWordInput() {
|
||||
let word_arr = word.value.split('\n')
|
||||
// 先判断是不是有空行
|
||||
if (rowCount.value == 1 && word_arr[0] == '') {
|
||||
rowCount.value = 0
|
||||
} else {
|
||||
rowCount.value = word_arr.length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加分割符号
|
||||
*/
|
||||
async function AddSplitChar() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加分割符',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: split_ref,
|
||||
initData: write_setting.value.split_char,
|
||||
placeholder: '请输入分割符'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
write_setting.value.split_char = split_ref.value.data
|
||||
// 保存数据
|
||||
await window.api.SaveDefineConfigJsonByProperty(
|
||||
JSON.stringify(['clip_setting', 'write_setting', toRaw(write_setting.value), false]),
|
||||
(value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新input-number事件
|
||||
*/
|
||||
let timer = null
|
||||
async function mergeCountChange(value) {
|
||||
// 添加一个防抖函数,防止修改次数太多导致卡顿
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(async () => {
|
||||
// 执行你的逻辑代码
|
||||
//
|
||||
await window.api.SaveDefineConfigJsonByProperty(
|
||||
JSON.stringify(['clip_setting', 'write_setting', toRaw(write_setting.value), false]),
|
||||
(value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
}
|
||||
)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键格式化
|
||||
*/
|
||||
async function formateWrite() {
|
||||
let split_arr = Array.from(write_setting.value.split_char)
|
||||
split_arr.forEach((item) => {
|
||||
let specialCharacters = [
|
||||
'.',
|
||||
'*',
|
||||
'?',
|
||||
'+',
|
||||
'^',
|
||||
'$',
|
||||
'[',
|
||||
']',
|
||||
'(',
|
||||
')',
|
||||
'{',
|
||||
'}',
|
||||
'|',
|
||||
'\\'
|
||||
]
|
||||
let regex
|
||||
|
||||
if (specialCharacters.includes(item)) {
|
||||
regex = new RegExp('\\' + item, 'g')
|
||||
} else {
|
||||
regex = new RegExp(item, 'g')
|
||||
}
|
||||
word.value = word.value.replace(regex, '\n')
|
||||
})
|
||||
// 删除空行
|
||||
let word_arr = word.value.split('\n')
|
||||
word_arr = word_arr.filter((item) => item != '' && item != null)
|
||||
word.value = word_arr.join('\n')
|
||||
ChangeWordInput()
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数据
|
||||
*/
|
||||
async function mergeWord() {
|
||||
let step = write_setting.value.merge_count ? write_setting.value.merge_count : 3
|
||||
let tmp_arr = []
|
||||
let word_arr = word.value.split('\n')
|
||||
for (let i = 0; i < word_arr.length; i += step) {
|
||||
let group = word_arr.slice(i, i + step)
|
||||
tmp_arr.push(group.join(write_setting.value.merge_char) + write_setting.value.end_char)
|
||||
}
|
||||
word.value = tmp_arr.join('\n')
|
||||
ChangeWordInput()
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制数据到剪贴板
|
||||
*/
|
||||
async function CopyWord() {
|
||||
// 获取当前的所有的word
|
||||
try {
|
||||
await navigator.clipboard.writeText(word.value)
|
||||
message.success('复制成功')
|
||||
} catch (error) {
|
||||
message.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
rowCount,
|
||||
ChangeWordInput,
|
||||
word,
|
||||
word_arr,
|
||||
AddSplitChar,
|
||||
split_ref,
|
||||
write_setting,
|
||||
mergeCountChange,
|
||||
formateWrite,
|
||||
mergeWord,
|
||||
CopyWord
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,718 @@
|
||||
<template>
|
||||
<div id="import_word_and_srt">
|
||||
<div style="height: 30px; margin-bottom: 5px">
|
||||
<n-button
|
||||
v-if="type == 'original' || type == 'new_original'"
|
||||
color="#1e90ff"
|
||||
size="small"
|
||||
style="margin-left: 20px"
|
||||
@click="OpenOneDialog"
|
||||
>导入字幕</n-button
|
||||
>
|
||||
<n-button color="#1e90ff" size="small" style="margin-left: 20px" @click="ImportSrtAndGetTime"
|
||||
>导入srt</n-button
|
||||
>
|
||||
<n-button
|
||||
color="#1e90ff"
|
||||
size="small"
|
||||
style="margin-left: 20px"
|
||||
@click="SaveCopywritingInformation"
|
||||
>保存文案信息</n-button
|
||||
>
|
||||
</div>
|
||||
<n-data-table
|
||||
:max-height="maxHeight"
|
||||
:row-key="rowKey"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h, defineComponent, onMounted, ref, toRaw, watch, onUnmounted } from 'vue'
|
||||
import { NButton, NDataTable, useMessage, NInput, NImage, useDialog } from 'naive-ui'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import EditWord from './EditWord.vue'
|
||||
import { checkStringValueDeleteSuffix } from '../../../../../../main/Public/generalTools'
|
||||
import { useReverseManageStore } from '../../../../../../stores/reverseManage'
|
||||
import InputDialogContent from '../../../Original/Components/InputDialogContent.vue'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { OperateBookType } from '../../../../../../define/enum/bookEnum'
|
||||
const buttonArr = [
|
||||
{
|
||||
title: '下对齐',
|
||||
key: 'AlignNextWord',
|
||||
color: '#8a2be0'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NDataTable,
|
||||
NInput,
|
||||
NImage
|
||||
},
|
||||
props: ['initData', 'menuType', 'height', 'type'],
|
||||
setup(props) {
|
||||
let hh = props.height
|
||||
let maxHeight = props.height - 80
|
||||
debugger
|
||||
let data = ref(JSON.parse(JSON.stringify(props.initData)))
|
||||
const message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let selectKey = ref([])
|
||||
let editWordRef = ref(null)
|
||||
let wenkeRef = ref(null)
|
||||
let type = ref(props.type ? props.type : 'original')
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
|
||||
const createColumns = ({ AlignNextWord }) => {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
disabled(row) {
|
||||
return row.name === 'Edward King 3'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '编号',
|
||||
key: 'no',
|
||||
width: 70
|
||||
},
|
||||
{
|
||||
title: '分镜文案',
|
||||
key: 'after_gpt',
|
||||
width: 300,
|
||||
render(row, index) {
|
||||
return h(NInput, {
|
||||
value: row.word,
|
||||
type: 'textarea',
|
||||
autosize: true,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].word = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '字幕',
|
||||
key: 'srt',
|
||||
width: '300',
|
||||
render(row) {
|
||||
let tmp = row.subValue.map((item) =>
|
||||
h('div', { style: 'display:flex; height : auto; margin: 2px 0' }, [
|
||||
h(NInput, {
|
||||
size: 'tiny',
|
||||
value: item.srt_value
|
||||
}),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: 'margin : 0 2px',
|
||||
size: 'tiny',
|
||||
color: '#ecd452',
|
||||
onClick: () => Upper(row, item, true)
|
||||
},
|
||||
{ default: () => '向上' }
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: 'margin : 0 2px',
|
||||
size: 'tiny',
|
||||
color: '#ee7959',
|
||||
onClick: () => Down(row, item)
|
||||
},
|
||||
{ default: () => '向下' }
|
||||
)
|
||||
])
|
||||
)
|
||||
return tmp
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'options',
|
||||
width: 130,
|
||||
render(row) {
|
||||
const btn = buttonArr.map((item) => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
style: 'margin : 2px',
|
||||
strong: true,
|
||||
size: 'tiny',
|
||||
color: item.color,
|
||||
onClick: () => {
|
||||
if (item.key == 'AlignNextWord') {
|
||||
AlignNextWord(row)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
})
|
||||
return btn
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '时间范围',
|
||||
key: 'timeLimit',
|
||||
width: 120
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 设置窗体的高度
|
||||
function setHeight() {
|
||||
let div = document.getElementById('import_word_and_srt')
|
||||
div.style.height = hh + 'px'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
setHeight()
|
||||
|
||||
// 监听 ctrl + alt + w 键盘的按键事件
|
||||
window.addEventListener('keydown', ImportWenKeSrt)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keydown', ImportWenKeSrt)
|
||||
})
|
||||
|
||||
/**
|
||||
* 导入文刻软件里面的srt
|
||||
*/
|
||||
async function ImportWenKeSrt(e) {
|
||||
if (!(e.ctrlKey && e.altKey && e.key == 'k')) {
|
||||
return
|
||||
}
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
name: 'importWenkeWord',
|
||||
title: '导入外部对齐文件',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: wenkeRef,
|
||||
initData: null,
|
||||
placeholder: '请导入外部对齐文件'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
debugger
|
||||
console.log(wenkeRef.value.word)
|
||||
let word = wenkeRef.value.data
|
||||
if (word == null || word == '') {
|
||||
message.error('数据为空')
|
||||
return
|
||||
}
|
||||
word = JSON.parse(word)
|
||||
// 将文案数据写入到本地
|
||||
data.value = []
|
||||
let word_arr = word.rows
|
||||
let lastId = ''
|
||||
let end_word = []
|
||||
for (let i = 0; i < word_arr.length; i++) {
|
||||
const element = word_arr[i]
|
||||
// 判断下面的text是不是存在,存在的话,获取数据然后记录事件
|
||||
let row_text = ''
|
||||
let start_time = 0
|
||||
let end_time = 0
|
||||
let subValue = []
|
||||
if (element.texts) {
|
||||
for (let j = 0; j < element.texts.length; j++) {
|
||||
const text = element.texts[j]
|
||||
row_text += text.text + ','
|
||||
if (j == 0) {
|
||||
start_time = text.start
|
||||
}
|
||||
if (j == element.texts.length - 1) {
|
||||
end_time = text.start + text.duration
|
||||
}
|
||||
subValue.push({
|
||||
id: uuidv4(),
|
||||
srt_value: text.text,
|
||||
start_time: text.start / 1000,
|
||||
end_time: text.start / 1000 + text.duration / 1000
|
||||
})
|
||||
}
|
||||
}
|
||||
let id = uuidv4()
|
||||
lastId = id
|
||||
|
||||
data.value.push({
|
||||
no: i + 1,
|
||||
id: id,
|
||||
lastId: lastId,
|
||||
word: checkStringValueDeleteSuffix(row_text, ','),
|
||||
after_gpt: checkStringValueDeleteSuffix(row_text, ','),
|
||||
start_time: start_time / 1000,
|
||||
end_time: end_time / 1000,
|
||||
timeLimit: `${start_time / 1000} -- ${end_time / 1000}`,
|
||||
subValue: subValue
|
||||
})
|
||||
end_word.push(checkStringValueDeleteSuffix(row_text, ','))
|
||||
}
|
||||
|
||||
// 将文案数据写入到本地
|
||||
let local_word = end_word.filter((item) => item != '' && item != null)
|
||||
await window.api.saveWordTxt(toRaw(local_word), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('文案写入失败' + value.message)
|
||||
return
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算指定索引值的时间
|
||||
* @param {int} index
|
||||
*/
|
||||
function modifyTime(index) {
|
||||
// 判断下一个行或者是上一行是不是存在,存在才会计算时间
|
||||
if (data.value[index]) {
|
||||
let s_v = data.value[index].subValue
|
||||
let len = s_v.length
|
||||
if (len > 0) {
|
||||
let s_t = s_v[0].start_time
|
||||
let e_t = s_v[len - 1].end_time
|
||||
data.value[index].start_time = s_t
|
||||
data.value[index].end_time = e_t
|
||||
data.value[index].timeLimit = `${s_t} -- ${e_t}`
|
||||
} else {
|
||||
data.value[index].start_time = null
|
||||
data.value[index].end_time = null
|
||||
data.value[index].timeLimit = ``
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前行的子数据的长度是不是大于0。不大于0 的话下面的字幕往上替补
|
||||
* @param {data value 的索引} index
|
||||
*/
|
||||
function moveUp(index) {
|
||||
let s_v = data.value[index].subValue
|
||||
if (s_v.length == 0) {
|
||||
// 将下面所有的字幕数据往上移动
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
// const element = data.value[i];
|
||||
if (data.value[i + 1] != null) {
|
||||
data.value[i].subValue = data.value[i + 1].subValue
|
||||
} else {
|
||||
// 检查当前这个是不是为空。为空直接删除
|
||||
// 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除)
|
||||
if (
|
||||
data.value[i].word == null ||
|
||||
data.value[i].word == '' ||
|
||||
data.value[i].after_gpt == null ||
|
||||
data.value[i].after_gpt == ''
|
||||
) {
|
||||
// 直接删除
|
||||
data.value.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字幕文件向上
|
||||
*/
|
||||
async function Upper(row, item, isM) {
|
||||
// 向上合并。
|
||||
// 当前item。向上的话。判断是不是第一个。是第一个的话。直接网上一个合并。
|
||||
// 不是第一个的话。将上面的所有的合并。
|
||||
// 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。
|
||||
// 向下合并。逻辑相同。
|
||||
if (row.lastId == null || row.lastId == '') {
|
||||
message.error('当前是第一个。不能向上合并')
|
||||
return
|
||||
}
|
||||
//判断当前行是不是第一个
|
||||
let itemId = item.id
|
||||
let itemIndex = row.subValue.findIndex((item) => item.id == itemId)
|
||||
let thisIndex = data.value.findIndex((item) => item.id == row.id)
|
||||
if (itemIndex < 0 && isM) {
|
||||
message.error('数据错误')
|
||||
return
|
||||
} else {
|
||||
// 先获取上一行的数据
|
||||
let lastRowIndex = data.value.findIndex((item) => item.id == row.lastId)
|
||||
//将当前及之前的数据向上合并
|
||||
for (let i = 0; i <= itemIndex; i++) {
|
||||
const element = row.subValue[i]
|
||||
data.value[lastRowIndex].subValue.push(element)
|
||||
}
|
||||
|
||||
// 删除指定的数据
|
||||
data.value[lastRowIndex + 1].subValue.splice(0, itemIndex + 1)
|
||||
moveUp(thisIndex)
|
||||
|
||||
// 判断当前行的文案和洗稿后文件是不是还有。(没有的话直接删除)
|
||||
if (
|
||||
data.value[thisIndex] &&
|
||||
data.value[thisIndex].subValue == null &&
|
||||
(row.word == null || row.word == '' || row.after_gpt == null || row.after_gpt == '')
|
||||
) {
|
||||
// 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId)
|
||||
if (data.value[thisIndex + 1] != null) {
|
||||
data.value[thisIndex + 1].lastId = row.lastId
|
||||
}
|
||||
// 直接删除
|
||||
data.value.splice(thisIndex, 1)
|
||||
// 调整下面的所有的编号
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
data.value[i].no = data.value[i].no - 1
|
||||
}
|
||||
}
|
||||
// 合并之后重新计算时间(计算上一个和当前的)
|
||||
modifyTime(lastRowIndex)
|
||||
modifyTime(thisIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字幕文件向下
|
||||
*/
|
||||
async function Down(row, item) {
|
||||
// 向下合并。逻辑相同。
|
||||
// 当前item。。判断是不是最后一个。是最后一个的话。直接向下合并。
|
||||
// 不是第一个的话。将下面的所有的合并。
|
||||
// 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。
|
||||
//判断当前行是不是第一个
|
||||
let itemId = item.id
|
||||
let itemIndex = row.subValue.findIndex((item) => item.id == itemId)
|
||||
let thisIndex = data.value.findIndex((item) => item.id == row.id)
|
||||
// 判断下一个是不是存在
|
||||
if (data.value[thisIndex + 1] == null) {
|
||||
message.error('当前已经是最后一个。不能向下合并')
|
||||
return
|
||||
}
|
||||
|
||||
if (itemIndex < 0) {
|
||||
message.error('数据错误')
|
||||
return
|
||||
} else {
|
||||
// 获取下一行数据
|
||||
let nextRowIndex = data.value.findIndex((item) => item.lastId == row.id)
|
||||
|
||||
//将当前以及下面的所有进行合并
|
||||
for (let i = row.subValue.length - 1; i >= itemIndex; i--) {
|
||||
const element = row.subValue[i]
|
||||
data.value[nextRowIndex].subValue.unshift(element)
|
||||
}
|
||||
|
||||
// 删除指定的数据
|
||||
data.value[thisIndex].subValue.splice(itemIndex)
|
||||
|
||||
// 判断该当前字幕是不是为空(为空的话。下面的往上移动)
|
||||
moveUp(thisIndex)
|
||||
|
||||
// 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除)
|
||||
if (
|
||||
data.value[thisIndex].subValue == null &&
|
||||
(row.word == null || row.word == '' || row.after_gpt == null || row.after_gpt == '')
|
||||
) {
|
||||
// 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId)
|
||||
if (data.value[nextRowIndex] != null) {
|
||||
data.value[nextRowIndex].lastId = row.lastId
|
||||
}
|
||||
// 直接删除
|
||||
data.value.splice(thisIndex, 1)
|
||||
// 调整下面的所有的编号
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
data.value[i].no = data.value[i].no - 1
|
||||
}
|
||||
}
|
||||
// 合并之后重新计算时间(计算上一个和当前的)
|
||||
modifyTime(nextRowIndex)
|
||||
modifyTime(thisIndex)
|
||||
}
|
||||
}
|
||||
|
||||
function removePunctuationIncludingEllipsis(sentence) {
|
||||
// 扩展正则表达式以包含中文标点符号和省略号
|
||||
// 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符
|
||||
const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g
|
||||
|
||||
// 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串
|
||||
return sentence.replace(punctuationRegExp, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 对齐当前列即下面的数据
|
||||
*/
|
||||
async function AlignNextWord(row) {
|
||||
// 判断是不是已经到到导入过字幕了
|
||||
dialog.create({
|
||||
type: 'warning',
|
||||
title: '警告',
|
||||
showIcon: true,
|
||||
content: '会自动对齐下面的数据。没有对齐成功就要手动调整在自动对齐',
|
||||
style: `width : 400px;`,
|
||||
maskClosable: false,
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
// 下对齐
|
||||
let itemId = row.id
|
||||
let thisIndex = data.value.findIndex((item) => item.id == itemId)
|
||||
if (thisIndex < 0) {
|
||||
message.error('数据错误')
|
||||
return
|
||||
}
|
||||
|
||||
let nextSrt = []
|
||||
// 先将当前行下面的所有的数据拿出来
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
nextSrt = nextSrt.concat(element.subValue)
|
||||
}
|
||||
let init_num = thisIndex
|
||||
// let data
|
||||
let tmp_str = ''
|
||||
for (let i = 0; i < nextSrt.length; ) {
|
||||
const element = nextSrt[i]
|
||||
let current_text = `第 ${thisIndex + 1} 数据。字幕:${
|
||||
element.srt_value
|
||||
}与文案不匹配。亲检查上下文。`
|
||||
tmp_str += element.srt_value
|
||||
|
||||
if (
|
||||
removePunctuationIncludingEllipsis(data.value[thisIndex].after_gpt).startsWith(
|
||||
removePunctuationIncludingEllipsis(tmp_str)
|
||||
)
|
||||
) {
|
||||
// data.value[thisIndex].subValue.push(element);
|
||||
if (init_num < thisIndex || i > 0) {
|
||||
Upper(data.value[thisIndex + 1], element, false)
|
||||
}
|
||||
i++
|
||||
} else {
|
||||
if (
|
||||
removePunctuationIncludingEllipsis(data.value[thisIndex + 1].after_gpt).startsWith(
|
||||
removePunctuationIncludingEllipsis(element.srt_value)
|
||||
)
|
||||
) {
|
||||
thisIndex++
|
||||
tmp_str = ''
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
message: current_text
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存字幕文案信息,用于自动生成视频
|
||||
*/
|
||||
async function SaveCopywritingInformation() {
|
||||
if (type.value == 'original') {
|
||||
if (props.menuType == 'mj') {
|
||||
await window.mj.SvaeMJWordSrt([toRaw(data.value), 'srt_time_information'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
})
|
||||
return
|
||||
} else {
|
||||
// 先保存单个文件(然后在config文件中指向对应的文件)
|
||||
await window.api.OriginalAddWebuiJson(JSON.stringify(data.value), async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
if (value.data) {
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
let name = String(data.value[i].no).padStart(5, '0')
|
||||
data.value[i].prompt_json = value.data + '/' + name + '.png.json'
|
||||
data.value[i].name = name + '.png'
|
||||
}
|
||||
}
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[toRaw(data.value), 'srt_time_information'],
|
||||
(value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
} else if (type.value == 'mj_reverse' || type.value == 'sd_reverse') {
|
||||
debugger
|
||||
if (data.value.length != reverseManageStore.selectBookTaskDetail.length) {
|
||||
message.error('检测到导入的字幕数据和分镜对不上,请先对齐')
|
||||
return
|
||||
}
|
||||
// 将对齐后的数据写入到数据库中
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
let res = await window.db.UpdateBookTaskDetailData(element.id, {
|
||||
startTime: element.start_time,
|
||||
endTime: element.end_time,
|
||||
timeLimit: element.timeLimit,
|
||||
subValue: JSON.stringify(element.subValue)
|
||||
})
|
||||
}
|
||||
} else if (type.value == 'new_original') {
|
||||
// 判断是不是没有对齐(就是所有的数据 文案 时间 srt 都要有,不然就报错)
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
if (
|
||||
isEmpty(element.word) ||
|
||||
isEmpty(element.after_gpt) ||
|
||||
!element.hasOwnProperty('start_time') ||
|
||||
!element.hasOwnProperty('end_time') ||
|
||||
!element.hasOwnProperty('subValue') ||
|
||||
element.subValue.length == 0
|
||||
) {
|
||||
message.error("保存数据失败,数据不完整,请检查'文案'、'时间'、'字幕' 等,数据是否完整")
|
||||
return
|
||||
}
|
||||
}
|
||||
// 开始保存数据(这个要全部保存。会提前判断是不是又分镜信息存在,存在的话就直修改时间,但是需要判断分镜数量是不是对的,不存在的话就会新建分镜数据)后端判断
|
||||
let res = await window.book.SaveCopywriting(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
toRaw(data.value),
|
||||
OperateBookType.BOOKTASK
|
||||
)
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
} else {
|
||||
message.error('未知的小说类型,请检查')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入字幕并进行分镜输出,显示时间,将分镜给他传进去
|
||||
*/
|
||||
async function ImportSrtAndGetTime() {
|
||||
let srt_path = null
|
||||
// 先选择SRT文件
|
||||
await window.api.SelectFile(['srt'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
srt_path = value.value
|
||||
})
|
||||
|
||||
await window.api.ImportSrtAndGetTime([toRaw(data.value), srt_path], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 写入时间
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const item = value.data[i]
|
||||
if (i >= data.value.length) {
|
||||
item.no = i + 1
|
||||
item.timeLimit = `${item.start_time} -- ${item.end_time}`
|
||||
data.value.push(item)
|
||||
} else {
|
||||
data.value[i].start_time = item.start_time
|
||||
data.value[i].end_time = item.end_time
|
||||
data.value[i].timeLimit = `${item.start_time} -- ${item.end_time}`
|
||||
data.value[i].subValue = item.subValue
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开添加字幕信息的窗口
|
||||
*/
|
||||
async function OpenOneDialog() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.7
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(EditWord, { ref: editWordRef, initData: data }),
|
||||
style: `width : ${dialogWidth}px; minHeight : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
positiveText: '保存',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
data.value = []
|
||||
let word_arr = editWordRef.value.word.split('\n')
|
||||
let lastId = ''
|
||||
for (let i = 0; i < word_arr.length; i++) {
|
||||
const element = word_arr[i]
|
||||
if (element != '' && element != null) {
|
||||
let id = uuidv4()
|
||||
let obj = {
|
||||
no: i + 1,
|
||||
id: id,
|
||||
lastId: lastId,
|
||||
word: element,
|
||||
after_gpt: element,
|
||||
subValue: []
|
||||
}
|
||||
lastId = id
|
||||
data.value.push(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// 将文案数据写入到本地
|
||||
let local_word = word_arr.filter((item) => item != '' && item != null)
|
||||
await window.api.saveWordTxt(toRaw(local_word), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('文案写入失败' + value.message)
|
||||
return
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns: createColumns({
|
||||
AlignNextWord
|
||||
}),
|
||||
rowKey: (row) => row.id,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys
|
||||
},
|
||||
ImportSrtAndGetTime,
|
||||
SaveCopywritingInformation,
|
||||
OpenOneDialog,
|
||||
maxHeight,
|
||||
hh,
|
||||
wenkeRef,
|
||||
type,
|
||||
reverseManageStore
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -119,6 +119,7 @@ import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts
|
||||
import { CloseSharp } from '@vicons/ionicons5'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { useSoftwareStore } from '../../../../../../stores/software.ts'
|
||||
import { BookType } from '../../../../../../define/enum/bookEnum'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -175,6 +176,16 @@ export default defineComponent({
|
||||
} else if (propertyName == 'srtPath') {
|
||||
ext = ['srt']
|
||||
}
|
||||
|
||||
// 判断是不是原创,原创不能选择视频文件
|
||||
if (
|
||||
reverseManageStore.selectBook.type == BookType.ORIGINAL &&
|
||||
propertyName == 'oldVideoPath'
|
||||
) {
|
||||
message.error('原创小说不能选择视频文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 选择MP4文件夹
|
||||
await window.api.SelectFile(ext, (value) => {
|
||||
debugger
|
||||
@@ -212,8 +223,17 @@ export default defineComponent({
|
||||
}
|
||||
let rules = {
|
||||
name: ruleObj('书名必填'),
|
||||
oldVideoPath: ruleObj('反推的视频文件地址必填'),
|
||||
type: ruleObj('小说类型必填')
|
||||
type: ruleObj('小说类型必填'),
|
||||
oldVideoPath: {
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
// 判断是不是原创,原创不能选择视频文件,反推必选
|
||||
if(reverseManageStore.selectBook.type == BookType.SD_REVERSE || reverseManageStore.selectBook.type == BookType.MJ_REVERSE){
|
||||
return new Error("MJ反推和SD反推,视频文件必选")
|
||||
}
|
||||
},
|
||||
trigger: ['input', 'blur', 'change']
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,7 +311,8 @@ export default defineComponent({
|
||||
bookRef,
|
||||
softwareStore,
|
||||
OpenFolder,
|
||||
backgroundMusicOptions,loading
|
||||
backgroundMusicOptions,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -774,7 +774,8 @@ export default defineComponent({
|
||||
after_gpt: element.afterGpt,
|
||||
subValue: element.subValue ? element.subValue : [],
|
||||
name: element.name + '.png',
|
||||
prompt: element.prompt
|
||||
prompt: element.prompt,
|
||||
timeLimit: element.timeLimit
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="width: 500px; position: relative">
|
||||
<n-form ref="formRef" label-placement="top" :model="characterData" :rules="rules">
|
||||
<n-form-item label="人物名称" path="label">
|
||||
<n-input
|
||||
style="width: 400px"
|
||||
v-model:value="characterData.label"
|
||||
placeholder="请输入人物名称"
|
||||
/>
|
||||
<n-checkbox style="margin-left: 20px" v-model:checked="characterData.isShow"
|
||||
>显示</n-checkbox
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: 520px; top: 0; display: flex; flex-direction: column"
|
||||
>
|
||||
<n-image
|
||||
width="120"
|
||||
height="120"
|
||||
:src="png_base64 ? png_base64 : characterData.show_image"
|
||||
/>
|
||||
<n-button color="#e18a3b" style="margin-top: 5px" size="tiny" @click="SelecImage"
|
||||
>本地上传图片</n-button
|
||||
>
|
||||
</div>
|
||||
</n-form-item>
|
||||
<n-form-item label="人物别名(可多个)">
|
||||
<n-dynamic-tags type="success" v-model:value="alias_tags" />
|
||||
</n-form-item>
|
||||
<n-form-item label="人物提示词(中文)">
|
||||
<n-input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
v-model:value="characterData.chinese_prompt"
|
||||
placeholder="请输入人物提示词"
|
||||
/>
|
||||
<!-- 里面的按钮设置上下居中 -->
|
||||
|
||||
<div style="width: 100px; margin-left: 20px">
|
||||
<n-button color="#a76283" size="tiny" @click="TranslatePrompt" :loading="loading"
|
||||
>翻译提示词</n-button
|
||||
>
|
||||
<n-button
|
||||
color="#e18a3b"
|
||||
size="tiny"
|
||||
style="margin-top: 10px"
|
||||
@click="GenerateCharacterImage"
|
||||
:loading="imageLoading"
|
||||
>SD生成图片</n-button
|
||||
>
|
||||
</div>
|
||||
</n-form-item>
|
||||
<n-form-item label="人物提示词(英文)" path="prompt">
|
||||
<n-input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
v-model:value="characterData.prompt"
|
||||
placeholder="请输入人物提示词"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:人物图片链接(cref)">
|
||||
<n-input
|
||||
v-model:value="characterData.image_url"
|
||||
placeholder="请输入人物图片链接"
|
||||
style="margin-right: 20px"
|
||||
/>
|
||||
<n-image
|
||||
v-if="characterData.image_url"
|
||||
:src="characterData.image_url"
|
||||
style="width: 80px; height: 80px"
|
||||
:alt="characterData.image_url"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:角色迁移 cw 值(0-100)">
|
||||
<n-input-number
|
||||
:show-button="false"
|
||||
v-model:value="characterData.cref_cw"
|
||||
min="0"
|
||||
max="100"
|
||||
placeholder="请输入人物风格迁移的CW值"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-select
|
||||
v-model:value="characterData.lora"
|
||||
style="width: 250px"
|
||||
:options="lora_options"
|
||||
placeholder="选择人物lora"
|
||||
/>
|
||||
<n-input-number
|
||||
style="width: 120px; margin-left: 10px"
|
||||
placeholder="权重"
|
||||
v-model:value="characterData.lora_weight"
|
||||
:show-button="false"
|
||||
:max="2"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
S
|
||||
:min="0"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div style="text-align: right">
|
||||
<n-button type="success" @click="SaveCharacterTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch, nextTick } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NDynamicTags,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NCheckbox
|
||||
} from 'naive-ui'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NDynamicTags,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NCheckbox
|
||||
},
|
||||
props: ['currentCharacter', 'initFunc', 'currentTags', 'lora_options'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let alias_tags = ref(props.currentTags)
|
||||
let characterData = ref(props.currentCharacter)
|
||||
let lora_options = ref(props.lora_options)
|
||||
let loading = ref(false)
|
||||
let imageLoading = ref(false)
|
||||
let formRef = ref(null)
|
||||
|
||||
let png_base64 = ref(null)
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.currentCharacter,
|
||||
(value) => {
|
||||
characterData.value = value
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.currentTags,
|
||||
(value) => {
|
||||
alias_tags.value = value
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
async function SaveCharacterTag(e) {
|
||||
e.preventDefault()
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error('请检查必填字段')
|
||||
return
|
||||
}
|
||||
// 保存之前要处理数据(判断该当前的将别名数组中的数据进行拆分)
|
||||
let children = []
|
||||
alias_tags.value.forEach((item) => {
|
||||
children.push({
|
||||
label: item,
|
||||
key: uuidv4(),
|
||||
type: 'min',
|
||||
chinese_prompt: characterData.value.chinese_prompt,
|
||||
prompt: characterData.value.prompt,
|
||||
image_url: characterData.value.image_url,
|
||||
show_image: characterData.value.show_image
|
||||
? characterData.value.show_image.split('?t')[0]
|
||||
: null,
|
||||
cref_cw: characterData.value.cref_cw,
|
||||
lora: characterData.value.lora,
|
||||
lora_weight: characterData.value.lora_weight
|
||||
})
|
||||
})
|
||||
characterData.value['children'] = children
|
||||
console.log(characterData.value)
|
||||
characterData.value['type'] = 'character_main'
|
||||
characterData.value['show_image'] = characterData.value.show_image
|
||||
? characterData.value.show_image.split('?t')[0]
|
||||
: null
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData(
|
||||
[JSON.stringify(characterData.value), 'character_tags'],
|
||||
(value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
//保存成功在initFunc
|
||||
props.initFunc()
|
||||
// 清楚数据
|
||||
characterData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: 'character_main',
|
||||
chinese_prompt: null,
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
show_image: window.config.space_image,
|
||||
cref_cw: 20,
|
||||
chinese_prompt: null,
|
||||
lora: '无',
|
||||
lora_weight: 1
|
||||
}
|
||||
png_base64.value = null
|
||||
alias_tags.value = []
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// 翻译提示词
|
||||
async function TranslatePrompt() {
|
||||
loading.value = true
|
||||
if (characterData.value.chinese_prompt == '' || characterData.value.chinese_prompt == null) {
|
||||
message.error('请输入中文提示词')
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
await window.api.TranslateReturnNow(
|
||||
[characterData.value.chinese_prompt, 'zh', 'en', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
characterData.value.prompt = value.data[0].src
|
||||
}
|
||||
)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == '') return new Error(errorMessage)
|
||||
return true
|
||||
},
|
||||
trigger: ['input', 'blur', 'change']
|
||||
}
|
||||
]
|
||||
}
|
||||
let rules = {
|
||||
label: ruleObj('必填人物名称'),
|
||||
prompt: ruleObj('必填英文提示词')
|
||||
}
|
||||
|
||||
/**
|
||||
* 单张生成角色图片,预览
|
||||
*/
|
||||
async function GenerateCharacterImage() {
|
||||
// 判断lora是不是存在
|
||||
let lora = ''
|
||||
if (
|
||||
characterData.value.lora != null &&
|
||||
characterData.value.lora != '' &&
|
||||
characterData.value.lora != '无'
|
||||
) {
|
||||
lora = `<lora:${characterData.value.lora}:${characterData.value.lora_weight}>`
|
||||
}
|
||||
let d = {
|
||||
prompt: `${characterData.value.prompt}, ${lora}`,
|
||||
width: 1024,
|
||||
height: 1024
|
||||
}
|
||||
if (isEmpty(characterData.value.prompt)) {
|
||||
message.error('请填写英文提示词')
|
||||
return
|
||||
}
|
||||
imageLoading.value = true
|
||||
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
imageLoading.value = false
|
||||
return
|
||||
}
|
||||
if (value.data && value.data.length > 0) {
|
||||
let d = value.data[0]
|
||||
characterData.value.show_image = d.image_path.replaceAll('\\', '/')
|
||||
png_base64.value = 'data:image/png;base64,' + d.base64
|
||||
console.log(characterData.value.show_image)
|
||||
imageLoading.value = false
|
||||
}
|
||||
// 使用 Vue.nextTick 等待 Vue 更新视图
|
||||
imageLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择本地图片
|
||||
*/
|
||||
async function SelecImage() {
|
||||
await window.api.SelectFile(
|
||||
[
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif',
|
||||
'bmp',
|
||||
'tiff',
|
||||
'tif',
|
||||
'webp',
|
||||
'svg',
|
||||
'raw',
|
||||
'cr2',
|
||||
'nef',
|
||||
'arw'
|
||||
],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
png_base64.value = null
|
||||
characterData.value.show_image = value.value
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
characterData,
|
||||
SelecImage,
|
||||
alias_tags,
|
||||
SaveCharacterTag,
|
||||
TranslatePrompt,
|
||||
loading,
|
||||
rules,
|
||||
formRef,
|
||||
lora_options,
|
||||
imageLoading,
|
||||
GenerateCharacterImage,
|
||||
png_base64
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top">
|
||||
<n-form-item label="前缀名称">
|
||||
<n-input v-model:value="prefixData.label" placeholder="请输入场景名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="前缀提示词描述">
|
||||
<n-input type="textarea" :rows="3" v-model:value="prefixData.prompt" placeholder="请输入前缀提示词描述" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="prefixData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="prefixData.image_url" :src="prefixData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="prefixData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="prefixData.lora" placeholder="请输入风格Lora" />
|
||||
<n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button>
|
||||
</n-form-item> -->
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SavePrefixTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentPrefix", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let prefixData = ref(props.currentPrefix);
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentPrefix, (value) => {
|
||||
prefixData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
async function SavePrefixTag() {
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
prefixData.value['type'] = "prefix_main";
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(prefixData.value), "prefix_tags"], (value) => {
|
||||
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
prefixData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "prefix_main",
|
||||
prompt: null,
|
||||
// image_url: null,
|
||||
// cref_cw: 20,
|
||||
// lora: null,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
prefixData,
|
||||
SavePrefixTag
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<div style="width: 500px">
|
||||
<n-form label-placement="top" ref="formRef" :rules="rules" :model="sceneData">
|
||||
<n-form-item label="场景名称" path="label">
|
||||
<n-input
|
||||
style="width: 400px"
|
||||
v-model:value="sceneData.label"
|
||||
placeholder="请输入场景名称"
|
||||
/>
|
||||
<n-checkbox style="margin-left: 20px" v-model:checked="sceneData.isShow"
|
||||
>显示</n-checkbox
|
||||
>
|
||||
</n-form-item>
|
||||
<n-form-item label="场景提示词描述(中文)">
|
||||
<n-input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model:value="sceneData.chinese_prompt"
|
||||
placeholder="请输入场景提示词描述"
|
||||
/>
|
||||
<n-button
|
||||
type="info"
|
||||
size="small"
|
||||
style="margin-left: 20px"
|
||||
@click="TranslatePrompt"
|
||||
:loading="loading"
|
||||
>翻译提示词</n-button
|
||||
>
|
||||
</n-form-item>
|
||||
<n-form-item label="场景提示词描述(英文)" path="prompt">
|
||||
<n-input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model:value="sceneData.prompt"
|
||||
placeholder="请输入场景提示词描述"
|
||||
/>
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="sceneData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="sceneData.image_url" :src="sceneData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="sceneData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="sceneData.lora" placeholder="请输入风格Lora" />
|
||||
<n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button>
|
||||
</n-form-item> -->
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right">
|
||||
<n-button type="success" @click="SaveSceneTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NDynamicTags,
|
||||
NInputNumber,
|
||||
NCheckbox
|
||||
} from 'naive-ui'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NDynamicTags,
|
||||
NInputNumber,
|
||||
NCheckbox
|
||||
},
|
||||
props: ['currentScene', 'initFunc'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let sceneData = ref(props.currentScene)
|
||||
let loading = ref(false)
|
||||
let formRef = ref(null)
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.currentScene,
|
||||
(value) => {
|
||||
sceneData.value = value
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
// 保存操作
|
||||
async function SaveSceneTag(e) {
|
||||
e.preventDefault()
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error('请检查必填字段')
|
||||
return
|
||||
}
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
sceneData.value['type'] = 'scene_main'
|
||||
await window.mj.SaveTagPropertyData(
|
||||
[JSON.stringify(sceneData.value), 'scene_tags'],
|
||||
(value) => {
|
||||
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
//保存成功在initFunc
|
||||
props.initFunc()
|
||||
// 清楚数据
|
||||
sceneData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: 'scene_main',
|
||||
prompt: null
|
||||
// image_url: null,
|
||||
// cref_cw: 20,
|
||||
// lora: null,
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// 翻译提示词
|
||||
async function TranslatePrompt() {
|
||||
loading.value = true
|
||||
if (sceneData.value.chinese_prompt == '' || sceneData.value.chinese_prompt == null) {
|
||||
message.error('请输入中文提示词')
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
await window.api.TranslateReturnNow(
|
||||
[sceneData.value.chinese_prompt, 'zh', 'en', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
//
|
||||
console.log(value.data)
|
||||
sceneData.value.prompt = value.data[0].src
|
||||
}
|
||||
)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == '') return new Error(errorMessage)
|
||||
return true
|
||||
},
|
||||
trigger: ['input', 'blur', 'change']
|
||||
}
|
||||
]
|
||||
}
|
||||
let rules = {
|
||||
label: ruleObj('必填人物名称'),
|
||||
prompt: ruleObj('必填英文提示词')
|
||||
}
|
||||
|
||||
return {
|
||||
sceneData,
|
||||
SaveSceneTag,
|
||||
loading,
|
||||
TranslatePrompt,
|
||||
rules,
|
||||
formRef
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<div style="width: 500px">
|
||||
<n-form label-placement="top" ref="formRef" :rules="rules" :model="styleData">
|
||||
<n-form-item label="风格名称" path="label">
|
||||
<n-input v-model:value="styleData.label" placeholder="请输入风格名称" />
|
||||
<div style="position: absolute; left: 520px; top: 0; display: flex; flex-direction: column">
|
||||
<n-image width="120" height="120" :src="png_base64 ? png_base64 : styleData.show_image" />
|
||||
<n-button color="#e18a3b" style="margin-top: 5px" size="tiny" @click="SelecImage"
|
||||
>本地上传图片</n-button
|
||||
>
|
||||
</div>
|
||||
</n-form-item>
|
||||
<n-form-item label="风格提示词描述(中文)">
|
||||
<n-input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
v-model:value="styleData.chinese_prompt"
|
||||
placeholder="请输入风格提示词描述(中文)"
|
||||
/>
|
||||
<div style="width: 100px; margin-left: 20px">
|
||||
<n-button type="info" size="tiny" @click="TranslatePrompt" :loading="loading"
|
||||
>翻译提示词</n-button
|
||||
>
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
color="#e18a3b"
|
||||
size="tiny"
|
||||
style="margin-top: 10px"
|
||||
@click="GenerateStyleImage"
|
||||
:loading="imageLoading"
|
||||
>SD生成图片</n-button
|
||||
>
|
||||
</template>
|
||||
使用SD出图模式生成风格图片,提示词为1gril加上风格提示词
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</n-form-item>
|
||||
<n-form-item label="风格提示词描述(英文)" path="prompt">
|
||||
<n-input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model:value="styleData.prompt"
|
||||
placeholder="请输入风格提示词描述(英文)"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input
|
||||
v-model:value="styleData.image_url"
|
||||
placeholder="请输入风格垫图链接"
|
||||
style="margin-right: 20px"
|
||||
/>
|
||||
<n-image
|
||||
v-if="styleData.image_url"
|
||||
:src="styleData.image_url"
|
||||
style="width: 80px; height: 80px"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 sw 值(0-1000)">
|
||||
<n-input-number
|
||||
:show-button="false"
|
||||
v-model:value="styleData.sref_sw"
|
||||
min="0"
|
||||
max="1000"
|
||||
placeholder="请输入人物风格迁移的SW值"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-select :options="lora_options" v-model:value="styleData.lora"> </n-select>
|
||||
<n-input-number
|
||||
style="width: 120px; margin-left: 10px"
|
||||
placeholder="权重"
|
||||
v-model:value="styleData.lora_weight"
|
||||
:show-button="false"
|
||||
:max="2"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
S
|
||||
:min="0"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right">
|
||||
<n-button type="success" @click="SaveStyleTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NDynamicTags,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NTooltip
|
||||
} from 'naive-ui'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NDynamicTags,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NTooltip
|
||||
},
|
||||
props: ['currentStyle', 'initFunc', 'lora_options'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let styleData = ref(
|
||||
props.currentStyle
|
||||
? props.currentStyle
|
||||
: { show_image: window.config.space_image, lora_weight: 1, sref_sw: 50, lora: '无' }
|
||||
)
|
||||
let loading = ref(false)
|
||||
let imageLoading = ref(false)
|
||||
let formRef = ref(null)
|
||||
let lora_options = ref(props.lora_options)
|
||||
let png_base64 = ref(null)
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.currentStyle,
|
||||
(value) => {
|
||||
styleData.value = value
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
async function SaveStyleTag(e) {
|
||||
e.preventDefault()
|
||||
formRef.value?.validate(async (errors) => {
|
||||
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
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData(
|
||||
[JSON.stringify(styleData.value), 'style_tags'],
|
||||
(value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
//保存成功在initFunc
|
||||
props.initFunc()
|
||||
// 清楚数据
|
||||
styleData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: 'style_main',
|
||||
prompt: null,
|
||||
show_image: window.config.space_image,
|
||||
image_url: null,
|
||||
cref_cw: 20,
|
||||
lora: '无'
|
||||
}
|
||||
png_base64.value = null
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译中文提示词
|
||||
*/
|
||||
async function TranslatePrompt() {
|
||||
loading.value = true
|
||||
if (styleData.value.chinese_prompt == '' || styleData.value.chinese_prompt == null) {
|
||||
message.error('请输入中文提示词')
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
await window.api.TranslateReturnNow(
|
||||
[styleData.value.chinese_prompt, 'zh', 'en', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
console.log(value.data)
|
||||
styleData.value.prompt = value.data[0].src
|
||||
}
|
||||
)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == '') return new Error(errorMessage)
|
||||
return true
|
||||
},
|
||||
trigger: ['input', 'blur', 'change']
|
||||
}
|
||||
]
|
||||
}
|
||||
let rules = {
|
||||
label: ruleObj('必填人物名称')
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成风格图片
|
||||
*/
|
||||
async function GenerateStyleImage() {
|
||||
if (isEmpty(styleData.value.prompt)) {
|
||||
message.error('请输入提示词')
|
||||
imageLoading.value = false
|
||||
return
|
||||
}
|
||||
// 判断lora是不是存在
|
||||
let lora = ''
|
||||
if (
|
||||
styleData.value.lora != null &&
|
||||
styleData.value.lora != '' &&
|
||||
styleData.value.lora != '无'
|
||||
) {
|
||||
lora = `<lora:${styleData.value.lora}:${styleData.value.lora_weight}>`
|
||||
}
|
||||
let d = {
|
||||
prompt: `1gril, ${styleData.value.prompt}, ${lora}`,
|
||||
width: 1024,
|
||||
height: 1024
|
||||
}
|
||||
imageLoading.value = true
|
||||
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
imageLoading.value = false
|
||||
return
|
||||
}
|
||||
if (value.data && value.data.length > 0) {
|
||||
let d = value.data[0]
|
||||
styleData.value.show_image =
|
||||
d.image_path.replaceAll('\\', '/') + '?t=' + new Date().getTime()
|
||||
png_base64.value = 'data:image/png;base64,' + d.base64
|
||||
console.log(styleData.value.show_image)
|
||||
imageLoading.value = false
|
||||
}
|
||||
// 使用 Vue.nextTick 等待 Vue 更新视图
|
||||
imageLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择本地的图片应用到风格图片
|
||||
*/
|
||||
async function SelecImage() {
|
||||
await window.api.SelectFile(
|
||||
[
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif',
|
||||
'bmp',
|
||||
'tiff',
|
||||
'tif',
|
||||
'webp',
|
||||
'svg',
|
||||
'raw',
|
||||
'cr2',
|
||||
'nef',
|
||||
'arw'
|
||||
],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 读取文件并转换为Base64
|
||||
fetch(value.value)
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const reader = new FileReader()
|
||||
reader.onloadend = () => {
|
||||
png_base64.value = reader.result
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
})
|
||||
// 更新显示的图片
|
||||
|
||||
png_base64.value = null
|
||||
styleData.value.show_image = value.value + '?t=' + new Date().getTime()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
styleData,
|
||||
SelecImage,
|
||||
SaveStyleTag,
|
||||
loading,
|
||||
imageLoading,
|
||||
rules,
|
||||
formRef,
|
||||
TranslatePrompt,
|
||||
lora_options,
|
||||
png_base64,
|
||||
GenerateStyleImage
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top">
|
||||
<n-form-item label="后缀名称">
|
||||
<n-input v-model:value="suffixData.label" placeholder="请输入后缀名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="后缀提示词描述">
|
||||
<n-input type="textarea" :rows="3" v-model:value="suffixData.prompt" placeholder="请输入后缀提示词描述" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="suffixData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="suffixData.image_url" :src="suffixData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="suffixData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="suffixData.lora" placeholder="请输入风格Lora" />
|
||||
<n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button>
|
||||
</n-form-item> -->
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SavePrefixTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentSuffix", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let suffixData = ref(props.currentSuffix);
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentSuffix, (value) => {
|
||||
suffixData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
async function SavePrefixTag() {
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
suffixData.value['type'] = "suffix_main";
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(suffixData.value), "suffix_tags"], (value) => {
|
||||
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
suffixData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "suffix_main",
|
||||
prompt: null,
|
||||
// image_url: null,
|
||||
// cref_cw: 20,
|
||||
// lora: null,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
suffixData,
|
||||
SavePrefixTag
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<n-card>
|
||||
<n-tabs
|
||||
class="card-tabs"
|
||||
default-value="autoAnalyze"
|
||||
size="large"
|
||||
animated
|
||||
pane-wrapper-style="margin: 0 -4px"
|
||||
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;"
|
||||
>
|
||||
<n-tab-pane name="autoAnalyze" tab="自动分析">
|
||||
<n-button color="#ee7959" size="medium" @click="autoAnalyzeCharacter" :loading="loading"
|
||||
>自动分析</n-button
|
||||
>
|
||||
<n-input
|
||||
style="margin-top: 20px"
|
||||
type="textarea"
|
||||
:autosize="{
|
||||
minRows: 10,
|
||||
maxRows: 10
|
||||
}"
|
||||
placeholder="自动分析的数据"
|
||||
v-model:value="autoCharacterString"
|
||||
></n-input>
|
||||
<div>
|
||||
<div style="color: red">注意:上面推理的不满意可以手动修改</div>
|
||||
<div style="color: red">自动推理的的人物只能对内置的四种模式生效</div>
|
||||
</div>
|
||||
<div style="text-align: right">
|
||||
<n-button type="success" @click="SaveAutoAnalyzeCharacter">保存</n-button>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="Analyze" tab="手动设置标签集">
|
||||
<CharacterTags :height="height"></CharacterTags>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, h, onMounted, toRaw } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NCard,
|
||||
NTabs,
|
||||
NTabPane,
|
||||
NTree
|
||||
} from 'naive-ui'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import CharacterTags from './CharacterTags.vue'
|
||||
import { useReverseManageStore } from '../../../../../../stores/reverseManage'
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let autoCharacterString = ref('')
|
||||
|
||||
let props = defineProps({
|
||||
word: undefined,
|
||||
height: undefined
|
||||
})
|
||||
let message = useMessage()
|
||||
let word = ref(props.word)
|
||||
let height = ref(props.height)
|
||||
let loading = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
autoCharacterString.value = reverseManageStore.selectBookTask.autoAnalyzeCharacter
|
||||
})
|
||||
|
||||
/**
|
||||
* 自动分析角色
|
||||
*/
|
||||
async function autoAnalyzeCharacter() {
|
||||
if (word.value == '' || word.value == null) {
|
||||
message.error('请输入要分析的文案')
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
|
||||
await window.api.AutoAnalyzeCharacter(toRaw(word.value), (value) => {
|
||||
loading.value = false
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
autoCharacterString.value = value.data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自动分析的设置
|
||||
*/
|
||||
async function SaveAutoAnalyzeCharacter() {
|
||||
if (autoCharacterString.value == '' || autoCharacterString.value == null) {
|
||||
message.error('没有分析出来的人物信息')
|
||||
return
|
||||
}
|
||||
// 保存信息
|
||||
reverseManageStore.selectBookTask.autoAnalyzeCharacter = autoCharacterString.value
|
||||
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
|
||||
autoAnalyzeCharacter: autoCharacterString.value
|
||||
})
|
||||
if (res.code == 1) {
|
||||
message.success(res.message)
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-tabs .n-tabs-nav--bar-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,470 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-split direction="horizontal" :max="0.3" :min="0.18" :default-size="0.18">
|
||||
<template #1>
|
||||
<div id="tree_define_content" style="display: flex; overflow-y: auto">
|
||||
<n-tree :node-props="nodeProps" style="width: 200px" block-line :data="treeData" />
|
||||
</div>
|
||||
</template>
|
||||
<template #2>
|
||||
<div style="margin-left: 20px">
|
||||
<AddCharacterTag
|
||||
v-if="currentType.startsWith('character')"
|
||||
:currentCharacter="currentCharacter"
|
||||
:currentTags="currentTags"
|
||||
:initFunc="InitData"
|
||||
:lora_options="lora_options"
|
||||
/>
|
||||
<AddStyleTags
|
||||
v-else-if="currentType.startsWith('style')"
|
||||
:currentStyle="currentStyle"
|
||||
:initFunc="InitData"
|
||||
:lora_options="lora_options"
|
||||
/>
|
||||
<AddSceneTags
|
||||
v-else-if="currentType.startsWith('scene')"
|
||||
:currentScene="currentScene"
|
||||
:initFunc="InitData"
|
||||
/>
|
||||
<AddPrefixTags
|
||||
v-else-if="currentType.startsWith('prefix')"
|
||||
:currentPrefix="currentPrefix"
|
||||
:initFunc="InitData"
|
||||
/>
|
||||
<AddSuffixTags
|
||||
v-else-if="currentType.startsWith('suffix')"
|
||||
:currentSuffix="currentSuffix"
|
||||
:initFunc="InitData"
|
||||
/>
|
||||
<n-empty v-else description="是一个飞机">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<airplane />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-empty>
|
||||
</div>
|
||||
</template>
|
||||
</n-split>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NCard,
|
||||
NTabs,
|
||||
NTabPane,
|
||||
NTree,
|
||||
NSplit,
|
||||
NIcon,
|
||||
NEmpty
|
||||
} from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../../define/define_string'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import AddCharacterTag from './AddCharacterTag.vue'
|
||||
import AddStyleTags from './AddStyleTags.vue'
|
||||
import AddSceneTags from './AddSceneTags.vue'
|
||||
import AddPrefixTags from './AddPrefixTags.vue'
|
||||
import AddSuffixTags from './AddSuffixTags.vue'
|
||||
import { Trash, Airplane } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NCard,
|
||||
NTabs,
|
||||
NTabPane,
|
||||
NTree,
|
||||
NSplit,
|
||||
AddCharacterTag,
|
||||
AddStyleTags,
|
||||
AddSceneTags,
|
||||
AddPrefixTags,
|
||||
AddSuffixTags,
|
||||
NIcon,
|
||||
Trash,
|
||||
NEmpty,
|
||||
Airplane
|
||||
},
|
||||
props: ['height'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let currentCharacter = ref({})
|
||||
let currentStyle = ref({})
|
||||
let currentScene = ref({})
|
||||
let currentPrefix = ref({})
|
||||
let currentSuffix = ref({})
|
||||
let currentTags = ref([])
|
||||
let currentType = ref('default')
|
||||
|
||||
let lora_options = ref([])
|
||||
|
||||
let treeData = ref([
|
||||
{
|
||||
label: '人物',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: 'character',
|
||||
suffix: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
color: '#e18a3b',
|
||||
onClick: (e) => {
|
||||
AddCharacterTags(e)
|
||||
}
|
||||
},
|
||||
{ default: () => '新加' }
|
||||
)
|
||||
},
|
||||
{
|
||||
label: '场景',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: 'scene',
|
||||
suffix: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
color: '#e18a3b',
|
||||
onClick: (e) => {
|
||||
AddSceneTags(e)
|
||||
}
|
||||
},
|
||||
{ default: () => '新加' }
|
||||
)
|
||||
},
|
||||
{
|
||||
label: '风格预设',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: 'style',
|
||||
suffix: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
color: '#e18a3b',
|
||||
onClick: (e) => {
|
||||
AddStyleTags(e)
|
||||
}
|
||||
},
|
||||
{ default: () => '新加' }
|
||||
)
|
||||
},
|
||||
{
|
||||
label: '前缀',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: 'prefix',
|
||||
suffix: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
color: '#e18a3b',
|
||||
onClick: (e) => {
|
||||
AddPrefixTags(e)
|
||||
}
|
||||
},
|
||||
{ default: () => '新加' }
|
||||
)
|
||||
},
|
||||
{
|
||||
label: '后缀',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: 'suffix',
|
||||
suffix: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
color: '#e18a3b',
|
||||
onClick: (e) => {
|
||||
AddSuffixTags(e)
|
||||
}
|
||||
},
|
||||
{ default: () => '新加' }
|
||||
)
|
||||
}
|
||||
])
|
||||
|
||||
/**
|
||||
* 初始化标签数据
|
||||
*/
|
||||
async function InitData() {
|
||||
// 获取动态配置里面是不是又标签数(有加载,没有设置默认值)
|
||||
await window.mj.GetTagDataByTypeAndProperty(['dynamic', null], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 判断是不是包含指定的属性
|
||||
if (value.data.hasOwnProperty('character_tags')) {
|
||||
treeData.value[0].children = value.data.character_tags
|
||||
}
|
||||
if (value.data.hasOwnProperty('scene_tags')) {
|
||||
treeData.value[1].children = value.data.scene_tags
|
||||
}
|
||||
if (value.data.hasOwnProperty('style_tags')) {
|
||||
treeData.value[2].children = value.data.style_tags
|
||||
}
|
||||
if (value.data.hasOwnProperty('prefix_tags')) {
|
||||
treeData.value[3].children = value.data.prefix_tags
|
||||
}
|
||||
if (value.data.hasOwnProperty('suffix_tags')) {
|
||||
treeData.value[4].children = value.data.suffix_tags
|
||||
}
|
||||
|
||||
for (let i = 0; i < treeData.value.length; i++) {
|
||||
treeData.value[i].children.map((item) => {
|
||||
item.suffix = () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'error',
|
||||
size: 'medium',
|
||||
onClick: (e) => {
|
||||
DeleteTags(e, item)
|
||||
}
|
||||
},
|
||||
{ default: () => h(NIcon, null, { default: () => h(Trash) }) }
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
// 加载lora数据
|
||||
// 获取当前mj配置信息
|
||||
await window.api.GetDefineConfigJsonByProperty(
|
||||
JSON.stringify(['sd_setting', 'lora', false, []]),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 判断是不是有数据
|
||||
if (value.data) {
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i]
|
||||
lora_options.value.push({
|
||||
label: element.name,
|
||||
value: element.name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
SetAutoHeight()
|
||||
await InitData()
|
||||
})
|
||||
|
||||
// 设置标签集的自动高度,传一个外面的高度进来
|
||||
function SetAutoHeight() {
|
||||
let div = document.getElementById('tree_define_content')
|
||||
div.style.height = props.height - 180 + 'px'
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的数据
|
||||
*/
|
||||
async function DeleteTags(e, value) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
let type = value.type.replace('main', 'tags')
|
||||
await window.mj.DeleteTagPropertyData([value.key, type], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
await InitData()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加人物标签
|
||||
*/
|
||||
async function AddCharacterTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
// 初始化值,然后赋值给当前添加人物的组件
|
||||
currentCharacter.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: 'character_main',
|
||||
chinese_prompt: null,
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
show_image: window.config.space_image,
|
||||
cref_cw: 50,
|
||||
lora: '无',
|
||||
lora_weight: 1,
|
||||
isShow: false
|
||||
}
|
||||
currentTags.value = []
|
||||
currentType.value = 'character_main'
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加场景标签
|
||||
*/
|
||||
function AddSceneTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
currentScene.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: 'scene_main',
|
||||
prompt: null,
|
||||
isShow: false
|
||||
}
|
||||
currentType.value = 'scene_main'
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加 风格迁移预设
|
||||
* @param {*} e
|
||||
*/
|
||||
function AddStyleTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
currentStyle.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: 'style_main',
|
||||
show_image: window.config.space_image,
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
sref_sw: 50,
|
||||
lora: '无',
|
||||
lora_weight: 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前缀
|
||||
*/
|
||||
function AddPrefixTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
currentPrefix.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: 'prefix_main',
|
||||
prompt: null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加后缀
|
||||
* @param {*} e
|
||||
*/
|
||||
function AddSuffixTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
currentSuffix.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: 'suffix_main',
|
||||
prompt: null
|
||||
}
|
||||
}
|
||||
|
||||
function nodeProps({ option }) {
|
||||
return {
|
||||
onClick() {
|
||||
// 执行切换组件和数据
|
||||
if (option.type == 'style') {
|
||||
currentType.value = 'style'
|
||||
AddStyleTags(null)
|
||||
return
|
||||
} else if (option.type == 'scene') {
|
||||
AddSceneTags(null)
|
||||
currentType.value = 'scene'
|
||||
// message.error("主节点不允许操作");
|
||||
return
|
||||
} else if (option.type == 'character') {
|
||||
AddCharacterTags(null)
|
||||
currentType.value = 'character'
|
||||
return
|
||||
} else if (option.type == 'prefix') {
|
||||
AddPrefixTags(null)
|
||||
currentType.value = 'prefix'
|
||||
return
|
||||
} else if (option.type == 'suffix') {
|
||||
AddSuffixTags(null)
|
||||
currentType.value = 'suffix'
|
||||
return
|
||||
} else if (option.type == 'min') {
|
||||
message.error('别名节点不允许操作')
|
||||
} else {
|
||||
console.log(option)
|
||||
currentType.value = option.type
|
||||
// 修改当前属性
|
||||
if (currentType.value.startsWith('character')) {
|
||||
// 修改tags的值
|
||||
currentCharacter.value = option
|
||||
if (!currentCharacter.value.hasOwnProperty('isShow')) {
|
||||
currentCharacter.value.isShow = false
|
||||
}
|
||||
currentTags.value = option.children.map((item) => {
|
||||
return item.label
|
||||
})
|
||||
} else if (currentType.value.startsWith('scene')) {
|
||||
currentScene.value = option
|
||||
} else if (currentType.value.startsWith('style')) {
|
||||
currentStyle.value = option
|
||||
} else if (currentType.value.startsWith('prefix')) {
|
||||
currentPrefix.value = option
|
||||
} else if (currentType.value.startsWith('suffix')) {
|
||||
currentSuffix.value = option
|
||||
} else {
|
||||
message.error('未知的类型')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
treeData,
|
||||
AddCharacterTags,
|
||||
AddSceneTags,
|
||||
AddStyleTags,
|
||||
nodeProps,
|
||||
currentCharacter,
|
||||
currentTags,
|
||||
InitData,
|
||||
currentType,
|
||||
currentStyle,
|
||||
currentScene,
|
||||
currentPrefix,
|
||||
currentSuffix,
|
||||
lora_options
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -38,7 +38,7 @@ export default defineComponent({
|
||||
{
|
||||
title: 'No.',
|
||||
key: 'name',
|
||||
width: 80,
|
||||
width: 50,
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
@@ -145,7 +145,7 @@ export default defineComponent({
|
||||
debugger
|
||||
// 视口的高度
|
||||
let height = window.innerHeight
|
||||
maxHeight.value = height - 120
|
||||
maxHeight.value = height - 100
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
<template>
|
||||
<div style="margin-bottom: 5px">
|
||||
<ManageBookButton />
|
||||
</div>
|
||||
<div style="margin-right: 5px">
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:size="softwareStore.softWare.reverse_data_table_size"
|
||||
:data="reverseManageStore.bookData"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:remote="true"
|
||||
:striped="softwareStore.softWare.reverse_show_book_striped"
|
||||
:scroll-x="1000"
|
||||
:row-props="rowProps"
|
||||
:max-height="maxHeight"
|
||||
/>
|
||||
<div>
|
||||
<div style="margin-bottom: 5px">
|
||||
<ManageBookButton />
|
||||
</div>
|
||||
<div style="margin-right: 5px">
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:size="softwareStore.softWare.reverse_data_table_size"
|
||||
:data="reverseManageStore.bookData"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:remote="true"
|
||||
:striped="softwareStore.softWare.reverse_show_book_striped"
|
||||
:scroll-x="1000"
|
||||
:row-props="rowProps"
|
||||
:max-height="maxHeight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<template>
|
||||
<div style="height: 100%; display: flex; width: 100%; overflow: auto">
|
||||
<div v-if="true" class="left_container" style="flex: 10; min-width: 500px">
|
||||
<ManageBookDetailButton />
|
||||
<ManageBookReverseTable style="margin-top: 5px" />
|
||||
</div>
|
||||
<div v-if="softwareStore.show_logger">
|
||||
<n-divider vertical style="height: 100%"></n-divider>
|
||||
<div style="height: 100%; display: flex; width: 100%; overflow: auto; padding: 0; margin: 0">
|
||||
<div>
|
||||
<div v-if="reverseManageStore.selectBook.type == bookType.MJ_REVERSE">
|
||||
<div v-if="true" class="left_container" style="flex: 10; min-width: 500px">
|
||||
<ManageBookDetailButton />
|
||||
<ManageBookReverseTable style="margin-top: 5px" />
|
||||
</div>
|
||||
<div v-if="softwareStore.show_logger">
|
||||
<n-divider vertical style="height: 100%"></n-divider>
|
||||
</div>
|
||||
</div>
|
||||
<OriginalMainPage
|
||||
v-else-if="reverseManageStore.selectBook.type == bookType.ORIGINAL"
|
||||
></OriginalMainPage>
|
||||
</div>
|
||||
<div v-if="softwareStore.show_logger" class="right_container">
|
||||
<ManageBookShowLogger />
|
||||
@@ -21,16 +28,18 @@ import ManageBookReverseTable from './MJReverse/ManageBookReverseTable.vue'
|
||||
import ManageBookShowLogger from './Components/ManageBook/ManageBookShowLogger.vue'
|
||||
import { useSoftwareStore } from '../../../../stores/software'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import { DialogType } from '../../../../define/enum/bookEnum'
|
||||
import { BookType, DialogType } from '../../../../define/enum/bookEnum'
|
||||
import { ResponseMessageType } from '../../../../define/enum/softwareEnum'
|
||||
import { useReverseManageStore } from '../../../../stores/reverseManage'
|
||||
import OriginalMainPage from './Original/OriginalMainPage.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ManageBookDetailButton,
|
||||
ManageBookReverseTable,
|
||||
ManageBookShowLogger,
|
||||
NDivider
|
||||
NDivider,
|
||||
OriginalMainPage
|
||||
},
|
||||
|
||||
setup() {
|
||||
@@ -38,6 +47,7 @@ export default defineComponent({
|
||||
let message = useMessage()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let bookType = ref(BookType)
|
||||
onMounted(async () => {
|
||||
// 添加一个总的监听器,主要监听后端数据变化,修改后端数据后,前端数据也会随之变化
|
||||
if (!window['mainReturn']) {
|
||||
@@ -89,8 +99,6 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
function MJImageResponse(value) {
|
||||
debugger
|
||||
console.log(value)
|
||||
//开始修改
|
||||
if (value.data) {
|
||||
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
@@ -179,7 +187,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
return { softwareStore, reverseManageStore }
|
||||
return { softwareStore, reverseManageStore, bookType }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="
|
||||
reverseManageStore.selectBookTask.imageCategory == 'sd' ||
|
||||
reverseManageStore.selectBookTask.imageCategory == 'flux-forge'
|
||||
"
|
||||
>
|
||||
<n-checkbox
|
||||
style="margin-left: 5px; height: 100%"
|
||||
:checked="row.adetailer"
|
||||
label="修脸"
|
||||
@update:checked="onUpdateChecked"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="reverseManageStore.selectBookTask.imageCategory == 'mj'"></div>
|
||||
<div v-else-if="reverseManageStore.selectBookTask.imageCategory == 'd3'"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { NCheckbox } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let props = defineProps({
|
||||
initData: undefined,
|
||||
index: undefined
|
||||
})
|
||||
let row = ref(props.initData)
|
||||
|
||||
async function onUpdateChecked(value) {
|
||||
console.log(value)
|
||||
row.value.adetailer = value
|
||||
let res = await window.db.UpdateBookTaskDetailData(row.value.id, {
|
||||
adetailer: value
|
||||
})
|
||||
window.api.showGlobalMessage(res)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<div style="width: 100%">
|
||||
<div style="height: 65px; overflow-y: auto; width: 100%">
|
||||
<span>人物:</span>
|
||||
<n-tag
|
||||
v-for="(character, index) in characterTags"
|
||||
v-model:checked="character.checked"
|
||||
checkable
|
||||
@click="UpdateCharacterTagsDebounced(character, index)"
|
||||
size="small"
|
||||
style="margin: 0 3px 3px 0"
|
||||
>
|
||||
{{ character.label }}
|
||||
</n-tag>
|
||||
</div>
|
||||
<div style="margin-top: 5px; height: 65px; overflow-y: auto; width: 100%">
|
||||
<span>场景:</span>
|
||||
<n-tag
|
||||
v-for="(scene, index) in sceneTags"
|
||||
v-model:checked="scene.checked"
|
||||
checkable
|
||||
@click="UpdateSceneTagsDebounced(scene, index)"
|
||||
size="small"
|
||||
style="margin: 0 3px 3px 0"
|
||||
>
|
||||
{{ scene.label }}
|
||||
</n-tag>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div v-else>
|
||||
<div>
|
||||
<div>人物:</div>
|
||||
<n-select
|
||||
style="width: 190px"
|
||||
size="tiny"
|
||||
:render-tag="renderTag"
|
||||
v-model:value="select_character_tags"
|
||||
multiple
|
||||
filterable
|
||||
:options="characterTags"
|
||||
placeholder="选择人物"
|
||||
@update:value="UpdateCharacterSelect"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-top: 3px">
|
||||
<div>场景:</div>
|
||||
<n-select
|
||||
style="width: 190px"
|
||||
size="tiny"
|
||||
:render-tag="renderTag"
|
||||
v-model:value="select_scene_tags"
|
||||
multiple
|
||||
filterable
|
||||
:options="sceneTags"
|
||||
placeholder="选择场景"
|
||||
@update:value="UpdateSceneSelect"
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, h } from 'vue'
|
||||
import { NSelect, NTag, useMessage } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { debounce } from 'lodash'
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
let props = defineProps({
|
||||
initData: undefined,
|
||||
index: undefined
|
||||
})
|
||||
let row = ref(props.initData)
|
||||
let characterTags = ref([])
|
||||
let sceneTags = ref([])
|
||||
// 存储当权选择的tag数据
|
||||
|
||||
let select_character_tags = ref([])
|
||||
let select_scene_tags = ref([])
|
||||
|
||||
watch(
|
||||
() => row.value.characterTags,
|
||||
(value) => {
|
||||
characterTags.value = characterTags.value?.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
value: item.id,
|
||||
label: item.label,
|
||||
checked: value && value.includes(item.id) ? true : false
|
||||
}
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => reverseManageStore.characterTags,
|
||||
(value) => {
|
||||
characterTags.value = value?.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
value: item.id,
|
||||
label: item.label,
|
||||
checked: row.value.characterTags && row.value.characterTags.includes(item.id) ? true : false
|
||||
}
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
watch(
|
||||
() => reverseManageStore.sceneTags,
|
||||
(value) => {
|
||||
sceneTags.value = value?.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
value: item.id,
|
||||
label: item.label,
|
||||
checked: row.value.sceneTags && row.value.sceneTags.includes(item.id) ? true : false
|
||||
}
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(() => {})
|
||||
|
||||
// 选择人物时更行的方法
|
||||
let UpdateCharacterTagsDebounced = debounce(UpdateCharacterTags, 500)
|
||||
|
||||
async function UpdateCharacterTags(character, index) {
|
||||
debugger
|
||||
// 开始保存,获取所有的选中的人物
|
||||
let checkedCharacterTags = characterTags.value
|
||||
.filter((item) => item.checked)
|
||||
.map((item) => item.id)
|
||||
// 开始保存
|
||||
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == row.value.id
|
||||
)
|
||||
if (findIndex != -1) {
|
||||
reverseManageStore.selectBookTaskDetail[findIndex].characterTags = checkedCharacterTags
|
||||
}
|
||||
let res = await window.db.UpdateBookTaskDetailData(row.value.id, {
|
||||
characterTags: checkedCharacterTags
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
let UpdateSceneTagsDebounced = debounce(UpdateSceneTags, 500)
|
||||
async function UpdateSceneTags(scene, index) {
|
||||
debugger
|
||||
console.log('1233', scene, index)
|
||||
// 开始保存,获取所有的选中的场景
|
||||
let checkedSceneTags = sceneTags.value.filter((item) => item.checked).map((item) => item.id)
|
||||
// 开始保存
|
||||
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == row.value.id
|
||||
)
|
||||
if (findIndex != -1) {
|
||||
reverseManageStore.selectBookTaskDetail[findIndex].sceneTags = checkedSceneTags
|
||||
}
|
||||
let res = await window.db.UpdateBookTaskDetailData(row.value.id, {
|
||||
sceneTags: checkedSceneTags
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染tag
|
||||
const renderTag = ({ option, handleClose }) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
size: 'tiny',
|
||||
type: 'success',
|
||||
closable: true,
|
||||
onClose: (e) => {
|
||||
e.stopPropagation()
|
||||
handleClose()
|
||||
// 移除当前option
|
||||
let k = option.key
|
||||
characterTags.value.forEach((item) => {
|
||||
if (item.key == k) {
|
||||
item.checked = false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ default: () => option.label }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateSelect">
|
||||
<n-button
|
||||
size="tiny"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
@click="TranslateSelect('english')"
|
||||
>翻译英文</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-button
|
||||
size="tiny"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
@click="SinglePrompt"
|
||||
>单句推理</n-button
|
||||
>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
size="tiny"
|
||||
@click="NextGptPrompt"
|
||||
>下推理</n-button
|
||||
>
|
||||
<n-input
|
||||
size="tiny"
|
||||
placeholder="请生成或推理提示词"
|
||||
type="textarea"
|
||||
v-model:value="row.gptPrompt"
|
||||
@input="InputDebounced"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { NInput, NButton, NDropdown, useMessage } from 'naive-ui'
|
||||
import { debounce } from 'lodash'
|
||||
import { TranslateType } from '../../../../../define/enum/translate'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
|
||||
let props = defineProps({
|
||||
initData: undefined,
|
||||
index: undefined
|
||||
})
|
||||
let message = useMessage()
|
||||
let row = ref(props.initData)
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
|
||||
let translate_options = ref([
|
||||
{ label: '翻译中文', key: 'chinese' },
|
||||
{ label: '下翻译 英文 2 中文', key: 'down_english_chinese' },
|
||||
{ label: '下翻译 中文 2 英文', key: 'down_chinese_english' }
|
||||
])
|
||||
|
||||
let InputDebounced = debounce(handleInput, 500)
|
||||
async function handleInput() {
|
||||
console.log('开始修改')
|
||||
let res = await window.db.UpdateBookTaskDetailData(row.value.id, {
|
||||
gptPrompt: row.value.gptPrompt
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 单句翻译和下翻译
|
||||
async function TranslateSelect(key) {
|
||||
let translateData = []
|
||||
if (key == 'english') {
|
||||
let tempData = {
|
||||
text: row.value.gptPrompt,
|
||||
from: 'zh',
|
||||
to: 'en',
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false,
|
||||
bookTaskDetailId: row.value.id,
|
||||
responseMessgeName: DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN
|
||||
}
|
||||
translateData.push(tempData)
|
||||
} else if (key == 'chinese') {
|
||||
let tempData = {
|
||||
text: row.value.gptPrompt,
|
||||
from: 'en',
|
||||
to: 'zh',
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false,
|
||||
bookTaskDetailId: row.value.id,
|
||||
responseMessgeName: DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN
|
||||
}
|
||||
translateData.push(tempData)
|
||||
} else if (key == 'down_english_chinese') {
|
||||
// 讲下面的所有的进行翻译
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
if (i >= props.index) {
|
||||
translateData.push({
|
||||
text: row.value.gptPrompt,
|
||||
from: 'en',
|
||||
to: 'zh',
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false,
|
||||
bookTaskDetailId: element.id,
|
||||
responseMessgeName: DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (key == 'down_chinese_english') {
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
if (i >= props.index) {
|
||||
translateData.push({
|
||||
text: row.value.gptPrompt,
|
||||
from: 'zh',
|
||||
to: 'en',
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false,
|
||||
bookTaskDetailId: element.id,
|
||||
responseMessgeName: DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.error('未知的翻译类型')
|
||||
}
|
||||
|
||||
if (translateData.length <= 0) {
|
||||
message.error('没有需要翻译的数据')
|
||||
return
|
||||
}
|
||||
// 开始提交翻译任务
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = `翻译中 ... 0 / ${translateData.length}`
|
||||
// 开始翻译
|
||||
let res = await window.translate.TranslateNowReturn(translateData)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
}
|
||||
|
||||
// 单句推理
|
||||
async function SinglePrompt() {
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '即将开始执行推理提示词任务,请稍等。。。'
|
||||
let res = await window.book.OriginalGetPrompt(row.value.id, OperateBookType.BOOKTASKDETAIL, true)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessage(res)
|
||||
}
|
||||
|
||||
// 下推理
|
||||
async function NextGptPrompt() {
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '即将开始执行推理提示词任务,请稍等。。。'
|
||||
let res = await window.book.OriginalGetPrompt(row.value.id, OperateBookType.UNDERBOOKTASK, true)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessage(res)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<span style="margin-right: 5px">人物/场景</span>
|
||||
<!-- <n-select
|
||||
style="width: 75px"
|
||||
size="tiny"
|
||||
:options="characterSelectMoedlOptions"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
v-model:value="softwareStore.globalSetting.character_select_model"
|
||||
placeholder="选择模式"
|
||||
></n-select> -->
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
text
|
||||
color="#e18a3b"
|
||||
style="margin-left: 5px; font-size: 24px"
|
||||
@click="RefreshTagData"
|
||||
>
|
||||
<n-icon>
|
||||
<ReloadCircle />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>刷新任务标签数据</span>
|
||||
</n-popover>
|
||||
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#c0d695"
|
||||
style="margin-left: 5px; font-size: 24px"
|
||||
text
|
||||
@click="AutoMatchUser"
|
||||
>
|
||||
<n-icon>
|
||||
<ExtensionPuzzle />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>自动匹配人物</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, toRaw } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NButton,
|
||||
useDialog,
|
||||
NInput,
|
||||
NTag,
|
||||
NSelect,
|
||||
NIcon,
|
||||
NPopover
|
||||
} from 'naive-ui'
|
||||
import { ReloadCircle, ExtensionPuzzle } from '@vicons/ionicons5'
|
||||
import { BookTagSelectType } from '../../../../../define/enum/bookEnum'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
|
||||
let dialog = useDialog()
|
||||
let message = useMessage()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
|
||||
let characterSelectMoedlOptions = ref([
|
||||
{ label: '标签', value: BookTagSelectType.TAG },
|
||||
{ label: '下拉', value: BookTagSelectType.DROP }
|
||||
])
|
||||
|
||||
// 重新加载预设数据
|
||||
async function RefreshTagData() {
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在刷新任务标签数据,请稍等...'
|
||||
let res = await reverseManageStore.GetPresetData()
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 1) {
|
||||
message.success('刷新任务标签数据成功')
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 自动匹配人物
|
||||
async function AutoMatchUser() {
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在自动匹配人物,请稍等...'
|
||||
await window.mj.AutoMatchUser(
|
||||
JSON.stringify(reverseManageStore.selectBookTaskDetail),
|
||||
async (res) => {
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 1) {
|
||||
message.success('自动匹配人物成功')
|
||||
// 这边修改数据
|
||||
console.log('AutoMatchUser', res.data)
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
let element = res.data[i]
|
||||
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == element.id
|
||||
)
|
||||
if (findIndex != -1) {
|
||||
// TODO 这边后面修改数据库,这边要修改数据,讲key 修改为id
|
||||
reverseManageStore.selectBookTaskDetail[findIndex].characterTags =
|
||||
element.match_character.map((item) => item.key)
|
||||
// TODO 后面修改为后端报错
|
||||
// 手动保存一下
|
||||
let res = await window.db.UpdateBookTaskDetailData(element.id, {
|
||||
characterTags: element.match_character.map((item) => item.key)
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 更新图片生成类型
|
||||
async function UpdateImageGenerateCategory(value, options) {
|
||||
softwareStore.globalSetting.character_select_model = value
|
||||
|
||||
// 同步修改下数据库文件
|
||||
let saveRes = await window.setting.SaveSoftWareSetting({
|
||||
globalSetting: JSON.stringify(toRaw(softwareStore.globalSetting))
|
||||
})
|
||||
if (saveRes.code == 0) {
|
||||
message.error(saveRes.message)
|
||||
return
|
||||
}
|
||||
// 保存文件
|
||||
window.api.ModifySampleSetting(
|
||||
JSON.stringify({
|
||||
character_select_model: value
|
||||
}),
|
||||
(value) => {
|
||||
if (value.code == 1) {
|
||||
window.api.getSettingDafultData((value) => {
|
||||
window.config = value
|
||||
softwareStore.globalSetting = value
|
||||
})
|
||||
message.success('标签选择方式修改成功')
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,291 @@
|
||||
<template>
|
||||
<div>
|
||||
<span style="margin-right: 5px">推理提示词</span>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="AddPrefix"
|
||||
>通用前缀</n-button
|
||||
>
|
||||
</template>
|
||||
<span>添加通用前缀,只能添加英文,并且通用前缀不为空,推理会自动添加前缀</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="AddSuffix"
|
||||
>通用后缀</n-button
|
||||
>
|
||||
</template>
|
||||
<span>添加通用后缀,只能添加英文,并且通用后缀不为空,推理会自动添加后缀</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="SelectStyle"
|
||||
>风格</n-button
|
||||
>
|
||||
</template>
|
||||
<span>选择生成图片的风格</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<span>
|
||||
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateAll">
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="TranslateAll('english')"
|
||||
>一键翻译</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
<span
|
||||
>将下面的推理提示词或者是手写的提示词翻译成英文(只会翻译存在中文的数据),下面的翻译中文会将所有的推理提示词翻译为中文(全部都会翻译)</span
|
||||
>
|
||||
</n-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, h, toRaw } from 'vue'
|
||||
import { NPopover, NButton, NDropdown, useMessage, useDialog } from 'naive-ui'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import SelectImageStyle from '../../Components/SelectImageStyle.vue'
|
||||
import InputDialogContent from '../../Original/Components/InputDialogContent.vue'
|
||||
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { TranslateType } from '../../../../../define/enum/translate'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let translate_options = ref([{ label: '一键 英文 2 中文', key: 'chinese' }])
|
||||
let selectStyleRef = ref(null)
|
||||
let prefixPromptRef = ref(null)
|
||||
let suffixPromptRef = ref(null)
|
||||
|
||||
// 添加/修改通用前缀
|
||||
async function AddPrefix() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加前缀',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: prefixPromptRef,
|
||||
initData: reverseManageStore.selectBookTask.prefixPrompt,
|
||||
placeholder: '请输入前缀'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
let prefix = prefixPromptRef.value.data
|
||||
reverseManageStore.selectBookTask.prefixPrompt = prefix
|
||||
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
|
||||
prefixPrompt: prefix
|
||||
})
|
||||
if (res.code == 1) {
|
||||
message.success('修改小说通用前缀成功')
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 添加/修改通用前缀
|
||||
async function AddSuffix() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加后缀',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: suffixPromptRef,
|
||||
initData: reverseManageStore.selectBookTask.suffixPrompt,
|
||||
placeholder: '请输入后缀'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
let suffix = suffixPromptRef.value.data
|
||||
reverseManageStore.selectBookTask.suffixPrompt = suffix
|
||||
let res = await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
|
||||
suffixPrompt: suffix
|
||||
})
|
||||
if (res.code == 1) {
|
||||
message.success('修改小说人物通用后缀成功')
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 一键 中文翻译英文
|
||||
async function TranslateChineseToEnglish() {
|
||||
let da = dialog.warning({
|
||||
title: '一键翻译英文提示',
|
||||
content: `一键翻译英文,会将所有的分镜推理提示词中的中文翻译为英文,没有中文不翻译,当前执行并发为 ${softwareStore.globalSetting.task_number},在执行翻译时不可停止,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
// 获取数据
|
||||
let translateData = []
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
if (isEmpty(element.gptPrompt)) {
|
||||
continue
|
||||
}
|
||||
// 判断是不是有中文
|
||||
if (!ContainsChineseOrPunctuation(element.gptPrompt)) {
|
||||
continue
|
||||
}
|
||||
let tempData = {
|
||||
text: element.gptPrompt,
|
||||
from: 'zh',
|
||||
to: 'en',
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
|
||||
bookTaskDetailId: element.id,
|
||||
responseMessgeName: DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN
|
||||
}
|
||||
translateData.push(tempData)
|
||||
}
|
||||
|
||||
if (translateData.length <= 0) {
|
||||
message.error('当前所有分镜中不存在推理提示词或者是提示词中没有中文')
|
||||
return
|
||||
}
|
||||
// 开始提交翻译任务
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = `翻译中 ... 0 / ${translateData.length}`
|
||||
// 开始翻译
|
||||
let res = await window.translate.TranslateNowReturn(translateData)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 一键 英文翻译中文
|
||||
async function TranslateEnglishToChinese() {
|
||||
let da = dialog.warning({
|
||||
title: '一键翻译中文提示',
|
||||
content: `一键翻译中文,会将所有的分镜推理提示词翻译为中文,当前执行并发为 ${softwareStore.globalSetting.task_number},在执行翻译时不可停止,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
// 获取数据
|
||||
let translateData = []
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
if (isEmpty(element.gptPrompt)) {
|
||||
continue
|
||||
}
|
||||
let tempData = {
|
||||
text: element.gptPrompt,
|
||||
from: 'en',
|
||||
to: 'zh',
|
||||
type: TranslateType.GPT_PROMPT_TRANSLATE,
|
||||
isSplit: false, // 该参数设置为true时,会将文本按照标点符号分割成多个句子进行翻译
|
||||
bookTaskDetailId: element.id,
|
||||
responseMessgeName: DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN
|
||||
}
|
||||
translateData.push(tempData)
|
||||
}
|
||||
|
||||
if (translateData.length <= 0) {
|
||||
message.error('当前所有分镜中不存在推理提示词')
|
||||
return
|
||||
}
|
||||
// 开始提交翻译任务
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = `翻译中 ... 0 / ${translateData.length}`
|
||||
// 开始翻译
|
||||
let res = await window.translate.TranslateNowReturn(translateData)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 一键翻译
|
||||
async function TranslateAll(key) {
|
||||
if (key == 'english') {
|
||||
// 中文翻译英文
|
||||
await TranslateChineseToEnglish()
|
||||
} else if (key == 'chinese') {
|
||||
await TranslateEnglishToChinese()
|
||||
} else {
|
||||
message.error('未知的翻译类型')
|
||||
}
|
||||
}
|
||||
|
||||
// 选择风格
|
||||
async function SelectStyle() {
|
||||
// 先获取风格
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.8
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(SelectImageStyle, {
|
||||
height: dialogHeight,
|
||||
ref: selectStyleRef,
|
||||
imageStyle: reverseManageStore.selectBookTask.imageStyle,
|
||||
customizeImageStyle: reverseManageStore.selectBookTask.customizeImageStyle
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
debugger
|
||||
console.log(selectStyleRef)
|
||||
// 将数据保存到当前的数据中
|
||||
// reverseManageStore.selectBookTask.styleList = toRaw(selectStyleRef.value.selectStyle)
|
||||
|
||||
// 保存当前的风格到数据库
|
||||
let res = await window.book.SaveImageStyle(
|
||||
toRaw(selectStyleRef.value.selectStyle),
|
||||
reverseManageStore.selectBookTask.id
|
||||
)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
}
|
||||
reverseManageStore.selectBookTask.imageStyle = res.data.imageStyle
|
||||
reverseManageStore.selectBookTask.customizeImageStyle = res.data.customizeImageStyle
|
||||
message.success('保存成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<span style="margin-right: 5px">提示词命令</span>
|
||||
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
@click="SetMergeModel"
|
||||
text
|
||||
style="font-size: 24px"
|
||||
>
|
||||
<n-icon>
|
||||
<Construct />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>设置合并排序模式</span>
|
||||
</n-popover>
|
||||
<n-dropdown trigger="hover" :options="MergePromptOptions" @select="MergePromptSelect">
|
||||
<n-button
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MergePrompt(null)"
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
>合并命令</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ZHUYANTUO"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MJBadPromptCheck"
|
||||
>敏感词检查</n-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, h, toRaw, warn } from 'vue'
|
||||
import { NPopover, NButton, NDropdown, NIcon, useDialog, useMessage } from 'naive-ui'
|
||||
import { Construct } from '@vicons/ionicons5'
|
||||
import DynamicTagsSelect from '../../Components/DynamicTagsSelect.vue'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
|
||||
let MergePromptOptions = ref([
|
||||
{ label: 'SD模式合并', key: 'sd_merge' },
|
||||
{ label: 'MJ模式合并', key: 'mj_merge' }
|
||||
])
|
||||
|
||||
/**
|
||||
* 保存合并模式的方法
|
||||
* @param value
|
||||
* @param options
|
||||
*/
|
||||
async function SaveFunction(value, options) {
|
||||
// 判断当前的数据是不是options中的数据数量是不是相同
|
||||
if (value.length != options.length) {
|
||||
message.error('要保存的数据量和下拉选择的的数据量不一致')
|
||||
return
|
||||
}
|
||||
// 开始保存
|
||||
await window.pmpt.SavePromptSort(value, (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
reutrn
|
||||
}
|
||||
message.success(value.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置合并的模式
|
||||
*/
|
||||
async function SetMergeModel() {
|
||||
let promptSortRes = await window.pmpt.GetPromptSort()
|
||||
if (promptSortRes.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
let promptSort = promptSortRes.data
|
||||
// 获取当前的排序数据
|
||||
await window.pmpt.GetPromptSortOptions((value) => {
|
||||
let options = value.data
|
||||
|
||||
dialog.create({
|
||||
name: 'setMergeModel',
|
||||
title: '设置合并排序模式',
|
||||
content: () =>
|
||||
h(DynamicTagsSelect, {
|
||||
options: options,
|
||||
tagType: 'success',
|
||||
placeholder: '选择当前的prompt排序方式',
|
||||
SaveFunction: SaveFunction,
|
||||
initData: promptSort,
|
||||
max: 4
|
||||
}),
|
||||
style: `width : 500px; `,
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 合并提示词的方法
|
||||
async function MergePrompt(type = null) {
|
||||
let image_generate_category = reverseManageStore.selectBookTask.imageCategory
|
||||
let mergeType = 'mj_merge'
|
||||
if (!type) {
|
||||
if (image_generate_category == 'sd') {
|
||||
mergeType = 'sd_merge'
|
||||
} else if (image_generate_category == 'mj') {
|
||||
mergeType = 'mj_merge'
|
||||
} else if (image_generate_category == 'd3') {
|
||||
message.error('D3模式不支持')
|
||||
} else if (image_generate_category == 'flux-api' || image_generate_category == 'flux-forge') {
|
||||
mergeType = 'sd_merge'
|
||||
} else {
|
||||
throw new Error('未知的合并模式,请检查')
|
||||
}
|
||||
} else {
|
||||
mergeType = type
|
||||
}
|
||||
|
||||
// 合并之前,要确认保存
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '合并命令中。。。'
|
||||
let res = await window.book.MergePrompt(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
mergeType,
|
||||
OperateBookType.BOOKTASK
|
||||
)
|
||||
softwareStore.spin.spinning = false
|
||||
if (res.code == 0) {
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
return
|
||||
}
|
||||
// 修改前端显示数据
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
const element = res.data[i]
|
||||
let selectBookDetailIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == element.id
|
||||
)
|
||||
reverseManageStore.selectBookTaskDetail[selectBookDetailIndex].prompt = element.prompt
|
||||
}
|
||||
message.success(res.message)
|
||||
}
|
||||
|
||||
// 下拉框合并提示词
|
||||
async function MergePromptSelect(key) {
|
||||
if (key == 'sd_merge') {
|
||||
await MergePrompt('sd_merge')
|
||||
} else if (key == 'mj_merge') {
|
||||
await MergePrompt('mj_merge')
|
||||
} else {
|
||||
message.error('未知的合并模式,请检查')
|
||||
}
|
||||
}
|
||||
|
||||
// 敏感词检测
|
||||
async function MJBadPromptCheck() {
|
||||
message.error('敏感词检测功能暂未开放')
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-button
|
||||
size="tiny"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
@click="SingleMergePrompt"
|
||||
>单句合并</n-button
|
||||
>
|
||||
<n-button
|
||||
size="tiny"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
@click="SingleGenerateImage"
|
||||
>单句生图</n-button
|
||||
>
|
||||
<n-button
|
||||
size="tiny"
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
@click="NextGenerateImage"
|
||||
>下生图</n-button
|
||||
>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
style="margin-right: 5px"
|
||||
size="tiny"
|
||||
@click="ImportMJImageUrl"
|
||||
>导入图片</n-button
|
||||
>
|
||||
</template>
|
||||
<span>粘贴MJ里面的图片链接。自动下载到对应的行。并分割。</span>
|
||||
</n-popover>
|
||||
|
||||
<n-input
|
||||
:status="input_status"
|
||||
size="tiny"
|
||||
placeholder="请生成出图命令或是提示词"
|
||||
type="textarea"
|
||||
v-model:value="row.prompt"
|
||||
@input="InputDebounced"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { NButton, NInput, NPopover, useMessage } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import {
|
||||
BookBackTaskType,
|
||||
BookImageCategory,
|
||||
OperateBookType,
|
||||
TaskExecuteType
|
||||
} from '../../../../../define/enum/bookEnum'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { debounce } from 'lodash'
|
||||
let props = defineProps({
|
||||
initData: undefined,
|
||||
index: undefined
|
||||
})
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
|
||||
let row = ref(props.initData)
|
||||
|
||||
let InputDebounced = debounce(handleInput, 500)
|
||||
async function handleInput() {
|
||||
console.log('开始修改')
|
||||
let res = await window.db.UpdateBookTaskDetailData(row.value.id, {
|
||||
prompt: row.value.prompt
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 单句合并
|
||||
async function SingleMergePrompt() {
|
||||
let image_generate_category = reverseManageStore.selectBookTask.imageCategory
|
||||
let mergeType = 'mj_merge'
|
||||
if (image_generate_category == 'sd') {
|
||||
mergeType = 'sd_merge'
|
||||
} else if (image_generate_category == 'mj') {
|
||||
mergeType = 'mj_merge'
|
||||
} else if (image_generate_category == 'd3') {
|
||||
message.error('D3模式不支持')
|
||||
} else if (image_generate_category == 'flux-api' || image_generate_category == 'flux-forge') {
|
||||
mergeType = 'sd_merge'
|
||||
} else {
|
||||
throw new Error('未知的合并模式,请检查')
|
||||
}
|
||||
|
||||
let res = await window.book.MergePrompt(row.value.id, mergeType, OperateBookType.BOOKTASKDETAIL)
|
||||
if (res.code == 1) {
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
const element = res.data[i]
|
||||
let selectBookDetailIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == element.id
|
||||
)
|
||||
reverseManageStore.selectBookTaskDetail[selectBookDetailIndex].prompt = element.prompt
|
||||
}
|
||||
message.success(res.message)
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 单句生图
|
||||
async function SingleGenerateImage() {
|
||||
let res = undefined
|
||||
// 判断当前是不是被锁定
|
||||
if (row.value.imageLock) {
|
||||
message.error('当前分镜图片已被锁定,无法生成图片')
|
||||
return
|
||||
}
|
||||
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
|
||||
res = await window.mj.AddMJGenerateImageTask(
|
||||
row.value.id,
|
||||
OperateBookType.BOOKTASKDETAIL,
|
||||
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN,
|
||||
true
|
||||
)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
|
||||
// 直接添加SD出图任务
|
||||
res = await window.task.AddBookBackTask(
|
||||
reverseManageStore.selectBook.id,
|
||||
BookBackTaskType.SD_IMAGE,
|
||||
TaskExecuteType.AUTO,
|
||||
reverseManageStore.selectBookTask.id,
|
||||
row.value.id,
|
||||
DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
|
||||
)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
|
||||
res = await window.task.AddBookBackTask(
|
||||
reverseManageStore.selectBook.id,
|
||||
BookBackTaskType.D3_IMAGE,
|
||||
TaskExecuteType.AUTO,
|
||||
reverseManageStore.selectBookTask.id,
|
||||
row.value.id,
|
||||
DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
|
||||
)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
|
||||
res = await window.task.AddBookBackTask(
|
||||
reverseManageStore.selectBook.id,
|
||||
BookBackTaskType.FLUX_API_IMAGE,
|
||||
TaskExecuteType.AUTO,
|
||||
reverseManageStore.selectBookTask.id,
|
||||
row.value.id,
|
||||
DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
|
||||
)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) {
|
||||
res = await window.task.AddBookBackTask(
|
||||
reverseManageStore.selectBook.id,
|
||||
BookBackTaskType.FLUX_FORGE_IMAGE,
|
||||
TaskExecuteType.AUTO,
|
||||
reverseManageStore.selectBookTask.id,
|
||||
row.value.id,
|
||||
DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
|
||||
)
|
||||
} else {
|
||||
message.error('不支持的生图类型')
|
||||
return
|
||||
}
|
||||
if (res?.code == 1) {
|
||||
message.success(res.message)
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 下生图
|
||||
async function NextGenerateImage() {
|
||||
debugger
|
||||
let res = undefined
|
||||
let bookTaskDetails = reverseManageStore.selectBookTaskDetail.filter(
|
||||
(item, index) => index >= props.index && !item.imageLock
|
||||
)
|
||||
if (bookTaskDetails.length <= 0) {
|
||||
message.error('未找到可生成图片的分镜,或者分镜都被锁定')
|
||||
return
|
||||
}
|
||||
|
||||
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
|
||||
// 添加MJ生图任务
|
||||
res = await window.mj.AddMJGenerateImageTask(
|
||||
row.value.id,
|
||||
OperateBookType.UNDERBOOKTASK,
|
||||
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN
|
||||
)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
|
||||
// 添加SD出图任务
|
||||
let tasksList = []
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i]
|
||||
tasksList.push({
|
||||
bookId: reverseManageStore.selectBook.id,
|
||||
type: BookBackTaskType.SD_IMAGE,
|
||||
executeType: TaskExecuteType.AUTO,
|
||||
bookTaskId: reverseManageStore.selectBookTask.id,
|
||||
bookTaskDetailId: element.id,
|
||||
messageName: DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
|
||||
})
|
||||
}
|
||||
res = await window.task.AddMultiBookBackTask(tasksList)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) {
|
||||
// 本地的forge flux
|
||||
let tasksList = []
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i]
|
||||
tasksList.push({
|
||||
bookId: reverseManageStore.selectBook.id,
|
||||
type: BookBackTaskType.FLUX_FORGE_IMAGE,
|
||||
executeType: TaskExecuteType.AUTO,
|
||||
bookTaskId: reverseManageStore.selectBookTask.id,
|
||||
bookTaskDetailId: element.id,
|
||||
messageName: DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
|
||||
})
|
||||
}
|
||||
res = await window.task.AddMultiBookBackTask(tasksList)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
|
||||
// 本地的forge flux
|
||||
let tasksList = []
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i]
|
||||
tasksList.push({
|
||||
bookId: reverseManageStore.selectBook.id,
|
||||
type: BookBackTaskType.FLUX_API_IMAGE,
|
||||
executeType: TaskExecuteType.AUTO,
|
||||
bookTaskId: reverseManageStore.selectBookTask.id,
|
||||
bookTaskDetailId: element.id,
|
||||
messageName: DEFINE_STRING.BOOK.FLUX_API_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++) {
|
||||
const element = bookTaskDetails[i]
|
||||
tasksList.push({
|
||||
bookId: reverseManageStore.selectBook.id,
|
||||
type: BookBackTaskType.D3_IMAGE,
|
||||
executeType: TaskExecuteType.AUTO,
|
||||
bookTaskId: reverseManageStore.selectBookTask.id,
|
||||
bookTaskDetailId: element.id,
|
||||
messageName: DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
|
||||
})
|
||||
}
|
||||
res = await window.task.AddMultiBookBackTask(tasksList)
|
||||
} else {
|
||||
message.error('不支持的生图类型')
|
||||
return
|
||||
}
|
||||
if (res?.code == 1) {
|
||||
message.success(res.message)
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,502 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<n-button strong secondary @click="ReturnMain" size="small">返回</n-button>
|
||||
<n-divider vertical style="height: 30px" />
|
||||
<!-- <n-button strong secondary @click="ReturnMain" size="small">一键全自动</n-button>
|
||||
<n-divider vertical style="height: 30px" /> -->
|
||||
<n-button :color="softwareStore.SoftColor.ORANGE" @click="ImportWord" size="small"
|
||||
>导入字幕/SRT</n-button
|
||||
>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ORANGE"
|
||||
style="margin-left: 10px"
|
||||
@click="CharacterLibrary"
|
||||
size="small"
|
||||
>角色分析(标签集)</n-button
|
||||
>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ORANGE"
|
||||
style="margin-left: 10px"
|
||||
@click="ImportPrompt"
|
||||
size="small"
|
||||
>导入提示词</n-button
|
||||
>
|
||||
<n-dropdown trigger="hover" :options="GptButtonOptions" @select="ButtonSelect">
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ORANGE"
|
||||
style="margin-left: 10px"
|
||||
@click="GetPromptAll"
|
||||
size="small"
|
||||
>一键推理提示词</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-dropdown trigger="hover" :options="GenerateImageOptions" @select="ButtonSelect">
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ORANGE"
|
||||
style="margin-left: 10px"
|
||||
@click="GenerateImageAll(true)"
|
||||
size="small"
|
||||
>生成所有的图片</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<n-dropdown trigger="hover" :options="ResetDataOptions" @select="ButtonSelect">
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ORANGE"
|
||||
style="margin-left: 10px"
|
||||
@click="resetALLData"
|
||||
size="small"
|
||||
>重置数据</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.ORANGE"
|
||||
style="margin-left: 10px"
|
||||
@click="OpenPromptSetting"
|
||||
size="small"
|
||||
>设置</n-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h, ref } from 'vue'
|
||||
import { NButton, useDialog, NPopover, NDropdown, NDivider, useMessage } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import ImportWordAndSrt from '../Components/ImportWord/ImportWordAndSrt.vue'
|
||||
import CharacterAnalyze from '../Components/PresetLibrary/CharacterAnalyze.vue'
|
||||
import PromptSetting from '../../Original/Components/PromptSetting.vue'
|
||||
import {
|
||||
BookBackTaskType,
|
||||
BookImageCategory,
|
||||
BookType,
|
||||
OperateBookType,
|
||||
TaskExecuteType
|
||||
} from '../../../../../define/enum/bookEnum'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let dialog = useDialog()
|
||||
let message = useMessage()
|
||||
let router = useRouter()
|
||||
|
||||
let GptButtonOptions = ref([
|
||||
{ label: '推理空白提示词', key: 'get_space_prompt' },
|
||||
{ label: '重置推理提示词', key: 'reset_prompt' }
|
||||
])
|
||||
let GenerateImageOptions = ref([
|
||||
{ label: '生成未出图分镜', key: 'generate_space_image' },
|
||||
{ label: '停止生成图片任务', key: 'stop_generate_image' }
|
||||
])
|
||||
let ResetDataOptions = ref([
|
||||
{ label: '重置GPT提示词', key: 'reset_gpt_prompt' },
|
||||
{ label: '重置提示词', key: 'reset_prompt' },
|
||||
{ label: '重置图片', key: 'reset_image' }
|
||||
])
|
||||
|
||||
function ReturnMain() {
|
||||
router.push('/book_management')
|
||||
}
|
||||
|
||||
// 导入原创文案,需对齐数据
|
||||
async function ImportWord() {
|
||||
let dialogWidth = window.innerWidth * 0.95
|
||||
let dialogHeight = window.innerHeight * 0.95
|
||||
|
||||
let tempData = []
|
||||
for (
|
||||
let i = 0;
|
||||
reverseManageStore.selectBookTaskDetail && i < reverseManageStore.selectBookTaskDetail.length;
|
||||
i++
|
||||
) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
tempData.push({
|
||||
no: element.no,
|
||||
id: element.id,
|
||||
lastId: i == 0 ? '' : reverseManageStore.selectBookTaskDetail[i - 1].id,
|
||||
word: element.word,
|
||||
after_gpt: element.afterGpt,
|
||||
subValue: element.subValue ? element.subValue : [],
|
||||
name: element.name + '.png',
|
||||
prompt: element.prompt,
|
||||
timeLimit: element.timeLimit
|
||||
})
|
||||
}
|
||||
|
||||
// ImportWordAndSrt
|
||||
let da = dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(ImportWordAndSrt, {
|
||||
initData: tempData,
|
||||
height: dialogHeight,
|
||||
type: BookType.NEW_ORIGINAL
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
da.destroy()
|
||||
// 重新加载数据
|
||||
softwareStore.loading.originDatatableDataLoading = true
|
||||
let res = await reverseManageStore.GetBookTaskDetail(reverseManageStore.selectBookTask.id)
|
||||
softwareStore.loading.originDatatableDataLoading = false
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 角色分析(标签集)
|
||||
async function CharacterLibrary() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.6
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
let word = '' // 这边要拼接一下word
|
||||
debugger
|
||||
let temp_arr = []
|
||||
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
|
||||
const element = reverseManageStore.selectBookTaskDetail[i]
|
||||
temp_arr.push(element.afterGpt)
|
||||
}
|
||||
word = temp_arr.join('\n')
|
||||
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(CharacterAnalyze, { word: word, height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// // 重新加载数据
|
||||
// // 判断是不是已经有生成(有有生成显示)
|
||||
// await window.api.GetConfigJson(JSON.stringify(['auto_analyze_character', '']), (value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message)
|
||||
// return
|
||||
// }
|
||||
// AnalyzeCharacter.value = value.data
|
||||
// })
|
||||
// // 判断是不是有标签树
|
||||
// await window.api.GetConfigJson(JSON.stringify(['tag_tree', []]), async (value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message)
|
||||
// return
|
||||
// }
|
||||
// tagTreeData.value = value.data
|
||||
// })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GPT推理空白提示词
|
||||
async function GetSpacePrompt() {
|
||||
// GPT推理空白提示词
|
||||
let da = dialog.warning({
|
||||
title: '生成空白提示词提示',
|
||||
content: `即将开始生成 空白 提示词,当前并发数为 ${softwareStore.globalSetting.task_number},在生成提示词时,不可取消,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
let res = undefined
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '即将开始执行推理提示词任务,请稍等。。。'
|
||||
res = await window.book.OriginalGetPrompt(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
OperateBookType.BOOKTASK,
|
||||
false
|
||||
)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 重置所有的GPT提示词数据
|
||||
async function ResetGPTPrompt() {
|
||||
let da = dialog.warning({
|
||||
title: '重置推理提示词提示',
|
||||
content: '执行该操作会删除所有的GPT推理提示词,并且此操作不可逆,是否继续?',
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
softwareStore.spin.spinning = true
|
||||
debugger
|
||||
softwareStore.spin.tip = '正在重置推理提示词,请稍后。。。'
|
||||
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 = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 重置所有的生成图片信息
|
||||
async function ResetImage() {
|
||||
message.info('ResetImage')
|
||||
let da = dialog.warning({
|
||||
title: '重置图片提示',
|
||||
content: '该操作会删除 未被锁定的分镜 的生成图片信息,并且此操作不可逆,是否继续?',
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在重置图片,请稍后。。。'
|
||||
let 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 = []
|
||||
}
|
||||
}
|
||||
message.success(res.message)
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 下拉选项的选择按钮操作
|
||||
async function ButtonSelect(key) {
|
||||
if (key == 'get_space_prompt') {
|
||||
// 推理空白提示词
|
||||
await GetSpacePrompt()
|
||||
} else if (key == 'reset_prompt') {
|
||||
// 重置推理提示词
|
||||
await ResetGPTPrompt()
|
||||
} else if (key == 'generate_space_image') {
|
||||
// 生成未出图分镜
|
||||
await GenerateImageAll(false)
|
||||
} else if (key == 'reset_image') {
|
||||
await ResetImage()
|
||||
} else {
|
||||
message.error('未知的操作')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有的GPT提示词
|
||||
async function GetPromptAll() {
|
||||
let da = dialog.warning({
|
||||
title: '生成提示词提示',
|
||||
content: `即将开始生成提示词,当前并发数为 ${softwareStore.globalSetting.task_number},在生成提示词时,不可取消,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '即将开始执行推理提示词任务,请稍等。。。'
|
||||
let res = await window.book.OriginalGetPrompt(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
OperateBookType.BOOKTASK,
|
||||
true
|
||||
)
|
||||
softwareStore.spin.spinning = false
|
||||
window.api.showGlobalMessageDialog(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定类型为所有书籍任务生成图像
|
||||
* @param {String} type - 小说图像类别
|
||||
* @param {Boolean} cover - 是否覆盖现有图像
|
||||
*/
|
||||
async function AddFluxImageAll(type, cover) {
|
||||
debugger
|
||||
let messageName = DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN
|
||||
let taskType = BookBackTaskType.FLUX_API_IMAGE
|
||||
if (type == BookImageCategory.FLUX_FORGE) {
|
||||
messageName = DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN
|
||||
taskType = BookBackTaskType.FLUX_FORGE_IMAGE
|
||||
} else if (type == BookImageCategory.D3) {
|
||||
messageName = DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN
|
||||
taskType = BookBackTaskType.D3_IMAGE
|
||||
} else if (type == BookImageCategory.SD) {
|
||||
messageName = DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN
|
||||
taskType = BookBackTaskType.SD_IMAGE
|
||||
} else {
|
||||
message.error('未知的图像类别')
|
||||
return
|
||||
}
|
||||
// 这边过滤所有的锁定的分镜
|
||||
let bookTaskDetails = reverseManageStore.selectBookTaskDetail.filter((item) => !item.imageLock)
|
||||
|
||||
if (cover) {
|
||||
// 添加全部的任务
|
||||
let tasks = []
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i]
|
||||
tasks.push({
|
||||
bookId: reverseManageStore.selectBook.id,
|
||||
type: taskType,
|
||||
executeType: TaskExecuteType.AUTO,
|
||||
bookTaskId: reverseManageStore.selectBookTask.id,
|
||||
bookTaskDetailId: element.id,
|
||||
messageName: messageName
|
||||
})
|
||||
}
|
||||
if (tasks.length <= 0) {
|
||||
message.error('没有需要生成的分镜')
|
||||
return
|
||||
}
|
||||
let res = await await window.task.AddMultiBookBackTask(tasks)
|
||||
window.api.showGlobalMessage(res)
|
||||
} else {
|
||||
// 添加未生成的任务
|
||||
let tasks = []
|
||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||
const element = bookTaskDetails[i]
|
||||
if (!element.mjMessage) {
|
||||
tasks.push({
|
||||
bookId: reverseManageStore.selectBook.id,
|
||||
type: taskType,
|
||||
executeType: TaskExecuteType.AUTO,
|
||||
bookTaskId: reverseManageStore.selectBookTask.id,
|
||||
bookTaskDetailId: element.id,
|
||||
messageName: messageName
|
||||
})
|
||||
}
|
||||
}
|
||||
if (tasks.length == 0) {
|
||||
message.error('没有未生成的分镜,或者都被锁定了')
|
||||
return
|
||||
}
|
||||
let res = await await window.task.AddMultiBookBackTask(tasks)
|
||||
window.api.showGlobalMessage(res)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 生成所有图片
|
||||
async function GenerateImageAll(cover = true) {
|
||||
let da = dialog.warning({
|
||||
title: '生成所有图片提示',
|
||||
content: `即将开始生成 ${cover ? '所有的' : '从未生成过的分镜'} 图片,当前的生图模式为 ${
|
||||
reverseManageStore.selectBookTask.imageCategory
|
||||
},开始执行会有1-20秒的延迟,除了被锁定的图片,其余的图片都会重新生成,是否继续?`,
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
da?.destroy()
|
||||
let res = undefined
|
||||
if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.MJ) {
|
||||
res = await window.mj.AddMJGenerateImageTask(
|
||||
reverseManageStore.selectBookTask.id,
|
||||
OperateBookType.BOOKTASK,
|
||||
DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN,
|
||||
cover
|
||||
)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.SD) {
|
||||
await AddFluxImageAll(BookImageCategory.SD, cover)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_FORGE) {
|
||||
await AddFluxImageAll(BookImageCategory.FLUX_FORGE, cover)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.FLUX_API) {
|
||||
await AddFluxImageAll(BookImageCategory.FLUX_FORGE, cover)
|
||||
} else if (reverseManageStore.selectBookTask.imageCategory == BookImageCategory.D3) {
|
||||
await AddFluxImageAll(BookImageCategory.D3, cover)
|
||||
} else {
|
||||
message.error('不支持的生图类型')
|
||||
return
|
||||
}
|
||||
if (res?.code == 1) {
|
||||
message.success(res.message)
|
||||
} else {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新的弹窗
|
||||
*/
|
||||
async function OpenPromptSetting() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.4
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(PromptSetting, {}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false
|
||||
})
|
||||
}
|
||||
|
||||
// 导入提示词
|
||||
async function ImportPrompt() {
|
||||
message.error('导入提示词该方法暂未实现')
|
||||
return
|
||||
// 弹窗选择文件
|
||||
window.api.SelectFile(['txt'], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 拿到提示词文件地址,然后还是获取
|
||||
await window.pmpt.OpenPromptFileTxt(value.value, (res) => {
|
||||
debugger
|
||||
console.log(res)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
// 判断获取的数据是不是小于当前的data的行
|
||||
if (res.data.length > data.value.length) {
|
||||
dialog.warning({
|
||||
title: '提示',
|
||||
content: '导入的数据行数大于当前的数据行数,多余的数据会被删除,是否继续导入?',
|
||||
positiveText: '继续',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
debugger
|
||||
// 开始导入
|
||||
for (let i = 0; i < data.value.length && i < res.data.length; i++) {
|
||||
const element = res.data[i]
|
||||
// 开始修改
|
||||
data.value[i].gpt_prompt = element
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 开始导入
|
||||
for (let i = 0; i < data.value.length && i < res.data.length; i++) {
|
||||
const element = res.data[i]
|
||||
// 开始修改
|
||||
data.value[i].gpt_prompt = element
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-data-table
|
||||
id="1111"
|
||||
:max-height="maxHeight"
|
||||
:loading="softwareStore.loading.originDatatableDataLoading"
|
||||
:row-key="rowKey"
|
||||
:columns="createColumns({})"
|
||||
:data="reverseManageStore.selectBookTaskDetail"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
:scroll-x="0"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
h,
|
||||
onMounted,
|
||||
defineComponent,
|
||||
onUnmounted,
|
||||
watch,
|
||||
toRaw,
|
||||
render,
|
||||
onBeforeUnmount,
|
||||
provide,
|
||||
nextTick
|
||||
} from 'vue'
|
||||
import {
|
||||
NDataTable,
|
||||
NImage,
|
||||
useMessage,
|
||||
NInput,
|
||||
NButton,
|
||||
NSwitch,
|
||||
NPopover,
|
||||
useDialog,
|
||||
NCheckbox,
|
||||
NIcon
|
||||
} from 'naive-ui'
|
||||
import DatatableHeaderAfterGpt from '../Components/DatatableHeaderAfterGpt.vue'
|
||||
import DatatableAfterGpt from '../Components/DatatableAfterGpt.vue'
|
||||
import ODatatableHeaderCharacterAndScene from './ODataTableHeaderCharacterAndScene.vue'
|
||||
import ODatatableCharacterAndScene from './ODataTableCharacterAndScene.vue'
|
||||
import ODataTableGptPrompt from './ODataTableGptPrompt.vue'
|
||||
import ODataTableHeaderGptPrompt from './ODataTableHeaderGptPrompt.vue'
|
||||
import ODataTableHeaderPrompt from './ODatatableHeaderPrompt.vue'
|
||||
import ODatatablePrompt from './ODatatablePrompt.vue'
|
||||
import ODataTableAction from './ODataTableAction.vue'
|
||||
import DatatableHeaderGenerateImage from '../Components/DatatableHeaderGenerateImage.vue'
|
||||
import DatatableGenerateImage from '../Components/DatatableGenerateImage.vue'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { set } from 'lodash'
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let maxHeight = ref(0)
|
||||
let message = useMessage()
|
||||
|
||||
onMounted(async () => {
|
||||
setTimeout(() => {
|
||||
let div = document.getElementsByClassName('empty-margin')
|
||||
for (let i = 0; i < div.length; i++) {
|
||||
div[i].style.padding = '0px 5px'
|
||||
}
|
||||
getMaxHeight()
|
||||
}, 100)
|
||||
// 加载初始数据
|
||||
// 这边开始加载数据(就是小说分镜任务)
|
||||
softwareStore.spin.spinning = false
|
||||
let data = await reverseManageStore.GetBookTaskDetail(reverseManageStore.selectBookTask.id)
|
||||
softwareStore.loading.originDatatableDataLoading = false
|
||||
if (data.code == 0) {
|
||||
message.error(data.message)
|
||||
}
|
||||
|
||||
// 加载全部的人物和场景预设数据
|
||||
let res = await reverseManageStore.GetPresetData()
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
})
|
||||
|
||||
// 计算当前程序界面的高度,减去上下的高度,设置maxHegiht
|
||||
async function getMaxHeight() {
|
||||
let height = window.innerHeight
|
||||
maxHeight.value = height - 100
|
||||
}
|
||||
|
||||
const createColumns = ({}) => {
|
||||
return [
|
||||
{
|
||||
title: 'No.',
|
||||
key: 'no',
|
||||
width: 50,
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h(DatatableHeaderAfterGpt)
|
||||
},
|
||||
key: 'srt',
|
||||
width: '180',
|
||||
className: 'empty-margin',
|
||||
fixed: 'left',
|
||||
render(row, index) {
|
||||
return h(DatatableAfterGpt, { initData: row, index: index })
|
||||
}
|
||||
},
|
||||
{
|
||||
title() {
|
||||
return h(ODatatableHeaderCharacterAndScene)
|
||||
},
|
||||
className: 'empty-margin',
|
||||
key: 'character',
|
||||
width: '210',
|
||||
render(row, index) {
|
||||
return h(ODatatableCharacterAndScene, {
|
||||
initData: row,
|
||||
index: index
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h(ODataTableHeaderGptPrompt)
|
||||
},
|
||||
key: 'gpt_prompt',
|
||||
className: 'empty-margin',
|
||||
resizable: true,
|
||||
minWidth: 330,
|
||||
maxWidth: 700,
|
||||
width: '350',
|
||||
render(row, index) {
|
||||
return h(ODataTableGptPrompt, { initData: row, index: index })
|
||||
}
|
||||
},
|
||||
{
|
||||
// 提示词命令
|
||||
title(row) {
|
||||
return h(ODataTableHeaderPrompt)
|
||||
},
|
||||
key: 'prompt',
|
||||
width: '300',
|
||||
className: 'empty-margin',
|
||||
render(row, index) {
|
||||
return h(ODatatablePrompt, { initData: row, index: index })
|
||||
}
|
||||
},
|
||||
{
|
||||
// 参数
|
||||
title: '参数',
|
||||
key: 'parameter',
|
||||
width: '130',
|
||||
render(row, index) {
|
||||
return h(ODataTableAction, { initData: row, index: index })
|
||||
}
|
||||
},
|
||||
{
|
||||
// 出图
|
||||
title(row) {
|
||||
return h(DatatableHeaderGenerateImage)
|
||||
},
|
||||
key: 'options',
|
||||
className: 'empty-margin',
|
||||
width: '300',
|
||||
render(row, index) {
|
||||
return h(DatatableGenerateImage, {
|
||||
initData: row,
|
||||
index: index
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div>
|
||||
<OriginalMainButton />
|
||||
</div>
|
||||
<div>
|
||||
<OriginalMainDatatale />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../../stores/software'
|
||||
import { useReverseManageStore } from '../../../../../stores/reverseManage'
|
||||
import OriginalMainButton from './OriginalMainButton.vue'
|
||||
import OriginalMainDatatale from './OriginalMainDatatable.vue'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
let softwareStore = useSoftwareStore()
|
||||
let reverseManageStore = useReverseManageStore()
|
||||
let message = useMessage()
|
||||
|
||||
// 获取推理提示词任务返回
|
||||
function OriginalGetPromptReturn(value) {
|
||||
console.log(' OriginalGetPromptReturn ', value)
|
||||
if (value.code == 1) {
|
||||
if (!value.id) {
|
||||
message.error('没有返回的ID')
|
||||
}
|
||||
let index = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
|
||||
if (index == -1) {
|
||||
message.error('没有找到对应的ID')
|
||||
}
|
||||
reverseManageStore.selectBookTaskDetail[index].gptPrompt = value.data.content
|
||||
softwareStore.spin.tip = `正在推理提示词,推理进度 ${value.data.progress.current} / ${value.data.progress.total}`
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: value.code,
|
||||
message: value.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取GPT推理词翻译返回
|
||||
function OriginalGPTPromptReturn(value) {
|
||||
console.log(' OriginalGPTPromptReturn ', value)
|
||||
// 开始修改数据
|
||||
if (value.code == 1 && value.data) {
|
||||
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == value.data.bookTaskDetailId
|
||||
)
|
||||
if (findIndex != -1) {
|
||||
reverseManageStore.selectBookTaskDetail[findIndex].gptPrompt = value.data.prompt
|
||||
}
|
||||
softwareStore.spin.tip = `翻译中 ... ${value.data.progress} / ${value.data.total}`
|
||||
}
|
||||
}
|
||||
|
||||
// 原创MJ生图返回的数据进行修改
|
||||
function OriginalMJImageResponseReturn(value) {
|
||||
//开始修改
|
||||
console.log('OriginalMJImageResponseReturn', value)
|
||||
if (value.data) {
|
||||
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == value.data.id
|
||||
)
|
||||
if (dataIndex >= 0) {
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].mjMessage = value.data
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].outImagePath = value.data.outImagePath
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].subImagePath = value.data.subImagePath
|
||||
if (value.data.status == 'error') {
|
||||
message.error(value.data.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 原创SD生图返回的数据进行修改
|
||||
function OriginalSDImageResponseReturn(value) {
|
||||
console.log('OriginalSDImageResponseReturn', value)
|
||||
// 开始修改
|
||||
if (value.data) {
|
||||
let dataIndex = reverseManageStore.selectBookTaskDetail.findIndex(
|
||||
(item) => item.id == value.data.id
|
||||
)
|
||||
if (dataIndex >= 0) {
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].mjMessage = value.data
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].outImagePath = value.data.outImagePath
|
||||
reverseManageStore.selectBookTaskDetail[dataIndex].subImagePath = value.data.subImagePath
|
||||
}
|
||||
if (value.data.status == 'error') {
|
||||
message.error(value.data.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 监听推理提示词任务返回
|
||||
window.api.setEventListen([DEFINE_STRING.BOOK.ORIGINAL_GPT_PROMPT_RETURN], (value) =>
|
||||
OriginalGetPromptReturn(value)
|
||||
)
|
||||
// 监听GPT推理词翻译返回
|
||||
window.api.setEventListen([DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN], (value) =>
|
||||
OriginalGPTPromptReturn(value)
|
||||
)
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN], (value) =>
|
||||
OriginalMJImageResponseReturn(value)
|
||||
)
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN], (value) =>
|
||||
OriginalSDImageResponseReturn(value)
|
||||
)
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN], (value) =>
|
||||
OriginalSDImageResponseReturn(value)
|
||||
)
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN], (value) =>
|
||||
OriginalSDImageResponseReturn(value)
|
||||
)
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN], (value) => {
|
||||
OriginalSDImageResponseReturn(value)
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.BOOK.ORIGINAL_GPT_PROMPT_RETURN)
|
||||
window.api.removeEventListen(DEFINE_STRING.TRANSLATE.GPT_PROMPT_TRANSLATE_RETRUN)
|
||||
window.api.removeEventListen(DEFINE_STRING.BOOK.MJ_IMAGE_GENERATE_RETURN)
|
||||
window.api.removeEventListen(DEFINE_STRING.BOOK.SD_IMAGE_GENERATE_RETURN)
|
||||
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)
|
||||
})
|
||||
</script>
|
||||
@@ -22,7 +22,7 @@
|
||||
>
|
||||
</n-menu>
|
||||
</n-layout-sider>
|
||||
<n-layout-content content-style="padding: 20px 5px 5px 20px; height:100%">
|
||||
<n-layout-content content-style="padding: 5px 5px 5px 10px; height:100%">
|
||||
<!-- <Setting></Setting> -->
|
||||
<router-view></router-view>
|
||||
</n-layout-content>
|
||||
@@ -201,17 +201,18 @@ export default defineComponent({
|
||||
})
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.SHOW_GLOABAL_MESSAGE], (value) => {
|
||||
window.api.setEventListen([DEFINE_STRING.SHOW_MAIN_MESSAGE], (value) => {
|
||||
let mess = value.message
|
||||
let type = 'success'
|
||||
let title = '成功'
|
||||
if (value.code == 0) {
|
||||
type = 'error'
|
||||
title = '成功但失败1'
|
||||
message.error(mess)
|
||||
} else if (value.code == 2) {
|
||||
;(type = 'warning'), (title = '警告')
|
||||
message.warning(mess)
|
||||
} else if (value.code == 1) {
|
||||
message.success(mess)
|
||||
} else {
|
||||
message.error('未知的提示框类型')
|
||||
}
|
||||
message[type](mess, title)
|
||||
})
|
||||
|
||||
// 弹窗,获取机器码
|
||||
@@ -513,7 +514,7 @@ export default defineComponent({
|
||||
onClick: () => {
|
||||
OpneBackTask()
|
||||
},
|
||||
style : 'font-weight: bold;'
|
||||
style: 'font-weight: bold;'
|
||||
},
|
||||
{
|
||||
default: () => '后台任务'
|
||||
|
||||
@@ -366,7 +366,6 @@ export default defineComponent({
|
||||
let temp_data = props.func.getData(null)
|
||||
// 调用方法,然后,传递一个回调函数
|
||||
props.func.modifyData(() => {
|
||||
|
||||
console.log(temp_data)
|
||||
// 修改当前指定ID的data行
|
||||
let index = temp_data.findIndex((x) => x.id == value.id)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">参数</span>
|
||||
</div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('sd')">
|
||||
<div v-else-if="type.startsWith('data') && (type.endsWith('sd') || type.endsWith('flux-api') || type.endsWith('flux-forge'))">
|
||||
<n-checkbox
|
||||
style="margin-left: 5px; height: 100%"
|
||||
:checked="row.adetailer"
|
||||
@@ -11,6 +11,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('mj')"></div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('mj')"></div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('d3')"></div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -144,6 +144,12 @@ export default defineComponent({
|
||||
)
|
||||
} else if (ca == 'd3') {
|
||||
// D3 生图
|
||||
} else if (ca == 'flux-api') {
|
||||
message.info(ca)
|
||||
// FLUX API 生图
|
||||
|
||||
} else if (ca == 'flux-forge') {
|
||||
message.info(ca)
|
||||
} else {
|
||||
message.error('生图的类别错误')
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div v-if="type == 'title'" style="display: flex">
|
||||
<span>出图</span>
|
||||
<n-select
|
||||
style="width: 70px; margin-left: 5px"
|
||||
style="width: 100px; margin-left: 5px"
|
||||
size="tiny"
|
||||
:options="image_options"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
|
||||
@@ -564,7 +564,7 @@ export default defineComponent({
|
||||
// 计算当前程序界面的高度,减去上下的高度,设置maxHegiht
|
||||
async function getMaxHeight() {
|
||||
let height = window.innerHeight
|
||||
maxHeight.value = height - 120
|
||||
maxHeight.value = height - 100
|
||||
}
|
||||
|
||||
// 监听程序界面的高度变化
|
||||
|
||||
@@ -172,10 +172,7 @@ export default defineComponent({
|
||||
* GPT分析提示词
|
||||
*/
|
||||
async function GPTPrompt() {
|
||||
// 将所有的人物添加到后台队列任务中
|
||||
console.log(data.value)
|
||||
// 处理数据,只推理prompt为空的数据
|
||||
|
||||
let tmpData = cloneDeep(toRaw(data.value))
|
||||
tmpData = tmpData.filter((item) => {
|
||||
return item.gpt_prompt == null || item.gpt_prompt == ''
|
||||
|
||||
@@ -92,6 +92,18 @@
|
||||
placeholder="输入迭代步数"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
style="width: 300px"
|
||||
label="FLXU API模型(和GPT公用一个服务商)"
|
||||
path="flux_model"
|
||||
>
|
||||
<n-select
|
||||
placeholder="请选择FLUX API的生图模型"
|
||||
v-model:value="formValue.flux_model"
|
||||
:options="flux_model_options"
|
||||
>
|
||||
</n-select>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button attr-type="button" type="primary" @click="SaveSDConfig"> 保存设置 </n-button>
|
||||
</n-form-item>
|
||||
@@ -118,6 +130,7 @@ import {
|
||||
NCheckbox
|
||||
} from 'naive-ui'
|
||||
import SDADetailerSetting from '../Components/SDADetailerSetting.vue'
|
||||
import { FLxuAPIImageType } from '../../../../define/enum/image'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -147,7 +160,8 @@ export default defineComponent({
|
||||
style_weight: 1,
|
||||
cfg_scale: 1,
|
||||
sd_models: null,
|
||||
lora: null
|
||||
lora: null,
|
||||
flux_model: FLxuAPIImageType.FLUX
|
||||
})
|
||||
|
||||
let samplers_options = ref([])
|
||||
@@ -274,7 +288,29 @@ export default defineComponent({
|
||||
LoadSDServiceData,
|
||||
samplers_options,
|
||||
lora_options,
|
||||
sd_models_options
|
||||
sd_models_options,
|
||||
flux_model_options: [
|
||||
{
|
||||
label: 'FLUX',
|
||||
value: FLxuAPIImageType.FLUX
|
||||
},
|
||||
{
|
||||
label: 'FLUX DEV',
|
||||
value: FLxuAPIImageType.FLUX_DEV
|
||||
},
|
||||
{
|
||||
label: 'FLUX PRO',
|
||||
value: FLxuAPIImageType.FLUX_PRO
|
||||
},
|
||||
{
|
||||
label: 'FLUX PRO MAX',
|
||||
value: FLxuAPIImageType.FLUX_PRO_MAX
|
||||
},
|
||||
{
|
||||
label: 'FLUX SCHNELL',
|
||||
value: FLxuAPIImageType.FLUX_SCHNELL
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -293,6 +293,15 @@ export default defineComponent({
|
||||
message.error('请检查必填字段')
|
||||
return
|
||||
}
|
||||
// 同步修改下数据库文件
|
||||
let saveRes = await window.setting.SaveSoftWareSetting({
|
||||
globalSetting: JSON.stringify(toRaw(formValue.value))
|
||||
})
|
||||
if (saveRes.code == 0) {
|
||||
message.error(saveRes.message)
|
||||
return
|
||||
}
|
||||
softwareStore.globalSetting = toRaw(formValue.value)
|
||||
// 保存文件
|
||||
window.api.ModifySampleSetting(JSON.stringify(toRaw(formValue.value)), (value) => {
|
||||
if (value.code == 1) {
|
||||
@@ -305,7 +314,6 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
softwareStore.SetSoftware({ theme: formValue.value.theme })
|
||||
await softwareStore.SaveSoftware()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -478,24 +486,6 @@ export default defineComponent({
|
||||
label: '备用主站点',
|
||||
value: LaiAPIType.BAK_MAIN
|
||||
}
|
||||
],
|
||||
translation_options: [
|
||||
{
|
||||
label: '百度翻译',
|
||||
value: TranslateAPIType.BAIDU
|
||||
},
|
||||
{
|
||||
label: '腾讯翻译',
|
||||
value: TranslateAPIType.TENCENT
|
||||
},
|
||||
{
|
||||
label: '火山翻译',
|
||||
value: TranslateAPIType.VOLCENGINE
|
||||
},
|
||||
{
|
||||
label: '阿里翻译',
|
||||
value: TranslateAPIType.ALI
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user