Initial commit 添加MJ功能
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>LAITool 123</title>
|
||||
<!-- <meta http-equiv="Content-Security-Policy"
|
||||
content="default-src '*'; connect-src *; script-src '*'; img-src 'self' file: data: ; style-src 'self' 'unsafe-inline'" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<n-config-provider :hljs="hljs" :theme="themeData == 'dark' ? darkTheme : null">
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-notification-provider>
|
||||
<RouterView></RouterView>
|
||||
</n-notification-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from 'vue'
|
||||
import hljs from 'highlight.js/lib/core'
|
||||
import javascript from 'highlight.js/lib/languages/javascript'
|
||||
import { NMessageProvider, NDialogProvider, NConfigProvider, darkTheme, NNotificationProvider } from "naive-ui"
|
||||
hljs.registerLanguage('javascript', javascript)
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NConfigProvider, NDialogProvider, NMessageProvider, NNotificationProvider
|
||||
},
|
||||
setup() {
|
||||
let themeData = ref("");
|
||||
|
||||
onMounted(() => {
|
||||
window.api.getSettingDafultData(async (value) => {
|
||||
themeData.value = value.theme;
|
||||
await window.darkMode.toggle(value.theme)
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
javascript,
|
||||
hljs,
|
||||
darkTheme,
|
||||
themeData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import './assets/css/styles.less';
|
||||
</style>
|
||||
@@ -0,0 +1,207 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family:
|
||||
Roboto,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Helvetica Neue',
|
||||
'Segoe UI',
|
||||
'Oxygen',
|
||||
'Ubuntu',
|
||||
'Cantarell',
|
||||
'Open Sans',
|
||||
sans-serif;
|
||||
color: #86a5b1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
code {
|
||||
font-weight: 600;
|
||||
padding: 3px 5px;
|
||||
border-radius: 2px;
|
||||
background-color: #26282e;
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
SF Mono,
|
||||
Menlo,
|
||||
Consolas,
|
||||
Liberation Mono,
|
||||
monospace;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #9feaf9;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
#app {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.versions {
|
||||
margin: 0 auto;
|
||||
float: none;
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
font-family: 'Menlo', 'Lucida Console', monospace;
|
||||
color: #c2f5ff;
|
||||
line-height: 1;
|
||||
transition: all 0.3s;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
float: left;
|
||||
border-right: 1px solid rgba(194, 245, 255, 0.4);
|
||||
padding: 0 20px;
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
margin-top: -0.4rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
@media (max-width: 840px) {
|
||||
.versions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
margin-top: -1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
font-weight: 400;
|
||||
color: #c2f5ff;
|
||||
text-align: center;
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 660px) {
|
||||
.hero-logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-tagline {
|
||||
text-align: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.link-item {
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.features {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: -6px;
|
||||
|
||||
.feature-item {
|
||||
width: 33.33%;
|
||||
box-sizing: border-box;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
article {
|
||||
background-color: rgba(194, 245, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #d4e8ef;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
color: #c2f5ff;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 660px) {
|
||||
.features .feature-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.links {
|
||||
flex-direction: column;
|
||||
line-height: 32px;
|
||||
|
||||
.link-dot {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.features .feature-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<symbol id="electron" viewBox="0 0 900 300">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g class="hero-apps" style="fill: #71abb7;">
|
||||
<path d="M15 138l-4.9-.64L8 133l-2.1 4.36L1 138l3.6 3.26-.93 4.74L8 143.67l4.33 2.33-.93-4.74z"></path>
|
||||
<path d="M897.2 114.0912l-5.2 3.63v-2.72c0-.55-.45-1-1-1h-8c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h8c.55 0 1-.45 1-1v-2.72l5.2 3.63c.33.23.8 0 .8-.41v-10c0-.41-.47-.64-.8-.41z"></path>
|
||||
<path d="M65.4 188.625h-1.6c.88 0 1.6-.7313 1.6-1.625v-1.625c0-.8937-.72-1.625-1.6-1.625h-1.6c-.88 0-1.6.7313-1.6 1.625V187c0 .8937.72 1.625 1.6 1.625h-1.6c-.88 0-1.6.7313-1.6 1.625v3.25h1.6v4.875c0 .8937.72 1.625 1.6 1.625h1.6c.88 0 1.6-.7313 1.6-1.625V193.5H67v-3.25c0-.8937-.72-1.625-1.6-1.625zm-3.2-3.25h1.6V187h-1.6v-1.625zm3.2 6.5h-1.6v6.5h-1.6v-6.5h-1.6v-1.625h4.8v1.625zm3.344-5.6875c0-3.2175-2.576-5.8337-5.744-5.8337-3.168 0-5.744 2.6162-5.744 5.8337 0 .455.048.8937.144 1.3162v3.2175c-.976-1.2512-1.6-2.8112-1.6-4.55 0-4.03 3.232-7.3125 7.2-7.3125s7.2 3.2825 7.2 7.3125c0 1.7225-.624 3.2988-1.6 4.55v-3.2175c.096-.4387.144-.8612.144-1.3162zm6.256 0c0 4.68-2.608 8.7425-6.4 10.7738v-1.7063c2.976-1.885 4.944-5.2325 4.944-9.0675 0-5.915-4.72-10.7087-10.544-10.7087-5.824 0-10.544 4.7937-10.544 10.7087 0 3.835 1.968 7.1825 4.944 9.0675v1.7063c-3.792-2.0313-6.4-6.0938-6.4-10.7738C51 179.46 56.376 174 63 174s12 5.46 12 12.1875z"></path>
|
||||
<path d="M830.7143 142.3333c-.8643 0-1.5714.7125-1.5714 1.5834v3.1666c0 .871.707 1.5834 1.5713 1.5834h12.5714c.8643 0 1.5714-.7125 1.5714-1.5834v-3.1666c0-.871-.707-1.5834-1.5713-1.5834h-12.5714zm12.5714 2.771l-1.9643 1.979h-2.357L837 145.1043l-1.9643 1.979h-2.357l-1.9644-1.979v-1.1876h1.1786l1.964 1.979 1.9644-1.979h2.3572l1.9643 1.979 1.964-1.979h1.1787v1.1875zm-9.4286 5.1457h6.286v1.5833h-6.286V150.25zM837 136c-6.0657 0-11 4.6075-11 10.2917v7.125c0 .8708.707 1.5833 1.5714 1.5833h18.8572c.8643 0 1.5714-.7125 1.5714-1.5833v-7.125C848 140.6075 843.0657 136 837 136zm9.4286 17.4167h-18.8572v-7.125c0-4.8925 4.1486-8.851 9.4286-8.851 5.28 0 9.4286 3.9585 9.4286 8.851v7.125z"></path>
|
||||
<path d="M75 91.8065V96h4.1935L90.376 84.8174l-4.1934-4.1935L75 91.8064zm4.1935 2.7957h-2.7957v-2.7957h1.398v1.3978h1.3977v1.398zM93.591 81.6024l-1.817 1.817-4.1935-4.1934 1.817-1.817c.5453-.5453 1.426-.5453 1.971 0l2.2226 2.2224c.5453.5452.5453 1.4258 0 1.971z"></path>
|
||||
<path d="M797 187h4v4h-4v-4zm12-1v19c0 1.1-.9 2-2 2h-20c-1.1 0-2-.9-2-2v-24c0-1.1.9-2 2-2h15l7 7zm-2 1l-6-6h-14v22l6-10 4 8 4-4 6 6v-16z"></path>
|
||||
<path d="M138 125c-6.62 0-12 5-12 11 0 9.04 12 21 12 21s12-11.96 12-21c0-6-5.38-11-12-11zm0 29.1c-3.72-4.06-10-12.22-10-18.1 0-4.96 4.5-9 10-9 2.68 0 5.22.96 7.12 2.72 1.84 1.72 2.88 3.94 2.88 6.28 0 5.88-6.28 14.04-10 18.1zm4-18.1c0 2.22-1.78 4-4 4-2.22 0-4-1.78-4-4 0-2.22 1.78-4 4-4 2.22 0 4 1.78 4 4z"></path>
|
||||
<path d="M771 82h8v2h-8v-2zm0 6h8v-2h-8v2zm0 4h8v-2h-8v2zm22-10h-8v2h8v-2zm0 4h-8v2h8v-2zm0 4h-8v2h8v-2zm4-12v18c0 1.1-.9 2-2 2h-11l-2 2-2-2h-11c-1.1 0-2-.9-2-2V78c0-1.1.9-2 2-2h11l2 2 2-2h11c1.1 0 2 .9 2 2zm-16 1l-1-1h-11v18h12V79zm14-1h-11l-1 1v17h12V78z"></path>
|
||||
<path d="M176 203h-24c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v7l7-7h13c1.1 0 2-.9 2-2v-16c0-1.1-.9-2-2-2zm0 18h-14l-4 4v-4h-6v-16h24v16z"></path>
|
||||
<path d="M673 88.921c0 2.18-.9 4.18-2.34 5.66l-1.34-1.34c1.1-1.12 1.78-2.62 1.78-4.32 0-1.7-.68-3.22-1.78-4.32l1.34-1.34c1.44 1.44 2.34 3.44 2.34 5.66zm-8.56-11.48l-7.44 7.44h-4c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h4l7.44 7.44c.94.94 2.56.28 2.56-1.06v-20.76c0-1.34-1.62-2-2.56-1.06zm11.88.16l-1.34 1.34c2.56 2.56 4.12 6.06 4.12 9.96 0 3.88-1.56 7.4-4.12 9.96l1.34 1.34c2.9-2.9 4.68-6.9 4.68-11.32 0-4.44-1.78-8.44-4.68-11.32v.04zm-2.82 2.82l-1.38 1.34c1.84 1.84 2.96 4.38 2.96 7.16 0 2.78-1.12 5.32-2.96 7.12l1.38 1.34c2.16-2.16 3.5-5.16 3.5-8.46 0-3.3-1.34-6.32-3.5-8.5z"></path>
|
||||
<path d="M226 79h-16c0-1.1-.9-2-2-2h-8c-1.1 0-2 .9-2 2-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V81c0-1.1-.9-2-2-2zm-18 4h-8v-2h8v2zm9 14c-3.88 0-7-3.12-7-7s3.12-7 7-7 7 3.12 7 7-3.12 7-7 7zm5-7c0 2.76-2.26 5-5 5s-5-2.26-5-5 2.26-5 5-5 5 2.26 5 5z"></path>
|
||||
<path d="M725.8393 157h-15.6498c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h15.6298C727 153 727 153.82 727 155c0 1.18 0 2-1.1807 2h.02zm-11.6473-10c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h11.6273C727 143 727 143.82 727 145c0 1.18 0 2-1.1807 2H714.192zM695 146.82l2.8218-2.6 3.182 3.18 8.185-8.4 2.8218 2.82-11.0068 11-6.0038-6zM710.1895 163h15.6298C727 163 727 163.82 727 165c0 1.18 0 2-1.1807 2h-15.6298c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2z"></path>
|
||||
<path d="M223 152v24c0 1.65 1.35 3 3 3h36c1.65 0 3-1.35 3-3v-24c0-1.65-1.35-3-3-3h-36c-1.65 0-3 1.35-3 3zm39 0l-18 15-18-15h36zm-36 4.5l12 9-12 9v-18zm3 19.5l10.5-9 4.5 4.5 4.5-4.5 10.5 9h-30zm33-1.5l-12-9 12-9v18z"></path>
|
||||
<path d="M648 182h-3v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-9v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-3c-1.65 0-3 1.35-3 3v33c0 1.65 1.35 3 3 3h33c1.65 0 3-1.35 3-3v-33c0-1.65-1.35-3-3-3zm0 36h-33v-27h33v27zm-24-33h-3v-6h3v6zm18 0h-3v-6h3v6zm-15 12h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3z"></path>
|
||||
</g>
|
||||
<g class="hero-icons" style="fill: #c2f5ff;">
|
||||
<path d="M441.1132 69.724c7.681 0 13.9075-6.207 13.9075-13.8636 0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9076 6.207-13.9076 13.8634 0 7.6566 6.2266 13.8635 13.9076 13.8635zm0-5.7932c-4.4713 0-8.096-3.6132-8.096-8.0704 0-4.457 3.6247-8.0703 8.096-8.0703 4.4712 0 8.096 3.6133 8.096 8.0704 0 4.4572-3.6248 8.0704-8.096 8.0704z"></path>
|
||||
<path d="M354.8995 220.2693c7.681 0 13.9075-6.207 13.9075-13.8635s-6.2266-13.8634-13.9075-13.8634c-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635zm0-5.793c-4.4713 0-8.096-3.6133-8.096-8.0705 0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703c0 4.4572-3.6247 8.0704-8.096 8.0704z"></path>
|
||||
<path d="M541.0343 206.4058c0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635 7.681 0 13.9075-6.207 13.9075-13.8635zm-5.8115 0c0 4.4572-3.6247 8.0704-8.096 8.0704s-8.096-3.6132-8.096-8.0704c0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703z"></path>
|
||||
<path d="M397.6943 214.5258c9.7012 27.0033 25.5723 43.629 43.419 43.629 13.0157 0 25.0578-8.8443 34.4482-24.4154.827-1.371.3822-3.1507-.9932-3.975-1.3755-.824-3.1607-.3808-3.9876.9902-8.439 13.9938-18.8052 21.6072-29.4675 21.6072-14.8247 0-28.9803-14.8288-37.9476-39.7892-.541-1.506-2.2044-2.2897-3.7153-1.7504-1.511.5394-2.297 2.1975-1.756 3.7036z"></path>
|
||||
<path d="M514.124 163.4733c18.5545-21.85 25.033-43.826 16.122-59.2117-6.557-11.321-20.419-17.2982-38.841-17.537-1.6047-.021-2.9225 1.259-2.9434 2.8586-.0208 1.5996 1.263 2.9132 2.8678 2.934 16.5683.2148 28.5106 5.3642 33.8836 14.641 7.4018 12.7797 1.6243 32.3774-15.5247 52.5722-1.037 1.221-.8844 3.0487.3405 4.0822 1.2248 1.0336 3.0584.8817 4.0952-.3393z"></path>
|
||||
<path d="M411.5672 88.457c-28.3373-5.1448-50.7424.24-59.672 15.6575-6.6635 11.505-4.7588 26.7585 4.6193 43.0637.7982 1.3878 2.574 1.8678 3.966 1.072 1.3923-.7956 1.874-2.5656 1.0756-3.9534-8.4477-14.688-10.0915-27.8524-4.628-37.2857 7.418-12.8074 27.403-17.6105 53.5978-12.8546 1.579.2866 3.092-.7568 3.3794-2.3307.2876-1.5738-.7592-3.082-2.338-3.3687z"></path>
|
||||
<path d="M486.3075 209.2436c5.022-15.998 7.7194-34.453 7.7194-53.6842 0-47.9875-16.849-89.3545-40.8478-99.977-1.4667-.649-3.1837.0098-3.835 1.472-.6512 1.462.01 3.1735 1.4766 3.8227 21.404 9.474 37.3945 48.7337 37.3945 94.6824 0 18.6574-2.612 36.5297-7.454 51.954-.4794 1.5268.3736 3.1518 1.9052 3.6295s3.1617-.3727 3.641-1.8994z"></path>
|
||||
<path d="M466.439 89.4215c-16.7763 3.583-34.6332 10.5886-51.7827 20.4585-42.434 24.4216-70.1147 60.4323-66.2703 86.5432.233 1.5828 1.709 2.6776 3.297 2.4453 1.5877-.2323 2.686-1.7037 2.453-3.2865-3.4135-23.1838 22.825-57.3183 63.426-80.685 16.6365-9.5746 33.9267-16.3578 50.0946-19.811 1.5692-.335 2.5687-1.8748 2.2325-3.439-.336-1.5642-1.8807-2.5606-3.45-2.2255z"></path>
|
||||
<path d="M371.2508 166.997c11.458 12.5516 26.3438 24.3243 43.3203 34.0947 41.106 23.6572 84.866 29.9805 106.4328 15.3217 1.326-.9013 1.668-2.7033.7638-4.025-.904-1.3217-2.712-1.6626-4.0378-.7614-19.302 13.1195-60.871 7.1128-100.253-15.5523-16.469-9.4783-30.8834-20.8782-41.9277-32.9767-1.08-1.1832-2.9178-1.2695-4.1048-.1928-1.187 1.0766-1.2735 2.9086-.1934 4.0918z"></path>
|
||||
<path d="M443.2374 165.3634c-5.432 1.17-10.7838-2.2712-11.9598-7.686-1.1714-5.415 2.2785-10.7498 7.7106-11.922 5.432-1.17 10.7838 2.2712 11.9598 7.686 1.1737 5.415-2.2785 10.7498-7.7106 11.922z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px;">
|
||||
<n-card title="自动保存(在高清的时候)">
|
||||
|
||||
<div class="first_row" style="display: flex; margin-top: 10px">
|
||||
<div>
|
||||
<div style="margin-top: 10px;"></div>
|
||||
<div>
|
||||
<n-checkbox v-model:checked="auto_save_setting.auto_save">自动保存(选择后生效)</n-checkbox>
|
||||
</div>
|
||||
<div>
|
||||
<n-checkbox v-model:checked="auto_save_setting.auto_match">自动匹配(再跑批量的时候匹配)</n-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 10px;">
|
||||
<div style="margin-bottom: 3px;">选择输出文件夹</div>
|
||||
<n-input placeholder="请选择主文件夹" v-model:value="auto_save_setting.main_save_folder"
|
||||
style="width: 200px; margin-right: 10px;"></n-input>
|
||||
<n-button type="info" @click="SelectMainFolder">选择文件夹</n-button>
|
||||
</div>
|
||||
<div style="margin-left: 10px;">
|
||||
<div style="margin-bottom: 3px; opacity: 0; ">12</div>
|
||||
<n-button type="success" @click="SaveImageAutoSaveSetting">保存设置</n-button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="first_row" style="display: flex; margin-top: 15px">
|
||||
<!-- <div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">保存文件夹分类</div>
|
||||
<n-select v-model:value="auto_save_setting.save_folder_classify" :options="setting_options"
|
||||
placeholder="请选择保存文件夹分类" style="width: 200px;">
|
||||
</n-select>
|
||||
</div> -->
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">保存前面多少行(空为全部)</div>
|
||||
<n-input-number v-model:value="auto_save_setting.save_match_count" :show-button="false"
|
||||
placeholder=""></n-input-number>
|
||||
</div>
|
||||
</template>
|
||||
勾选并设置之后,每次出图之后出设置的张数。其余的会自动在设置的文件中随机匹配
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card style=" margin-top: 10px;" title="手动保存">
|
||||
<div class="first_row" style="display: flex; margin-top: 15px">
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">选择保存到的文件夹</div>
|
||||
<n-input placeholder="选择保存到的文件夹" v-model:value="save_setting.save_folder"
|
||||
style="width: 200px; margin-right: 10px;"></n-input>
|
||||
<n-button type="info" @click="SelectOutFolder">选择文件夹</n-button>
|
||||
</div>
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">保存前面多少行(空为全部)</div>
|
||||
<n-input-number v-model:value="save_setting.save_match_count" :show-button="false"
|
||||
placeholder=""></n-input-number>
|
||||
</div>
|
||||
<div style="margin-left: 10px;">
|
||||
<div style="margin-bottom: 3px; opacity: 0; ">12</div>
|
||||
<n-dropdown trigger="hover" :options="options" @select="handleSelect">
|
||||
<n-button type="success" @click="SaveImage">开始另存</n-button>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="first_row" style="display: flex; margin-top: 15px">
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">选择要保存的文件夹</div>
|
||||
<n-input placeholder="选择要保存的文件夹" v-model:value="save_setting.select_folder"
|
||||
style="width: 400px; margin-right: 10px;"></n-input>
|
||||
<n-button type="info" @click="SelectSaveFolder">选择文件夹</n-button>
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, toRaw } from "vue";
|
||||
import { NButton, useMessage, NSpin, NSpace, NSelect, NDivider, NInput, NCode, NCard, NCheckbox, NGradientText, NFormItem, NForm, NInputNumber, NTooltip, NDropdown } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
import { isEmpty, isNull } from "lodash";
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NSpace, NSelect, NDivider, NInput, NCode, NCard, NCheckbox, NGradientText, NForm, NFormItem, NInputNumber, NTooltip, NDropdown
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage()
|
||||
let auto_save_setting = ref({
|
||||
auto_save: false,
|
||||
auto_match: false,
|
||||
save_match_count: 0,
|
||||
main_save_folder: null,
|
||||
save_folder_classify: null,
|
||||
})
|
||||
|
||||
let save_setting = ref({
|
||||
save_folder: null,
|
||||
save_match_count: 0,
|
||||
select_folder: null
|
||||
})
|
||||
let setting_options = ref([])
|
||||
|
||||
/**
|
||||
* 选择保存图片的主文件夹
|
||||
*/
|
||||
async function SelectMainFolder() {
|
||||
await window.api.selectFolder({ default_path: toRaw(auto_save_setting.value).main_save_folder }, (value) => {
|
||||
if (value.length > 0) {
|
||||
auto_save_setting.value.main_save_folder = value[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function InitData() {
|
||||
await window.api.GetAutoSaveImageClassifyOptions((value) => {
|
||||
setting_options.value = value.data
|
||||
})
|
||||
// 获取自动保存设置
|
||||
|
||||
await window.api.GetImageAutoSaveSetting((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
auto_save_setting.value = value.data
|
||||
save_setting.value.save_match_count = value.data.save_match_count
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
await InitData();
|
||||
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存图片自动保存设置
|
||||
*/
|
||||
async function SaveImageAutoSaveSetting() {
|
||||
await window.api.SaveImageAutoSaveSetting(JSON.stringify(toRaw(auto_save_setting.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
return
|
||||
}
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "保存成功" })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉框点击事件
|
||||
* @param {*} value
|
||||
*/
|
||||
async function handleSelect(value) {
|
||||
if (value == 'stop_save') {
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER, "all", "default"], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return
|
||||
}
|
||||
message.success("停止成功")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 另存图片的方法
|
||||
*/
|
||||
async function SaveImage() {
|
||||
debugger
|
||||
if (isEmpty(save_setting.value.save_folder)) {
|
||||
message.error("请选择保存到的文件夹")
|
||||
return
|
||||
}
|
||||
if (save_setting.value.save_match_count == 0) {
|
||||
message.error("请填写保存前面多少行,不能为0")
|
||||
return
|
||||
}
|
||||
|
||||
await window.api.SaveImageToOtherFolder(JSON.stringify(toRaw(save_setting.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success("保存成功")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择要保存的文件夹
|
||||
*/
|
||||
async function SelectSaveFolder() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择要保存到的文件夹
|
||||
*/
|
||||
async function SelectOutFolder() {
|
||||
await window.api.selectFolder({ default_path: toRaw(auto_save_setting.value).main_save_folder }, (value) => {
|
||||
if (value.length > 0) {
|
||||
save_setting.value.save_folder = value[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
auto_save_setting,
|
||||
SelectMainFolder,
|
||||
setting_options,
|
||||
SaveImageAutoSaveSetting,
|
||||
save_setting,
|
||||
options: [
|
||||
{ label: '停止任务', key: 'stop_save' },
|
||||
],
|
||||
handleSelect,
|
||||
SaveImage,
|
||||
SelectOutFolder
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,623 @@
|
||||
<template>
|
||||
<div id="word_option"
|
||||
style=" margin-left: 20px; position:absolute; left: 0; top: 0; z-index: 100; height: 30px; width: 100%;">
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;" @click="AIModifyWord">一键洗稿</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;"
|
||||
@click="ContinueAIModifyWord">继续写稿(勾选了没有出来的)</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;" @click="SaveNewWord">保存洗稿后文件</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;" @click="ImportSrtAndGetTime">导入字幕</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;"
|
||||
@click="SaveCopywritingInformation">保存文案信息</n-button>
|
||||
</div>
|
||||
<n-data-table style="margin-top: 15px;" :row-key="rowKey" :columns="columns" :data="data" :pagination="pagination"
|
||||
:bordered="false" @update:checked-row-keys="handleCheck" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h, defineComponent, onMounted, ref, toRaw } from "vue";
|
||||
import { NButton, NDataTable, useMessage, NInput, NImage, useDialog } from "naive-ui";
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "切换",
|
||||
key: "switch",
|
||||
color: "#8a2be2"
|
||||
},
|
||||
{
|
||||
title: "洗稿",
|
||||
key: 'aiWord',
|
||||
color: "#8a2be2"
|
||||
},
|
||||
{
|
||||
title: "下对齐",
|
||||
key: 'AlignNextWord',
|
||||
color: "#8a2be0"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NInput, NImage
|
||||
},
|
||||
setup() {
|
||||
const message = useMessage();
|
||||
let data = ref([]);
|
||||
let selectKey = ref([]);
|
||||
let textData = [];
|
||||
let dialog = useDialog();
|
||||
let replace_before = ref("");
|
||||
let replace_after = ref("");
|
||||
|
||||
const createColumns = ({
|
||||
AIModifyOneWord,
|
||||
SwitchOldWord
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
type: "selection",
|
||||
disabled(row) {
|
||||
return row.name === "Edward King 3";
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "编号",
|
||||
key: 'no',
|
||||
width: 50
|
||||
},
|
||||
{
|
||||
title: "文案",
|
||||
key: "word",
|
||||
width: 250,
|
||||
render(row, index) {
|
||||
return h(NInput, {
|
||||
value: row.word,
|
||||
type: "textarea",
|
||||
autosize: true,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].word = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
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 == "aiWord") {
|
||||
AIModifyOneWord(row);
|
||||
} else if (item.key == "switch") {
|
||||
SwitchOldWord(row);
|
||||
} else if (item.key == "AlignNextWord") {
|
||||
AlignNextWord(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h("div", {
|
||||
style: "display : flex; align-items: center;"
|
||||
}, [
|
||||
h("span", {
|
||||
style: "margin-right : 2px; width : 80px "
|
||||
}, "洗稿后"),
|
||||
h("div", {
|
||||
style: " margin-right : 5px"
|
||||
}, [
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
placeholder: "替换前的值",
|
||||
value: replace_before.value,
|
||||
onUpdateValue(v) {
|
||||
replace_before.value = v
|
||||
}
|
||||
}),
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
placeholder: "替换后的值",
|
||||
value: replace_after.value,
|
||||
onUpdateValue(v) {
|
||||
replace_after.value = v
|
||||
}
|
||||
})
|
||||
]),
|
||||
h("div", {}, [
|
||||
h(NButton, {
|
||||
style: "",
|
||||
size: "tiny",
|
||||
color: "#70a1ff",
|
||||
onClick: () => ReplaceText()
|
||||
}, { default: () => "替换" }),
|
||||
])
|
||||
])
|
||||
},
|
||||
key: "after_gpt",
|
||||
width: 300,
|
||||
render(row, index) {
|
||||
return h(NInput, {
|
||||
value: row.after_gpt,
|
||||
placeholder: "洗稿后的值",
|
||||
type: "textarea",
|
||||
autosize: true,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].after_gpt = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "字幕",
|
||||
key: "srt",
|
||||
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: "#5f4321",
|
||||
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: "timeLimit",
|
||||
width: 120
|
||||
}, {
|
||||
title: "原图",
|
||||
width: 120,
|
||||
render(row) {
|
||||
return h(NImage, {
|
||||
src: "file:\\" + (row.old_image ? row.old_image : row.outImagePath),
|
||||
alt: 'Image',
|
||||
width: '120'
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// 初始化加载项目下面的分镜好的文案
|
||||
// 并判断是不是有洗稿后的文件。一并加载
|
||||
await window.api.GetProjectWord(value => {
|
||||
debugger;
|
||||
data.value = value.data;
|
||||
})
|
||||
|
||||
let div = document.getElementById("word_option");
|
||||
if (window.config.theme == "dark") {
|
||||
div.style.backgroundColor = "#24242c"
|
||||
} else {
|
||||
div.style.backgroundColor = "#FFF"
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 替换洗稿后的所有数据
|
||||
*/
|
||||
async function ReplaceText() {
|
||||
// 开始替换
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].after_gpt = data.value[i].after_gpt.replaceAll(replace_before.value, replace_after.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算指定索引值的时间
|
||||
* @param {data value 值的索引} index
|
||||
*/
|
||||
function modifyTime(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) {
|
||||
debugger
|
||||
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].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("数据错误 2222");
|
||||
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 SwitchOldWord(row) {
|
||||
data.value.forEach(item => {
|
||||
if (item.no == row.no) {
|
||||
item.after_gpt = item.word;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句写稿
|
||||
*/
|
||||
async function AIModifyOneWord(row) {
|
||||
await window.api.AIModifyOneWord([row.no, row.word], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
// 修改数据
|
||||
let filter_data = data.value.filter(item => item.no == value.data.no);
|
||||
filter_data[0].after_gpt = value.data.content;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键洗稿,AI修改选择的
|
||||
*/
|
||||
async function AIModifyWord() {
|
||||
let word_data = [];
|
||||
for (let i = 0; i < selectKey.value.length; i++) {
|
||||
const item = selectKey.value[i];
|
||||
let obj = {
|
||||
no: data.value[item - 1].no,
|
||||
word: data.value[item - 1].word
|
||||
}
|
||||
word_data.push(obj);
|
||||
}
|
||||
for (let i = 0; i < word_data.length; i++) {
|
||||
const item = word_data[i];
|
||||
await AIModifyOneWord(item)
|
||||
}
|
||||
|
||||
// 洗完稿之后全局提示
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "一键洗稿完成" })
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存洗稿后文件
|
||||
*/
|
||||
async function SaveNewWord() {
|
||||
let new_data_word = [];
|
||||
// 先填充没有洗稿的
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
if (!item.after_gpt) {
|
||||
item.after_gpt = item.word;
|
||||
}
|
||||
new_data_word.push(item.after_gpt);
|
||||
}
|
||||
textData = new_data_word;
|
||||
// 开始保存
|
||||
await window.api.SaveNewWord(new_data_word, (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
message.success("保存成功,并写入到了剪贴板");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存字幕文案信息,用于自动生成视频
|
||||
*/
|
||||
async function SaveCopywritingInformation() {
|
||||
await window.api.SaveCopywritingInformation([toRaw(data.value), "srt_time_information"], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入字幕并进行分镜输出,显示时间,将分镜给他传进去
|
||||
*/
|
||||
async function ImportSrtAndGetTime() {
|
||||
if (textData.length <= 0) {
|
||||
message.error("请先保存洗稿后文件,不洗稿也要保存一下");
|
||||
return;
|
||||
}
|
||||
await window.api.ImportSrtAndGetTime([toRaw(data.value)], (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 ContinueAIModifyWord() {
|
||||
// 获取所有被选中的data
|
||||
let word_data = [];
|
||||
for (let i = 0; i < selectKey.value.length; i++) {
|
||||
const item = selectKey.value[i];
|
||||
|
||||
if (!data.value[item - 1].after_gpt) {
|
||||
let obj = {
|
||||
no: data.value[item - 1].no,
|
||||
word: data.value[item - 1].word
|
||||
}
|
||||
word_data.push(obj);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < word_data.length; i++) {
|
||||
const item = word_data[i];
|
||||
await AIModifyOneWord(item)
|
||||
}
|
||||
// 洗完稿之后全局提示
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "继续写稿完成" })
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns: createColumns({
|
||||
AIModifyOneWord,
|
||||
SwitchOldWord
|
||||
}),
|
||||
rowKey: (row) => row.no,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys;
|
||||
},
|
||||
pagination: false,
|
||||
AIModifyWord,
|
||||
AIModifyOneWord,
|
||||
ContinueAIModifyWord,
|
||||
ImportSrtAndGetTime,
|
||||
SaveNewWord,
|
||||
SaveCopywritingInformation,
|
||||
replace_before,
|
||||
replace_after,
|
||||
ReplaceText
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<n-tabs animated default-value="inGetFrame">
|
||||
<n-tab-pane size="large" name="draftGetFrame" tab="剪映草稿抽帧">
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
正在抽帧
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<div style="color: red;">
|
||||
注意:该项是使用剪映内的草稿进行分镜
|
||||
</div>
|
||||
<n-select v-model:value="selectedValue" filterable placeholder="选择草稿" :options="options" />
|
||||
<n-button round type="primary" @click="getFrameFunc">开始抽帧</n-button>
|
||||
</n-space>
|
||||
</n-spin>
|
||||
<n-divider />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="inGetFrame" tab="软件内抽帧">
|
||||
<div style="color: red; font-size: large;">
|
||||
注意:该功能需要对环境要求较高。(环境需要自行安装,第一次执行需要下载模型需要魔法)<span class="url_class" @click="OpenTeachDoc">环境安装教程</span>
|
||||
<br>
|
||||
分割敏感度:值越小分割越精细,值越大分割越粗糙
|
||||
</div>
|
||||
<n-form style="margin-top: 50px;" ref="formRef" label-placement="left" inline :model="frameValue"
|
||||
:rules="rules" size="medium">
|
||||
<n-form-item label="分割敏感度" path="sensitivity" style="width: 300px;">
|
||||
<n-slider v-model:value=frameValue.sensitivity show-tooltip />
|
||||
</n-form-item>
|
||||
<n-form-item label="分镜视频地址" path="video_path">
|
||||
<n-input v-model:value="frameValue.video_path" placeholder="选择或输入要分镜的视频地址" style="width: 300px;" />
|
||||
<n-button type="primary" @click="GetVideoFile">
|
||||
选择文件(MP4)
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="StartStoryboarding" :loading="storyLoading">
|
||||
开始分镜
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-divider />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<n-code style="padding: 0; font-size: small;" :code="code" language="js" word-wrap />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, toRaw } from "vue";
|
||||
import { NSelect, NButton, useMessage, NSpin, NDivider, NCode, NTabs, NTabPane, NSpace, NForm, NFormItem, NSlider, NInput } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect, NButton, NSpin, NDivider, NCode, NTabs, NTabPane, NSpace, NForm, NFormItem, NSlider, NInput
|
||||
},
|
||||
setup() {
|
||||
let options = [];
|
||||
let out_dir = ref(null);
|
||||
let selectedValue = ref(null);
|
||||
let show = ref(false);
|
||||
let code = ref("");
|
||||
const message = useMessage();
|
||||
let storyLoading = ref(false);
|
||||
|
||||
let frameValue = ref({
|
||||
sensitivity: 30,
|
||||
video_path: null
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.api.getDraftFileList((value) => {
|
||||
value.forEach(element => {
|
||||
let obj = {
|
||||
label: element,
|
||||
value: element
|
||||
}
|
||||
options.push(obj)
|
||||
});
|
||||
})
|
||||
window.api.setEventListen([DEFINE_STRING.GET_FRAME_RETUN], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
code.value = code.value + "\n" + value.data;
|
||||
return;
|
||||
}
|
||||
// 完成
|
||||
if (value.type == 0) {
|
||||
storyLoading.value = false;
|
||||
message.success("分镜抽帧完成");
|
||||
}
|
||||
code.value = code.value + "\n" + value.data;
|
||||
})
|
||||
})
|
||||
|
||||
async function getFrameFunc(e) {
|
||||
out_dir = window.config.project_path
|
||||
if (selectedValue.value == null || selectedValue.value == undefined) {
|
||||
message.error("请选择剪映草稿和输出草稿")
|
||||
return;
|
||||
}
|
||||
show.value = true
|
||||
// 抽帧
|
||||
await window.api.getFrame([selectedValue.value, out_dir], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("抽帧失败");
|
||||
code.value = value.message;
|
||||
return;
|
||||
}
|
||||
message.success("抽帧成功");
|
||||
code.value = value.data;
|
||||
show.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
function selectExportFolder(e) {
|
||||
window.api.selectFolder(null, (value) => {
|
||||
if (value.length <= 0) {
|
||||
message.error("必须选择输出文件夹位置");
|
||||
return;
|
||||
}
|
||||
out_dir.value = value[0]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择指定的视频文件
|
||||
*/
|
||||
async function GetVideoFile() {
|
||||
await window.api.SelectFile(['mp4'], (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
frameValue.value.video_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始分镜执行分镜任务
|
||||
*/
|
||||
async function StartStoryboarding() {
|
||||
storyLoading.value = true;
|
||||
if (frameValue.value.video_path == null) {
|
||||
message.error("选择分镜的视频地址");
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
if (toRaw(frameValue.value).video_path.split('.')[toRaw(frameValue.value).video_path.split('.').length - 1].toUpperCase() != "MP4") {
|
||||
message.error("目前只支持MP4格式");
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.StartStoryboarding(toRaw(frameValue.value))
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开环境安装网站
|
||||
*/
|
||||
function OpenTeachDoc() {
|
||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/docx/VrBVd2KUDosmNfxat3OceWuInjd?from=from_copylink");
|
||||
}
|
||||
|
||||
return {
|
||||
selectedValue,
|
||||
options,
|
||||
out_dir,
|
||||
getFrameFunc,
|
||||
selectExportFolder,
|
||||
show,
|
||||
code,
|
||||
frameValue,
|
||||
GetVideoFile,
|
||||
StartStoryboarding,
|
||||
OpenTeachDoc,
|
||||
storyLoading
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.url_class {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.url_class:hover {
|
||||
color: brown;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<div>一键矩阵功能:有对应的字幕配置文件,直接自动的生图。高清。合成视频 <span
|
||||
style="margin-left: 20px ;font-size: 20px; color: red;">注意:前三步还是必做</span></div>
|
||||
<n-form ref="configRef" inline :model="config" :rules="rules">
|
||||
<n-form-item label="SRT字幕文件" path="srt_path">
|
||||
<n-input disabled v-model:value="config.srt_path" placeholder="选择SRT文件" />
|
||||
<n-button color="#e5a84b" @click="SelectSrt">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="配音文件" path="audio_path">
|
||||
<n-input disabled="" v-model:value="config.audio_path" placeholder="选择配音文件" />
|
||||
<n-button color="#e5a84b" @click="SelectAudio">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="背景音乐文件夹" path="background_music">
|
||||
<n-select style="width: 300px;" v-model:value="config.background_music" :options="background_music_options"
|
||||
placeholder="选择背景音乐文件夹" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button :type="buttonLabel.type" @click="AutoConditionCheck">
|
||||
{{ buttonLabel.label }}
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="success" @click="ActionTask">
|
||||
一键全自动
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-divider />
|
||||
<n-data-table :columns="columns" :data="data" size="small" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, toRaw, h, onUnmounted } from "vue";
|
||||
import { NSelect, NForm, NFormItem, NButton, NInput, NIcon, NPopover, useMessage, NDivider, NDataTable, NTag } from "naive-ui"
|
||||
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
|
||||
function statusAndMessage(row) {
|
||||
if (row.status == 'wait') {
|
||||
return {
|
||||
tagLabel: "等待中",
|
||||
color: '',
|
||||
type: "warning"
|
||||
}
|
||||
} else if (row.status == 'queue') {
|
||||
return {
|
||||
tagLabel: "队列中",
|
||||
color: '',
|
||||
type: "info"
|
||||
}
|
||||
} else if (row.status == 'ok') {
|
||||
return {
|
||||
tagLabel: "生图完成(未高清)",
|
||||
color: { color: "#2ed573", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
else if (row.status == 'error') {
|
||||
return {
|
||||
tagLabel: "生图错误",
|
||||
color: "",
|
||||
type: "error"
|
||||
}
|
||||
}
|
||||
else if (row.status == "video_improvied") {
|
||||
return {
|
||||
tagLabel: "高清完成",
|
||||
color: { color: "#70a1ff", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_improving") {
|
||||
return {
|
||||
tagLabel: "高清中",
|
||||
color: { color: "#fedc5e", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_improvie_error") {
|
||||
return {
|
||||
tagLabel: "高清失败",
|
||||
color: { color: "#d23918", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
else if (row.status == "video_queue") {
|
||||
return {
|
||||
tagLabel: "合成队列中",
|
||||
color: { color: "#55efc4", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_error") {
|
||||
return {
|
||||
tagLabel: "合成失败",
|
||||
color: { color: "#ff4757", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_ok") {
|
||||
return {
|
||||
tagLabel: "合成完成",
|
||||
color: { color: "#1dd1a1", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tagLabel: "状态错误",
|
||||
color: { color: "#d2dae2", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const createColumns = ({
|
||||
RemoveTask
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
title: "批次名称",
|
||||
key: "out_folder"
|
||||
},
|
||||
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: (() => statusAndMessage(row))().type,
|
||||
color: (() => statusAndMessage(row))().color,
|
||||
bordered: false
|
||||
},
|
||||
{
|
||||
default: (() => statusAndMessage(row).tagLabel)
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: "small",
|
||||
type: "warning",
|
||||
onClick: async () => await RemoveTask(row)
|
||||
},
|
||||
{ default: () => "停止任务" }
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect, NForm, NFormItem, NButton, NInput, NIcon, FolderOpen, NPopover, NDivider, NDataTable, NTag
|
||||
},
|
||||
setup() {
|
||||
let config = ref({
|
||||
srt_path: null,
|
||||
audio_path: null,
|
||||
draft_srt_style: "0",
|
||||
background_music: null,
|
||||
friendly_reminder: "0"
|
||||
})
|
||||
let background_music_options = ref([]);
|
||||
let configRef = ref(null);
|
||||
let message = useMessage();
|
||||
let buttonLabel = ref({
|
||||
label: "未进行条件检测",
|
||||
type: "warning"
|
||||
})
|
||||
let data = ref([]);
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取队列信息
|
||||
// 获取背景音乐文件夹信息
|
||||
await window.api.GetBackgroundMusicConfigList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < value.value.length; i++) {
|
||||
const element = value.value[i];
|
||||
let obj = {
|
||||
label: element.name,
|
||||
value: element.id,
|
||||
}
|
||||
background_music_options.value.push(obj);
|
||||
}
|
||||
})
|
||||
|
||||
// 获取上一次选择的字幕,配音。未年检
|
||||
await window.api.GetVideoGenerateConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("获取初始化数据失败");
|
||||
return;
|
||||
}
|
||||
config.value.srt_path = value.data.srt_path;
|
||||
config.value.audio_path = value.data.audio_path;
|
||||
config.value.background_music = value.data.background_music;
|
||||
|
||||
})
|
||||
|
||||
// 获取初始化数据(队列任务-这边不能添加)
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
data.value = value.data.task_list;
|
||||
})
|
||||
|
||||
// 监听状态刷新事件
|
||||
window.api.setEventListen([DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
let index = data.value.findIndex(item => item.id == value.id);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
// 修改
|
||||
data.value[index].status = value.status;
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH);
|
||||
})
|
||||
|
||||
/**
|
||||
* 全自动条件检测
|
||||
*/
|
||||
async function AutoConditionCheck(e) {
|
||||
e.preventDefault();
|
||||
configRef.value?.validate(async (errors) => {
|
||||
debugger
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return;
|
||||
}
|
||||
await window.api.AutoConditionCheck(toRaw(config.value), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
buttonLabel.value = {
|
||||
label: "条件检测失败",
|
||||
type: "error"
|
||||
}
|
||||
return;
|
||||
}
|
||||
message.success("条件检测成功");
|
||||
buttonLabel.value = {
|
||||
label: "条件检测成功",
|
||||
type: "success"
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始执行全自动任务
|
||||
*/
|
||||
async function ActionTask(e) {
|
||||
e.preventDefault();
|
||||
configRef.value?.validate(async (errors) => {
|
||||
debugger
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return;
|
||||
}
|
||||
if (buttonLabel.value.type != "success") {
|
||||
message.error("请先进行条件检测");
|
||||
return;
|
||||
}
|
||||
// 像后端传递传递开始任务
|
||||
await window.api.ActionAutoVideoTask([[...toRaw(data.value)], toRaw(config.value)], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
// let index = data.value.findIndex(item=>item.id == value)
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectSrt() {
|
||||
window.api.SelectFile(['srt'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
config.value.srt_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectAudio() {
|
||||
window.api.SelectFile(['mp3', 'wav'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
config.value.audio_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除任务
|
||||
* @param {*} row
|
||||
*/
|
||||
async function RemoveTask(row) {
|
||||
// 先删除出现的任务
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE, "all", `${row.out_folder}_image`], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 在删除高清任务
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE, "all", `${row.out_folder}_improve`], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 在删除和合成视频任务
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE, "all", `${row.out_folder}_video`], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
await window.api.ModifyGenerateTaskStatus(['out_folder', row.out_folder, 'wait'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
config,
|
||||
SelectSrt,
|
||||
SelectAudio,
|
||||
background_music_options,
|
||||
AutoConditionCheck,
|
||||
ActionTask,
|
||||
configRef,
|
||||
buttonLabel,
|
||||
data,
|
||||
columns: createColumns({
|
||||
RemoveTask
|
||||
}),
|
||||
rules: {
|
||||
srt_path: {
|
||||
required: true,
|
||||
message: "请输入SRT文件",
|
||||
trigger: ["input", "blur"]
|
||||
},
|
||||
audio_path: {
|
||||
required: true,
|
||||
message: "请选择配音文件",
|
||||
trigger: ["input", "blur"]
|
||||
},
|
||||
background_music: {
|
||||
required: true,
|
||||
message: "请选择背景音乐",
|
||||
trigger: ["input", "blur"]
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
正在反推
|
||||
</template>
|
||||
<n-button type="info" @click="PushBackPrompt"> 开始反推 </n-button>
|
||||
<n-button type="error" style="margin-left: 50px;" @click="DeleteBadPrompt"> 一键去除所有不想要的值 </n-button>
|
||||
<n-button type="warning" style="margin-left: 50px;" @click="ManagePrompt"> 管理不想要的值 </n-button>
|
||||
<n-divider />
|
||||
</n-spin>
|
||||
<n-code style="padding: 0;" :code="code" language="js" word-wrap />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, h, toRaw } from "vue";
|
||||
import { NButton, useMessage, useDialog, NDivider, NCode, NSpin } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import ManageBadPrompt from "../Components/ManageBadPrompt.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDivider, NCode, NSpin
|
||||
},
|
||||
setup() {
|
||||
let dialog = useDialog();
|
||||
let code = ref("");
|
||||
let show = ref(false);
|
||||
let promprRef = ref();
|
||||
let message = useMessage();
|
||||
|
||||
// 开始反推
|
||||
async function PushBackPrompt() {
|
||||
show.value = true;
|
||||
await window.api.PushBackPrompt((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("反推报错");
|
||||
code.value = value.message;
|
||||
return;
|
||||
}
|
||||
message.success("反推成功");
|
||||
code.value = value.data;
|
||||
show.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理不想要的值
|
||||
*/
|
||||
async function ManagePrompt() {
|
||||
// 弹窗,管理不想要的值
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
title: "管理不想要的值",
|
||||
content: () => h(ManageBadPrompt, { ref: promprRef }),
|
||||
style: `width : 650px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
console.log(toRaw(promprRef.value.tags));
|
||||
// 将数据写入
|
||||
await window.api.SaveBadPrompt(toRaw(promprRef.value.tags), (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return false;
|
||||
}
|
||||
message.success("添加成功");
|
||||
})
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键删除不想要的值
|
||||
*/
|
||||
async function DeleteBadPrompt() {
|
||||
await window.api.DeleteBadPrompt((value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("去除成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
code,
|
||||
show,
|
||||
PushBackPrompt,
|
||||
ManagePrompt,
|
||||
DeleteBadPrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,495 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>当前选择风格:<n-button style="margin-left: 20px;" type="info" @click="ShowImageStyleDialog">选择风格</n-button>
|
||||
<n-button style="margin-left: 20px;" color="#dc6b82" @click="SaveImageSetting">保存图片设置</n-button>
|
||||
</div>
|
||||
<div>
|
||||
<ShowImageTag :selectStyle="selectStyle"></ShowImageTag>
|
||||
</div>
|
||||
<n-form ref="formRef" inline :label-width="80" :model="formValue">
|
||||
<n-form-item label="前缀" path="image_style">
|
||||
<n-input v-model:value="formValue.image_style" placeholder="填写前缀" />
|
||||
</n-form-item>
|
||||
<n-form-item label="输出轮次" path="output_rounds">
|
||||
<n-input-number v-model:value="formValue.output_rounds" placeholder="输出轮次" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="AddImageTask">
|
||||
添加任务
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="GenerateAllImage">
|
||||
批量生成
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="RefreshTaskList">
|
||||
刷新列表
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="warning" @click="StopTask">
|
||||
停止生成任务
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
<n-data-table :row-key="rowKey" @update:checked-row-keys="handleCheck" :columns="columns" :data="task_list_data"
|
||||
:pagination="false" :bordered="true" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw, vModelCheckbox } from "vue";
|
||||
import { NButton, useMessage, NSpin, NInputNumber, NDivider, NForm, NFormItem, NInput, NDataTable, NTag, useDialog, NPopover, NImage } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
import SelectImageStyle from "../Components/SelectImageStyle.vue"
|
||||
import ShowImageTag from "../Components/ShowImageTag.vue"
|
||||
import BatchSaveImageSetting from "./BatchSaveImageSetting.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NInputNumber, NDivider, NForm, NFormItem, NInput, NDataTable, NTag, NPopover, ShowImageTag
|
||||
},
|
||||
setup() {
|
||||
let show = ref(false);
|
||||
let count = ref(0);
|
||||
let message = useMessage();
|
||||
let selectKey = ref([]);
|
||||
let formValue = ref({
|
||||
image_style: null,
|
||||
lora: null,
|
||||
output_rounds: 0,
|
||||
image_style_list: []
|
||||
})
|
||||
let task_list_data = ref([]);
|
||||
let dialog = useDialog();
|
||||
let selectStyle = ref([]);
|
||||
|
||||
const createColumns = ({
|
||||
DeleteImageTaskList,
|
||||
RemoveImageTask
|
||||
}) => {
|
||||
return [{
|
||||
type: "selection",
|
||||
},
|
||||
{
|
||||
title: "编号",
|
||||
key: "no"
|
||||
},
|
||||
{
|
||||
title: "输出文件夹",
|
||||
key: "out_folder"
|
||||
},
|
||||
{
|
||||
title: "风格",
|
||||
key: "image_style_list",
|
||||
render(row, index) {
|
||||
return h("div", {}, [
|
||||
h(ShowImageTag, { selectStyle: row.image_style_list_information })
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "前缀",
|
||||
key: "image_style"
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
render(row) {
|
||||
return h(NPopover, {
|
||||
trigger: 'hover',
|
||||
disabled: (() => {
|
||||
debugger
|
||||
if (row.status == "error") {
|
||||
return false;
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})()
|
||||
},
|
||||
{
|
||||
default: () => (() => {
|
||||
if (row.status == "wait") {
|
||||
return "warning"
|
||||
} else if (row.status == "queue") {
|
||||
return "info"
|
||||
} else if (row.status == "ok") {
|
||||
return "success"
|
||||
} else if (row.status == "error") {
|
||||
return row.errorMessage
|
||||
}
|
||||
})(),
|
||||
trigger: () => h(
|
||||
NTag,
|
||||
{
|
||||
type: (() => {
|
||||
if (row.status == "wait") {
|
||||
return "warning"
|
||||
} else if (row.status == "queue") {
|
||||
return "info"
|
||||
} else if (row.status == "ok") {
|
||||
return "success"
|
||||
} else if (row.status == "error") {
|
||||
return "error"
|
||||
} else if (row.status.startsWith("video")) {
|
||||
return "success"
|
||||
}
|
||||
})(),
|
||||
color: '',
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
if (row.status == "wait") {
|
||||
return "等待中"
|
||||
} else if (row.status == "queue") {
|
||||
return "队列中"
|
||||
} else if (row.status == "ok") {
|
||||
return "完成"
|
||||
} else if (row.status == "error") {
|
||||
return "错误"
|
||||
} else if (row.status.startsWith("video")) {
|
||||
return "合成视频相关"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return h("div", {}, [
|
||||
h(NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
type: "error",
|
||||
size: "small",
|
||||
onClick: () => DeleteImageTaskList(row)
|
||||
},
|
||||
{ default: () => "删除" }),
|
||||
h(NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
style: "margin-left: 10px;",
|
||||
type: "error",
|
||||
size: "small",
|
||||
onClick: async () => await RemoveImageTask(row)
|
||||
},
|
||||
{ default: () => "停止当前任务" })]
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
async function getInitData() {
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
for (let i = 0; i < task_list_data.value.length; i++) {
|
||||
const element = task_list_data.value[i];
|
||||
// 获取指定的风格数据
|
||||
await window.api.GetImageStyleInfomation(JSON.stringify(toRaw(element.image_style_list)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// selectStyle.value = JSON.parse(value.data[0].value);
|
||||
task_list_data.value[i]['image_style_list_information'] = value.data;
|
||||
})
|
||||
}
|
||||
await window.api.GetConfigJson(JSON.stringify(["image_style", []]), async (value) => {
|
||||
debugger;
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
await window.api.GetImageStyleInfomation(JSON.stringify(toRaw(value.data)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
selectStyle.value = value.data;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getInitData();
|
||||
|
||||
// 修改一个
|
||||
window.api.setEventListen([DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
// 修改当前状态
|
||||
let index = task_list_data.value.findIndex(item => item.out_folder == value.out_folder);
|
||||
if (index < 0) {
|
||||
message.error("数据错误");
|
||||
return;
|
||||
}
|
||||
// 获取指定的数据进行状态更新
|
||||
task_list_data.value[index].status = value.status;
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存批次
|
||||
*/
|
||||
async function AddImageTask() {
|
||||
if (formValue.value.output_rounds <= 0) {
|
||||
message.error("输出轮次不能为0或更小");
|
||||
return;
|
||||
}
|
||||
// 添加listId
|
||||
let tmp_arr = [];
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i];
|
||||
tmp_arr.push(element.id);
|
||||
}
|
||||
formValue.value.image_style_list = tmp_arr;
|
||||
await window.api.AddImageTask(toRaw(formValue.value), (value) => {
|
||||
debugger;
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
await getInitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对应的行的任务
|
||||
*/
|
||||
async function DeleteImageTaskList(row) {
|
||||
// 判断当前的状态
|
||||
if (row.status == "queue") {
|
||||
message.error("队列中的状态不能删除");
|
||||
return;
|
||||
}
|
||||
|
||||
if (row.status == "ok" || row.status.startsWith("video")) {
|
||||
dialog.warning({
|
||||
title: "确认",
|
||||
content: "确定删除?会将生成的图全部删除!",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {
|
||||
await window.api.DeleteImageTaskList(row.id, async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.DeleteImageTaskList(row.id, async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成全部
|
||||
*/
|
||||
async function GenerateAllImage() {
|
||||
debugger
|
||||
// 生成 对应的json 文件
|
||||
await window.api.AddWebuiJson(async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
await window.api.GenerateImageInSelectTask(toRaw(selectKey.value), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("生图任务添加到队列成功");
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新任务列表
|
||||
*/
|
||||
async function RefreshTaskList() {
|
||||
await getInitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示风格选择对话框
|
||||
*/
|
||||
async function ShowImageStyleDialog() {
|
||||
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.8;
|
||||
let dialogHeight = window.innerHeight * 0.9;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(SelectImageStyle, { selectStyle: toRaw(selectStyle.value), height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 重新加载数据
|
||||
debugger;
|
||||
console.log(selectStyle.value);
|
||||
let tmp_arr = [];
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i];
|
||||
tmp_arr.push(element.id);
|
||||
}
|
||||
// 将当前的风格保存
|
||||
await window.api.SaveCopywritingInformation([JSON.stringify(tmp_arr), "image_style", true], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function StopTask() {
|
||||
await getInitData();
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, null, "default"], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功,但正在进行的任务无法停止");
|
||||
})
|
||||
}
|
||||
|
||||
async function RemoveImageTask(row) {
|
||||
//`${task_list.out_folder}_${images[0]}`, batch, task_list.out_folder
|
||||
// 停止当前行的生图任务
|
||||
debugger;
|
||||
console.log(row);
|
||||
await getInitData();
|
||||
// 判断该当前状态
|
||||
// if (row.status != "queue") {
|
||||
// message.error("只有在队列中的任务才能停止");
|
||||
// return;
|
||||
// }
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, "all", row.out_folder], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功");
|
||||
// 修改状态
|
||||
await window.api.ModifyGenerateTaskStatus(['id', row.id, 'wait'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
await getInitData();
|
||||
})
|
||||
}
|
||||
|
||||
async function ShowAutoImage() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 800;
|
||||
let dialogHeight = 600;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(BatchSaveImageSetting, { selectStyle: toRaw(selectStyle.value), height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动保存图片的设置
|
||||
*/
|
||||
async function SaveImageSetting() {
|
||||
|
||||
// 获取当前权限
|
||||
await window.api.GetPermission(async (value) => {
|
||||
let permission = value.permissions;
|
||||
// let permission = [];
|
||||
if (permission && permission.length >= 0) {
|
||||
if (permission.indexOf(DEFINE_STRING.PERMISSIONS.AUTO_SAVE_IMAGE_PERMISSION) == -1) {
|
||||
message.error("没有权限");
|
||||
return;
|
||||
} else {
|
||||
await ShowAutoImage();
|
||||
}
|
||||
} else {
|
||||
await ShowAutoImage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
show,
|
||||
count,
|
||||
formValue,
|
||||
task_list_data,
|
||||
selectKey,
|
||||
AddImageTask,
|
||||
rowKey: (row) => row.id,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys;
|
||||
},
|
||||
GenerateAllImage,
|
||||
columns: createColumns({
|
||||
DeleteImageTaskList,
|
||||
RemoveImageTask
|
||||
}),
|
||||
RefreshTaskList,
|
||||
ShowImageStyleDialog,
|
||||
selectStyle,
|
||||
StopTask,
|
||||
SaveImageSetting
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<n-tabs default-value="reDraws" 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="reDraws" tab="重绘/单独合成">
|
||||
<oneImageReDrwa></oneImageReDrwa>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="autoAll" tab="一键全自动">
|
||||
<OneKeyMatrix></OneKeyMatrix>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, onUnmounted, h, toRaw } from "vue";
|
||||
import { NTabPane, NTabs } from "naive-ui"
|
||||
|
||||
import OneKeyMatrix from "../Backstep/OneKeyMatrix.vue"
|
||||
import oneImageReDrwa from "../Backstep/oneImageReDrwa.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NTabPane, NTabs, OneKeyMatrix, oneImageReDrwa
|
||||
},
|
||||
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,647 @@
|
||||
<template>
|
||||
<div style="margin-bottom: 30px;">
|
||||
<div>注意:</div>
|
||||
<div>这里加载的是当前<strong>项目地址</strong>下面的输出文件夹</div>
|
||||
</div>
|
||||
<n-form style="margin-top: 10px; margin-bottom: 30px;" :model="formValue">
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="选择srt地址">
|
||||
<n-input style="width: 300px; margin-right: 5px;" v-model:value="formValue.srt_path" type="text"
|
||||
placeholder="SRT字幕地址" />
|
||||
<n-button color="#e5a84b" @click="SelectSrtFile">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item style="margin-left: 10px;" label="选择配音">
|
||||
<n-input style="width: 300px; margin-right: 5px;" v-model:value="formValue.audio_path" type="text"
|
||||
placeholder="配音地址(mp3或wav)" />
|
||||
|
||||
<n-button color="#e5a84b" @click="SelectMusicFile">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
|
||||
<!-- <n-form-item style="flex: 1;" label="选择字幕样式">
|
||||
<n-select v-model:value="formValue.draft_srt_style" filterable placeholder="选择样式" :options="options" />
|
||||
</n-form-item> -->
|
||||
<n-form-item style="margin-left: 10px;" label="选择背景音乐文件夹(随机匹配,自动合成视频必选)">
|
||||
<n-select style="width: 300px;" v-model:value="formValue.background_music" filterable placeholder="选择样式"
|
||||
:options="backgroundMusicOptions" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item style="flex: 1; margin-left: 20px;" label="选择温馨提示(不要就选无,可以选择随机)">
|
||||
<n-select v-model:value="formValue.friendly_reminder" filterable placeholder="选择样式"
|
||||
:options="friendlyReminderOptions" />
|
||||
</n-form-item> -->
|
||||
|
||||
<n-form-item style="margin-left: 10px;">
|
||||
<n-button type="info" @click="SaveVideoSrtAndAudioMessage">保存</n-button>
|
||||
</n-form-item>
|
||||
</div>
|
||||
<n-form-item label="注意:高清不会管是不是已经高清过,都会再次高清">
|
||||
<n-button type="primary" @click="ExportAllDraft">一键导出所有草稿</n-button>
|
||||
<n-button style="margin-left: 40px;" type="primary" @click="HdAllImages">一键高清所有图片</n-button>
|
||||
<n-button style="margin-left: 40px;" type="primary" @click="VideoGenerate">一键生成所有视频</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-data-table :columns="columns" :data="data" :pagination="pagination" :bordered="false" />
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, onUnmounted, h, toRaw } from "vue";
|
||||
import { NButton, useMessage, NInput, NDataTable, NSelect, NForm, NFormItem, useDialog, NPopover, NTag, NTabPane, NTabs, NIcon } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import OneKeyMatrix from "../Backstep/OneKeyMatrix.vue"
|
||||
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "重绘界面",
|
||||
key: 'reDraw'
|
||||
},
|
||||
{
|
||||
title: "生成草稿",
|
||||
key: "addClipDraft"
|
||||
},
|
||||
{
|
||||
title: "高清",
|
||||
key: "Improve"
|
||||
},
|
||||
{
|
||||
title: "生成视频",
|
||||
key: "GenerateVideo"
|
||||
},
|
||||
{
|
||||
title: "打开文件夹",
|
||||
key: "OpenFolder"
|
||||
}
|
||||
]
|
||||
|
||||
function statusAndMessage(row) {
|
||||
|
||||
if (!row.status.startsWith("video")) {
|
||||
if (row.status != "ok") {
|
||||
return {
|
||||
tagLabel: "生图未完成",
|
||||
color: { color: "#eccc68", textColor: '#ffffff' }
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tagLabel: "生图完成(未高清)",
|
||||
color: { color: "#2ed573", textColor: '#ffffff' }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (row.status == "video_improvied") {
|
||||
return {
|
||||
tagLabel: "高清完成",
|
||||
color: { color: "#70a1ff", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_improving") {
|
||||
return {
|
||||
tagLabel: "高清中",
|
||||
color: { color: "#fedc5e", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_improvie_error") {
|
||||
return {
|
||||
tagLabel: "高清失败",
|
||||
color: { color: "#d23918", textColor: '#ffffff' }
|
||||
}
|
||||
}
|
||||
else if (row.status == "video_queue") {
|
||||
return {
|
||||
tagLabel: "合成队列中",
|
||||
color: { color: "#55efc4", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_error") {
|
||||
return {
|
||||
tagLabel: "合成失败",
|
||||
color: { color: "#ff4757", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_ok") {
|
||||
return {
|
||||
tagLabel: "合成完成",
|
||||
color: { color: "#1dd1a1", textColor: '#ffffff' }
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tagLabel: "状态错误",
|
||||
color: { color: "#d2dae2", textColor: '#ffffff' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NInput, NSelect, NForm, NFormItem, NPopover, NTag, NTabPane, NTabs, OneKeyMatrix, NIcon, FolderOpen
|
||||
},
|
||||
setup() {
|
||||
let image_folder = ref("");
|
||||
let message = useMessage();
|
||||
let dialog = useDialog();
|
||||
let data = ref([]);
|
||||
let options = ref([]);
|
||||
let backgroundMusicOptions = ref([]);
|
||||
let friendlyReminderOptions = ref([]);
|
||||
|
||||
let formValue = ref({
|
||||
srt_path: null,
|
||||
audio_path: null,
|
||||
draft_srt_style: '0',
|
||||
background_music: null,
|
||||
friendly_reminder: '0'
|
||||
})
|
||||
|
||||
/**
|
||||
* 创建行
|
||||
* @param {执行的方法} param0
|
||||
*/
|
||||
const createColumns = ({
|
||||
showRegenerateWindow,
|
||||
AddClipDraft,
|
||||
ImproveResolution,
|
||||
GenerateVideo,
|
||||
OpenFolder
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
title: "文件夹",
|
||||
key: "folder",
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
width: 600,
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
style: "margin-right : 10px",
|
||||
strong: true,
|
||||
type: "info",
|
||||
size: "medium",
|
||||
onClick: () => {
|
||||
if (item.key == "reDraw") {
|
||||
showRegenerateWindow(row);
|
||||
} else if (item.key == "addClipDraft") {
|
||||
AddClipDraft(row);
|
||||
} else if (item.key == "Improve") {
|
||||
// 高清
|
||||
ImproveResolution(row);
|
||||
} else if (item.key == "GenerateVideo") {
|
||||
// 生成视频
|
||||
GenerateVideo(row);
|
||||
} else if (item.key == "OpenFolder") {
|
||||
// 打开对应的文件夹
|
||||
OpenFolder(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
btn.push(h(
|
||||
NButton, {
|
||||
style: "margin-right : 10px",
|
||||
strong: true,
|
||||
type: "warning",
|
||||
size: "medium",
|
||||
onClick: async () => {
|
||||
await StopTask(row);
|
||||
}
|
||||
}, {
|
||||
default: () => "停止任务"
|
||||
}
|
||||
))
|
||||
return btn;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
render(row) {
|
||||
return h(NPopover, {
|
||||
trigger: 'hover',
|
||||
disabled: (() => {
|
||||
if (row.status == "video_error") {
|
||||
return false;
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})()
|
||||
},
|
||||
{
|
||||
default: () => (() => {
|
||||
if (row.status == "wait") {
|
||||
return "warning"
|
||||
} else if (row.status == "queue") {
|
||||
return "info"
|
||||
} else if (row.status == "ok") {
|
||||
return "success"
|
||||
} else if (row.status == "error") {
|
||||
return row.errorMessage
|
||||
}
|
||||
})(),
|
||||
trigger: () => h(
|
||||
NTag,
|
||||
{
|
||||
color: (() => statusAndMessage(row))().color
|
||||
},
|
||||
{
|
||||
default: (() => statusAndMessage(row).tagLabel)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止当前行的任务
|
||||
*/
|
||||
async function StopTask(row) {
|
||||
// 判断当前的状态是不是高清中和生成视频中
|
||||
|
||||
if (row.status == "video_queue") {
|
||||
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE_SINGLE, row.folder, "default"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功");
|
||||
// 修改状态为 高清完成
|
||||
// 修改状态
|
||||
await window.api.ModifyGenerateTaskStatus(['out_folder', row.folder, 'video_improvied'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
} else if (row.status == "video_improving") {
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.LOCAL_IMAGE_IMPROVE, row.folder, "default"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功");
|
||||
// 修改状态
|
||||
await window.api.ModifyGenerateTaskStatus(['out_folder', row.folder, 'ok'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
message.error("当前状态不是高清中或者生成视频中,无法停止");
|
||||
}
|
||||
|
||||
await InitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 高清文件夹,只是加入队列
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function ImproveResolution(row) {
|
||||
await window.api.ImproveResolution([row.folder], (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("高清任务加入队列成功,请勿关闭软件");
|
||||
} else {
|
||||
message.error(`Error Message ${value.message}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将生成视频加入到队列中(目前一次最多三个)
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function GenerateVideo(row) {
|
||||
let validateRes = await validateMustProperty();
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
await window.api.AutoGenerateVideo([[row.folder], toRaw(formValue.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(row.folder + "添加到队列成功");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键生成所有的视频(加入队列中)
|
||||
*/
|
||||
async function VideoGenerate() {
|
||||
let validateRes = await validateMustProperty();
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
let f = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
f.push(item.folder);
|
||||
}
|
||||
|
||||
// await GenerateVideo(toRaw(item));
|
||||
await window.api.AutoGenerateVideo([[...f], toRaw(formValue.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("所有任务添加到队列成功");
|
||||
})
|
||||
}
|
||||
|
||||
function OpenFolder(row) {
|
||||
window.api.OpenFolder(row.folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 高清所有文件夹,将所有需要高清的全部加入队列中
|
||||
*/
|
||||
async function HdAllImages() {
|
||||
dialog.warning({
|
||||
title: "确认是不是要全部高清",
|
||||
content: "确认是不是全部高清!分辨率太高,电脑会卡到爆的哦",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {
|
||||
let arr = [];
|
||||
for (let item of data.value) {
|
||||
arr.push(item.folder); // 这里应该可以正常访问和操作每个元素
|
||||
}
|
||||
|
||||
// 一次高清五个
|
||||
await window.api.ImproveResolution(arr, (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 1) {
|
||||
message.success("所有的高清任务加入队列成功,请勿关闭软件");
|
||||
} else {
|
||||
message.error(`Error Message ${value.message}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新的窗口
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function showRegenerateWindow(row) {
|
||||
window.api.showNewWindow(["ReDrawImage", window.config.project_path + "/tmp/" + row.folder]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前表单的必填项
|
||||
*/
|
||||
async function validateMustProperty(isBackgroundMusic = true) {
|
||||
let validateData = toRaw(formValue.value);
|
||||
if (validateData.srt_path == null || validateData.srt_path == "") {
|
||||
message.error("SRT地址必填");
|
||||
return false;
|
||||
}
|
||||
if (validateData.audio_path == null || validateData.audio_path == "") {
|
||||
message.error("配音地址必填");
|
||||
return false;
|
||||
}
|
||||
if (validateData.draft_srt_style == null || validateData.draft_srt_style == "") {
|
||||
message.error("草稿样式必选");
|
||||
return false;
|
||||
}
|
||||
if (isBackgroundMusic) {
|
||||
if (validateData.background_music == null || validateData.background_music == "") {
|
||||
message.error("背景音乐必填");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (validateData.friendly_reminder == null || validateData.friendly_reminder == "") {
|
||||
message.error("友情提示必填");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成剪映草稿
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function AddClipDraft(row) {
|
||||
let validateRes = await validateMustProperty(false);
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
await window.api.addDraft([row.folder, toRaw(formValue.value)], (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
} else {
|
||||
message.success(`添加成功,草稿名字:${value.draft_name}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键保存所有的草稿
|
||||
*/
|
||||
async function ExportAllDraft() {
|
||||
let validateRes = await validateMustProperty(false);
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
await window.api.addDraft([element.folder, toRaw(formValue.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
} else {
|
||||
message.success(`添加成功,草稿名字:${value.draft_name}`);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectSrtFile() {
|
||||
window.api.SelectFile(['srt'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
formValue.value.srt_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectMusicFile() {
|
||||
window.api.SelectFile(['mp3', 'wav'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
formValue.value.audio_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
async function InitData() {
|
||||
// 获取对应的输出文件
|
||||
await window.api.getSubFolderList(["start", "output_crop"], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
data.value = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取对应的输出文件
|
||||
await InitData();
|
||||
|
||||
// // 初始化获取字体样式
|
||||
// await window.api.getTextStyleList((value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message);
|
||||
// return;
|
||||
// }
|
||||
// for (let i = 0; i < value.value.length; i++) {
|
||||
// const item = value.value[i];
|
||||
// let obj = {
|
||||
// label: item.name,
|
||||
// value: item.id,
|
||||
// }
|
||||
// options.value.push(obj);
|
||||
// }
|
||||
// // 设置默认选项
|
||||
// if (options.value.length > 0) {
|
||||
// formValue.value.draft_srt_style = options.value.filter(item => item.value == "1")[0].value;
|
||||
// }
|
||||
// })
|
||||
|
||||
// 获取初始化的背景音乐列表
|
||||
await window.api.GetBackgroundMusicConfigList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < value.value.length; i++) {
|
||||
const element = value.value[i];
|
||||
let obj = {
|
||||
label: element.name,
|
||||
value: element.id,
|
||||
}
|
||||
backgroundMusicOptions.value.push(obj);
|
||||
}
|
||||
})
|
||||
|
||||
// 获取温馨提示列表
|
||||
// await window.api.getFriendlyReminderList((value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message);
|
||||
// return;
|
||||
// }
|
||||
// value.value.forEach(item => {
|
||||
// let obj = {
|
||||
// label: item.name,
|
||||
// value: item.id
|
||||
// };
|
||||
// friendlyReminderOptions.value.push(obj);
|
||||
// });
|
||||
// // 设置默认选项
|
||||
// if (friendlyReminderOptions.value.length > 0) {
|
||||
// formValue.value.friendly_reminder = friendlyReminderOptions.value.filter(item => item.value == "1")[0].value;
|
||||
// }
|
||||
// })
|
||||
|
||||
// 监听状态更新事件
|
||||
window.api.setEventListen([DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
// 修改当前状态
|
||||
let index = data.value.findIndex(item => item.folder == value.out_folder);
|
||||
if (index < 0) {
|
||||
message.error("数据错误");
|
||||
return;
|
||||
}
|
||||
// 获取指定的数据进行状态更新
|
||||
data.value[index].status = value.status;
|
||||
})
|
||||
|
||||
// 获取设置的配置
|
||||
// 获取上一次选择的字幕,配音。未年检
|
||||
await window.api.GetVideoGenerateConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("获取初始化数据失败");
|
||||
return;
|
||||
}
|
||||
formValue.value.srt_path = value.data.srt_path;
|
||||
formValue.value.audio_path = value.data.audio_path;
|
||||
formValue.value.background_music = value.data.background_music;
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH);
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存视频的基础信息
|
||||
*/
|
||||
async function SaveVideoSrtAndAudioMessage() {
|
||||
await window.api.SaveVideoSrtAndAudioMessage(JSON.stringify(toRaw(formValue.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
image_folder,
|
||||
columns: createColumns({
|
||||
showRegenerateWindow,
|
||||
AddClipDraft,
|
||||
ImproveResolution,
|
||||
GenerateVideo,
|
||||
OpenFolder
|
||||
}),
|
||||
data,
|
||||
options,
|
||||
backgroundMusicOptions,
|
||||
friendlyReminderOptions,
|
||||
formValue,
|
||||
SelectSrtFile,
|
||||
SelectMusicFile,
|
||||
HdAllImages,
|
||||
ExportAllDraft,
|
||||
VideoGenerate,
|
||||
SaveVideoSrtAndAudioMessage
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-button @click="addDraft">添加草稿 </n-button>
|
||||
<n-button @click="downloadModel">下载模型 </n-button>
|
||||
<n-code style="padding: 0;" :code="code" language="js" word-wrap />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { NButton, useMessage, NSpin, NSpace, NSelect, NDivider, NInput, NCode } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NSpace, NSelect, NDivider, NInput, NCode
|
||||
},
|
||||
setup() {
|
||||
|
||||
let code = ref("");
|
||||
async function addDraft() {
|
||||
await window.api.addDraft(['D:\\AI 推文\\番茄小说\\如画室友\\03\\tmp\\output_crop', "D:\\AI 推文\\番茄小说\\如画室友\\03\\1705237224836_gbyfbbGz.srt"], (value) => {
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.api.setEventListen([DEFINE_STRING.GET_FRAME_RETUN], (value) => {
|
||||
debugger;
|
||||
code.value = code.value + "\n" + value.data;
|
||||
})
|
||||
})
|
||||
|
||||
async function downloadModel() {
|
||||
window.api.DownloadModel();
|
||||
}
|
||||
|
||||
return {
|
||||
addDraft,
|
||||
downloadModel,
|
||||
code
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
正在对齐
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<n-select v-model:value="selectedValue" filterable placeholder="选择草稿" :options="options" />
|
||||
<n-button round type="primary" @click="AlginDraftImg">开始对齐(草稿内单条对齐)</n-button>
|
||||
<n-button round type="primary" @click="AlginDraftImgToText">多行对齐(提供内容)</n-button>
|
||||
<n-button round type="primary" @click="AlginDraftImgToTextAll">一键对齐</n-button>
|
||||
</n-space>
|
||||
</n-spin>
|
||||
<n-divider />
|
||||
<!-- <pre>{{ items }}</pre> -->
|
||||
<n-input v-model:value="inputValue" type="textarea" placeholder="自动调整尺寸" :autosize="{
|
||||
minRows: 3
|
||||
}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { NButton, useMessage, NSpin, NSpace, NSelect, NDivider, NInput } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NSpace, NSelect, NDivider, NInput
|
||||
},
|
||||
setup() {
|
||||
let show = ref(false);
|
||||
let options = ref([]);
|
||||
let items = ref([]);
|
||||
let selectedValue = ref(null);
|
||||
let message = useMessage()
|
||||
let inputValue = ref("")
|
||||
|
||||
// 单句对齐
|
||||
function AlginDraftImg() {
|
||||
if (selectedValue.value == null || selectedValue.value == "") {
|
||||
message.error("请选择需要对齐的草稿");
|
||||
return;
|
||||
}
|
||||
show.value = true;
|
||||
window.api.alginDraftImg(selectedValue.value, (type, value) => {
|
||||
items.value.push(value)
|
||||
console.log(value)
|
||||
if (type == DEFINE_STRING.PYTHON_CLOSE) {
|
||||
show.value = false;
|
||||
message.success("字幕对齐成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照内容对齐
|
||||
*/
|
||||
function AlginDraftImgToText() {
|
||||
if (selectedValue.value == null || selectedValue.value == "" || inputValue.value == null || inputValue.value == "") {
|
||||
message.error("请选择需要对齐的草稿和输入分镜内容");
|
||||
return;
|
||||
}
|
||||
// 文案内容
|
||||
let text_value = inputValue.value.split("\n")
|
||||
window.api.alginDraftImgToText([selectedValue.value, text_value], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("对齐成功");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键全部对齐
|
||||
*/
|
||||
function AlginDraftImgToTextAll() {
|
||||
if (inputValue.value == null || inputValue.value == "") {
|
||||
message.error("请输入分镜内容");
|
||||
return;
|
||||
}
|
||||
debugger;
|
||||
// 文案内容
|
||||
let text_value = inputValue.value.split("\n")
|
||||
// 获取当前项目下面地所有草稿
|
||||
console.log(window);
|
||||
let draft_value = options.value.filter(item => item.value.startsWith(window.config.project_name));
|
||||
for (let i = 0; i < draft_value.length; i++) {
|
||||
const element = draft_value[i];
|
||||
window.api.alginDraftImgToText([element.value, text_value], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(element.value + "对齐成功");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.api.getDraftFileList((value) => {
|
||||
value.forEach(element => {
|
||||
let obj = {
|
||||
label: element,
|
||||
value: element
|
||||
}
|
||||
options.value.push(obj)
|
||||
});
|
||||
})
|
||||
})
|
||||
return {
|
||||
show,
|
||||
options,
|
||||
selectedValue,
|
||||
AlginDraftImg,
|
||||
AlginDraftImgToText,
|
||||
AlginDraftImgToTextAll,
|
||||
items,
|
||||
inputValue
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div style="height: 200px;">
|
||||
<div style="height: 40px;"></div>
|
||||
<div style="display: flex;">
|
||||
<n-input :disabled="true" v-model:value="machineId" type="text" placeholder="唯一码" />
|
||||
<n-popover trigger="click">
|
||||
<template #trigger>
|
||||
<n-button text style="width: 24px; font-size: 24px; color: skyblue; display: flex; heigh">
|
||||
<n-icon>
|
||||
<InformationCircle />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>VX:xiangbie88</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
<div style="color: red; margin: 10px">如未激活,将上面的码给管理员激活
|
||||
<n-popover trigger="hover" raw :show-arrow="false">
|
||||
<template #trigger>
|
||||
<n-tag type="info">
|
||||
客服微信
|
||||
</n-tag>
|
||||
</template>
|
||||
<n-image width="250" src="http://qiniuyun.upsurging.xyz/LAI/a41237ef53a18b6c7fba5d966e9a28f.jpg"
|
||||
preview-disabled />
|
||||
</n-popover>
|
||||
</div>
|
||||
<div style="margin: 10px;">详细的教程文档:<span class="url_class" @click="OpenTeachDoc">教程文档</span></div>
|
||||
<div style="margin: 10px;">问题/需求收集表:<span class="url_class" @click="OpenQueDoc">问题/需求收集表</span></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { useMessage, NInput, NButton, NIcon, NPopover, NImage, NTag } from "naive-ui"
|
||||
import { InformationCircle } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NInput, NButton, NIcon, InformationCircle, NPopover, NImage, NTag
|
||||
},
|
||||
setup() {
|
||||
let machineId = ref("");
|
||||
let message = useMessage();
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取机械码
|
||||
await window.api.GetMachineId((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
machineId.value = value.value;
|
||||
})
|
||||
})
|
||||
|
||||
function OpenTeachDoc() {
|
||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/docx/CAjGdTDlboJ3nVx0cQccOuNHnvd?from=from_copylink");
|
||||
}
|
||||
|
||||
function OpenQueDoc() {
|
||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/sheets/G5s3sX0KahH1XQtWDazcF70anUb?from=from_copylink");
|
||||
}
|
||||
return {
|
||||
machineId,
|
||||
OpenQueDoc,
|
||||
OpenTeachDoc
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.url_class {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.url_class:hover {
|
||||
color: brown;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,461 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<div style="flex: 5; margin-right: 20px">
|
||||
<n-form ref="formRef" :model="prompt" :rules="rules" :size="size">
|
||||
<n-form-item label="提示词" path="prompt">
|
||||
<n-input type="textarea" v-model:value="prompt.prompt" placeholder="提示词" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-form
|
||||
ref="formRef"
|
||||
inline
|
||||
:label-width="80"
|
||||
:model="prompt"
|
||||
:rules="rules"
|
||||
:size="size"
|
||||
>
|
||||
<n-form-item label="重绘幅度" path="denoising_strength">
|
||||
<n-input-number
|
||||
:step="0.05"
|
||||
:precision="2"
|
||||
v-model:value="prompt.denoising_strength"
|
||||
placeholder="重绘幅度"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="种子" path="seed">
|
||||
<n-input v-model:value="prompt.seed" placeholder="种子" />
|
||||
</n-form-item>
|
||||
<n-form-item label="生图模式" path="model">
|
||||
<n-select
|
||||
style="width: 150px"
|
||||
v-model:value="prompt.model"
|
||||
:options="modelOption"
|
||||
placeholder="种子"
|
||||
/>
|
||||
<n-checkbox
|
||||
style="margin-left: 20px"
|
||||
v-model:checked="prompt.adetailer"
|
||||
label="修脸"
|
||||
></n-checkbox>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="GenerateAll">全部重绘</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="图片显示大小">
|
||||
<n-slider
|
||||
style="width: 120px"
|
||||
:min="150"
|
||||
:max="300"
|
||||
v-model:value="image_width"
|
||||
:step="10"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
<div style="flex: 1; width: 150px; height: 150px">
|
||||
<n-image style="width: 150px; height: 150px" :src="prompt.oldImage" />
|
||||
<div style="display: flex; justify-content: left; align-items: center">
|
||||
<span style="font-size: 18px">{{ prompt.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-container" style="display: flex">
|
||||
<n-image-group show-toolbar-tooltip :theme-overrides="imageGroupThemeOverrides">
|
||||
<div style="display: flex; flex-wrap: wrap">
|
||||
<div
|
||||
v-for="(image, index) in images"
|
||||
style="margin: 0 10px 5px 10px"
|
||||
:key="image.id"
|
||||
>
|
||||
<n-image
|
||||
@contextmenu.prevent="handleContextMenu"
|
||||
:width="image_width"
|
||||
:height="image_height"
|
||||
:src="image.img_path"
|
||||
alt="图片描述"
|
||||
lazy
|
||||
style="display: block"
|
||||
/>
|
||||
<n-button
|
||||
:disabled="image.disabled"
|
||||
style="width: 100%"
|
||||
type="info"
|
||||
@click="ReGenerateImage(image)"
|
||||
>重绘</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="margin-top: 10px; display: flex; justify-content: center; align-items: center"
|
||||
>
|
||||
<n-button type="error" style="margin-right: 20px" @click="GetLast"
|
||||
>上一组(F1)</n-button
|
||||
>
|
||||
<n-button type="info" @click="GetNext">下一组(F2)</n-button>
|
||||
</div>
|
||||
|
||||
<n-dropdown
|
||||
placement="bottom-start"
|
||||
trigger="manual"
|
||||
:x="x"
|
||||
:y="y"
|
||||
:options="dropOption"
|
||||
:show="showDropdownRef"
|
||||
:on-clickoutside="onClickoutside"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
onMounted,
|
||||
watch,
|
||||
computed,
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
toRaw,
|
||||
} from "vue";
|
||||
import {
|
||||
NImage,
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
useMessage,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NCheckbox,
|
||||
useThemeVars,
|
||||
NImageGroup,
|
||||
NSlider,
|
||||
NDropdown,
|
||||
} from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NCheckbox,
|
||||
NImageGroup,
|
||||
NSlider,
|
||||
NDropdown,
|
||||
},
|
||||
props: {
|
||||
ImageInitData: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
debugger;
|
||||
let images = ref(
|
||||
window.ImageInitData.filter((item) => item.id == window.currentImageId)[0]
|
||||
.output_list
|
||||
);
|
||||
let prompt = ref(
|
||||
window.ImageInitData.filter((item) => item.id == window.currentImageId)[0]
|
||||
);
|
||||
const showDropdownRef = ref(false);
|
||||
const x = ref(0);
|
||||
const y = ref(0);
|
||||
let current_img = ref("");
|
||||
|
||||
let modelOption = ref([
|
||||
{
|
||||
label: "文生图",
|
||||
value: "txt2img",
|
||||
},
|
||||
{
|
||||
label: "图生图",
|
||||
value: "img2img",
|
||||
},
|
||||
]);
|
||||
|
||||
let dropOption = [
|
||||
{
|
||||
label: "保存到封面",
|
||||
key: "savaCover",
|
||||
},
|
||||
{
|
||||
label: "替换参考图",
|
||||
key: "replaceImage",
|
||||
},
|
||||
];
|
||||
|
||||
let image_width = ref(150);
|
||||
let image_height = ref(150 * (prompt.value.height / prompt.value.width));
|
||||
|
||||
// 监听myRef的变化
|
||||
const stopWatch = watch(image_width, (newValue, oldValue) => {
|
||||
image_height.value = newValue * (prompt.value.height / prompt.value.width);
|
||||
// 根据myRef的新值执行操作...
|
||||
});
|
||||
|
||||
function handleFunc(event) {
|
||||
if (event.key == "F1") {
|
||||
// 数字 112 是 F1 键的 keyCode
|
||||
GetLast();
|
||||
// 阻止默认操作
|
||||
event.preventDefault();
|
||||
// 这里调用您的函数
|
||||
} else if (event.key == "F2") {
|
||||
GetNext();
|
||||
// 阻止默认操作
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 重新计算 images,判断是不是disabled
|
||||
for (let i = 0; i < images.value.length; i++) {
|
||||
const element = images.value[i];
|
||||
if (
|
||||
prompt.value.oldImage.replaceAll("\\", "/") ==
|
||||
element.img_path.replaceAll("\\", "/")
|
||||
) {
|
||||
images.value[i].disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.isWatch) {
|
||||
window.api.setEventListen(
|
||||
[DEFINE_STRING.REGENERATE_IMAGE_RETUN, window.id],
|
||||
(value) => {
|
||||
if (value.type != 1) {
|
||||
return;
|
||||
}
|
||||
// 修改图片的数据源
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
let index = window.ImageInitData.findIndex(
|
||||
(item) => item.id == window.currentImageId
|
||||
);
|
||||
images.value = window.ImageInitData[index].output_list;
|
||||
let filteArr = images.value.filter((item) => item.id == value.id);
|
||||
filteArr[0].img_path =
|
||||
filteArr[0].img_path.split("?")[0] + `?time=${new Date().getTime()}`;
|
||||
message.success(JSON.stringify(value));
|
||||
}
|
||||
);
|
||||
window.isWatch = true;
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
window.addEventListener("keydown", handleFunc);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("keydown", handleFunc);
|
||||
stopWatch();
|
||||
});
|
||||
|
||||
/**
|
||||
* 重绘图片
|
||||
*/
|
||||
async function ReGenerateImage(data) {
|
||||
debugger;
|
||||
console.log(data);
|
||||
let obj = {
|
||||
id: data.id,
|
||||
denoising_strength: prompt.value.denoising_strength,
|
||||
image: data.img_path.split("?")[0],
|
||||
name: prompt.value.name,
|
||||
negative_prompt: prompt.value.negative_prompt,
|
||||
prompt: prompt.value.prompt,
|
||||
sampler_name: prompt.value.sampler_name,
|
||||
seed: -1,
|
||||
timedata: new Date().getTime(),
|
||||
image_style: data.image_style,
|
||||
lora: data.lora,
|
||||
image_style_list: toRaw(data.image_style_list),
|
||||
model: prompt.value.model,
|
||||
adetailer: prompt.value.adetailer,
|
||||
};
|
||||
// 开始重新生成
|
||||
await window.api.ReGenerateImage([window.id, obj, 1], (value) => {
|
||||
console.log(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键全部重绘
|
||||
*/
|
||||
async function GenerateAll() {
|
||||
for (let i = 0; i < images.value.length; i++) {
|
||||
const element = images.value[i];
|
||||
await ReGenerateImage(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上一组
|
||||
*/
|
||||
function GetLast() {
|
||||
debugger;
|
||||
let index = window.ImageInitData.findIndex(
|
||||
(item) => item.id == window.currentImageId
|
||||
);
|
||||
if (index > 0) {
|
||||
images.value = window.ImageInitData[index - 1].output_list;
|
||||
prompt.value = window.ImageInitData[index - 1];
|
||||
window.currentImageId = prompt.value.id;
|
||||
} else {
|
||||
message.error("没有上一组了");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一组
|
||||
*/
|
||||
function GetNext() {
|
||||
debugger;
|
||||
let index = window.ImageInitData.findIndex(
|
||||
(item) => item.id === window.currentImageId
|
||||
);
|
||||
if (index + 1 >= window.ImageInitData.length) {
|
||||
// 已经是最后一个元素
|
||||
message.error("当前已经是最后一个了");
|
||||
} else {
|
||||
// 返回下一个对象
|
||||
images.value = window.ImageInitData[index + 1].output_list;
|
||||
prompt.value = window.ImageInitData[index + 1];
|
||||
window.currentImageId = prompt.value.id;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
images,
|
||||
ReGenerateImage,
|
||||
GetLast,
|
||||
GetNext,
|
||||
prompt,
|
||||
modelOption,
|
||||
current_img,
|
||||
GenerateAll,
|
||||
image_width,
|
||||
image_height,
|
||||
imageGroupThemeOverrides: computed(() => {
|
||||
const {
|
||||
popoverColor,
|
||||
boxShadow2,
|
||||
textColor2,
|
||||
borderRadius,
|
||||
} = useThemeVars().value;
|
||||
const themeOverrides = {
|
||||
toolbarColor: popoverColor,
|
||||
toolbarBoxShadow: boxShadow2,
|
||||
toolbarIconColor: textColor2,
|
||||
toolbarBorderRadius: borderRadius,
|
||||
};
|
||||
return themeOverrides;
|
||||
}),
|
||||
handleContextMenu(e) {
|
||||
e.preventDefault();
|
||||
let img_src = decodeURI(e.target.src);
|
||||
if (img_src) {
|
||||
current_img.value = img_src
|
||||
.split("?")[0]
|
||||
.replace(/^file:\/\/\//, "")
|
||||
.replace(/^\//, "");
|
||||
nextTick().then(() => {
|
||||
showDropdownRef.value = true;
|
||||
x.value = e.clientX;
|
||||
y.value = e.clientY;
|
||||
});
|
||||
} else {
|
||||
message.error("文件路径错误");
|
||||
return;
|
||||
}
|
||||
},
|
||||
x: x,
|
||||
y: y,
|
||||
dropOption,
|
||||
showDropdownRef,
|
||||
onClickoutside() {
|
||||
showDropdownRef.value = false;
|
||||
},
|
||||
handleSelect(key) {
|
||||
showDropdownRef.value = false;
|
||||
if (key == "savaCover") {
|
||||
// 将指定的文件写入写入到封面中
|
||||
window.api.DownloadImageFile(
|
||||
[toRaw(current_img.value), "cover", window.id],
|
||||
(value) => {
|
||||
if (value.code == 1) {
|
||||
message.success(toRaw(current_img.value) + "保存封面成功");
|
||||
return;
|
||||
}
|
||||
message.error("保存失败:" + value.message);
|
||||
}
|
||||
);
|
||||
} else if (key == "replaceImage") {
|
||||
// 替换参考图
|
||||
// 先替换界面。然后修改配置文件
|
||||
prompt.value.old_old_image = prompt.value.oldImage;
|
||||
prompt.value.oldImage = current_img.value;
|
||||
// 修改配置文件
|
||||
let arg = {
|
||||
name: prompt.value.name,
|
||||
prompt: prompt.value.prompt,
|
||||
denoising_strength: prompt.value.denoising_strength,
|
||||
model: prompt.value.model,
|
||||
adetailer: prompt.value.adetailer,
|
||||
modify_old: prompt.value.oldImage,
|
||||
};
|
||||
window.api.ModifyInpurCropJson(arg, (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("替换参考图成功");
|
||||
// 修改image中的disable
|
||||
for (let i = 0; i < images.value.length; i++) {
|
||||
const element = images.value[i];
|
||||
if (
|
||||
prompt.value.oldImage.replaceAll("\\", "/") ==
|
||||
element.img_path.replaceAll("\\", "/")
|
||||
) {
|
||||
images.value[i].disabled = true;
|
||||
} else {
|
||||
images.value[i].disabled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.image {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image img {
|
||||
/* 固定高度 */
|
||||
margin: 10px 15px 5px 15px;
|
||||
/* 间隔 */
|
||||
}
|
||||
|
||||
/* .btn {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
} */
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px;">
|
||||
<div style="color: red;">注意:删除所有是删除当前查找到的(就是下面显示的tag)</div>
|
||||
<div style="margin-top: 5px; display: flex; margin-bottom: 10px;">
|
||||
<n-input v-model:value="inputTag" placeholder="输入不想要的标签,多个以英文逗号隔开"></n-input>
|
||||
<n-button style="width: 60px;margin-left: 5px;" type="info" @click="AddTags">添加</n-button>
|
||||
</div>
|
||||
<div style="margin-top: 5px; display: flex; margin-bottom: 10px;">
|
||||
<n-input v-model:value="searchTag" placeholder="输入搜索的值,多个以英文逗号隔开"></n-input>
|
||||
<n-button style="width: 60px;margin-left: 5px;" type="info" @click="SearchTags">搜索</n-button>
|
||||
<n-button style="width: 100px;margin-left: 5px;" type="info" @click="DeleteTags">删除所有</n-button>
|
||||
</div>
|
||||
<n-tag style="margin: 5px;" v-for="(tag, index) in show_tags" :key="index" type="success" closable
|
||||
@close="handleClose(tag)">{{ tag }}</n-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, h, toRaw } from "vue";
|
||||
import { NButton, useMessage, NInput, NTag } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NInput, NTag
|
||||
},
|
||||
setup() {
|
||||
let inputTag = ref("");
|
||||
let searchTag = ref("");
|
||||
let show_tags = ref([]);
|
||||
let tags = ref([]);
|
||||
let message = useMessage();
|
||||
|
||||
onMounted(async () => {
|
||||
// 初始化获取提示词
|
||||
window.api.GetBadPrompt((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.value != null) {
|
||||
tags.value = value.value.split(',');
|
||||
show_tags.value = value.value.split(',');
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 添加标签
|
||||
*/
|
||||
async function AddTags() {
|
||||
if (inputTag.value == null || inputTag.value == "") {
|
||||
message.error("请输入内容");
|
||||
return;
|
||||
}
|
||||
let newTag = inputTag.value.replaceAll(',', ',').split(',');
|
||||
let mergeTag = [...newTag, ...toRaw(tags.value)];
|
||||
tags.value = [...new Set(mergeTag)]
|
||||
show_tags.value = [...new Set(mergeTag)]
|
||||
inputTag.value = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async function handleClose(tag) {
|
||||
debugger;
|
||||
// 删除对应的数据
|
||||
tags.value = tags.value.filter(item => item != tag);
|
||||
show_tags.value = show_tags.value.filter(item => item != tag);
|
||||
await SearchTags();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查找筛选值(返回符合条件的tag)
|
||||
*/
|
||||
async function SearchTags() {
|
||||
if (searchTag.value == null || searchTag.value == "") {
|
||||
show_tags.value = tags.value;
|
||||
return;
|
||||
}
|
||||
let s_ts = searchTag.value.replaceAll(',', ',').split(',');
|
||||
let temp_tag_list = [];
|
||||
for (let i = 0; i < s_ts.length; i++) {
|
||||
const element = s_ts[i];
|
||||
let t = show_tags.value.filter(item => item.includes(element));
|
||||
let t2 = [...temp_tag_list, ...t];
|
||||
temp_tag_list = [...new Set(t2)]
|
||||
}
|
||||
show_tags.value = temp_tag_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所选的tag(全部)
|
||||
*/
|
||||
async function DeleteTags() {
|
||||
debugger
|
||||
let d_l = toRaw(show_tags.value);
|
||||
for (let i = 0; i < d_l.length; i++) {
|
||||
const element = d_l[i];
|
||||
// tags.value = tags.value.filter(item => item != element);
|
||||
// show_tags.value = show_tags.value.filter(item => item != element);
|
||||
await handleClose(element)
|
||||
}
|
||||
// await SearchTags();
|
||||
}
|
||||
|
||||
return {
|
||||
tags,
|
||||
inputTag,
|
||||
AddTags,
|
||||
handleClose,
|
||||
SearchTags,
|
||||
DeleteTags,
|
||||
show_tags,
|
||||
searchTag
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div style="position: relative;">
|
||||
<n-input placeholder="新加tag,多个用逗号隔开" size="tiny" @keyup="AddTag" v-model:value="input_data"></n-input>
|
||||
|
||||
<span v-for="(tag, index) in data.chinese_prompt">
|
||||
<n-tag @click="TagClickEdit(tag, index)" size="small" :key="index" type="success" closable
|
||||
@close="handleClose(tag)">
|
||||
<template #default>
|
||||
<n-input v-if="tag.edit" size="tiny" @keyup="ModifyTag($event, tag, index)" style="width: 70px;"
|
||||
v-model:value="tag.dst">{{ tag.dst }}</n-input>
|
||||
<span
|
||||
style="max-width: 130px; min-width: 10px; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
|
||||
:title="tag.dst" v-else>{{ tag.dst }}</span>
|
||||
</template>
|
||||
</n-tag>
|
||||
<n-button size="tiny" type="info" quaternary @click="AddTagInPosition(index)">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<add />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h, defineComponent, onMounted, ref, toRaw, watch, onUnmounted } from "vue";
|
||||
import { NButton, NDataTable, useMessage, NInput, useDialog, NTag, NIcon } from "naive-ui";
|
||||
import { isEmpty } from "lodash";
|
||||
import { Add } from "@vicons/ionicons5"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NInput, NTag, Add, NIcon
|
||||
},
|
||||
props: ["initData", "type", "translateMode"],
|
||||
setup(props) {
|
||||
const message = useMessage();
|
||||
let input_data = ref("");
|
||||
let data = ref(props.initData);
|
||||
let type = ref(props.type);
|
||||
let translateMode = ref(props.translateMode);
|
||||
|
||||
|
||||
// 修改prompt
|
||||
async function ModifyPrompt() {
|
||||
// 修改prompt
|
||||
let res = "";
|
||||
for (let i = 0; i < data.value.chinese_prompt.length; i++) {
|
||||
const element = data.value.chinese_prompt[i];
|
||||
res += element.src + ',';
|
||||
}
|
||||
data.value.prompt = res;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
console.log(123)
|
||||
// 处理数据
|
||||
});
|
||||
|
||||
/**
|
||||
* 添加 Tag 的 方法
|
||||
* @param {*} e
|
||||
*/
|
||||
async function AddTag(e) {
|
||||
if (e.key.toLowerCase() == "enter") {
|
||||
// 添加
|
||||
if (input_data.value == "" || input_data.value == null) {
|
||||
message.error("请输入标签");
|
||||
return;
|
||||
}
|
||||
if (!data.value.chinese_prompt) {
|
||||
message.error("添加标签前请先英翻中");
|
||||
return;
|
||||
}
|
||||
let tags = input_data.value.replaceAll(',', ',');
|
||||
// 自动翻译
|
||||
if (translateMode.value) {
|
||||
// 翻译单个。马上返回
|
||||
await window.api.TranslateReturnNow([tags, 'zh', 'en', true], (value) => {
|
||||
debugger
|
||||
console.log(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.chinese_prompt.push(element);
|
||||
}
|
||||
ModifyPrompt();
|
||||
message.success("添加成功");
|
||||
input_data.value = "";
|
||||
})
|
||||
} else {
|
||||
let tags = input_data.value.split(',');
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const element = tags[i];
|
||||
if (!isEmpty(element)) {
|
||||
data.value.chinese_prompt.push(
|
||||
{
|
||||
"src": "",
|
||||
"dst": element
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭tag的方法
|
||||
* @param {要删除的tag} tag
|
||||
*/
|
||||
async function handleClose(tag) {
|
||||
debugger
|
||||
// 删除对应的数据
|
||||
data.value.chinese_prompt = data.value.chinese_prompt.filter(item => item.src != tag.src);
|
||||
ModifyPrompt();
|
||||
await SearchTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找筛选值(返回符合条件的tag)
|
||||
*/
|
||||
async function SearchTags() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以编辑标签
|
||||
* @param {*} e
|
||||
*/
|
||||
async function TagClickEdit(tag, index) {
|
||||
console.log(tag, index);
|
||||
data.value.chinese_prompt[index]["edit"] = true;
|
||||
// 将其他的数据变成false
|
||||
for (let i = 0; i < data.value.chinese_prompt.length; i++) {
|
||||
if (i != index) {
|
||||
data.value.chinese_prompt[i]["edit"] = false;
|
||||
}
|
||||
}
|
||||
ModifyPrompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改标签的数据
|
||||
*/
|
||||
async function ModifyTag(e, tag, index) {
|
||||
if (e.key.toLowerCase() == "enter") {
|
||||
if (tag.dst == "" || tag.dst == null) {
|
||||
message.error("请输入标签");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tag.dst.includes(',') || tag.dst.includes(',')) {
|
||||
message.error("单个标签不能有逗号");
|
||||
return;
|
||||
}
|
||||
|
||||
// 实时翻译一下
|
||||
let tags = tag.dst.replaceAll(',', ',');
|
||||
// 自动翻译
|
||||
if (translateMode.value) {
|
||||
// 翻译单个。马上返回
|
||||
await window.api.TranslateReturnNow([tags, 'zh', 'en', false], (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
data.value.chinese_prompt[index] = value.data[0];
|
||||
|
||||
ModifyPrompt();
|
||||
message.success("修改成功");
|
||||
input_data.value = "";
|
||||
})
|
||||
}
|
||||
data.value.chinese_prompt[index]["edit"] = false;
|
||||
ModifyPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据到指定的位置
|
||||
*/
|
||||
async function AddTagInPosition(index) {
|
||||
// 先判断是不是有没有添加的
|
||||
let ii = data.value.chinese_prompt.findIndex(item => item.edit == true);
|
||||
if (ii >= 0) {
|
||||
message.error("请先保存当前添加的标签");
|
||||
return;
|
||||
}
|
||||
data.value.chinese_prompt.splice(index + 1, 0, { "src": "", "dst": "", edit: true });
|
||||
}
|
||||
|
||||
return {
|
||||
AddTag,
|
||||
handleClose,
|
||||
SearchTags,
|
||||
type,
|
||||
translateMode,
|
||||
TagClickEdit,
|
||||
ModifyTag,
|
||||
input_data,
|
||||
data,
|
||||
AddTagInPosition
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
|
||||
<n-form label-width="auto" :model="wmSetting" label-placement="left" style="margin-top: 50px;">
|
||||
<n-form-item v-if="showTextLabel" :disable="true" style=" margin-right: 20px;" label="水印字体(要换行输入\N)"
|
||||
path="fontName">
|
||||
<n-input style="width: 400px;" v-model:value="wmSetting.showText" />
|
||||
</n-form-item>
|
||||
<n-form-item style=" margin-right: 20px;" label="字体" path="fontName">
|
||||
<n-input style="width: 300px;" v-model:value="wmSetting.fontName" :options="fontNameOptions" filterable />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="字号(80差不多)">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.fontSize" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="颜色">
|
||||
<n-color-picker style="width: 200px;" v-model:value="wmSetting.fontColor" :show-alpha="false" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="透明度">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.transparent" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-right: 20px;" label="位置">
|
||||
<n-input-number style="width: 60px; margin-right: 10px;" v-model:value="wmSetting.positionX" placeholder="X"
|
||||
:show-button="false" />
|
||||
<n-input-number style="width: 60px;" v-model:value="wmSetting.positionY" placeholder="Y"
|
||||
:show-button="false" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { NForm, NFormItem, NInput, NInputNumber, NColorPicker, NSelect, NButton } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NColorPicker, NSelect, NButton
|
||||
},
|
||||
props: ["initData", "type"],
|
||||
setup(props) {
|
||||
let showTextLabel = ref(false);
|
||||
let fontNameOptions = ref([]);
|
||||
let wmSetting = ref(props.initData);
|
||||
onMounted(() => {
|
||||
if (props.type == "watermarkConfig") {
|
||||
showTextLabel.value = true;
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
wmSetting,
|
||||
fontNameOptions,
|
||||
showTextLabel
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="color: red; margin-bottom: 5px;">
|
||||
注:添加ADetailer配置,只要选择修脸修手都会调用<br>
|
||||
</div>
|
||||
<n-dynamic-input v-model:value="ADetailer" :on-create="onCreate">
|
||||
<template #create-button-default>
|
||||
添加ADetailer数据
|
||||
</template>
|
||||
<template #default="{ value }">
|
||||
<div style="display: flex; align-items: center; width: 100%">
|
||||
<n-input v-model:value="value.ad_model" placeholder="输入模型名称"> </n-input>
|
||||
<n-input-number v-model:value="value.ad_confidence" style="margin-right: 12px; width: 160px" />
|
||||
</div>
|
||||
</template>
|
||||
</n-dynamic-input>
|
||||
<n-button type="primary" style="margin-top: 10px;" @click="SaveADetailerConfig">保存</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { useMessage, NDynamicInput, NInput, NInputNumber, NButton } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NDynamicInput, NInput, NInputNumber, NButton
|
||||
},
|
||||
setup() {
|
||||
let ADetailer = ref([]);
|
||||
let message = useMessage();
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.GetADetailerList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data != null) {
|
||||
ADetailer.value = value.data;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 添加的状态
|
||||
*/
|
||||
function onCreate() {
|
||||
return {
|
||||
ad_model: null,
|
||||
ad_confidence: 0.3
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存修脸配置
|
||||
*/
|
||||
async function SaveADetailerConfig() {
|
||||
// 判断添加的数据是不是都有值
|
||||
let isValue = true;
|
||||
let obj = [];
|
||||
for (let i = 0; i < ADetailer.value.length; i++) {
|
||||
const element = ADetailer.value[i];
|
||||
if (element.ad_model == null || element.ad_model == "" || element.ad_confidence == null) {
|
||||
isValue = false;
|
||||
}
|
||||
obj.push({
|
||||
ad_confidence: element.ad_confidence,
|
||||
ad_model: element.ad_model
|
||||
});
|
||||
}
|
||||
if (!isValue) {
|
||||
message.error("添加的项,值不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.SaveADetailerConfig(toRaw(obj), (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
return;
|
||||
}
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
onCreate,
|
||||
ADetailer,
|
||||
SaveADetailerConfig
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-select v-model:value="draf_name" filterable placeholder="选择草稿" :options="options"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { NSelect } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect
|
||||
},
|
||||
|
||||
setup() {
|
||||
let draf_name = ref(null);
|
||||
let options = ref([]);
|
||||
onMounted(async () => {
|
||||
await window.api.getDraftFileList((value) => {
|
||||
value.forEach(element => {
|
||||
let obj = {
|
||||
label: element,
|
||||
value: element
|
||||
}
|
||||
options.value.push(obj)
|
||||
});
|
||||
})
|
||||
})
|
||||
return {
|
||||
draf_name,
|
||||
options,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<n-layout id="layout_height" has-sider style="height: 300px;">
|
||||
<n-layout-sider bordered :width="240" :native-scrollbar="false">
|
||||
<n-menu @update:value="SelectMenu" :options="menuOptions" v-model:value="selectedKeyRef" />
|
||||
</n-layout-sider>
|
||||
<n-layout :native-scrollbar="false" style="margin-top: 30px;">
|
||||
<div style="margin-left: 20px;">
|
||||
<div>选择的风格:</div>
|
||||
<ShowImageTag :selectStyle="selectStyle"></ShowImageTag>
|
||||
<div style="margin-top: 5px;">
|
||||
<div class="image-container" style="display: flex;">
|
||||
<n-image-group show-toolbar-tooltip>
|
||||
<div style="display: flex; flex-wrap: wrap;">
|
||||
<div v-for="(image, index) in style_image_list " style="margin: 0 5px 5px 5px;"
|
||||
:key="image.id">
|
||||
<n-image @contextmenu.prevent="handleContextMenu($event, image)" :width="150"
|
||||
:height="150" :src="image.image" alt="图片描述" lazy style="display: block;" />
|
||||
</div>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
|
||||
<n-dropdown style="z-index: 100;" trigger="manual" :x="x" :y="y" :options="dropOption" :show="showDropdownRef"
|
||||
:on-clickoutside="onClickoutside" @select="handleSelect" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, watch, nextTick } from "vue";
|
||||
import { NDropdown, NImage, NLayout, NLayoutContent, NLayoutSider, NMenu, NSpace, NTag, NTooltip, messageDark, useMessage } from "naive-ui"
|
||||
import ShowImageTag from "./ShowImageTag.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSpace, NLayout, NLayoutSider, NLayoutContent, NMenu, NTag, NTooltip, NImage, NDropdown, ShowImageTag
|
||||
},
|
||||
props: ["selectStyle", "height"],
|
||||
setup(props) {
|
||||
let current_menu = null;
|
||||
let selectedKeyRef = ref(null);
|
||||
|
||||
let selectStyle = ref(props.selectStyle ? props.selectStyle : []);
|
||||
let style_image_list = ref([]);
|
||||
let message = useMessage();
|
||||
let showDropdownRef = ref(false);
|
||||
let current_img = ref(null);
|
||||
let menuOptions = ref([]);
|
||||
|
||||
const x = ref(0);
|
||||
const y = ref(0);
|
||||
|
||||
let dropOption = [{
|
||||
label: "选择风格",
|
||||
key: "selectStyle"
|
||||
}];
|
||||
|
||||
// 监听props.selectStyle修改
|
||||
watch(() => props.selectStyle, (value) => {
|
||||
selectStyle.value = value;
|
||||
if (!selectStyle.value) {
|
||||
selectStyle.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
setLayoutHeight("layout_height");
|
||||
await window.api.GetImageStyleMenu((value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
menuOptions.value = value.data;
|
||||
current_menu = value.data[0].id;
|
||||
});
|
||||
await getStyleImageSubList();
|
||||
selectedKeyRef.value = current_menu;
|
||||
})
|
||||
|
||||
/**
|
||||
* 菜单选中事件
|
||||
*/
|
||||
async function SelectMenu(key, item) {
|
||||
current_menu = key;
|
||||
await getStyleImageSubList();
|
||||
}
|
||||
|
||||
async function getStyleImageSubList() {
|
||||
await window.api.getStyleImageSubList(current_menu, (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
style_image_list.value = value.data;
|
||||
});
|
||||
}
|
||||
|
||||
// 将指定ID的元素的的高度设置到和当前组件一样高
|
||||
function setLayoutHeight(id) {
|
||||
let layout_height = document.getElementById(id);
|
||||
layout_height.style.height = props.height - 40 + "px";
|
||||
}
|
||||
|
||||
async function closeTag(image) {
|
||||
debugger;
|
||||
let index = selectStyle.value.findIndex(item => item.id == image.id);
|
||||
if (index != -1) {
|
||||
selectStyle.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selectStyle,
|
||||
menuOptions,
|
||||
SelectMenu,
|
||||
style_image_list,
|
||||
selectedKeyRef,
|
||||
showDropdownRef,
|
||||
dropOption,
|
||||
current_img,
|
||||
x: x,
|
||||
y: y,
|
||||
closeTag,
|
||||
handleContextMenu(e, image) {
|
||||
e.preventDefault();
|
||||
current_img.value = image;
|
||||
nextTick().then(() => {
|
||||
showDropdownRef.value = true;
|
||||
x.value = e.clientX;
|
||||
y.value = e.clientY;
|
||||
});
|
||||
},
|
||||
onClickoutside() {
|
||||
showDropdownRef.value = false;
|
||||
},
|
||||
handleSelect(key) {
|
||||
debugger
|
||||
showDropdownRef.value = false;
|
||||
if (key == "selectStyle") {
|
||||
// 将当前的风格添加到选择的风格中
|
||||
if (current_img.value) {
|
||||
selectStyle.value.push(current_img.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px; min-height: 40px;">
|
||||
<n-tooltip trigger="hover" v-for="item in selectStyle" placement="bottom-start">
|
||||
<template #trigger>
|
||||
<n-tag @close="closeTag(item)" :key="item.id" type="success"
|
||||
style="margin-right: 10px; margin-bottom: 10px" closable>
|
||||
{{ item.id.split("-")[0] }}
|
||||
</n-tag>
|
||||
</template>
|
||||
<div>
|
||||
<n-image :src="item.image" :width="200" :preview-disabled="true"></n-image>
|
||||
</div>
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, watch, nextTick } from "vue";
|
||||
import { NImage, NTag, NTooltip, useMessage } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NTag, NTooltip, NImage
|
||||
},
|
||||
props: ["selectStyle"],
|
||||
setup(props) {
|
||||
let selectStyle = ref(props.selectStyle ? props.selectStyle : []);
|
||||
let message = useMessage();
|
||||
|
||||
|
||||
// 监听props.selectStyle修改
|
||||
watch(() => props.selectStyle, (value) => {
|
||||
selectStyle.value = value;
|
||||
if (!selectStyle.value) {
|
||||
selectStyle.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
})
|
||||
|
||||
async function closeTag(image) {
|
||||
debugger;
|
||||
let index = selectStyle.value.findIndex(item => item.id == image.id);
|
||||
if (index != -1) {
|
||||
selectStyle.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selectStyle,
|
||||
closeTag,
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-layout has-sider style="height: 100vh; position: relative;">
|
||||
<n-layout-sider bordered collapse-mode="width" :collapsed-width="64" :width="220" :collapsed="collapsed"
|
||||
show-trigger @collapse="collapsed = true" @expand="collapsed = false">
|
||||
<n-menu :collapsed="collapsed" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions"
|
||||
:render-icon="renderMenuIcon" :expand-icon="expandIcon" />
|
||||
</n-layout-sider>
|
||||
<n-layout-content content-style="padding:30px">
|
||||
<!-- <Setting></Setting> -->
|
||||
<router-view></router-view>
|
||||
</n-layout-content>
|
||||
</n-layout>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from "vue"
|
||||
import { RouterLink } from "vue-router"
|
||||
import { NMenu, NSpace, NLayout, NLayoutSider, NLayoutContent, NIcon, useDialog, useNotification, useMessage, NSwitch, NButton } from "naive-ui";
|
||||
|
||||
import { BookmarkOutline, CaretDownOutline } from "@vicons/ionicons5";
|
||||
import CheckMachineId from "../Components/CheckMachineId.vue"
|
||||
import axios from 'axios';
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
import ShowMessage from './ShowMessage.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NMenu, NSpace, NLayout, NLayoutSider, NLayoutContent, NIcon, ShowMessage, NSwitch, NButton
|
||||
},
|
||||
setup() {
|
||||
let collapsed = ref(false);
|
||||
let dialog = useDialog();
|
||||
let machineRef = ref();
|
||||
let message = useMessage();
|
||||
let notification = useNotification();
|
||||
|
||||
function renderMenuIcon(option) {
|
||||
if (option.key === "sheep-man")
|
||||
return true;
|
||||
if (option.key === "food")
|
||||
return null;
|
||||
return h(NIcon, null, { default: () => h(BookmarkOutline) });
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载版本
|
||||
await window.api.GetVersion((value) => {
|
||||
document.title = "LAITool " + value;
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.SHOW_MESSAGE_DIALOG], (value) => {
|
||||
let message = value.message;
|
||||
let type = "success";
|
||||
let title = "成功"
|
||||
if (value.code == 0) {
|
||||
type = "error";
|
||||
title = "成功但失败"
|
||||
} else if (value.code == 2) {
|
||||
type = "warning",
|
||||
title = "警告"
|
||||
}
|
||||
dialog.create({
|
||||
type: type,
|
||||
title: title,
|
||||
showIcon: true,
|
||||
content: message,
|
||||
style: `width : 400px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
})
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.SHOW_MAIN_NOTIFICATION], (value) => {
|
||||
let message = value.message;
|
||||
let type = "success";
|
||||
let title = "成功"
|
||||
if (value.code == 0) {
|
||||
type = "error";
|
||||
title = "失败"
|
||||
} else if (value.code == 2) {
|
||||
type = "warning";
|
||||
title = "警告"
|
||||
}
|
||||
notification.create({
|
||||
type: type,
|
||||
title: title,
|
||||
content: message,
|
||||
keepAliveOnHover: true,
|
||||
duration: 2500,
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// 弹窗,获取机器码
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
content: () => h(CheckMachineId, { ref: machineRef }),
|
||||
style: `width : 400px; height: 300px`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
onClose: () => {
|
||||
// 关闭程序
|
||||
window.api.QuitApp();
|
||||
return false;
|
||||
},
|
||||
onEsc: () => {
|
||||
// 关闭程序
|
||||
window.api.QuitApp();
|
||||
return false;
|
||||
},
|
||||
onPositiveClick: async () => {
|
||||
debugger
|
||||
await new Promise(async (resolve, reject) => {
|
||||
await window.api.CheckMachineId(toRaw(machineRef.value.machineId), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
window.api.getSettingDafultData(async (value) => {
|
||||
window.config = value;
|
||||
})
|
||||
})
|
||||
|
||||
function expandIcon(value) {
|
||||
return h(NIcon, null, { default: () => h(CaretDownOutline) });
|
||||
}
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "sdoriginal",
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => "原创生图"
|
||||
}
|
||||
),
|
||||
key: "sdoriginal",
|
||||
},
|
||||
{
|
||||
label: "反推矩阵",
|
||||
key: "backward_matrix",
|
||||
children: [
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "getframe",
|
||||
}
|
||||
},
|
||||
{ default: () => "抽帧" }
|
||||
),
|
||||
key: "backward_frame"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "copywriting",
|
||||
}
|
||||
},
|
||||
{ default: () => "文案洗稿" }
|
||||
),
|
||||
key: "copywriting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "pushBackPrompt",
|
||||
}
|
||||
},
|
||||
{ default: () => "反推提示词" }
|
||||
),
|
||||
key: "push_back"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "regenerate",
|
||||
}
|
||||
},
|
||||
{ default: () => "批次生成" }
|
||||
),
|
||||
key: "regenerate"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "VideoGenerate",
|
||||
}
|
||||
},
|
||||
{ default: () => "重绘/视频合成" }
|
||||
),
|
||||
key: "VideoGenerate"
|
||||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// label: "剪辑",
|
||||
// key: "clip_options",
|
||||
// children: [
|
||||
// {
|
||||
// label: () => h(
|
||||
// RouterLink,
|
||||
// {
|
||||
// to: {
|
||||
// name: "align_draft",
|
||||
// }
|
||||
// },
|
||||
// { default: () => "剪映草稿对齐" }
|
||||
// ),
|
||||
// key: "align_draft"
|
||||
// },
|
||||
// {
|
||||
// label: () => h(
|
||||
// RouterLink,
|
||||
// {
|
||||
// to: {
|
||||
// name: "add_draft",
|
||||
// }
|
||||
// },
|
||||
// { default: () => "添加剪映草稿" }
|
||||
// ),
|
||||
// key: "add_draft"
|
||||
// }
|
||||
// ]
|
||||
|
||||
// },
|
||||
{
|
||||
label: "设置",
|
||||
key: "setting",
|
||||
children: [
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "global_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "通用设置" }
|
||||
),
|
||||
key: "global_setting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "clip_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "剪映设置" }
|
||||
),
|
||||
key: "clip_setting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "videogeneratesetting",
|
||||
}
|
||||
},
|
||||
{ default: () => "生成视频设置" }
|
||||
),
|
||||
key: "videogeneratesetting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "sd_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "SD设置" }
|
||||
),
|
||||
key: "sd_setting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "mj_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "MJ设置" }
|
||||
),
|
||||
key: "mj_setting"
|
||||
}
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
renderMenuIcon,
|
||||
menuOptions,
|
||||
expandIcon,
|
||||
collapsed,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,744 @@
|
||||
<template>
|
||||
<div id="menu_div"
|
||||
style="padding : 5px 0 5px 0; position: fixed; left: 0; top: 0; backgroundColor: white; height: 30px; width: 100%; z-index: 100; display: flex; opacity: 1;">
|
||||
<n-select style="width: 200px" v-model:value="output_folder" :options="select_options" />
|
||||
<n-button type="info" style="margin:0 10px 0 10px; width: 80px" @click="RefreshImageData">刷新</n-button>
|
||||
<n-input placeholder="修改前" v-model:value="modeifyBefore" style="width: 200px"></n-input>
|
||||
<div style="display: flex; margin: 0 10px 0 10px; color: black;align-items: center;">
|
||||
<span><strong id="menu_strong">替换为</strong></span>
|
||||
</div>
|
||||
<n-input placeholder="修改后(为空则删除,并没有替换后台数据,点击后面的一键修改)" v-model:value="modeifyAfter"
|
||||
style="width: 200px"></n-input>
|
||||
<n-button type="info" style="width: 100px; margin-left: 10px;" @click="ReplacePrompt">替换</n-button>
|
||||
<n-input placeholder="添加数据(只能输入英文,逗号分割)" v-model:value="globalPromptInput" style="width: 220px"></n-input>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button type="info" style="width: 100px; margin-left: 10px;"
|
||||
@click="AddTranslateGlobalPrompt">翻译后添加</n-button>
|
||||
</template>
|
||||
<span>翻译后添加。会有中文标签提示词和英文提示词。</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button type="info" style="width: 100px; margin-left: 10px;" @click="AddGlobalPrompt">添加提示词</n-button>
|
||||
</template>
|
||||
<span>直接添加提示词。没有进行翻译。没有中文标签。需自己手动全部翻译</span>
|
||||
</n-popover>
|
||||
<n-button type="info" style="width: 150px; margin-left: 10px; margin-right: 10px;"
|
||||
@click="ModifyAllSDPrompt">一键应用所有修改</n-button>
|
||||
</div>
|
||||
<n-data-table style="margin-top: 30px;" :columns="columns" :data="data"> </n-data-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from "vue"
|
||||
import { NDataTable, NImage, useMessage, NButton, NInput, NSelect, NInputNumber, useDialog, NModal, NCheckbox, NSwitch, NPopover, NConfigProvider, darkTheme } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import GenerateAllImages from "../Components/GenerateAllImages.vue"
|
||||
import ModifyPromptChinese from "../Components/ModifyPromptChinese.vue"
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "重绘",
|
||||
key: 'reDraw',
|
||||
type: "primary",
|
||||
}
|
||||
,
|
||||
{
|
||||
title: "重绘所有",
|
||||
key: "reDrawSelect",
|
||||
type: "warning",
|
||||
}
|
||||
,
|
||||
{
|
||||
title: "修改应用全部",
|
||||
key: "modifyJson",
|
||||
type: "info",
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NDataTable, NImage, NButton, NInput, NSelect, NInputNumber, NModal, NCheckbox, NSwitch, ModifyPromptChinese, NPopover, NConfigProvider
|
||||
},
|
||||
setup() {
|
||||
const containerRef = ref < HTMLElement | undefined > (undefined)
|
||||
let message = useMessage();
|
||||
let dialog = useDialog();
|
||||
let output_folder = ref("");
|
||||
let select_options = ref([]);
|
||||
let controlHeight = ref(0);
|
||||
let modeifyBefore = ref("");
|
||||
let modeifyAfter = ref("");
|
||||
let data = ref([]);
|
||||
let multiple_value = ref([]);
|
||||
let ImageInitData = ref(null);
|
||||
let projectFolderList = ref([]);
|
||||
let dialogWidth = ref(0);
|
||||
let isTagMode = ref(false);
|
||||
let promptChineseRef = ref(null)
|
||||
let zhAutoTranslate = ref(false);
|
||||
let globalPromptInput = ref(null);
|
||||
|
||||
let model = ref("");
|
||||
let modelOption = [{
|
||||
label: "文生图",
|
||||
value: "txt2img"
|
||||
}, {
|
||||
label: "图生图",
|
||||
value: "img2img"
|
||||
}];
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
async function RefreshImageData() {
|
||||
debugger
|
||||
data.value = [];
|
||||
console.log(window)
|
||||
await window.api.RefreshImageData([window.id, output_folder.value], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
value.value.forEach(item => {
|
||||
value.value.loading = false;
|
||||
});
|
||||
data.value = value.value;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换当前词
|
||||
*/
|
||||
async function ReplacePrompt() {
|
||||
if (modeifyBefore.value == "") {
|
||||
return;
|
||||
}
|
||||
// 循环data里面的所有的提示词,修改
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
let prompt = element.prompt;
|
||||
prompt = prompt.replaceAll(modeifyBefore.value, modeifyAfter.value);
|
||||
data.value[i].prompt = prompt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键应用所有的修改
|
||||
*/
|
||||
async function ModifyAllSDPrompt() {
|
||||
dialog.warning({
|
||||
title: "确认",
|
||||
content: "确定修改?后面执行的所有的重绘,都将被修改!",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {
|
||||
let count = 0;
|
||||
// 循环修改全部
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
let prompt = "";
|
||||
let chinese_prompt = [];
|
||||
if (element.chinese_prompt) {
|
||||
for (let i = 0; i < element.chinese_prompt.length; i++) {
|
||||
const item = element.chinese_prompt[i];
|
||||
prompt += item.src + ',';
|
||||
chinese_prompt.push(item);
|
||||
}
|
||||
} else {
|
||||
prompt = element.prompt;
|
||||
chinese_prompt = null;
|
||||
}
|
||||
let arg = {
|
||||
name: element.name,
|
||||
prompt: prompt,
|
||||
denoising_strength: element.denoising_strength,
|
||||
model: element.model,
|
||||
adetailer: element.adetailer,
|
||||
chinese_prompt: chinese_prompt
|
||||
}
|
||||
|
||||
await window.api.ModifyInpurCropJson(JSON.stringify(arg), (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
});
|
||||
}
|
||||
if (count >= data.value.length) {
|
||||
message.success("修改成功")
|
||||
}
|
||||
else {
|
||||
message.error(`第${count + 1}条修改错误`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const createColumns = ({ ReGenerate, ModifyInputJson, GenerateAllImageDialog, TranslatePrompt }) => {
|
||||
return [
|
||||
{
|
||||
title: '名字',
|
||||
key: 'name',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '原图像',
|
||||
key: 'old_image',
|
||||
width: 200,
|
||||
render(row) {
|
||||
return h(NImage, {
|
||||
src: "file:\\" + row.oldImage + `?time=${row.timedata}`,
|
||||
alt: 'Image',
|
||||
height: '150'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '图像',
|
||||
key: 'new_image',
|
||||
width: 200,
|
||||
render(row) {
|
||||
return h(NImage, {
|
||||
src: "file:\\" + row.image + `?time=${row.timedata}`,
|
||||
alt: 'Image',
|
||||
width: '150'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
width: 100,
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
strong: true,
|
||||
size: "small",
|
||||
type: item.type,
|
||||
style: "margin : 5px",
|
||||
onClick: () => {
|
||||
if (item.key == "reDraw") {
|
||||
ReGenerate(row);
|
||||
} else if (item.key == "modifyJson") {
|
||||
ModifyInputJson(row);
|
||||
} else if (item.key == "reDrawSelect") {
|
||||
GenerateAllImageDialog(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
return h('div', {
|
||||
style: "display : flex; flex-wrap : wrap"
|
||||
}, btn)
|
||||
},
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h("span", {}, [
|
||||
h("span", {
|
||||
style: "margin-right : 10px; width : 80px "
|
||||
}, "正向翻译"),
|
||||
h(NSwitch, {
|
||||
railStyle: ({
|
||||
focused,
|
||||
checked
|
||||
}) => {
|
||||
const style = {};
|
||||
if (checked) {
|
||||
style.background = "#f3a694";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #f3a694";
|
||||
}
|
||||
} else {
|
||||
style.background = "#775039";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #775039";
|
||||
}
|
||||
}
|
||||
return style;
|
||||
},
|
||||
onUpdateValue: (value) => {
|
||||
zhAutoTranslate.value = value;
|
||||
},
|
||||
value: zhAutoTranslate
|
||||
}, {
|
||||
checked: "自动翻译",
|
||||
unchecked: "手动翻译",
|
||||
}),
|
||||
h(NButton, {
|
||||
size: "tiny",
|
||||
color: "#e18a3b",
|
||||
style: "margin-left : 5px",
|
||||
onClick: async () => {
|
||||
// 只翻译chinese_prompt为null的
|
||||
let filterData = data.value.filter(item => item.chinese_prompt == null);
|
||||
await TranslatePrompt(JSON.stringify(filterData), 'en', 'zh', true);
|
||||
}
|
||||
}, {
|
||||
default: () => "翻译英文"
|
||||
}),
|
||||
])
|
||||
},
|
||||
key: 'prompt_translate',
|
||||
render(row, index) {
|
||||
return h("div", {}, [
|
||||
h(ModifyPromptChinese, { ref: promptChineseRef, initData: row, type: "zh", translateMode: zhAutoTranslate })
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title() {
|
||||
return h("span", {}, [
|
||||
h("span", {
|
||||
style: "margin-right : 10px; width : 80px "
|
||||
}, "正向提示词"),
|
||||
h(NButton, {
|
||||
size: "tiny",
|
||||
color: "#e18a3b",
|
||||
style: "margin-left : 5px",
|
||||
onClick: async () => {
|
||||
TranslateChineseToPrompt();
|
||||
}
|
||||
}, {
|
||||
default: () => "翻译中文"
|
||||
}),
|
||||
])
|
||||
},
|
||||
key: 'prompt',
|
||||
width: "300",
|
||||
render(row, index) {
|
||||
return h("div", {
|
||||
style: "position: relative;"
|
||||
}, [
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
style: "margin-top : 20px",
|
||||
value: row.prompt,
|
||||
type: "textarea",
|
||||
autosize: "{ minRows: 3 }",
|
||||
onUpdateValue(v) {
|
||||
data.value[index].prompt = v
|
||||
}
|
||||
}),
|
||||
h(NButton, {
|
||||
size: 'tiny',
|
||||
color: "#dd7694",
|
||||
style: "position: absolute; left : 0;top : 0",
|
||||
onClick: async () => {
|
||||
await window.api.TranslatePrompt([JSON.stringify([row]), 'en', 'zh', window.id, false], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
}, { default: () => "翻译" })
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "参数",
|
||||
key: "argument",
|
||||
width: 300,
|
||||
render(row, index) {
|
||||
return h('div', { class: "div_1" }, [
|
||||
h('div', { class: "div_2" }, [
|
||||
h('label', { class: 'input-label' }, '生图方式'),
|
||||
h(NSelect, {
|
||||
options: modelOption,
|
||||
style: "width : 100px",
|
||||
value: row.model,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].model = v;
|
||||
}
|
||||
}),
|
||||
h(NCheckbox, {
|
||||
style: "margin-left : 5px ; height : 100%",
|
||||
checked: row.adetailer,
|
||||
label: "修脸",
|
||||
onUpdateChecked(v) {
|
||||
data.value[index].adetailer = v;
|
||||
}
|
||||
|
||||
}),
|
||||
]),
|
||||
h('div', { class: "div_2", style: "margin: 10px 0" }, [
|
||||
h('label', { class: 'input-label' }, '重绘幅度'),
|
||||
h(NInputNumber, {
|
||||
step: "0.05",
|
||||
style: "width : 100px",
|
||||
precision: 2,
|
||||
value: row.denoising_strength,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].denoising_strength = v;
|
||||
}
|
||||
})
|
||||
])
|
||||
]);
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// 计算一行的提示词
|
||||
async function ModifyPrompt(data, id = null) {
|
||||
// 如果index 存在。替换单独
|
||||
if (id) {
|
||||
let index = data.value.findIndex(item => item.id == id);
|
||||
if (index < 0) {
|
||||
message.error("写入源数据ID错误。请检查");
|
||||
return;
|
||||
}
|
||||
let res = '';
|
||||
if (data.value[index].chinese_prompt && data.value[index].chinese_prompt.length > 0) {
|
||||
for (let i = 0; i < data.value[index].chinese_prompt.length; i++) {
|
||||
const item = data.value[index].chinese_prompt[i];
|
||||
res += item.src + ',';
|
||||
}
|
||||
} else {
|
||||
res = data.value[index].prompt
|
||||
}
|
||||
data.value[index].prompt = res;
|
||||
} else {
|
||||
// 修改全部
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
let res = "";
|
||||
if (element.chinese_prompt && element.chinese_prompt.length > 0) {
|
||||
for (let i = 0; i < element.chinese_prompt.length; i++) {
|
||||
const item = element.chinese_prompt[i];
|
||||
res += item.src + ',';
|
||||
}
|
||||
} else {
|
||||
res = element.prompt
|
||||
}
|
||||
data.value[i].prompt = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.getImagePromptList((value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message)
|
||||
return;
|
||||
}
|
||||
value.value.forEach(item => {
|
||||
value.value.loading = false;
|
||||
});
|
||||
|
||||
|
||||
data.value = value.value;
|
||||
ModifyPrompt(data);
|
||||
window.id = value.windowID;
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.REGENERATE_IMAGE_RETUN, window.id], (value) => {
|
||||
if (value.type != 0) {
|
||||
return;
|
||||
}
|
||||
// 修改图片的数据源
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
let filteArr = data.value.filter(item => item.id == value.id);
|
||||
filteArr[0].timedata = new Date().getTime();
|
||||
filteArr[0].loading = false;
|
||||
message.success(JSON.stringify(value));
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.TRANSLATE_RETURN_REFRESH, window.id], (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error("返回数据错误");
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
|
||||
// 直接
|
||||
let index = data.value.findIndex(item => item.id == value.rowId);
|
||||
if (index < 0) {
|
||||
message.error("翻译后修改源数据,对应数据ID未找到");
|
||||
return;
|
||||
}
|
||||
data.value[index].chinese_prompt = value.data;
|
||||
|
||||
ModifyPrompt(data, value.rowId)
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
window.api.getSubFolderList(["start", "output_crop"], (value) => {
|
||||
select_options.value = [];
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const v = value.data[i];
|
||||
let obj = {
|
||||
label: v.folder,
|
||||
value: v.folder
|
||||
};
|
||||
select_options.value.push(obj);
|
||||
}
|
||||
})
|
||||
|
||||
await window.api.getSettingDafultData((value) => {
|
||||
window.config = value;
|
||||
zhAutoTranslate.value = value.translation_auto;
|
||||
let div = document.getElementById("menu_div");
|
||||
let strong = document.getElementById("menu_strong");
|
||||
if (value.theme == "dark") {
|
||||
div.style.backgroundColor = "#24242c";
|
||||
strong.style.color = "#ccc"
|
||||
} else {
|
||||
div.style.backgroundColor = "#fff";
|
||||
strong.style.color = "#333"
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.TRANSLATE_RETURN_REFRESH);
|
||||
window.api.removeEventListen(DEFINE_STRING.REGENERATE_IMAGE_RETUN);
|
||||
})
|
||||
|
||||
/**
|
||||
* 将翻译添加到队列中的方式
|
||||
* @param {要翻译的数据(数组)} translateData
|
||||
* @param {原语言(zh,en)} from
|
||||
* @param {目标语言(zh,en)} to
|
||||
* @param {是否显示弹窗} isShow
|
||||
*/
|
||||
async function TranslatePrompt(translateData, from, to) {
|
||||
console.log(translateData);
|
||||
await window.api.TranslatePrompt([translateData, from, to, window.id, true], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译中文到提示词
|
||||
*/
|
||||
async function TranslateChineseToPrompt() {
|
||||
debugger;
|
||||
// 将当前的prompt和Chinese_prompt中的src全部删除(全部重新翻译)
|
||||
let fileterData = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
//
|
||||
// 判断chinese_prompt 是不是存在
|
||||
if (element.chinese_prompt && element.chinese_prompt.length > 0) {
|
||||
// 删除 prompt 和 chinese_prompt 中的src
|
||||
data.value[i].prompt = "";
|
||||
for (let j = 0; j < element.chinese_prompt.length; j++) {
|
||||
data.value[i].chinese_prompt[j].src = "";
|
||||
}
|
||||
// 将修改后的数据添加到fileterData
|
||||
fileterData.push(data.value[i]);
|
||||
}
|
||||
}
|
||||
console.log(fileterData);
|
||||
let tmp_str = JSON.stringify(fileterData);
|
||||
// 开始调用
|
||||
await window.api.TranslatePrompt([tmp_str, 'zh', 'en', window.id, true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示选择框
|
||||
* @param {} row
|
||||
*/
|
||||
async function GenerateAllImageDialog(row) {
|
||||
// 处理数据。获取当前的所有的数据
|
||||
dialogWidth.value = window.innerWidth * 0.8;
|
||||
window.ImageInitData = data.value;
|
||||
window.currentImageId = row.id;
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
content: () => h(GenerateAllImages),
|
||||
style: `width : ${dialogWidth.value}px`,
|
||||
maskClosable: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前的修改直接应用到json文件中
|
||||
* @param {数据列表的行数据} row
|
||||
*/
|
||||
async function ModifyInputJson(row) {
|
||||
dialog.warning({
|
||||
title: "确认",
|
||||
content: "确定修改?后面执行的所有的重绘,都将被修改!",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: () => {
|
||||
// 修改chinese_prompt 和prompt
|
||||
let prompt = "";
|
||||
let chinese_prompt = [];
|
||||
if (row.chinese_prompt) {
|
||||
for (let i = 0; i < row.chinese_prompt.length; i++) {
|
||||
const element = row.chinese_prompt[i];
|
||||
prompt += element.src + ',';
|
||||
chinese_prompt.push(element);
|
||||
}
|
||||
} else {
|
||||
prompt = row.prompt;
|
||||
chinese_prompt = null;
|
||||
}
|
||||
|
||||
let arg = {
|
||||
name: row.name,
|
||||
prompt: prompt,
|
||||
denoising_strength: row.denoising_strength,
|
||||
model: row.model,
|
||||
adetailer: row.adetailer,
|
||||
chinese_prompt: chinese_prompt
|
||||
}
|
||||
|
||||
window.api.ModifyInpurCropJson(JSON.stringify(arg), (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("修改成功");
|
||||
console.log(vaule);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function ReGenerate(rowData) {
|
||||
let obj = {
|
||||
id: rowData.id,
|
||||
denoising_strength: rowData.denoising_strength,
|
||||
image: rowData.image,
|
||||
name: rowData.name,
|
||||
negative_prompt: rowData.negative_prompt,
|
||||
prompt: rowData.prompt,
|
||||
sampler_name: rowData.sampler_name,
|
||||
seed: rowData.seed,
|
||||
timedata: new Date().getTime(),
|
||||
model: rowData.model,
|
||||
image_style: rowData.image_style,
|
||||
lora: rowData.lora,
|
||||
adetailer: rowData.adetailer,
|
||||
image_style_list: toRaw(rowData.image_style_list),
|
||||
}
|
||||
rowData.loading = true;
|
||||
// 开始重新生成
|
||||
window.api.ReGenerateImage([window.id, obj, 0], (value) => {
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
// 直接添加提示词到prompt中
|
||||
async function AddGlobalPrompt() {
|
||||
if (globalPromptInput.value == "" || globalPromptInput.value == null) {
|
||||
message.error("请输入要添加的数据");
|
||||
return;
|
||||
}
|
||||
// 直接添加数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
let add_p = globalPromptInput.value.replaceAll(',', ',');
|
||||
data.value[i].prompt = `${add_p},${data.value[i].prompt}`;
|
||||
}
|
||||
message.success("添加成功");
|
||||
globalPromptInput.value = null;
|
||||
}
|
||||
|
||||
// 添加翻译后的全局配置
|
||||
async function AddTranslateGlobalPrompt() {
|
||||
if (globalPromptInput.value == "" || globalPromptInput.value == null) {
|
||||
message.error("请输入要添加的数据");
|
||||
return;
|
||||
}
|
||||
|
||||
let tmp_s = globalPromptInput.value.replaceAll(",", ",");
|
||||
// 翻译立即返回
|
||||
// 翻译单个。马上返回
|
||||
await window.api.TranslateReturnNow([tmp_s, 'en', 'zh', true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].chinese_prompt.unshift(...value.data)
|
||||
}
|
||||
ModifyPrompt(data);
|
||||
message.success("添加成功");
|
||||
globalPromptInput.value = "";
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
ImageInitData,
|
||||
projectFolderList,
|
||||
columns: createColumns({
|
||||
ReGenerate(rowData) {
|
||||
ReGenerate(rowData)
|
||||
},
|
||||
ModifyInputJson,
|
||||
GenerateAllImageDialog,
|
||||
TranslatePrompt,
|
||||
TranslateChineseToPrompt
|
||||
}),
|
||||
RefreshImageData,
|
||||
output_folder,
|
||||
select_options,
|
||||
multiple_value,
|
||||
containerRef,
|
||||
controlHeight,
|
||||
modeifyAfter,
|
||||
modeifyBefore,
|
||||
ReplacePrompt,
|
||||
ModifyAllSDPrompt,
|
||||
modelOption,
|
||||
isTagMode,
|
||||
zhAutoTranslate,
|
||||
AddGlobalPrompt,
|
||||
AddTranslateGlobalPrompt,
|
||||
globalPromptInput,
|
||||
model,
|
||||
bodyStyle: {
|
||||
width: '600px'
|
||||
},
|
||||
segmented: {
|
||||
content: 'soft',
|
||||
footer: 'soft'
|
||||
},
|
||||
promptChineseRef,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.input-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.div_1 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.div_2 {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: auto;">
|
||||
<!-- <div style="display: flex; justify-content: center;">
|
||||
<n-image width="200" height="250" src="http://qiniuyun.upsurging.xyz/LAI/1280X1280.PNG" preview-disabled />
|
||||
<n-image width="250" style="margin-left: 100px;"
|
||||
src="http://qiniuyun.upsurging.xyz/LAI/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20240427225845.png"
|
||||
preview-disabled />
|
||||
</div> -->
|
||||
<div style="font-size: 15px ;display: flex; justify-content: center;">
|
||||
<div id="showmessage"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from "vue"
|
||||
import { NImage, useMessage } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage
|
||||
},
|
||||
setup() {
|
||||
onMounted(async () => {
|
||||
await window.api.GetShowMessage((value) => {
|
||||
if (value.code == 0) {
|
||||
return;
|
||||
}
|
||||
let parser = new DOMParser();
|
||||
let html = parser.parseFromString(value.data, 'text/html');
|
||||
let div = document.getElementById("showmessage");
|
||||
|
||||
div.innerHTML = value.data.shareInfo.note_list[0].html_content
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form ref="formRef" label-placement="top" :model="characterData" :rules="rules">
|
||||
<n-form-item label="人物名称" path="label">
|
||||
<n-input v-model:value="characterData.label" placeholder="请输入人物名称" />
|
||||
</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="请输入人物提示词" />
|
||||
<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="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" />
|
||||
</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-input v-model:value="characterData.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="SaveCharacterTag">保存</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 { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentCharacter", "initFunc", "currentTags"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let alias_tags = ref(props.currentTags);
|
||||
let characterData = ref(props.currentCharacter);
|
||||
let loading = ref(false);
|
||||
let formRef = 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,
|
||||
cref_cw: characterData.value.cref_cw,
|
||||
lora: characterData.value.lora,
|
||||
})
|
||||
})
|
||||
characterData.value["children"] = children;
|
||||
|
||||
debugger;
|
||||
console.log(characterData.value);
|
||||
characterData.value['type'] = "character_main";
|
||||
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(characterData.value), "character_tags"], (value) => {
|
||||
debugger;
|
||||
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",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
cref_cw: 20,
|
||||
lora: 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;
|
||||
}
|
||||
debugger
|
||||
//
|
||||
console.log(value.data);
|
||||
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("必填英文提示词")
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
characterData,
|
||||
alias_tags,
|
||||
SaveCharacterTag,
|
||||
TranslatePrompt,
|
||||
loading,
|
||||
rules,
|
||||
formRef
|
||||
}
|
||||
}
|
||||
})
|
||||
</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) => {
|
||||
debugger;
|
||||
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,141 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top" ref="formRef" :rules="rules" :model="sceneData">
|
||||
<n-form-item label="场景名称" path="label">
|
||||
<n-input v-model:value="sceneData.label" placeholder="请输入场景名称" />
|
||||
</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 } from "naive-ui";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
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) => {
|
||||
debugger;
|
||||
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,144 @@
|
||||
<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="请输入风格名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="风格提示词描述(中文)">
|
||||
<n-input type="textarea" :rows="3" v-model:value="styleData.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="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:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="styleData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="styleData.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="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 } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentStyle", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let styleData = ref(props.currentStyle);
|
||||
let loading = ref(false);
|
||||
let formRef = 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";
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(styleData.value), "style_tags"], (value) => {
|
||||
debugger;
|
||||
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,
|
||||
image_url: null,
|
||||
cref_cw: 20,
|
||||
lora: 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("必填人物名称"),
|
||||
prompt: ruleObj("必填英文提示词")
|
||||
};
|
||||
|
||||
return {
|
||||
styleData,
|
||||
SaveStyleTag,
|
||||
loading,
|
||||
rules,
|
||||
formRef,
|
||||
TranslatePrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
</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) => {
|
||||
debugger;
|
||||
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,139 @@
|
||||
<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: 3,
|
||||
}" placeholder="自动分析的数据" v-model:value="AnalyzeCharacter"></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>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NCard, NTabs, NTabPane, NTree } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import CharacterTags from "./CharacterTags.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NCard, NTabs, NTabPane, NTree, CharacterTags
|
||||
},
|
||||
props: ["initData", "height"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let data = ref(props.initData);
|
||||
let height = ref(props.height);
|
||||
let AnalyzeCharacter = ref("");
|
||||
let word = ref("");
|
||||
let loading = ref(false);
|
||||
let treeData = ref([{
|
||||
label: '人物',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
suffix: () => h(NButton,
|
||||
{ text: true, type: "primary" },
|
||||
{ default: () => "suffix" })
|
||||
},
|
||||
{
|
||||
label: '场景',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
suffix: () => h(NButton,
|
||||
{ text: true, type: "primary" },
|
||||
{ default: () => "suffix" })
|
||||
}]);
|
||||
|
||||
onMounted(async () => {
|
||||
let temp_arr = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
temp_arr.push(element.after_gpt);
|
||||
}
|
||||
word.value = temp_arr.join('\n');
|
||||
|
||||
// 判断是不是已经有生成(有有生成显示)
|
||||
await window.api.GetConfigJson(JSON.stringify(['auto_analyze_character', ""]), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
AnalyzeCharacter.value = value.data;
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 自动分析角色
|
||||
*/
|
||||
async function autoAnalyzeCharacter() {
|
||||
if (word.value == "" || word.value == null) {
|
||||
message.error("请输入要分析的文案");
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
|
||||
await window.api.AutoAnalyzeCharacter(toRaw(word.value), (value) => {
|
||||
debugger
|
||||
loading.value = false;
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
AnalyzeCharacter.value = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自动分析的设置
|
||||
*/
|
||||
async function SaveAutoAnalyzeCharacter() {
|
||||
if (AnalyzeCharacter.value == "" || AnalyzeCharacter.value == null) {
|
||||
message.error("没有分析分析出来的信息");
|
||||
return;
|
||||
}
|
||||
// 保存信息
|
||||
await window.api.SaveCopywritingInformation([toRaw(AnalyzeCharacter.value), "auto_analyze_character"], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
treeData,
|
||||
word,
|
||||
height,
|
||||
loading,
|
||||
AnalyzeCharacter,
|
||||
autoAnalyzeCharacter,
|
||||
SaveAutoAnalyzeCharacter,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-tabs .n-tabs-nav--bar-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,354 @@
|
||||
<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" />
|
||||
<AddStyleTags v-else-if="currentType.startsWith('style')" :currentStyle="currentStyle"
|
||||
:initFunc="InitData" />
|
||||
<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" />
|
||||
</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 } 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 } from "@vicons/ionicons5"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NCard, NTabs, NTabPane, NTree, NSplit, AddCharacterTag, AddStyleTags, AddSceneTags, AddPrefixTags, AddSuffixTags, NIcon, Trash
|
||||
},
|
||||
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("character");
|
||||
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) }) });
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
SetAutoHeight();
|
||||
await InitData();
|
||||
})
|
||||
|
||||
|
||||
// 设置标签集的自动高度,传一个外面的高度进来
|
||||
function SetAutoHeight() {
|
||||
debugger
|
||||
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",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
cref_cw: 50,
|
||||
lora: null,
|
||||
}
|
||||
currentTags.value = [];
|
||||
currentType.value = "character_main";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加场景标签
|
||||
*/
|
||||
function AddSceneTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
currentScene.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: "scene_main",
|
||||
prompt: null,
|
||||
}
|
||||
currentType.value = "scene_main";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加 风格迁移预设
|
||||
* @param {*} e
|
||||
*/
|
||||
function AddStyleTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
currentStyle.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: "style_main",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
sref_cw: 50,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前缀
|
||||
*/
|
||||
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() {
|
||||
debugger;
|
||||
// 执行切换组件和数据
|
||||
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 {
|
||||
debugger
|
||||
console.log(option);
|
||||
currentType.value = option.type;
|
||||
// 修改当前属性
|
||||
if (currentType.value.startsWith("character")) {
|
||||
// 修改tags的值
|
||||
currentCharacter.value = option;
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'" style="display: flex">
|
||||
<span style="margin-right: 5px">参数</span>
|
||||
<n-select
|
||||
style="width: 75px"
|
||||
size="tiny"
|
||||
:options="characterSelectMoedlOptions"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
v-model:value="title_character_select_model"
|
||||
placeholder="选择模式"
|
||||
></n-select>
|
||||
<n-button size="tiny" color="#e18a3b" style="margin-left: 5px" @click="RefreshTagData">
|
||||
<n-icon>
|
||||
<reload />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<div v-if="title_character_select_model == 'tag'">
|
||||
<div style="min-height: 65px; overflow-y: auto; width: 190px">
|
||||
<span>人物:</span>
|
||||
<n-tag
|
||||
v-for="(character, index) in character_tags"
|
||||
v-model:checked="character.checked"
|
||||
checkable
|
||||
@click="UpdateCharacterTags"
|
||||
size="small"
|
||||
style="margin: 0 3px 3px 0"
|
||||
>
|
||||
{{ character.label }}
|
||||
</n-tag>
|
||||
</div>
|
||||
<div style="margin-top: 5px; min-height: 65px; overflow-y: auto">
|
||||
<span>场景:</span>
|
||||
<n-tag
|
||||
v-for="(scene, index) in scene_tags"
|
||||
v-model:checked="scene.checked"
|
||||
checkable
|
||||
@click="UpdateSceneTags()"
|
||||
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="character_tags"
|
||||
placeholder="选择人物"
|
||||
/>
|
||||
</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="scene_tags"
|
||||
placeholder="选择场景"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NTag, NSelect, NIcon } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { cloneDeep, debounce, get, once } from 'lodash'
|
||||
import { Refresh, Reload } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NTag,
|
||||
NSelect,
|
||||
NIcon,
|
||||
Reload
|
||||
},
|
||||
props: ['type', 'row', 'characterSelectModel', 'tags', 'func'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let type = ref(props.type)
|
||||
let title_character_select_model = ref(props.characterSelectModel)
|
||||
let row = ref(props.row)
|
||||
let character_tags = ref([])
|
||||
let scene_tags = ref([])
|
||||
let tags = ref(props.tags)
|
||||
|
||||
let characterData = ref([])
|
||||
|
||||
// 存储当权选择的tag数据
|
||||
let select_character_tags = ref([])
|
||||
let select_scene_tags = ref([])
|
||||
|
||||
let characterSelectMoedlOptions = ref([])
|
||||
watch(
|
||||
() => props.characterSelectModel,
|
||||
(value) => {
|
||||
debugger
|
||||
// InitData(value);
|
||||
if (value.value == 'drop') {
|
||||
select_character_tags.value = []
|
||||
select_scene_tags.value = []
|
||||
|
||||
character_tags.value.forEach((item) => {
|
||||
if (item.checked) select_character_tags.value.push(item.key)
|
||||
})
|
||||
scene_tags.value.forEach((item) => {
|
||||
if (item.checked) select_scene_tags.value.push(item.key)
|
||||
})
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.tags,
|
||||
async (value) => {
|
||||
// 刷新数据
|
||||
await InitCharacterAndSceneData()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.row,
|
||||
async (value) => {
|
||||
row.value = value
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
// debugger
|
||||
// await InitCharacterAndSceneData()
|
||||
await window.mj.GetTagSelectModel((value) => {
|
||||
characterSelectMoedlOptions.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
// 更行当前行选中的人物角色tag数据
|
||||
async function UpdateCharacterTags(value, option) {
|
||||
console.log(value, option)
|
||||
if (title_character_select_model.value == 'tag') {
|
||||
row.value.character_tags = []
|
||||
// 循环当前界面,每次全部更新。
|
||||
character_tags.value.forEach((item) => {
|
||||
if (item.checked) {
|
||||
row.value.character_tags.push(item)
|
||||
} else {
|
||||
// 移除
|
||||
let index = row.value.character_tags.findIndex((tag) => tag.key == item.key)
|
||||
if (index != -1) {
|
||||
row.value.character_tags.splice(index, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 修改当前的场景信息tags数据
|
||||
async function UpdateSceneTags() {
|
||||
row.value.scene_tags = []
|
||||
scene_tags.value.forEach((item) => {
|
||||
if (item.checked) {
|
||||
row.value.scene_tags.push(item)
|
||||
} else {
|
||||
// 移除
|
||||
let index = row.value.scene_tags.findIndex((tag) => tag.key == item.key)
|
||||
if (index != -1) {
|
||||
row.value.scene_tags.splice(index, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染tag
|
||||
const renderTag = ({ option, handleClose }) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
size: 'tiny',
|
||||
type: 'success',
|
||||
closable: true,
|
||||
onClose: (e) => {
|
||||
e.stopPropagation()
|
||||
handleClose()
|
||||
// 移除当前option
|
||||
let k = option.key
|
||||
character_tags.value.forEach((item) => {
|
||||
if (item.key == k) {
|
||||
item.checked = false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ default: () => option.label }
|
||||
)
|
||||
}
|
||||
/**
|
||||
* 加载人物场景等信息
|
||||
* @param {*} tags
|
||||
*/
|
||||
async function InitCharacterAndSceneData() {
|
||||
debugger
|
||||
// if (!tags) return
|
||||
character_tags.value = cloneDeep(tags.value.character_tags)
|
||||
character_tags.value.forEach((item) => {
|
||||
item.children = null
|
||||
})
|
||||
scene_tags.value = cloneDeep(tags.value.scene_tags)
|
||||
|
||||
select_character_tags.value = []
|
||||
select_scene_tags.value = []
|
||||
// 初始化数据(选择为true)
|
||||
for (let i = 0; row.value.character_tags && i < row.value.character_tags.length; i++) {
|
||||
const element = row.value.character_tags[i]
|
||||
let index = character_tags.value.findIndex((tag) => tag.key == element.key)
|
||||
if (index != -1) {
|
||||
character_tags.value[index].checked = true
|
||||
// 将当前选中的数据生成到select_character_tags中
|
||||
select_character_tags.value.push(character_tags.value[index].key)
|
||||
}
|
||||
}
|
||||
for (let i = 0; row.value.scene_tags && i < row.value.scene_tags.length; i++) {
|
||||
const element = row.value.scene_tags[i]
|
||||
let index = scene_tags.value.findIndex((tag) => tag.key == element.key)
|
||||
if (index != -1) {
|
||||
scene_tags.value[index].checked = true
|
||||
// 将当前选中的数据生成到select_scene_tags中
|
||||
select_scene_tags.value.push(scene_tags.value[index].key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改属性
|
||||
* @param {*} value
|
||||
*/
|
||||
function UpdateImageGenerateCategory(value) {
|
||||
title_character_select_model.value = value
|
||||
// InitData(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新标签数据
|
||||
*/
|
||||
async function RefreshTagData() {
|
||||
props.func.refreshTagData()
|
||||
}
|
||||
|
||||
return {
|
||||
characterData,
|
||||
row,
|
||||
character_tags,
|
||||
select_character_tags,
|
||||
select_scene_tags,
|
||||
UpdateCharacterTags,
|
||||
UpdateSceneTags,
|
||||
scene_tags,
|
||||
renderTag,
|
||||
type,
|
||||
characterSelectMoedlOptions,
|
||||
InitCharacterAndSceneData,
|
||||
UpdateImageGenerateCategory,
|
||||
title_character_select_model,
|
||||
RefreshTagData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">推理提示词</span>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button color="#7c461e" style="margin-left: 5px" size="tiny" @click="AddPrefix"
|
||||
>添加前缀</n-button
|
||||
>
|
||||
</template>
|
||||
<span>添加前缀,只能添加英文,并且通用前缀不为空,推理会自动添加前缀</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button color="#e18a3b" style="margin-left: 5px" size="tiny" @click="AddSuffix"
|
||||
>添加后缀</n-button
|
||||
>
|
||||
</template>
|
||||
<span>添加后缀,只能添加英文,并且通用后缀不为空,推理会自动添加后缀</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
color="#c0d695"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="SelectGenerateImagesStyle"
|
||||
>风格</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="#779649"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="TranslateAll('english')"
|
||||
>一键翻译</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
<span
|
||||
>将下面的推理提示词或者是手写的提示词翻译成英文(只会翻译存在中文的数据),下面的翻译中文会将所有的推理提示词翻译为中文(全部都会翻译)</span
|
||||
>
|
||||
</n-popover>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateSelect">
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#dd7694"
|
||||
style="margin-right: 5px"
|
||||
@click="TranslateSelect('english')"
|
||||
>翻译英文</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-button size="tiny" color="#a76283" style="margin-right: 5px" @click="SinglePrompt"
|
||||
>单句推理</n-button
|
||||
>
|
||||
<n-button color="#a46244" style="margin-right: 5px" size="tiny" @click="NextGptPrompt"
|
||||
>下推理</n-button
|
||||
>
|
||||
<n-input
|
||||
size="tiny"
|
||||
placeholder="请生成或推理提示词"
|
||||
type="textarea"
|
||||
v-model:value="row.gpt_prompt"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import { NButton, NDropdown, NInput, NPopover, useMessage } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NInput,
|
||||
NPopover,
|
||||
NDropdown
|
||||
},
|
||||
props: ['type', 'row', 'index', 'func'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let type = ref(props.type)
|
||||
let row = ref(props.row)
|
||||
let translate_options = ref([{ label: '翻译中文', key: 'chinese' }])
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.row,
|
||||
(newVal) => {
|
||||
row.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
/**
|
||||
* 单句生图
|
||||
*/
|
||||
async function SingleGenerateImage() {
|
||||
await window.api.OriginalSDImageGenerate([JSON.stringify([row]), true, false], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加生图任务成功')
|
||||
})
|
||||
}
|
||||
|
||||
function containsChineseOrPunctuation(str) {
|
||||
return /[\u4e00-\u9fa5]|[\u3000-\u301e\u2013\u2014\u2018\u2019\u201c\u201d\u2026\u203b\uff08\uff09\uff1a\uff1b\uff1f\uff01\uff0c\u3001\uff0e\u3002\uff1f\uff01\u2018\u2019\u201c\u201d]/.test(
|
||||
str
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译选择
|
||||
*/
|
||||
async function TranslateSelect(key) {
|
||||
// 将中文翻译成英文
|
||||
if (key == 'english') {
|
||||
// 检查是不是有中文
|
||||
if (containsChineseOrPunctuation(row.value.gpt_prompt)) {
|
||||
// 开始翻译
|
||||
let t = [[toRaw(row.value)], 'zh', 'en', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else {
|
||||
message.error('当前翻译的数据里面没有中文')
|
||||
}
|
||||
} else if (key == 'chinese') {
|
||||
debugger
|
||||
let t = [[toRaw(row.value)], 'en', 'zh', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译所有的数据
|
||||
* @param {*} key
|
||||
*/
|
||||
async function TranslateAll(key) {
|
||||
props.func.translateAll(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前缀
|
||||
*/
|
||||
async function AddPrefix() {
|
||||
props.func.addPrefix()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加后缀
|
||||
*/
|
||||
async function AddSuffix() {
|
||||
props.func.addSuffix()
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择风格
|
||||
*/
|
||||
async function SelectGenerateImagesStyle() {
|
||||
props.func.selectGenerateImagesStyle()
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句推理
|
||||
*/
|
||||
async function SinglePrompt() {
|
||||
await props.func.singlePrompt(row.value)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 下推理
|
||||
*/
|
||||
async function NextGptPrompt() {
|
||||
await props.func.nextGptPrompt(row.value)
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
row,
|
||||
SingleGenerateImage,
|
||||
translate_options,
|
||||
TranslateSelect,
|
||||
TranslateAll,
|
||||
AddPrefix,
|
||||
AddSuffix,
|
||||
SelectGenerateImagesStyle,
|
||||
SinglePrompt,
|
||||
NextGptPrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">参数</span>
|
||||
</div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('sd')">
|
||||
<n-checkbox
|
||||
style="margin-left: 5px; height: 100%"
|
||||
:checked="row.adetailer"
|
||||
label="修脸"
|
||||
@update:checked="onUpdateChecked"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('mj')"></div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('d3')"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch, computed } from "vue";
|
||||
import { NButton, NCheckbox, NInput, useMessage } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NInput,
|
||||
NCheckbox,
|
||||
},
|
||||
props: ["type", "row", "index", "func"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let type = ref(props.type);
|
||||
let row = ref(props.row);
|
||||
let input_status = ref("default");
|
||||
|
||||
// 重新计算命令
|
||||
watch(
|
||||
() => props.type,
|
||||
(newVal) => {
|
||||
type.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.row,
|
||||
(newVal) => {
|
||||
row.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
watch(
|
||||
() => props.row.mj_message,
|
||||
(newVal) => {
|
||||
if (newVal && newVal["hasBadPrompt"]) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (row.value["mj_message"]?.hasBadPrompt) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
});
|
||||
|
||||
async function onUpdateChecked(v) {
|
||||
debugger;
|
||||
row.adetailer = v;
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
row,
|
||||
onUpdateChecked,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">提示词命令</span>
|
||||
<n-button size="tiny" @click="MergePrompt" color="#7c461e">合并命令</n-button>
|
||||
<n-button
|
||||
color="#e18a3b"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MJBadPromptCheck"
|
||||
>敏感词检查</n-button
|
||||
>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#dd7694"
|
||||
style="margin-right: 5px"
|
||||
@click="SingleGenerateImage"
|
||||
>单句生图</n-button
|
||||
>
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#a76283"
|
||||
style="margin-right: 5px"
|
||||
@click="NextGenerateImage"
|
||||
>下生图</n-button
|
||||
>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
color="#a46244"
|
||||
style="margin-right: 5px"
|
||||
size="tiny"
|
||||
@click="ImportMJImageUrl"
|
||||
>导入图片</n-button
|
||||
>
|
||||
</template>
|
||||
<span>粘贴MJ里面的图片链接。自动下载到对应的行。并分割。</span>
|
||||
</n-popover>
|
||||
|
||||
<n-input
|
||||
:status="input_status"
|
||||
readonly
|
||||
size="tiny"
|
||||
placeholder="请生成出图命令或是提示词"
|
||||
type="textarea"
|
||||
v-model:value="row.prompt"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue";
|
||||
import { NButton, NInput, NPopover, useMessage, useDialog } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import InputDialogContent from "./InputDialogContent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NInput,
|
||||
NPopover,
|
||||
},
|
||||
props: ["type", "row", "index", "func"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let dialog = useDialog();
|
||||
let type = ref(props.type);
|
||||
let row = ref(props.row);
|
||||
let input_status = ref("default");
|
||||
let image_url_ref = ref(null);
|
||||
|
||||
// let input_image_
|
||||
|
||||
// 重新计算命令
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.row,
|
||||
(newVal) => {
|
||||
row.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.row.mj_message,
|
||||
(newVal) => {
|
||||
if (newVal && newVal["hasBadPrompt"]) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (row.value["mj_message"]?.hasBadPrompt) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* SD单句生图
|
||||
* @param {*} row 要生图的行数据
|
||||
*/
|
||||
async function SingleGenerateImage() {
|
||||
debugger;
|
||||
let ca = window.config.image_generate_category;
|
||||
// 判断当前的生图模式
|
||||
if (ca == "sd") {
|
||||
// 生成SD生图
|
||||
await window.api.OriginalSDImageGenerate(
|
||||
[JSON.stringify([toRaw(row.value)]), true, false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加SD生图任务成功");
|
||||
}
|
||||
);
|
||||
} else if (ca == "mj") {
|
||||
// MJ生图
|
||||
await window.mj.OriginalMJImageGenerate(
|
||||
[JSON.stringify([toRaw(row.value)]), true, false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加MJ生图任务成功");
|
||||
}
|
||||
);
|
||||
} else if (ca == "d3") {
|
||||
// D3 生图
|
||||
} else {
|
||||
message.error("生图的类别错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下生图
|
||||
*/
|
||||
async function NextGenerateImage() {
|
||||
props.func.nextGenerateImage(row.value, window.config.image_generate_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ敏感词检测
|
||||
*/
|
||||
async function MJBadPromptCheck() {
|
||||
props.func.mJBadPromptCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并提示词
|
||||
*/
|
||||
async function MergePrompt() {
|
||||
props.func.mergePrompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入图片的链接,将图片链接下载,并且分割
|
||||
*/
|
||||
async function ImportMJImageUrl() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400;
|
||||
let dialogHeight = 150;
|
||||
dialog.create({
|
||||
title: "添加图片链接",
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: image_url_ref,
|
||||
initData: null,
|
||||
placeholder: "请输入图片链接",
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
debugger;
|
||||
let row_image_url = image_url_ref.value.data;
|
||||
|
||||
// 下载指定的图片地址并且分割
|
||||
await window.mj.DownloadImageUrlAndSplit(
|
||||
JSON.stringify([toRaw(row.value), row_image_url]),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 更新当前的数据
|
||||
row.value.outImagePath = value.data.outImagePath;
|
||||
row.value.subImagePath = value.data.subImagePath;
|
||||
row.value.mj_message = value.data.mj_message ? value.data.mj_message : {};
|
||||
row.value.mj_message.image_click = value.data.image_click;
|
||||
row.value.mj_message.image_path = value.data.image_path;
|
||||
row.value.mj_message.progress = 100;
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
row,
|
||||
SingleGenerateImage,
|
||||
NextGenerateImage,
|
||||
MJBadPromptCheck,
|
||||
input_status,
|
||||
MergePrompt,
|
||||
ImportMJImageUrl,
|
||||
image_url_ref,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'" style="display: flex">
|
||||
<span>出图</span>
|
||||
<n-select
|
||||
style="width: 70px; margin-left: 5px"
|
||||
size="tiny"
|
||||
:options="image_options"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
v-model:value="select_image_category"
|
||||
placeholder="选择生图模式"
|
||||
></n-select>
|
||||
<n-button
|
||||
v-if="select_image_category == 'mj'"
|
||||
color="#7c461e"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MJGetImage"
|
||||
>MJ采集图片</n-button
|
||||
>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<div style="height: 22px">进度:{{ data.mj_message?.progress }}%</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">
|
||||
<n-image
|
||||
class="g-image-class generate-image-show"
|
||||
v-if="outImagePath"
|
||||
:src="outImagePath"
|
||||
fit="cover"
|
||||
width="120px"
|
||||
height="120px"
|
||||
/>
|
||||
</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">
|
||||
<n-image
|
||||
subImage="true"
|
||||
class="g-image-class"
|
||||
draggable="true"
|
||||
:width="width"
|
||||
style="margin: 1px"
|
||||
v-for="(image, index) in images"
|
||||
:key="image.id"
|
||||
:src="image"
|
||||
alt="图片描述"
|
||||
lazy
|
||||
>
|
||||
</n-image>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
|
||||
import { NImage, useMessage, NDivider, NImageGroup, NButton, NSelect } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NDivider,
|
||||
NImageGroup,
|
||||
NButton,
|
||||
NSelect
|
||||
},
|
||||
props: ['initData', 'type', 'image_generate_category', 'func'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let data = ref(props.initData)
|
||||
let type = ref(props.type)
|
||||
let images = ref(props.initData.subImagePath)
|
||||
let outImagePath = ref(props.initData.outImagePath)
|
||||
let sd_batch_size = ref(null)
|
||||
let width = ref(null)
|
||||
|
||||
let dragTarget = null
|
||||
|
||||
let image_options = ref([])
|
||||
let select_image_category = ref(props.image_generate_category)
|
||||
|
||||
watch(
|
||||
() => select_image_category.value,
|
||||
(value) => {
|
||||
select_image_category.value = value
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 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
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 props.initData 修改
|
||||
watch(
|
||||
() => props.initData,
|
||||
(value) => {
|
||||
data.value = value
|
||||
setNodeAttribute()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.InitSDConfig((value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
sd_batch_size.value = value.data.batch_size
|
||||
width.value = 170 / 3
|
||||
console.log(width.value)
|
||||
})
|
||||
setNodeAttribute()
|
||||
|
||||
// 获取生图下拉列表的方式
|
||||
await window.api.GetImageGenerateCategory((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
image_options.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 图片拖拽结束事件
|
||||
async function imageDragDrop(e) {
|
||||
debugger
|
||||
console.log('end', e.target, dragTarget)
|
||||
// 判断原始数据是不是为空
|
||||
if (dragTarget == null) {
|
||||
message.error('只能拖拽到当前行的图片上')
|
||||
return
|
||||
}
|
||||
// 判断结束位置是不是主图片
|
||||
if (e.target.getAttribute('mainImage') != '1') {
|
||||
message.error('图片只能拖拽到主图片上')
|
||||
return
|
||||
}
|
||||
let source_img = dragTarget.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 UpdateImageGenerateCategory(value, option) {
|
||||
// 更新配置文件和缓存
|
||||
window.config.image_generate_category = value
|
||||
// 保存文件
|
||||
await window.api.ModifySampleSetting(
|
||||
JSON.stringify({
|
||||
image_generate_category: value
|
||||
}),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('修改成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ采集图片
|
||||
*/
|
||||
async function MJGetImage() {
|
||||
props.func.mJGetImage()
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
type,
|
||||
image_options,
|
||||
sd_batch_size,
|
||||
width,
|
||||
images,
|
||||
outImagePath,
|
||||
imageDragStart,
|
||||
imageDragDrop,
|
||||
imageDragOver,
|
||||
select_image_category,
|
||||
UpdateImageGenerateCategory,
|
||||
MJGetImage
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<n-input
|
||||
v-model:value="word"
|
||||
:autosize="{ minRows: 30, maxRows: 30 }"
|
||||
type="textarea"
|
||||
placeholder="输入分镜文案"
|
||||
@input="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 } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput
|
||||
},
|
||||
props: ['initData'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let rowCount = ref(0)
|
||||
let data = ref(props.initData)
|
||||
let word = ref(null)
|
||||
let word_arr = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
debugger
|
||||
console.log(data)
|
||||
// 拼接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()
|
||||
})
|
||||
|
||||
/**
|
||||
* 修改数据框时触发的事件
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
rowCount,
|
||||
ChangeWordInput,
|
||||
word,
|
||||
word_arr
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,562 @@
|
||||
<template>
|
||||
<div id="import_word_and_srt">
|
||||
<div style="height: 30px; margin-bottom: 5px">
|
||||
<n-button 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 } from 'vue'
|
||||
import { NButton, NDataTable, useMessage, NInput, NImage, useDialog } from 'naive-ui'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import EditWord from './EditWord.vue'
|
||||
const buttonArr = [
|
||||
{
|
||||
title: '下对齐',
|
||||
key: 'AlignNextWord',
|
||||
color: '#8a2be0'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NDataTable,
|
||||
NInput,
|
||||
NImage
|
||||
},
|
||||
props: ['initData', 'menuType', 'height'],
|
||||
setup(props) {
|
||||
let hh = props.height
|
||||
let maxHeight = props.height - 80
|
||||
let data = ref(props.initData)
|
||||
const message = useMessage()
|
||||
let selectKey = ref([])
|
||||
let dialog = useDialog()
|
||||
let editWordRef = ref(null)
|
||||
|
||||
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() {
|
||||
debugger
|
||||
let div = document.getElementById('import_word_and_srt')
|
||||
div.style.height = hh + 'px'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
setHeight()
|
||||
})
|
||||
|
||||
/**
|
||||
* 重新计算指定索引值的时间
|
||||
* @param {data value 值的索引} index
|
||||
*/
|
||||
function modifyTime(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].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 (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 {
|
||||
debugger
|
||||
// 先保存单个文件(然后在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('未知错误')
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入字幕并进行分镜输出,显示时间,将分镜给他传进去
|
||||
*/
|
||||
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) => {
|
||||
debugger
|
||||
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 () => {
|
||||
debugger
|
||||
console.log(editWordRef)
|
||||
debugger
|
||||
let word_arr = editWordRef.value.word.split('\n')
|
||||
let new_arr = []
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<n-input :placeholder="placeholder" v-model:value="data"></n-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from 'vue'
|
||||
import { NInput, useMessage } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NInput
|
||||
},
|
||||
props: ['initData', 'placeholder'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let data = ref(props.initData)
|
||||
let placeholder = ref(props.placeholder)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
return {
|
||||
data,
|
||||
placeholder
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div id="prompt-setting" style="margin-top: 30px">
|
||||
<n-card title="GPT设置" hoverable>
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<div style="margin-bottom: 3px">gpt模型选择</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="gpt_model_options"
|
||||
v-model:value="gpt_model"
|
||||
style="width: 160px"
|
||||
>
|
||||
</n-select>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<div style="margin-bottom: 3px">gpt自动推理模式</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="gpt_auto_inference_options"
|
||||
v-model:value="gpt_auto_inference"
|
||||
style="width: 180px"
|
||||
>
|
||||
</n-select>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<div style="margin-bottom: 3px">选择自定义推理词</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="customize_gpt_options"
|
||||
v-model:value="customize_gpt_select"
|
||||
style="width: 150px"
|
||||
>
|
||||
</n-select>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="SD设置" hoverable style="margin-top: 5px">
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<div style="margin-bottom: 3px">SD生图数量</div>
|
||||
<n-input-number
|
||||
style="width: 120px"
|
||||
v-model:value="sd_batch_size"
|
||||
:show-button="false"
|
||||
:min="1"
|
||||
:max="8"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<div style="margin-bottom: 3px">种子</div>
|
||||
<n-input-number
|
||||
style="width: 120px"
|
||||
v-model:value="sd_seed"
|
||||
:show-button="false"
|
||||
:min="-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style="margin-left: 10px; display: flex; flex-wrap: wrap; width: 130px">
|
||||
<div style="margin-bottom: 3px; width: 100%">生图尺寸</div>
|
||||
<n-input-number
|
||||
style="width: 50px"
|
||||
v-model:value="sd_image_width"
|
||||
:show-button="false"
|
||||
:min="-1"
|
||||
/>
|
||||
<span style="margin: 0 10px; line-height: 35px; font-size: 20px"> * </span>
|
||||
<n-input-number
|
||||
style="width: 50px"
|
||||
v-model:value="sd_image_height"
|
||||
:show-button="false"
|
||||
:min="-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="MJ设置" hoverable style="margin-top: 5px">
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<!-- <div style="margin-bottom: 3px;">铭感词相关</div> -->
|
||||
<n-button size="small" @click="AddMjBadPrompt" color="#7c461e">添加敏感词</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="其他设置" hoverable style="margin-top: 5px">
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<div style="margin-bottom: 3px">人物场景选择模式</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="character_select_model_options"
|
||||
v-model:value="character_select_model"
|
||||
style="width: 120px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<div style="text-align: right; margin-top: 5px">
|
||||
<n-button type="success" size="medium" @click="SaveSetting">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NSelect,
|
||||
NButton,
|
||||
NInputNumber,
|
||||
NInput,
|
||||
NCard,
|
||||
useDialog
|
||||
} from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import InputDialogContent from './InputDialogContent.vue'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NSelect,
|
||||
NButton,
|
||||
NInputNumber,
|
||||
NInput,
|
||||
NCard
|
||||
},
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let gpt_model = ref(window.config.gpt_model)
|
||||
let gpt_auto_inference = ref(window.config.gpt_auto_inference)
|
||||
let sd_batch_size = ref(null)
|
||||
let sd_seed = ref(null)
|
||||
let sd_image_width = ref(null)
|
||||
let sd_image_height = ref(null)
|
||||
let bad_prompt_ref = ref(null)
|
||||
|
||||
let gpt_model_options = ref([])
|
||||
let gpt_auto_inference_options = ref([])
|
||||
|
||||
let customize_gpt_options = ref([])
|
||||
let customize_gpt_select = ref(window.config.customize_gpt_prompt)
|
||||
|
||||
let character_select_model = ref(window.config.character_select_model)
|
||||
let character_select_model_options = ref([])
|
||||
|
||||
/**
|
||||
* 初始化GPT的option
|
||||
*/
|
||||
async function InitGptOptions() {
|
||||
await window.api.getGptModelOption('all', (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
gpt_model_options.value = value.data
|
||||
})
|
||||
await window.api.getGptAutoInferenceOptions('all', (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
gpt_auto_inference_options.value = value.data
|
||||
})
|
||||
|
||||
await window.api.GetCustomizeGptPrompt('all', (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
customize_gpt_options.value = value.data.map((item) => {
|
||||
return {
|
||||
label: item.prompt_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
await window.mj.GetTagSelectModel((value) => {
|
||||
character_select_model_options.value = value.data
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载全局配置
|
||||
// 获取SD配置
|
||||
await window.api.InitSDConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
sd_batch_size.value = value.data.batch_size
|
||||
sd_seed.value = value.data.seed
|
||||
sd_image_width.value = value.data.width
|
||||
sd_image_height.value = value.data.height
|
||||
})
|
||||
|
||||
await InitGptOptions()
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存设置
|
||||
*/
|
||||
async function SaveSetting() {
|
||||
try {
|
||||
window.config.gpt_model = gpt_model.value
|
||||
window.config.gpt_auto_inference = gpt_auto_inference.value
|
||||
window.config.character_select_model = character_select_model.value
|
||||
// 保存到通用配置文件
|
||||
let obj = {
|
||||
gpt_model: gpt_model.value,
|
||||
gpt_auto_inference: gpt_auto_inference.value,
|
||||
customize_gpt_prompt: customize_gpt_select.value,
|
||||
character_select_model: character_select_model.value
|
||||
}
|
||||
await window.api.ModifySampleSetting(JSON.stringify(obj), (value) => {
|
||||
if (value.code == 0) {
|
||||
throw new Error(value.message)
|
||||
}
|
||||
})
|
||||
// 保存到SD配置文件
|
||||
let sdObj = {
|
||||
batch_size: toRaw(sd_batch_size.value),
|
||||
width: toRaw(sd_image_width.value),
|
||||
height: toRaw(sd_image_height.value)
|
||||
}
|
||||
await window.api.SaveSDConfig(sdObj, (value) => {
|
||||
if (value.code == 0) {
|
||||
throw new Error(value.message)
|
||||
}
|
||||
})
|
||||
|
||||
message.success('保存成功')
|
||||
} catch (error) {
|
||||
debugger
|
||||
message.error('保存失败 ' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
async function AddMjBadPrompt() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加MJ敏感词',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: bad_prompt_ref,
|
||||
initData: null,
|
||||
placeholder: '请输入MJ敏感词'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
debugger
|
||||
let mj_bad_prompt = bad_prompt_ref.value.data
|
||||
|
||||
if (mj_bad_prompt == null || mj_bad_prompt == '') {
|
||||
return
|
||||
}
|
||||
|
||||
let data = {
|
||||
id: uuidv4(),
|
||||
value: mj_bad_prompt.toLowerCase()
|
||||
}
|
||||
|
||||
// 保存到后台
|
||||
await window.mj.AddMjBadPrompt(data, (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
gpt_model,
|
||||
gpt_model_options,
|
||||
gpt_auto_inference_options,
|
||||
gpt_auto_inference,
|
||||
sd_batch_size,
|
||||
SaveSetting,
|
||||
sd_seed,
|
||||
sd_image_width,
|
||||
sd_image_height,
|
||||
customize_gpt_options,
|
||||
customize_gpt_select,
|
||||
character_select_model_options,
|
||||
character_select_model,
|
||||
AddMjBadPrompt,
|
||||
bad_prompt_ref
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,926 @@
|
||||
<template>
|
||||
<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 {
|
||||
ref,
|
||||
h,
|
||||
onMounted,
|
||||
defineComponent,
|
||||
onUnmounted,
|
||||
watch,
|
||||
toRaw,
|
||||
render,
|
||||
onBeforeUnmount
|
||||
} from 'vue'
|
||||
import {
|
||||
NDataTable,
|
||||
NImage,
|
||||
useMessage,
|
||||
NInput,
|
||||
NButton,
|
||||
NSwitch,
|
||||
NPopover,
|
||||
useDialog,
|
||||
NCheckbox,
|
||||
NSelect,
|
||||
NIcon
|
||||
} from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import ModifyPromptChinese from '../Components/ModifyPromptChinese.vue'
|
||||
import InputDialogContent from './Components/InputDialogContent.vue'
|
||||
import DataTableShowGenerateImage from './Components/DataTableShowGenerateImage.vue'
|
||||
import { cloneDeep, debounce, min } from 'lodash'
|
||||
import SelectImageStyle from '../Components/SelectImageStyle.vue'
|
||||
import DataTableCharacterAndScene from './Components/DataTableCharacterAndScene.vue'
|
||||
import DataTablePromptRow from './Components/DataTablePromptRow.vue'
|
||||
import DataTableGptPromptRow from './Components/DataTableGptPromptRow.vue'
|
||||
import DataTableParameterRow from './Components/DataTableParameterRow.vue'
|
||||
import { Reload } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NDataTable,
|
||||
NInput,
|
||||
NButton,
|
||||
ModifyPromptChinese,
|
||||
NSwitch,
|
||||
NPopover,
|
||||
NCheckbox,
|
||||
SelectImageStyle,
|
||||
Reload,
|
||||
NIcon
|
||||
},
|
||||
props: ['initData', 'prefix_prompt', 'suffix_prompt'],
|
||||
setup(props) {
|
||||
console.log('输出文件夹位置', props.initData)
|
||||
let data = ref(props.initData)
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let selectKey = ref([])
|
||||
let promptChineseRef = ref(null)
|
||||
let zhAutoTranslate = ref(false)
|
||||
let maxHeight = ref(null)
|
||||
let prefix_prompt = ref(props.prefix_prompt)
|
||||
let suffix_prompt = ref(props.suffix_prompt)
|
||||
|
||||
let titleCharacterAndSceneRef = ref(null)
|
||||
let dataCharacterAndSceneRef = ref(null)
|
||||
|
||||
let prefix_prompt_ref = ref(null)
|
||||
let suffix_prompt_ref = ref(null)
|
||||
let selectStyle = ref([])
|
||||
let characterSelectModel = ref(
|
||||
window.config.character_select_model ? window.config.character_select_model : 'tag'
|
||||
)
|
||||
|
||||
let tags = ref({})
|
||||
|
||||
let AutoSaveDataJsonDebounced = debounce(AutoSaveDataJson, 3000)
|
||||
let SaveDataJsonDebounced = debounce(AutoSaveDataJson, 1000)
|
||||
|
||||
let image_generate_category = ref(
|
||||
window.config.image_generate_category ? window.config.image_generate_category : 'sd'
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.initData,
|
||||
async (newVal) => {
|
||||
data.value = newVal
|
||||
if (window.config.auto_save) {
|
||||
console.log('自动保存数据')
|
||||
AutoSaveDataJsonDebounced()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.prefix_prompt,
|
||||
(newVal) => {
|
||||
prefix_prompt.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.suffix_prompt,
|
||||
(newVal) => {
|
||||
suffix_prompt.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
/**
|
||||
* 自动保存数据到json文件
|
||||
*/
|
||||
async function AutoSaveDataJson() {
|
||||
await window.api.AutoSaveDataJson(JSON.stringify(toRaw(data.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('数据保存错误,错误信息:' + value.message)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const createColumns = ({}) => {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
disabled(row) {
|
||||
return row.name === 'Edward King 3'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'no',
|
||||
width: 50
|
||||
},
|
||||
{
|
||||
title: '字幕',
|
||||
key: 'srt',
|
||||
width: '180',
|
||||
render(row) {
|
||||
let tmp
|
||||
if (row.subValue.length > 0) {
|
||||
tmp = row.subValue.map((item) =>
|
||||
h('div', { style: 'display:flex; height : auto; margin: 2px 0' }, [
|
||||
h(NInput, {
|
||||
size: 'tiny',
|
||||
value: item.srt_value
|
||||
})
|
||||
])
|
||||
)
|
||||
} else {
|
||||
tmp = h('div', { style: 'display:flex; height : auto; margin: 2px 0' }, [
|
||||
h(NInput, {
|
||||
type: 'textarea',
|
||||
autosize: { minRows: 3, maxRows: 6 },
|
||||
size: 'tiny',
|
||||
value: row.after_gpt
|
||||
})
|
||||
])
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
},
|
||||
{
|
||||
// 修改title,往里面添加添加一个下拉选择框
|
||||
title(row, index) {
|
||||
return h(DataTableCharacterAndScene, {
|
||||
ref: titleCharacterAndSceneRef,
|
||||
type: 'title',
|
||||
row: row,
|
||||
characterSelectModel: characterSelectModel,
|
||||
func: {
|
||||
refreshTagData: async () => {
|
||||
try {
|
||||
await InitTags()
|
||||
message.success('人物角色刷新成功')
|
||||
} catch (error) {
|
||||
message.success('人物角色刷新失败,请重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
className: 'prompt_row',
|
||||
key: 'character',
|
||||
width: '200',
|
||||
render(row, index) {
|
||||
return h(DataTableCharacterAndScene, {
|
||||
ref: dataCharacterAndSceneRef,
|
||||
type: 'data',
|
||||
row: row,
|
||||
tags: tags,
|
||||
characterSelectModel: characterSelectModel
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title(row, index) {
|
||||
return h(DataTableGptPromptRow, {
|
||||
type: 'title',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
translateAll: TranslateAll,
|
||||
addPrefix: AddPrefix,
|
||||
addSuffix: AddSuffix,
|
||||
selectGenerateImagesStyle: SelectGenerateImagesStyle
|
||||
}
|
||||
})
|
||||
},
|
||||
key: 'gpt_prompt',
|
||||
className: 'prompt_row',
|
||||
render(row, index) {
|
||||
return h(DataTableGptPromptRow, {
|
||||
type: 'data',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
nextGptPrompt: NextGptPrompt,
|
||||
singlePrompt: SinglePrompt
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
// 提示词命令
|
||||
title(row, index) {
|
||||
return h(DataTablePromptRow, {
|
||||
type: 'title',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
mJBadPromptCheck: MJBadPromptCheck,
|
||||
mergePrompt: MergePrompt
|
||||
}
|
||||
})
|
||||
},
|
||||
key: 'prompt',
|
||||
width: '300',
|
||||
className: 'prompt_row',
|
||||
render(row, index) {
|
||||
return h(DataTablePromptRow, {
|
||||
type: 'data',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
nextGenerateImage: NextGenerateImage
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
// 参数
|
||||
title(row, index) {
|
||||
return h(DataTableParameterRow, {
|
||||
type: 'title',
|
||||
row: row,
|
||||
index: index,
|
||||
image_generate_category: image_generate_category
|
||||
})
|
||||
},
|
||||
key: 'parameter',
|
||||
width: '130',
|
||||
render(row, index) {
|
||||
return h(DataTableParameterRow, {
|
||||
type: `data_${image_generate_category.value}`,
|
||||
row: row,
|
||||
index: index
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
// 出图
|
||||
title(row, index) {
|
||||
return h(DataTableShowGenerateImage, {
|
||||
initData: row,
|
||||
index: index,
|
||||
type: 'title',
|
||||
image_generate_category: image_generate_category,
|
||||
func: {
|
||||
mJGetImage: MJGetImage
|
||||
}
|
||||
})
|
||||
},
|
||||
key: 'options',
|
||||
className: 'prompt_row',
|
||||
width: '300',
|
||||
render(row, index) {
|
||||
return h(DataTableShowGenerateImage, {
|
||||
initData: row,
|
||||
index: index,
|
||||
type: 'data'
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
let isSaved = false
|
||||
// 监听control+s 保存数据
|
||||
let saveListener = function (event) {
|
||||
debugger
|
||||
if (event.ctrlKey && event.key === 's') {
|
||||
event.preventDefault()
|
||||
if (!isSaved) {
|
||||
console.log('User pressed Ctrl+S')
|
||||
SaveDataJsonDebounced()
|
||||
isSaved = true
|
||||
}
|
||||
} else {
|
||||
isSaved = false
|
||||
}
|
||||
}
|
||||
|
||||
async function InitTags() {
|
||||
await window.mj.GetTagDataByTypeAndProperty(['dynamic', null], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
tags.value = value.data
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
zhAutoTranslate.value = window.config.translation_auto
|
||||
|
||||
await window.api.GetConfigJson(JSON.stringify(['image_style', []]), async (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
await window.api.GetImageStyleInfomation(JSON.stringify(toRaw(value.data)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
selectStyle.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
// 判断data中的数据是不是有chinese text
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.TRANSLATE_RETURN_REFRESH, window.id], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('返回数据错误')
|
||||
return
|
||||
}
|
||||
// 直接
|
||||
let index = data.value.findIndex((item) => item.id == value.rowId)
|
||||
if (index < 0) {
|
||||
message.error('翻译后修改源数据,对应数据ID未找到')
|
||||
return
|
||||
}
|
||||
data.value[index].gpt_prompt = value.data
|
||||
})
|
||||
getMaxHeight()
|
||||
|
||||
window.api.setEventListen(
|
||||
[DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, window.id],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('生成图片错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
// 修改data中的数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
if (data.value[i].id == value.id) {
|
||||
data.value[i].subImagePath = value.data.subImagePath.map(
|
||||
(item) => 'file://' + item.replaceAll('\\', '/') + '?time=' + new Date().getTime()
|
||||
)
|
||||
data.value[i].outImagePath =
|
||||
'file://' +
|
||||
value.data.outImagePath.replaceAll('\\', '/') +
|
||||
'?time=' +
|
||||
new Date().getTime()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
window.addEventListener('keydown', saveListener)
|
||||
|
||||
let div = document.getElementsByClassName('prompt_row')
|
||||
for (let i = 0; i < div.length; i++) {
|
||||
div[i].style.padding = '0px 5px'
|
||||
}
|
||||
|
||||
// 监听MJ信息的监听任务的返回
|
||||
|
||||
window.api.setEventListen(
|
||||
[DEFINE_STRING.DISCORD.MAIN_DISCORD_MESSAGE_CHANGE, window.id],
|
||||
(value) => {
|
||||
mainDiscordMessageChanged(value)
|
||||
}
|
||||
)
|
||||
|
||||
await InitTags()
|
||||
})
|
||||
|
||||
onBeforeUnmount(async () => {
|
||||
debugger
|
||||
await AutoSaveDataJson()
|
||||
})
|
||||
|
||||
// 接收Discord的修改信息
|
||||
async function mainDiscordMessageChanged(value) {
|
||||
if (value.code == 0) {
|
||||
message.error('Discord返回错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理数据
|
||||
if (value.type == 'created') {
|
||||
debugger
|
||||
console.log('接收Discord的创建消息', value)
|
||||
// 找到对应ID的data数据
|
||||
|
||||
let index = data.value.findIndex((item) => item.id == value.id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
delete value.type
|
||||
delete value.code
|
||||
|
||||
data.value[index]['mj_message'] = value
|
||||
} else if (value.type == 'updated') {
|
||||
console.log('接收Discord的更新消息', value)
|
||||
debugger
|
||||
// 修改对应的数据
|
||||
let index = data.value.findIndex((item) => item.mj_message?.message_id == value.message_id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
if (value.image_click != null && value.image_click != '') {
|
||||
data.value[index]['outImagePath'] = value.image_click
|
||||
}
|
||||
delete value.type
|
||||
delete value.code
|
||||
|
||||
data.value[index]['mj_message'] = value
|
||||
} else if (value.type == 'delete') {
|
||||
console.log('接收Discord的删除消息', value)
|
||||
} else if (value.type == 'finished') {
|
||||
console.log('接收Discord的完成消息', value)
|
||||
debugger
|
||||
|
||||
// 修改对应的数据
|
||||
let index = data.value.findIndex((item) => item.mj_message?.image_id == value.image_id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
|
||||
data.value[index]['outImagePath'] = value.image_path
|
||||
|
||||
delete value.type
|
||||
delete value.code
|
||||
value.progress = 100
|
||||
value.message_id = value.message_id
|
||||
|
||||
data.value[index]['mj_message'] = value
|
||||
|
||||
// 返回后端分割图片(传入图片地址,返回分割后的图片地址数组)
|
||||
await window.mj.ImageSplit(
|
||||
JSON.stringify([value.image_path, data.value[index].name]),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
data.value[index]['outImagePath'] = value.data.outImagePath
|
||||
data.value[index]['subImagePath'] = value.data.subImagePath
|
||||
}
|
||||
)
|
||||
} else {
|
||||
message.error('未知的操作类型')
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.TRANSLATE_RETURN_REFRESH)
|
||||
window.api.removeEventListen(DEFINE_STRING.DISCORD.MAIN_DISCORD_MESSAGE_CHANGE)
|
||||
window.removeEventListener('keydown', saveListener)
|
||||
})
|
||||
|
||||
/**
|
||||
* 将翻译添加到队列中的方式
|
||||
* @param {要翻译的数据(数组)} translateData
|
||||
* @param {原语言(zh,en)} from
|
||||
* @param {目标语言(zh,en)} to
|
||||
* @param {是否显示弹窗} isShow
|
||||
*/
|
||||
async function TranslatePrompt(translateData, from, to) {
|
||||
console.log(translateData)
|
||||
await window.api.TranslatePrompt([translateData, from, to, window.id, true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
// 计算当前程序界面的高度,减去上下的高度,设置maxHegiht
|
||||
async function getMaxHeight() {
|
||||
let height = window.innerHeight
|
||||
maxHeight.value = height - 150
|
||||
}
|
||||
|
||||
// 监听程序界面的高度变化
|
||||
window.onresize = function () {
|
||||
getMaxHeight()
|
||||
}
|
||||
|
||||
// 添加前缀的弹窗
|
||||
const AddPrefix = () => {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加前缀',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: prefix_prompt_ref,
|
||||
initData: prefix_prompt.value,
|
||||
placeholder: '请输入前缀'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
prefix_prompt.value = prefix_prompt_ref.value.data
|
||||
// 保存通用前缀
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[prefix_prompt_ref.value.data, 'prefix_prompt', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('保存前缀错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择生成图片的风格
|
||||
*/
|
||||
async function SelectGenerateImagesStyle() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.8
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(SelectImageStyle, {
|
||||
selectStyle: toRaw(selectStyle.value),
|
||||
height: dialogHeight
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 重新加载数据
|
||||
let tmp_arr = []
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i]
|
||||
tmp_arr.push(element.id)
|
||||
}
|
||||
// 将当前的风格保存
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[JSON.stringify(tmp_arr), 'image_style', true],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 添加后缀的弹窗
|
||||
const AddSuffix = () => {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加后缀',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: suffix_prompt_ref,
|
||||
initData: suffix_prompt.value,
|
||||
placeholder: '请输入后缀'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
suffix_prompt.value = suffix_prompt_ref.value.data
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[suffix_prompt_ref.value.data, 'suffix_prompt', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('保存前缀错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加MJ敏感词检查
|
||||
*/
|
||||
async function MJBadPromptCheck() {
|
||||
await window.mj.MJBadPromptCheck(JSON.stringify(toRaw(data.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
debugger
|
||||
// 先将所有的数据设置为false
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].mj_message = data.value[i].mj_message ? data.value[i].mj_message : {}
|
||||
data.value[i].mj_message['hasBadPrompt'] = false
|
||||
}
|
||||
|
||||
// 判断返回的ID数据是不是为空,不为空,修改数据
|
||||
if (value.data.length > 0) {
|
||||
let message_text = ''
|
||||
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i]
|
||||
message_text += ',' + element.no.toString()
|
||||
let index = data.value.findIndex((item) => item.id == element.id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
data.value[index].mj_message = data.value[index].mj_message
|
||||
? data.value[index].mj_message
|
||||
: {}
|
||||
data.value[index].mj_message['hasBadPrompt'] = true
|
||||
}
|
||||
|
||||
let show_text = `检测到 ${value.data.length} 条敏感词,分别在第 ${message_text.substring(
|
||||
1
|
||||
)} 行。有红色框标识。请检查`
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
message: show_text
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前和下面的所有的数据生成图片
|
||||
* @param {*} row
|
||||
*/
|
||||
async function NextGenerateImage(row, ca = 'sd') {
|
||||
let index = data.value.findIndex((item) => item.id == row.id)
|
||||
let tempData = []
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
tempData.push(element)
|
||||
}
|
||||
// 判断当前的生图模式
|
||||
if (ca == 'sd') {
|
||||
// 生成SD生图
|
||||
await window.api.OriginalSDImageGenerate(
|
||||
[JSON.stringify(tempData), true, true],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加SD生图任务成功')
|
||||
}
|
||||
)
|
||||
} else if (ca == 'mj') {
|
||||
// MJ生图
|
||||
await window.mj.OriginalMJImageGenerate([JSON.stringify(tempData), true, true], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加MJ生图任务成功')
|
||||
})
|
||||
} else if (ca == 'd3') {
|
||||
} else {
|
||||
message.error('生图的类别错误')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 往下继续推理
|
||||
* @param value 当前行数据
|
||||
*/
|
||||
async function NextGptPrompt(row) {
|
||||
let index = data.value.findIndex((item) => item.id == row.id)
|
||||
let tempData = []
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
tempData.push(element)
|
||||
}
|
||||
|
||||
await window.api.GPTPrompt(
|
||||
[JSON.stringify(tempData), true, JSON.stringify(toRaw(data.value))],
|
||||
(value) => {
|
||||
console.log('添加推理任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加推理任务成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句推理
|
||||
* @param row
|
||||
*/
|
||||
async function SinglePrompt(row) {
|
||||
await window.api.GPTPrompt(
|
||||
[JSON.stringify([toRaw(row)]), false, JSON.stringify(toRaw(data.value))],
|
||||
(value) => {
|
||||
console.log('添加推理任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加推理任务成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function containsChineseOrPunctuation(str) {
|
||||
return /[\u4e00-\u9fa5]|[\u3000-\u301e\u2013\u2014\u2018\u2019\u201c\u201d\u2026\u203b\uff08\uff09\uff1a\uff1b\uff1f\uff01\uff0c\u3001\uff0e\u3002\uff1f\uff01\u2018\u2019\u201c\u201d]/.test(
|
||||
str
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译传入的数据
|
||||
* @param {*} key 翻译的类型
|
||||
*/
|
||||
async function TranslateAll(key) {
|
||||
debugger
|
||||
// 将中文翻译成英文
|
||||
if (key == 'english') {
|
||||
let tmp_d = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = cloneDeep(data.value[i])
|
||||
if (containsChineseOrPunctuation(element.gpt_prompt)) {
|
||||
tmp_d.push(element)
|
||||
}
|
||||
}
|
||||
// 检查是不是有中文
|
||||
// 开始翻译
|
||||
let t = [tmp_d, 'zh', 'en', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else if (key == 'chinese') {
|
||||
debugger
|
||||
let t = [[...toRaw(data.value)], 'en', 'zh', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成提示词
|
||||
*/
|
||||
async function MergePrompt() {
|
||||
debugger
|
||||
try {
|
||||
// 判断该当前是不是有风格
|
||||
// 获取当前的风格ID
|
||||
let style_str = ''
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i]
|
||||
style_str += element.english_style + ','
|
||||
}
|
||||
|
||||
// 获取当前的前缀和后缀
|
||||
let prefix = prefix_prompt.value
|
||||
let suffix = suffix_prompt.value
|
||||
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
// 匹配提示词
|
||||
// 拼接当前选择的人物
|
||||
let character_string = ''
|
||||
element.character_tags?.forEach((item) => {
|
||||
character_string += item.prompt + ', '
|
||||
})
|
||||
|
||||
// 获取当前的场景提示词
|
||||
let scene_string = ''
|
||||
element.scene_tags?.forEach((item) => {
|
||||
scene_string += item.prompt + ', '
|
||||
})
|
||||
|
||||
// 开始拼接提示词
|
||||
// 风格 + 前缀 + 人物 + 场景 + 词 + 后缀
|
||||
let end_prompt = ` ${style_str} ${
|
||||
prefix ? prefix : ''
|
||||
}, ${character_string} ${scene_string} ${element.gpt_prompt}, ${suffix ? suffix : ''}`
|
||||
data.value[i].prompt = end_prompt
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('生成提示词错误,错误信息如下:' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
async function MJGetImage() {
|
||||
let pa = []
|
||||
// MJ采集图片
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
// 一般进度大于 50 会出现图片,
|
||||
if (!item.mj_message) {
|
||||
return
|
||||
}
|
||||
if (item.mj_message.progress && item.mj_message.progress == 100) {
|
||||
// 判断 image_path 是不是存在。
|
||||
if (item.mj_message.image_id && !item.mj_message.image_path) {
|
||||
// 通过当前的image_id获取图片
|
||||
pa.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pa.length > 0) {
|
||||
window.mj.GetGeneratedMJImageAndSplit(JSON.stringify(pa), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i]
|
||||
// 修改对应的数据
|
||||
let index = data.value.findIndex((item) => item.id == element.id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
data.value[index].outImagePath = element.outImagePath
|
||||
data.value[index].subImagePath = element.subImagePath
|
||||
data.value[index].mj_message.image_click = element.result
|
||||
data.value[index].mj_message.image_path = element.image_path
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.error('没有找到可以自动下载图片的数据,可以手动粘贴链接分割')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns: createColumns({}),
|
||||
selectStyle,
|
||||
rowKey: (row) => row.id,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys
|
||||
},
|
||||
promptChineseRef,
|
||||
zhAutoTranslate,
|
||||
TranslatePrompt,
|
||||
TranslateAll,
|
||||
maxHeight,
|
||||
prefix_prompt_ref,
|
||||
suffix_prompt_ref,
|
||||
characterSelectModel,
|
||||
image_generate_category,
|
||||
titleCharacterAndSceneRef,
|
||||
dataCharacterAndSceneRef
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div id="menu">
|
||||
<MenuButton :initData="data" :initDataFunction="InitData" :Character="AnalyzeCharacter" :treeData="TagTreeData">
|
||||
</MenuButton>
|
||||
</div>
|
||||
<div id="data-table" style="margin-top: 10px;">
|
||||
<DataTable :initData="data" :suffix_prompt="suffix_prompt" :prefix_prompt="prefix_prompt"></DataTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from "vue"
|
||||
import { NImage, useMessage } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import MenuButton from './MenuButton.vue'
|
||||
import DataTable from './DataTable.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, DataTable, MenuButton
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let data = ref([]);
|
||||
let AnalyzeCharacter = ref("");
|
||||
let TagTreeData = ref([]);
|
||||
let suffix_prompt = ref(null);
|
||||
let prefix_prompt = ref(null);
|
||||
let promptError = true;
|
||||
|
||||
async function InitData() {
|
||||
// 加载
|
||||
// 初始化加载项目下面的分镜好的文案
|
||||
// 并判断是不是有洗稿后的文件。一并加载
|
||||
await window.api.GetProjectWord(value => {
|
||||
data.value = value.data;
|
||||
})
|
||||
if (!data.value) {
|
||||
data.value = [];
|
||||
}
|
||||
|
||||
// 重新加载数据
|
||||
// 判断是不是已经有生成(有有生成显示)
|
||||
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([null, {}]), async (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
suffix_prompt.value = value.data.suffix_prompt;
|
||||
prefix_prompt.value = value.data.prefix_prompt;
|
||||
|
||||
})
|
||||
|
||||
// 读取data数据对应的prompt文件
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
await window.api.GetPromptJson(item.name, (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0 && promptError) {
|
||||
message.error(value.message);
|
||||
promptError = false;
|
||||
}
|
||||
data.value[i].prompt = value.data.webui_config.prompt;
|
||||
data.value[i].chinese_prompt = value.data?.chinese_prompt;
|
||||
// data.value[i].gpt_prompt = value.data?.gpt_prompt;
|
||||
data.value[i].adetailer = value.data?.adetailer;
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await InitData();
|
||||
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
data,
|
||||
AnalyzeCharacter,
|
||||
TagTreeData,
|
||||
prefix_prompt,
|
||||
suffix_prompt,
|
||||
InitData,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,369 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<n-button color="#ee7959" @click="ImportWord" size="small">导入字幕/SRT</n-button>
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="GetCharacter" size="small"
|
||||
>角色分析(标签集)</n-button
|
||||
>
|
||||
<n-dropdown trigger="hover" :options="GptButtonOptions" @select="ButtonSelect">
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="GPTPrompt" size="small"
|
||||
>一键推理提示词</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-dropdown trigger="hover" :options="GenerateImageOptions" @select="ButtonSelect">
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="GenerateImageAll" size="small"
|
||||
>生成所有的图片</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<n-dropdown trigger="hover" :options="ResetDataOptions" @select="ButtonSelect">
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="resetALLData" size="small"
|
||||
>重置数据</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="OpenPromptSetting" size="small"
|
||||
>设置</n-button
|
||||
>
|
||||
<div style="display: flex; align-items: center">
|
||||
<n-switch
|
||||
v-model:value="auto_save"
|
||||
:railStyle="railStyle"
|
||||
@update:value="AutoSaveUpdate"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
<template #checked> 自动保存 </template>
|
||||
<template #unchecked> Ctrl + S </template>
|
||||
</n-switch>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import { NImage, useMessage, NButton, useDialog, NPopover, NSwitch, NDropdown } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import ImportWordAndSrt from './Components/ImportWordAndSrt.vue'
|
||||
import CharacterAnalyze from './Components/CharacterAnalyze.vue'
|
||||
import PromptSetting from './Components/PromptSetting.vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
CharacterAnalyze,
|
||||
NPopover,
|
||||
NSwitch,
|
||||
NDropdown
|
||||
},
|
||||
props: ['initData', 'Character', 'treeData', 'autoSave', 'initDataFunction'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let data = ref(props.initData)
|
||||
let AnalyzeCharacter = ref(props.Character)
|
||||
let tagTreeData = ref(props.treeData)
|
||||
let auto_save = ref(window.config.auto_save ? window.config.auto_save : false)
|
||||
|
||||
watch(
|
||||
() => props.initData,
|
||||
(newVal) => {
|
||||
data.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.Character,
|
||||
(newVal) => {
|
||||
AnalyzeCharacter.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.treeData,
|
||||
(newVal) => {
|
||||
tagTreeData.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
window.api.setEventListen([DEFINE_STRING.GPT_GENERATE_PROMPT_RETURN], (value) => {
|
||||
console.log(value)
|
||||
// 修改data中指定的id的某个数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
if (data.value[i].id == value.id) {
|
||||
data.value[i].gpt_prompt = value.gpt_prompt
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
window.config.auto_save = auto_save.value
|
||||
})
|
||||
|
||||
/**
|
||||
* 导入字幕的配置
|
||||
*/
|
||||
async function ImportWord() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.95
|
||||
let dialogHeight = window.innerHeight * 0.95
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(ImportWordAndSrt, { initData: data.value, height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: () => {
|
||||
props.initDataFunction()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色自动分析
|
||||
*/
|
||||
async function GetCharacter() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.6
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(CharacterAnalyze, { initData: data.value, 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 GPTPrompt() {
|
||||
// 将所有的人物添加到后台队列任务中
|
||||
console.log(data.value)
|
||||
// 处理数据,只推理prompt为空的数据
|
||||
|
||||
let tmpData = cloneDeep(toRaw(data.value))
|
||||
tmpData = tmpData.filter((item) => {
|
||||
return item.gpt_prompt == null || item.gpt_prompt == ''
|
||||
})
|
||||
|
||||
if (tmpData.length == 0) {
|
||||
message.success('没有需要推理的数据')
|
||||
return
|
||||
}
|
||||
|
||||
await window.api.GPTPrompt(
|
||||
[JSON.stringify(tmpData), true, JSON.stringify(toRaw(data.value))],
|
||||
(value) => {
|
||||
console.log('添加推理任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加推理任务成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新的弹窗
|
||||
*/
|
||||
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 GenerateImageAll() {
|
||||
debugger
|
||||
console.log(data.value)
|
||||
let tmpData = cloneDeep(toRaw(data.value))
|
||||
tmpData = tmpData.filter((item) => {
|
||||
return item.outImagePath == null || item.outImagePath == ''
|
||||
})
|
||||
|
||||
if (tmpData.length == 0) {
|
||||
message.success('没有需要批量生成的图片')
|
||||
return
|
||||
}
|
||||
|
||||
// 判断当前是mj还是sd
|
||||
let ca = window.config.image_generate_category
|
||||
if (ca == 'sd') {
|
||||
// 将所有的人物添加到后台队列任务中
|
||||
await window.api.OriginalSDImageGenerate([JSON.stringify(tmpData), true, true], (value) => {
|
||||
console.log('添加生成图片任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加生成图片任务成功')
|
||||
})
|
||||
} else if (ca == 'mj') {
|
||||
// MJ生图
|
||||
await window.mj.OriginalMJImageGenerate([JSON.stringify(tmpData), true, true], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加MJ生图任务成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let railStyle = ({ focused, checked }) => {
|
||||
const style = {}
|
||||
if (checked) {
|
||||
style.background = '#f3a694'
|
||||
if (focused) {
|
||||
style.boxShadow = '0 0 0 2px #f3a694'
|
||||
}
|
||||
} else {
|
||||
style.background = '#775039'
|
||||
if (focused) {
|
||||
style.boxShadow = '0 0 0 2px #775039'
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
// 重置提示词数据
|
||||
async function ResetPrompt() {
|
||||
// 循环data删除数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].prompt = ''
|
||||
data.value[i].chinese_prompt = []
|
||||
}
|
||||
}
|
||||
|
||||
// 图片数据重置
|
||||
async function ResetImage() {
|
||||
// 循环data删除数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].outImagePath = null
|
||||
data.value[i].subImagePath = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉菜单选择值改变的方法
|
||||
* @param {*} value
|
||||
*/
|
||||
async function ButtonSelect(value) {
|
||||
if (value == 'stopGptPrompt') {
|
||||
await window.api.DeleteBackTask(
|
||||
[DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GPT_PROMPT, null, 'default'],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('停止推理任务成功')
|
||||
}
|
||||
)
|
||||
} else if (value == 'stopGenerateImage') {
|
||||
await window.api.DeleteBackTask(
|
||||
[DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE, null, 'default'],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('停止生成图片任务成功')
|
||||
}
|
||||
)
|
||||
await window.api.DeleteBackTask(
|
||||
[DEFINE_STRING.QUEUE_BATCH.MJ_ORIGINAL_GENERATE_IMAGE, null, 'default'],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('停止生成图片任务成功')
|
||||
}
|
||||
)
|
||||
} else if (value == 'resetPrompt') {
|
||||
// 重置提示词
|
||||
debugger
|
||||
console.log(data)
|
||||
await ResetPrompt()
|
||||
} else if (value == 'resetImage') {
|
||||
await ResetImage()
|
||||
}
|
||||
}
|
||||
|
||||
// 重置所有数据
|
||||
async function resetALLData() {
|
||||
await ResetPrompt()
|
||||
await ResetImage()
|
||||
}
|
||||
|
||||
return {
|
||||
ImportWord,
|
||||
data,
|
||||
GetCharacter,
|
||||
tagTreeData,
|
||||
GPTPrompt,
|
||||
OpenPromptSetting,
|
||||
GenerateImageAll,
|
||||
auto_save,
|
||||
railStyle,
|
||||
AutoSaveUpdate: (value) => {
|
||||
debugger
|
||||
auto_save.value = value
|
||||
window.config.auto_save = value
|
||||
},
|
||||
GptButtonOptions: [{ label: '停止推理任务', key: 'stopGptPrompt' }],
|
||||
GenerateImageOptions: [{ label: '停止生成图片任务', key: 'stopGenerateImage' }],
|
||||
ResetDataOptions: [
|
||||
{ label: '重置提示词', key: 'resetPrompt' },
|
||||
{ label: '重置图片', key: 'resetImage' }
|
||||
],
|
||||
ButtonSelect,
|
||||
resetALLData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<n-card>
|
||||
<n-tabs class="card-tabs" default-value="background_music_setting" size="large" animated>
|
||||
<n-tab-pane name="background_music_setting" tab="背景音乐设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">设置背景乐对应的文件夹,选择对应的分类,可以随机匹配音乐</div>
|
||||
</div>
|
||||
<BackgroundMusic></BackgroundMusic>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="keyfarme_setting" tab="关键帧设置">
|
||||
<KeyFrameSetting></KeyFrameSetting>
|
||||
</n-tab-pane>
|
||||
|
||||
<!-- <n-tab-pane name="text_setting" tab="字幕设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">在剪映里面设置一个字体模板,然后选择草稿文件(只提取第一个句子),点击提取</div>
|
||||
<div style="color: red;">提取所有的内容,显示字体,颜色,字号,偏移量(根据视频比例会出现不同)</div>
|
||||
</div>
|
||||
<n-divider />
|
||||
<div style="display: flex;">
|
||||
<n-input placeholder="输入预设名称" v-model:value="textName" @keydown.enter.prevent
|
||||
:style="{ flex: '4', marginRight: '10px' }"></n-input>
|
||||
<SelectDraft ref="selectDraft" style="flex: 5;"></SelectDraft>
|
||||
<n-button round type="primary" @click="getTextStyle" style="flex: 1; margin: 0 0 0 30px ;">提取</n-button>
|
||||
</div>
|
||||
<n-divider />
|
||||
<n-data-table :columns="columns" :data="data" :row-props="rowProps" />
|
||||
<n-dropdown placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options" :show="showDropdown"
|
||||
:on-clickoutside="onClickoutside" @select="handleSelect" />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="friendly_reminder_setting" tab="提示设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">在剪映里面设置一个右上角提示,然后选择草稿文件(只能设置一个),点击提取</div>
|
||||
</div>
|
||||
<n-divider />
|
||||
<div style="display: flex;">
|
||||
<n-input placeholder="提示名称" v-model:value="friendlyReminderTextName" @keydown.enter.prevent
|
||||
:style="{ flex: '3', marginRight: '10px' }"></n-input>
|
||||
<SelectDraft ref="friendlyReminderSelectDraft" style="flex: 5;"></SelectDraft>
|
||||
<n-button round type="primary" @click="GetFriendlyReminder"
|
||||
style="flex: 1; margin: 0 0 0 30px ;">提取</n-button>
|
||||
</div>
|
||||
<n-divider />
|
||||
<n-data-table :columns="friendlyReminderColums" :data="friendlyReminderData"
|
||||
:row-props="FriendlyReminderProps" />
|
||||
<n-dropdown placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options"
|
||||
:show="FriendlyReminderShowDropdown" :on-clickoutside="onClickoutside"
|
||||
@select="handleFriendReminderSelect" />
|
||||
</n-tab-pane> -->
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, nextTick } from "vue";
|
||||
import { NButton, NInput, NCard, NTabs, NTabPane, NDivider, useMessage, NDataTable, NDropdown } from "naive-ui"
|
||||
import SelectDraft from "../Components/SelectDraft.vue";
|
||||
import BackgroundMusic from "./Components/BackgroundMusic.vue"
|
||||
import KeyFrameSetting from "./Components/KeyFrameSetting.vue"
|
||||
const createColumns = (type) => {
|
||||
if (type == "srt") {
|
||||
return [
|
||||
{
|
||||
title: '名字',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: "视频比例",
|
||||
key: "ratio"
|
||||
},
|
||||
|
||||
{
|
||||
title: '字体',
|
||||
key: 'fonts'
|
||||
},
|
||||
{
|
||||
title: '字号',
|
||||
key: 'font_size'
|
||||
},
|
||||
{
|
||||
title: '字体样式',
|
||||
key: 'font_style'
|
||||
},
|
||||
{
|
||||
title: '偏移量X',
|
||||
key: 'transformX'
|
||||
},
|
||||
{
|
||||
title: '偏移量Y',
|
||||
key: 'transformY'
|
||||
}
|
||||
]
|
||||
}
|
||||
else if (type == "FR") {
|
||||
return [
|
||||
{
|
||||
title: "名字",
|
||||
key: "name"
|
||||
},
|
||||
{
|
||||
title: "提示字",
|
||||
key: "text_value"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: "编辑",
|
||||
key: "edit"
|
||||
},
|
||||
{
|
||||
label: () => h("span", { style: { color: "red" } }, "删除"),
|
||||
key: "delete"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NInput, NCard, NTabs, NTabPane, NDivider, NDataTable, SelectDraft, NDropdown, BackgroundMusic, KeyFrameSetting
|
||||
},
|
||||
setup() {
|
||||
let draf_name = ref("");
|
||||
let selectDraft = ref(null);
|
||||
let friendlyReminderSelectDraft = ref(null);
|
||||
let message = useMessage();
|
||||
let textName = ref(null);
|
||||
let friendlyReminderTextName = ref("");
|
||||
let data = ref([]);
|
||||
let friendlyReminderData = ref([]);
|
||||
const xRef = ref(0);
|
||||
const yRef = ref(0);
|
||||
const showDropdown = ref(false);
|
||||
const FriendlyReminderShowDropdown = ref(false);
|
||||
let selectOption = "";
|
||||
let FriendLyOption = "";
|
||||
|
||||
/**
|
||||
* 提取字体样式
|
||||
*/
|
||||
async function getTextStyle() {
|
||||
let draft_name = selectDraft.value.draf_name;
|
||||
if (draft_name == null || draf_name == "" || textName.value == null || textName.value == "") {
|
||||
message.error("请选择草稿");
|
||||
return;
|
||||
}
|
||||
await window.api.getDraftTextStyle([textName.value, draft_name], (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("提取成功");
|
||||
getDraftFileList();
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字幕样式泪列表
|
||||
*/
|
||||
async function getDraftFileList() {
|
||||
data.value = [];
|
||||
await window.api.getTextStyleList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
let tabledatas = value.value.filter(item => item.id != "0" && item.id != "1");
|
||||
for (let i = 0; i < tabledatas.length; i++) {
|
||||
const element = tabledatas[i];
|
||||
let obj = {
|
||||
id: element.id,
|
||||
name: element.name,
|
||||
fonts: element.fonts,
|
||||
font_size: element.font_size,
|
||||
font_style: element.style_name,
|
||||
transformX: element.clip.transform.x,
|
||||
transformY: element.clip.transform.y,
|
||||
ratio: element.ratio
|
||||
}
|
||||
data.value.push(obj)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取温馨提示列表(右上角)
|
||||
*/
|
||||
async function getFriendlyReminderList() {
|
||||
debugger;
|
||||
friendlyReminderData.value = [];
|
||||
await window.api.getFriendlyReminderList((value) => {
|
||||
friendlyReminderData.value = value.value.filter(item => item.id != "0" && item.id != "1")
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取温馨提示样式
|
||||
*/
|
||||
async function GetFriendlyReminder() {
|
||||
let friendlyReminderSelectDraft_name = friendlyReminderSelectDraft.value.draf_name;
|
||||
debugger;
|
||||
console.log(friendlyReminderSelectDraft_name);
|
||||
if (friendlyReminderTextName.value == "" || friendlyReminderTextName.value == null || friendlyReminderSelectDraft_name == "" || friendlyReminderSelectDraft_name == null) {
|
||||
message.error("请选择草稿地址和名称");
|
||||
return;
|
||||
}
|
||||
await window.api.GetFriendlyReminder([friendlyReminderSelectDraft_name, friendlyReminderTextName.value], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("写入成功!!");
|
||||
await getFriendlyReminderList();
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getDraftFileList();
|
||||
await getFriendlyReminderList();
|
||||
})
|
||||
|
||||
return {
|
||||
draf_name,
|
||||
getTextStyle,
|
||||
selectDraft,
|
||||
friendlyReminderSelectDraft,
|
||||
textName,
|
||||
data,
|
||||
options,
|
||||
x: xRef,
|
||||
y: yRef,
|
||||
showDropdown,
|
||||
FriendlyReminderShowDropdown,
|
||||
friendlyReminderTextName,
|
||||
getDraftFileList,
|
||||
GetFriendlyReminder,
|
||||
friendlyReminderData,
|
||||
columns: createColumns("srt"),
|
||||
friendlyReminderColums: createColumns("FR"),
|
||||
handleSelect: async (value) => {
|
||||
let id = JSON.parse(selectOption).id;
|
||||
// 删除对应的
|
||||
if (value == "delete") {
|
||||
await window.api.deleteDraftTextStyle(id, async (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await getDraftFileList();
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
showDropdown.value = false;
|
||||
},
|
||||
handleFriendReminderSelect: async (value) => {
|
||||
let id = JSON.parse(FriendLyOption).id;
|
||||
// 删除对应的
|
||||
if (value == "delete") {
|
||||
await window.api.deleteClipStettingFriendlyReminder(id, async (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await getFriendlyReminderList();
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
FriendlyReminderShowDropdown.value = false;
|
||||
},
|
||||
onClickoutside() {
|
||||
showDropdown.value = false;
|
||||
},
|
||||
rowProps: (row) => {
|
||||
return {
|
||||
onContextmenu: (e) => {
|
||||
selectOption = JSON.stringify(row, null, 2);
|
||||
e.preventDefault();
|
||||
showDropdown.value = false;
|
||||
nextTick().then(() => {
|
||||
showDropdown.value = true;
|
||||
xRef.value = e.clientX;
|
||||
yRef.value = e.clientY;
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
FriendlyReminderProps: (row) => {
|
||||
return {
|
||||
onContextmenu: (e) => {
|
||||
FriendLyOption = JSON.stringify(row, null, 2);
|
||||
e.preventDefault();
|
||||
FriendlyReminderShowDropdown.value = false;
|
||||
nextTick().then(() => {
|
||||
FriendlyReminderShowDropdown.value = true;
|
||||
xRef.value = e.clientX;
|
||||
yRef.value = e.clientY;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-tabs class="card-tabs" default-value="signin" 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="signin" tab="GPT服务商">
|
||||
<n-form ref="formRef" :model="gpt_select_value" :rules="rules">
|
||||
<n-form-item label="选择自定义的GPT服务商">
|
||||
<n-select style="width: 300px;" :options="gpt_options"
|
||||
v-model:value="gpt_select_key"></n-select>
|
||||
<n-button color="#e18a3b" style="margin-left: 10px;" @click="EditOption">编辑</n-button>
|
||||
<n-button color="#984f31" style="margin-left: 10px;" @click="AddOption">添加</n-button>
|
||||
<n-button type="error" style="margin-left: 10px;" @click="DeleteOption">删除</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item path="label" label="GPT 名称">
|
||||
<n-input v-model:value="gpt_select_value.label"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="value" label="GPT请求网址(写全)">
|
||||
<n-input v-model:value="gpt_select_value.value"></n-input>
|
||||
</n-form-item>
|
||||
<div style="text-align: right;">
|
||||
<n-button type="primary" @click="SaveGptOption">保存</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="signup" tab="GPT模型">
|
||||
<n-form ref="modelFormRef" :model="gpt_model_select_value" :rules="modelRules">
|
||||
<n-form-item label="选择自定义的GPT模型">
|
||||
<n-select style="width: 300px;" :options="gpt_model_options"
|
||||
v-model:value="gpt_model_select_key"></n-select>
|
||||
<n-button color="#e18a3b" style="margin-left: 10px;" @click="EditModelOption">编辑</n-button>
|
||||
<n-button color="#984f31" style="margin-left: 10px;" @click="AddModelOption">添加</n-button>
|
||||
<n-button type="error" style="margin-left: 10px;" @click="DeleteModelOption">删除</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item path="label" label="GPT 模型名称">
|
||||
<n-input v-model:value="gpt_model_select_value.label"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="value" label="GPT 模型值">
|
||||
<n-input v-model:value="gpt_model_select_value.value"></n-input>
|
||||
</n-form-item>
|
||||
<div style="text-align: right;">
|
||||
<n-button type="primary" @click="SaveGptModelOption">保存</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRaw, onMounted, defineComponent } from "vue"
|
||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, useMessage, NSelect, NTabs, NTabPane } from "naive-ui"
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSelect, NTabs, NTabPane
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let gpt_model_options = ref([]);
|
||||
let gpt_options = ref([]);
|
||||
let gpt_select_value = ref({});
|
||||
let gpt_select_key = ref("");
|
||||
let gpt_model_select_value = ref({});
|
||||
let gpt_model_select_key = ref("");
|
||||
let formRef = ref(null);
|
||||
let modelFormRef = ref(null);
|
||||
|
||||
/**
|
||||
* 加载GPT的配置信息
|
||||
*/
|
||||
async function InitGptOptions() {
|
||||
await window.api.getGptBusinessOption("dynamic", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data.length == 0) {
|
||||
message.error("请先添加自定义GPT服务商");
|
||||
return;
|
||||
}
|
||||
gpt_options.value = value.data;
|
||||
gpt_select_key.value = value.data[0].value;
|
||||
gpt_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
};
|
||||
})
|
||||
|
||||
await window.api.getGptModelOption("dynamic", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data.length == 0) {
|
||||
message.error("请先添加自定义GPT模型");
|
||||
return;
|
||||
}
|
||||
gpt_model_options.value = value.data;
|
||||
gpt_model_select_key.value = value.data[0].value;
|
||||
gpt_model_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
await InitGptOptions();
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存当前的GPT配置信息
|
||||
*/
|
||||
async function SaveGptOption(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存逻辑
|
||||
await window.api.SaveDynamicGptOption([JSON.stringify(toRaw(gpt_select_value.value)), "gpt_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前选中的GPT配置信息,写入下面已编辑
|
||||
*/
|
||||
async function EditOption() {
|
||||
let temp = gpt_options.value.find(x => x.value == gpt_select_key.value);
|
||||
gpt_select_value.value = cloneDeep(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前选中的GPT模型配置信息,写入下面已编辑
|
||||
*/
|
||||
async function EditModelOption() {
|
||||
let temp = gpt_model_options.value.find(x => x.value == gpt_model_select_key.value);
|
||||
gpt_model_select_value.value = cloneDeep(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前选中的GPT服务商信息
|
||||
*/
|
||||
async function DeleteOption() {
|
||||
let temp = gpt_options.value.find(x => x.value == gpt_select_key.value);
|
||||
let id = temp.id;
|
||||
await window.api.DeleteDynamicGptOption([id, "gpt_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前选中的GPT服务商信息
|
||||
*/
|
||||
async function DeleteModelOption() {
|
||||
let temp = gpt_model_options.value.find(x => x.value == gpt_model_select_key.value);
|
||||
let id = temp.id;
|
||||
await window.api.DeleteDynamicGptOption([id, "gpt_model_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存当前的GPT配置信息
|
||||
*/
|
||||
async function SaveGptModelOption(e) {
|
||||
e.preventDefault();
|
||||
modelFormRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存逻辑
|
||||
await window.api.SaveDynamicGptOption([JSON.stringify(toRaw(gpt_model_select_value.value)), "gpt_model_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个GPT服务商,只是清楚当前的数据
|
||||
*/
|
||||
async function AddOption() {
|
||||
gpt_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个GPT服务商,只是清楚当前的数据
|
||||
*/
|
||||
async function AddModelOption() {
|
||||
gpt_model_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur"]
|
||||
}]
|
||||
}
|
||||
|
||||
let rules = {
|
||||
label: ruleObj("必填GPT名称"),
|
||||
value: ruleObj("必填GPT请求网址"),
|
||||
};
|
||||
|
||||
let modelRules = {
|
||||
label: ruleObj("必填GPT名称"),
|
||||
value: ruleObj("必填GPT请求网址"),
|
||||
};
|
||||
|
||||
return {
|
||||
gpt_model_options,
|
||||
gpt_options,
|
||||
gpt_select_value,
|
||||
gpt_model_select_value,
|
||||
gpt_select_key,
|
||||
gpt_model_select_key,
|
||||
SaveGptOption,
|
||||
EditOption,
|
||||
DeleteOption,
|
||||
AddOption,
|
||||
SaveGptModelOption,
|
||||
rules,
|
||||
modelRules,
|
||||
formRef,
|
||||
modelFormRef,
|
||||
EditModelOption,
|
||||
AddModelOption,
|
||||
DeleteModelOption
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-tabs .n-tabs-nav--bar-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div style="height: 50px; display: flex; align-items: center;"> <n-select placeholder="选择编辑或是删除的数据"
|
||||
style="width: 300px;" v-model:value="customize_gpt_select" :options="customize_gpt_options"></n-select>
|
||||
<n-button size="small" style="margin: 0 10px 0 10px" color="#e18a3b"
|
||||
@click="EditCurrentSelectPrompt">编辑</n-button>
|
||||
<n-button size="small" style="margin: 0 10px 0 0px" color="#779649" @click="AddGptPrompt">新增</n-button>
|
||||
<n-button size="small" style="margin: 0 10px 0 0px" type="error" @click="DeleteGptPrompt">删除</n-button>
|
||||
</div>
|
||||
<div id="add-gpt-prompts" style="overflow-y: auto; padding-right: 20px;">
|
||||
<n-form ref="promptRef" :model="prompt_data" :rules="rules">
|
||||
<n-form-item label="提示词预设名字">
|
||||
<n-input v-model:value="prompt_data.prompt_name" placeholder="输入预设名字"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item label="提示词角色预设">
|
||||
<n-input type="textarea" style="height: 150px;" :autosize="true"
|
||||
v-model:value="prompt_data.prompt_roles" placeholder="输入提示词角色预设"></n-input>
|
||||
</n-form-item>
|
||||
<div v-for="(example, index) in prompt_data.prompt_example">
|
||||
<n-form-item :label="'案例输入 ' + (index + 1)">
|
||||
<n-input type="textarea" style="height: 60px ;margin-right: 10px;" :autosize="true"
|
||||
v-model:value="example.user_content" placeholder="输入案例输入"></n-input>
|
||||
<n-button color="#e18a3b" @click="GenerateGptExampleOut(example, index)">生成输出</n-button>
|
||||
<n-button tertiary type="error" style="font-size: 24px" @click="DeleteExampleOut(example, index)">
|
||||
<n-icon>
|
||||
<trash />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item :label="'案例输出 ' + (index + 1)">
|
||||
<n-input type="textarea" style="height: 60px" :autosize="true"
|
||||
v-model:value="example.assistant_content" placeholder="输入案例输出或是测试生成"></n-input>
|
||||
</n-form-item>
|
||||
</div>
|
||||
</n-form>
|
||||
</div>
|
||||
<div style="float: right; margin: 30px;">
|
||||
<n-button color="#e18a3b" @click="AddExamplePrompt">添加案例输出</n-button>
|
||||
<n-checkbox style="margin: 0 15px 0 15px;" v-model:checked="save_example">是否保存案例</n-checkbox>
|
||||
<n-button type="info" @click="SaveGptPrompt">保存</n-button>
|
||||
<!-- Your content here -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRaw, onMounted, defineComponent } from "vue"
|
||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, useMessage, NSelect, NTabs, NTabPane, NCheckbox, NIcon } from "naive-ui"
|
||||
import { cloneDeep, has, isEmpty } from "lodash";
|
||||
import { Trash } from "@vicons/ionicons5"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSelect, NTabs, NTabPane, NCheckbox, NIcon, Trash
|
||||
},
|
||||
props: ["height"],
|
||||
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let hh = props.height;
|
||||
let save_example = ref(true);
|
||||
let prompt_data = ref({
|
||||
prompt_name: null,
|
||||
prompt_roles: null,
|
||||
prompt_example: []
|
||||
})
|
||||
|
||||
// 获取的所有的数据
|
||||
let customize_gpt_all_data = ref([]);
|
||||
// 下拉的options
|
||||
let customize_gpt_options = ref([]);
|
||||
// 选择的数据
|
||||
let customize_gpt_select = ref(null);
|
||||
|
||||
|
||||
// 设置窗体的高度
|
||||
function setHeight() {
|
||||
let div = document.getElementById("add-gpt-prompts");
|
||||
div.style.height = hh - 145 - 50 + "px";
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
async function InitData() {
|
||||
await window.api.GetCustomizeGptPrompt("dynamic", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data.length == 0) {
|
||||
message.error("请先添加自定义提示词数据");
|
||||
return;
|
||||
}
|
||||
prompt_data.value = {
|
||||
prompt_name: null,
|
||||
prompt_roles: null,
|
||||
prompt_example: []
|
||||
}
|
||||
customize_gpt_all_data.value = value.data;
|
||||
customize_gpt_select.value = value.data[0].id;
|
||||
customize_gpt_options.value = value.data.map((item) => {
|
||||
return {
|
||||
label: item.prompt_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
setHeight();
|
||||
await InitData();
|
||||
})
|
||||
|
||||
/**
|
||||
* 生成案例输出
|
||||
* @param {*} input 输入的数据
|
||||
* @param {*} index 输入的数据的索引
|
||||
*/
|
||||
async function GenerateGptExampleOut(input, index) {
|
||||
console.log(input, index);
|
||||
if (!input.user_content) {
|
||||
message.error("请填写案例输入");
|
||||
return;
|
||||
}
|
||||
await window.api.GenerateGptExampleOut(JSON.stringify(toRaw(prompt_data.value)), (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
prompt_data.value.prompt_example[index].assistant_content = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加提示词案例输出数据
|
||||
*/
|
||||
async function AddExamplePrompt() {
|
||||
debugger;
|
||||
let last_data = prompt_data.value.prompt_example[prompt_data.value.prompt_example.length - 1];
|
||||
if (last_data != null) {
|
||||
if (isEmpty(last_data.user_content)) {
|
||||
message.error("上一个案例输入没有填写");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEmpty(last_data.assistant_content)) {
|
||||
message.error("上一个案例输出没有生成或填写");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
prompt_data.value.prompt_example.push({
|
||||
user_content: null,
|
||||
assistant_content: null
|
||||
})
|
||||
}
|
||||
|
||||
// 删除案例输出
|
||||
async function DeleteExampleOut(example, index) {
|
||||
debugger;
|
||||
if (index > prompt_data.value.prompt_example.length - 1) {
|
||||
message.error("删除的案例不存在");
|
||||
return;
|
||||
}
|
||||
prompt_data.value.prompt_example.splice(index, 1);
|
||||
}
|
||||
|
||||
// 保存提示词
|
||||
async function SaveGptPrompt() {
|
||||
// 判断是不是要保存案例输出
|
||||
let tmp_data = cloneDeep(prompt_data.value);
|
||||
if (!save_example.value) {
|
||||
tmp_data.prompt_example = [];
|
||||
}
|
||||
|
||||
// 检查数据是不是存在
|
||||
if (has(tmp_data, "prompt_name") && isEmpty(tmp_data.prompt_name)) {
|
||||
message.error("请填写提示词预设名字");
|
||||
return;
|
||||
}
|
||||
if (has(tmp_data, "prompt_roles") && isEmpty(tmp_data.prompt_roles)) {
|
||||
message.error("请填写提示词角色预设");
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tmp_data.prompt_example.length; i++) {
|
||||
if (isEmpty(tmp_data.prompt_example[i].user_content)) {
|
||||
message.error("案例输入" + (i + 1) + "没有填写");
|
||||
return;
|
||||
}
|
||||
if (isEmpty(tmp_data.prompt_example[i].assistant_content)) {
|
||||
message.error("案例输出" + (i + 1) + "没有填写");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存逻辑
|
||||
await window.api.SaveDynamicGptOption([JSON.stringify(tmp_data), "customize_gpt_prompt"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加成功");
|
||||
})
|
||||
|
||||
await InitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑当前选中的提示词设置
|
||||
*/
|
||||
async function EditCurrentSelectPrompt() {
|
||||
if (isEmpty(customize_gpt_select.value)) {
|
||||
message.error("请选择要编辑的数据");
|
||||
return;
|
||||
}
|
||||
let current_data = customize_gpt_all_data.value.find((item) => item.id == customize_gpt_select.value);
|
||||
if (isEmpty(current_data)) {
|
||||
message.error("当前选择的数据不存在");
|
||||
return;
|
||||
}
|
||||
prompt_data.value = current_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增提示词
|
||||
*/
|
||||
async function AddGptPrompt() {
|
||||
prompt_data.value = {
|
||||
prompt_name: null,
|
||||
prompt_roles: null,
|
||||
prompt_example: [{
|
||||
user_content: null,
|
||||
assistant_content: null
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除选中的提示词
|
||||
*/
|
||||
async function DeleteGptPrompt() {
|
||||
if (isEmpty(customize_gpt_select.value)) {
|
||||
message.error("请选择要删除的数据");
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.DeleteDynamicGptOption([toRaw(customize_gpt_select.value), "customize_gpt_prompt"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
})
|
||||
|
||||
await InitData();
|
||||
}
|
||||
|
||||
return {
|
||||
hh,
|
||||
prompt_data,
|
||||
save_example,
|
||||
GenerateGptExampleOut,
|
||||
AddExamplePrompt,
|
||||
DeleteExampleOut,
|
||||
SaveGptPrompt,
|
||||
customize_gpt_options,
|
||||
customize_gpt_select,
|
||||
customize_gpt_all_data,
|
||||
EditCurrentSelectPrompt,
|
||||
AddGptPrompt,
|
||||
DeleteGptPrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Customize the scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<n-form ref="formRef" inline :label-width="100" :model="formValue" :size="large">
|
||||
<n-form-item label="背景音乐文件夹信息" path="formValue.name">
|
||||
<n-input style="width: 150px;" v-model:value="formValue.name" placeholder="输入名称" />
|
||||
<n-input style="width: 500px;" v-model:value="formValue.folder_path" placeholder="输入文件夹地址" />
|
||||
<n-button attr-type="button" type="primary" @click="SelectFolder">选择文件夹</n-button>
|
||||
<n-button style="margin-left: 50px;" attr-type="button" type="info"
|
||||
@click="AddBackgroundMusic">保存</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<n-data-table :columns="background_music_columns" :data="data" :pagination="pagination" :bordered="false" />
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { defineComponent, ref, h, onMounted, nextTick } from "vue";
|
||||
import { NButton, useMessage, NDataTable, NForm, NFormItem, NInput } from "naive-ui"
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "删除",
|
||||
key: "delete"
|
||||
}
|
||||
]
|
||||
|
||||
const createColumns = ({ Delete }) => {
|
||||
return [
|
||||
{
|
||||
title: "名称",
|
||||
key: "name"
|
||||
},
|
||||
{
|
||||
title: "文件夹位置",
|
||||
key: "folder_path"
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
strong: true,
|
||||
size: "small",
|
||||
type: "error",
|
||||
tertiary: true,
|
||||
onClick: () => {
|
||||
if (item.key == "edit") {
|
||||
// Edit(row);
|
||||
} else if (item.key == "delete") {
|
||||
Delete(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NForm, NFormItem, NInput
|
||||
},
|
||||
setup() {
|
||||
let data = ref([]);
|
||||
let message = useMessage();
|
||||
let formValue = ref({
|
||||
name: "",
|
||||
id: "",
|
||||
folder_path: ""
|
||||
})
|
||||
|
||||
async function SelectFolder() {
|
||||
await window.api.selectFolder(null, (value) => {
|
||||
if (value.length > 0) {
|
||||
formValue.value.folder_path = value[0];
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function Delete(row) {
|
||||
await window.api.deleteClipSetting(row.id, async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await GetBackgroundMusicConfigList();
|
||||
})
|
||||
}
|
||||
|
||||
async function GetBackgroundMusicConfigList() {
|
||||
data.value = [];
|
||||
await window.api.GetBackgroundMusicConfigList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
data.value = value.value;
|
||||
})
|
||||
}
|
||||
|
||||
async function AddBackgroundMusic() {
|
||||
if (formValue.value.name == "" || formValue.value.folder_path == "") {
|
||||
message.error("请填入名称和文件夹位置");
|
||||
return;
|
||||
}
|
||||
// 判断当前名称是不是存在
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const d = data.value[i];
|
||||
if (d.name == formValue.value.name) {
|
||||
message.error("名称已存在");
|
||||
return;
|
||||
}
|
||||
}
|
||||
//添加音乐文件夹
|
||||
await window.api.AddBackgroundMusicFolder([formValue.value.name, formValue.value.folder_path], async (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
return;
|
||||
}
|
||||
formValue.value.name = "";
|
||||
formValue.value.folder_path = "";
|
||||
await GetBackgroundMusicConfigList();
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await GetBackgroundMusicConfigList();
|
||||
})
|
||||
|
||||
return {
|
||||
bodyStyle: {
|
||||
width: "600px"
|
||||
},
|
||||
segmented: {
|
||||
content: "soft",
|
||||
footer: "soft"
|
||||
},
|
||||
formValue,
|
||||
data,
|
||||
background_music_columns: createColumns({
|
||||
Delete
|
||||
}),
|
||||
message,
|
||||
AddBackgroundMusic,
|
||||
SelectFolder
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div style="margin-bottom: 10px; width: 100px;">选择打帧方式</div>
|
||||
<n-select v-model:value="key_frame_data.key_frame" :options="key_frame_options" style="width: 140px;" />
|
||||
<div style="width: 170px;">
|
||||
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<n-checkbox style="padding-left: 20px;" v-model:checked="key_frame_data.isFixedSpeed">
|
||||
匀速关键帧
|
||||
</n-checkbox>
|
||||
</template>
|
||||
勾选这个,图片的时长小于这个。会匀速关键帧
|
||||
</n-tooltip>
|
||||
|
||||
</div>
|
||||
|
||||
<span>关键帧时间:</span>
|
||||
<div>
|
||||
<n-input-number :show-button="false" :step="0.1" :min="0" :max="10"
|
||||
v-model:value="key_frame_data.key_frame_time" style="width: 100px;" />
|
||||
</div>
|
||||
<div>
|
||||
<n-button style="margin-left: 30px;" type="info" @click="SaveKeyFrameSetting">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-card style="margin-top: 10px;" title="上下关键帧设置">
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div>
|
||||
上关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.up_down_key_frame.start_position"
|
||||
style="width: 100px; margin-right: 10px;" />
|
||||
<div>
|
||||
下关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.up_down_key_frame.end_position"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
|
||||
<div>
|
||||
缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.up_down_key_frame.default_scale"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="左右关键帧设置" style="margin-top: 10px;">
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div>
|
||||
左关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.left_right_key_frame.start_position"
|
||||
style="width: 100px; margin-right: 10px;" />
|
||||
<div>
|
||||
右关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.left_right_key_frame.end_position"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
|
||||
<div>
|
||||
缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.left_right_key_frame.default_scale"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="缩放关键帧设置" style="margin-top: 10px;">
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div>
|
||||
开始缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.scale_key_frame.start_position"
|
||||
style="width: 100px; margin-right: 10px;" />
|
||||
<div>
|
||||
结束缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false"
|
||||
v-model:value="key_frame_data.scale_key_frame.end_position"></n-input-number>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { useMessage, NSelect, NCheckbox, NInputNumber, NCard, NButton, NTooltip } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect, NCheckbox, NInputNumber, NCard, NButton, NTooltip
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let key_frame_options = ref([]);
|
||||
let key_frame_data = ref({
|
||||
key_frame: null,
|
||||
isFixedSpeed: false,
|
||||
key_frame_time: 4,
|
||||
up_down_key_frame: {
|
||||
default_scale: 100,
|
||||
start_position: 0,
|
||||
end_position: 0
|
||||
},
|
||||
left_right_key_frame: {
|
||||
default_scale: 100,
|
||||
start_position: 0,
|
||||
end_position: 0
|
||||
},
|
||||
scale_key_frame: {
|
||||
default_scale: 100,
|
||||
start_position: 100,
|
||||
end_position: 100
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.GetKeyFrameConfigData((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("获取关键帧配置失败");
|
||||
return;
|
||||
}
|
||||
if (value.data) {
|
||||
key_frame_data.value = value.data;
|
||||
}
|
||||
})
|
||||
|
||||
await window.api.GetKeyFrameOptions((value) => {
|
||||
if (value) {
|
||||
key_frame_options.value = value.data;
|
||||
} else {
|
||||
message.error("获取关键帧操作列表失败");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 关键帧设置保存
|
||||
*/
|
||||
async function SaveKeyFrameSetting() {
|
||||
console.log(key_frame_data.value);
|
||||
// 简单的判断
|
||||
if (key_frame_data.value.key_frame == "") {
|
||||
message.error("请选择关键帧方式");
|
||||
return;
|
||||
}
|
||||
if (key_frame_data.value.key_frame && key_frame_data.value.key_frame == "KFTypePositionY") {
|
||||
if (key_frame_data.value.up_down_key_frame.start_position == null || key_frame_data.value.up_down_key_frame.end_position == null) {
|
||||
message.error("请输入上下关键帧位置");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (key_frame_data.value.key_frame && key_frame_data.value.key_frame == "KFTypePositionX") {
|
||||
if (key_frame_data.value.left_right_key_frame.start_position == null || key_frame_data.value.left_right_key_frame.end_position == null) {
|
||||
message.error("请输入左右关键帧位置");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (key_frame_data.value.key_frame && key_frame_data.value.key_frame == "KFTypeScale") {
|
||||
if (key_frame_data.value.scale_key_frame.start_position == null || key_frame_data.value.scale_key_frame.end_position == null) {
|
||||
message.error("请输入缩放关键帧位置");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await window.api.SaveKeyFrameSetting(JSON.stringify(toRaw(key_frame_data.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
key_frame_data,
|
||||
key_frame_options,
|
||||
SaveKeyFrameSetting
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<n-button type="info" round @click="OpenDiscordWindow">打开登录MJ</n-button>
|
||||
<n-button type="info" round @click="GetChannelRobots" style="margin-left: 10px;">获取频道机器人</n-button>
|
||||
<n-button type="info" round @click="SaveMjSetting" style="margin-left: 10px;">保存MJ配置</n-button>
|
||||
<n-divider />
|
||||
<n-form ref="formRef" label-placement="top" :model="mjSetting" :rules="rules">
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="服务器ID" style="width: 400px;" path="serviceID">
|
||||
<n-input v-model:value="mjSetting.serviceID" placeholder="登录MJ后切换服务器自动获取" />
|
||||
</n-form-item>
|
||||
<n-form-item label="频道ID" style="width: 400px; margin-left: 10px" path="channelID">
|
||||
<n-input v-model:value="mjSetting.channelID" placeholder="登录MJ后切换频道自动获取" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="MJ机器ID" path="mj.botId">
|
||||
<n-input v-model:value="mjSetting.mj.botId" placeholder="MJ机器ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ命令ID" style="margin-left: 10px" path="mj.commandId">
|
||||
<n-input v-model:value="mjSetting.mj.commandId" placeholder="MJ命令ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ命令名称" style="margin-left: 10px" path="mj.commandName">
|
||||
<n-input v-model:value="mjSetting.mj.commandName" placeholder="MJ命令名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ版本ID" style="margin-left: 10px" path="mj.versionId">
|
||||
<n-input v-model:value="mjSetting.mj.versionId" placeholder="MJ版本ID" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="NIJI机器ID" path="niji.botId">
|
||||
<n-input v-model:value="mjSetting.niji.botId" placeholder="NIJI机器ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="NIJI命令ID" style="margin-left: 10px" path="niji.commandId">
|
||||
<n-input v-model:value="mjSetting.niji.commandId" placeholder="NIJI命令ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="NIJI命令名称" style="margin-left: 10px" path="niji.commandName">
|
||||
<n-input v-model:value="mjSetting.niji.commandName" placeholder="NIJI命令名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="NIJI版本ID" style="margin-left: 10px" path="niji.versionId">
|
||||
<n-input v-model:value="mjSetting.niji.versionId" placeholder="NIJI版本ID" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
|
||||
<n-form-item label="用户token" style="width: 810px;" path="token">
|
||||
<n-input v-model:value="mjSetting.token" placeholder="登录MJ后切换服务器自动获取" />
|
||||
</n-form-item>
|
||||
<n-form-item label="用户Agent" style="width: 810px;" path="userAgent.userAgent">
|
||||
<n-input v-model:value="mjSetting.userAgent.userAgent" placeholder="登录MJ后切换服务器自动获取(可自定义)"
|
||||
style="margin-right: 10px;" />
|
||||
<n-tooltip trigger="hover" style="background-color: aliceblue; color: black;">
|
||||
<template #trigger>
|
||||
<n-checkbox v-model:checked="mjSetting.userAgent.customize" style="width: 100px;">
|
||||
自定义
|
||||
</n-checkbox>
|
||||
</template>
|
||||
开启自定义需要手动填写userAgent。可以填入浏览器的userAgent
|
||||
</n-tooltip>
|
||||
</n-form-item>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<n-form-item label="选择生图机器人" path="select_robot" style="width: 200px;">
|
||||
<n-select :options="select_robot_options" v-model:value="mjSetting.select_robot"></n-select>
|
||||
</n-form-item>
|
||||
<n-button color="#e18a3b" style="margin-left : 3px" @click="RefreshRobotOptions">
|
||||
<n-icon size="large">
|
||||
<reload></reload>
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, nextTick, watch, toRaw } from "vue";
|
||||
import { NButton, useMessage, NDataTable, NForm, NFormItem, NInput, NDivider, NCheckbox, NSelect, NTooltip, NIcon } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import { Reload } from "@vicons/ionicons5"
|
||||
import { isEmpty } from "lodash";
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NForm, NFormItem, NInput, NDivider, NCheckbox, NSelect, NTooltip, NIcon, Reload
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let formRef = ref(null);
|
||||
|
||||
let mjSetting = ref({
|
||||
serviceID: null,
|
||||
channelID: null,
|
||||
mj: {
|
||||
botId: null,
|
||||
commandId: null,
|
||||
commandName: null,
|
||||
versionId: null
|
||||
},
|
||||
niji: {
|
||||
botId: null,
|
||||
commandId: null,
|
||||
commandName: null,
|
||||
versionId: null
|
||||
},
|
||||
token: null,
|
||||
userAgent: {
|
||||
userAgent: null,
|
||||
customize: false
|
||||
|
||||
},
|
||||
select_robot: null,
|
||||
});
|
||||
|
||||
function checkNull(obj) {
|
||||
for (let key in obj) {
|
||||
if (obj[key] == undefined) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function modifyOption(value) {
|
||||
// 遍历mjSetting.mj,判断里面的属性数据是不是都存在,如果存在则添加到 select_robot_options 中
|
||||
select_robot_options.value = [];
|
||||
|
||||
let isObj = checkNull(value.mj);
|
||||
if (isObj) {
|
||||
select_robot_options.value.push({
|
||||
label: "MJ",
|
||||
value: value.mj.botId
|
||||
})
|
||||
}
|
||||
isObj = checkNull(value.niji);
|
||||
|
||||
if (isObj) {
|
||||
select_robot_options.value.push({
|
||||
label: "NIJI",
|
||||
value: value.niji.botId
|
||||
})
|
||||
}
|
||||
// 判断当前的select_robot是不是在select_robot_options中,如果不在则设置为null
|
||||
if (select_robot_options.value.findIndex((item) => { return item.value == mjSetting.value.select_robot }) == -1)
|
||||
mjSetting.value.select_robot = null;
|
||||
}
|
||||
|
||||
// 监听数据改变,生成下拉的options
|
||||
watch(mjSetting.value, (value) => {
|
||||
mjSetting.value = value;
|
||||
// 计算下拉的options
|
||||
modifyOption(value)
|
||||
})
|
||||
|
||||
|
||||
|
||||
let select_robot_options = ref([])
|
||||
|
||||
// 初始化现在设置的数据
|
||||
async function InitData() {
|
||||
// 获取当前mj配置信息
|
||||
await window.api.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mj_config", false, null]), (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 判断是不是有数据
|
||||
if (value.data) {
|
||||
mjSetting.value = value.data;
|
||||
modifyOption(value.data);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 监听修改当前界面的数据,后续可以将数据保存到本地配置
|
||||
window.api.setEventListen([DEFINE_STRING.DISCORD.OPERATE_REFRASH_DISCORD_URL], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 保存数据
|
||||
console.log(value);
|
||||
if (value.type == DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.URL) {
|
||||
mjSetting.value.serviceID = value.data.serviceID;
|
||||
mjSetting.value.channelID = value.data.channelID;
|
||||
} else if (value.type == DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.TOKEN) {
|
||||
mjSetting.value.token = value.data.authorization;
|
||||
if (!mjSetting.value.userAgent.customize) {
|
||||
mjSetting.value.userAgent.userAgent = value.data.userAgent;
|
||||
}
|
||||
} else {
|
||||
message.error("未知的数据类型");
|
||||
}
|
||||
})
|
||||
|
||||
await InitData();
|
||||
})
|
||||
|
||||
/**
|
||||
* 打开discord窗口
|
||||
*/
|
||||
async function OpenDiscordWindow() {
|
||||
await window.api.OpenDiscordWindow((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道机器人
|
||||
*/
|
||||
async function GetChannelRobots() {
|
||||
|
||||
// 判断是不是有数据(服务器ID,频道ID,用户token,用户Agent)
|
||||
if (isEmpty(mjSetting.value.serviceID) &&
|
||||
isEmpty(mjSetting.value.channelID) &&
|
||||
isEmpty(mjSetting.value.token) &&
|
||||
isEmpty(mjSetting.value.userAgent.userAgent)) {
|
||||
message.error("请先填写或获取完整的数据(服务器ID,频道ID,用户token,用户Agent)");
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'authorization': mjSetting.value.token
|
||||
}
|
||||
|
||||
await fetch(`https://discord.com/api/v9/guilds/${mjSetting.value.serviceID}/application-command-index`, {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
debugger
|
||||
// 修改配置信息(判断是不是niji或者是mj)
|
||||
// 填充mj,niji数据
|
||||
if (data.applications && data.application_commands) {
|
||||
let mj_inedx = data.applications.findIndex((item) => {
|
||||
return item.name.toLowerCase().startsWith("midjourney");
|
||||
})
|
||||
if (mj.index != -1) {
|
||||
mjSetting.value.mj.botId = data.applications[mj_inedx].id;
|
||||
let f = data.application_commands.find((a) => { return a.name == "imagine" && a.application_id == data.applications[mj_inedx].id })
|
||||
mjSetting.value.mj.commandId = f?.id;
|
||||
mjSetting.value.mj.commandName = "imagine";
|
||||
mjSetting.value.mj.versionId = f?.version;
|
||||
}
|
||||
|
||||
let niji_inedx = data.applications.findIndex((item) => {
|
||||
return item.name.toLowerCase().startsWith("niji");
|
||||
})
|
||||
if (mj.index != -1) {
|
||||
mjSetting.value.niji.botId = data.applications[niji_inedx].id;
|
||||
let f = data.application_commands.find((a) => { return a.name == "imagine" && a.application_id == data.applications[niji_inedx].id })
|
||||
mjSetting.value.niji.commandId = f?.id;
|
||||
mjSetting.value.niji.commandName = "imagine";
|
||||
mjSetting.value.niji.versionId = f?.version;
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
window.api.showGlobalNotificationDialog({
|
||||
code: 0,
|
||||
message: "获取频道机器人请求失败,错误信息如下: " + error.message.toString()
|
||||
})
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存MJ配置
|
||||
*/
|
||||
async function SaveMjSetting(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
|
||||
await window.api.SaveDefineConfigJsonByProperty(JSON.stringify(["img_base", "mj_config", toRaw(mjSetting.value), false]), (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
async function RefreshRobotOptions() {
|
||||
modifyOption(mjSetting.value);
|
||||
}
|
||||
|
||||
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 = {
|
||||
"serviceID": ruleObj("必填人物名称"),
|
||||
"channelID": ruleObj("必填英文提示词"),
|
||||
|
||||
"mj.botId": ruleObj("必填英文提示词"),
|
||||
|
||||
"mj.commandId": ruleObj("必填英文提示词"),
|
||||
"mj.commandName": ruleObj("必填英文提示词"),
|
||||
"mj.versionId": ruleObj("必填英文提示词"),
|
||||
"niji.botId": ruleObj("必填英文提示词"),
|
||||
"niji.commandId": ruleObj("必填英文提示词"),
|
||||
"niji.commandName": ruleObj("必填英文提示词"),
|
||||
"niji.versionId": ruleObj("必填英文提示词"),
|
||||
"token": ruleObj("必填英文提示词"),
|
||||
"userAgent.userAgent": ruleObj("必填英文提示词"),
|
||||
"select_robot": ruleObj("必填英文提示词"),
|
||||
};
|
||||
|
||||
return {
|
||||
OpenDiscordWindow,
|
||||
mjSetting,
|
||||
select_robot_options,
|
||||
GetChannelRobots,
|
||||
SaveMjSetting,
|
||||
RefreshRobotOptions,
|
||||
rules,
|
||||
formRef
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<n-tabs type="line" animated size="large">
|
||||
<n-tab-pane name="normal setting" tab="基础设置">
|
||||
<n-form ref="formRef" :label-width="80" :model="formValue">
|
||||
<n-form-item label="SD请求地址" path="webui_api_url">
|
||||
<n-input v-model:value="formValue.webui_api_url" placeholder="输入SD地址" />
|
||||
</n-form-item>
|
||||
<n-form-item label="出图方式(文生图、图生图)" path="type">
|
||||
<n-select v-model:value="formValue.type" :options="modelOption" placeholder="输入采样方法" />
|
||||
</n-form-item>
|
||||
<n-form-item label="正向提示词(和推导出来的tag进行拼接)" path="prompt">
|
||||
<n-input v-model:value="formValue.prompt" placeholder="输入正向提示词" />
|
||||
</n-form-item>
|
||||
<n-form-item label="反向提示词" path="negative_prompt">
|
||||
<n-input v-model:value="formValue.negative_prompt" placeholder="输入反向提示词" />
|
||||
</n-form-item>
|
||||
<div style="display: flex">
|
||||
<n-form-item label="CFG Scale" style="margin-right: 30px;">
|
||||
<n-input-number :min="0" :max="30" :step="0.05" v-model:value="formValue.cfg_scale"
|
||||
placeholder="输入重绘幅度" />
|
||||
</n-form-item>
|
||||
<n-form-item label="重绘幅度" path="denoising_strength">
|
||||
<n-input-number :precision="2" :step="0.05" v-model:value="formValue.denoising_strength"
|
||||
placeholder="输入重绘幅度" />
|
||||
<n-checkbox style="margin-left: 30px;" size="large" v-model:checked="formValue.adetailer"
|
||||
label="是否开启修脸/修手" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
<n-form-item label="采样方式" path="sampler_name">
|
||||
<n-input v-model:value="formValue.sampler_name" placeholder="输入模型" />
|
||||
</n-form-item>
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="迭代步数" path="steps">
|
||||
<n-input-number style="width: 150px;" v-model:value="formValue.steps" placeholder="输入迭代步数" />
|
||||
</n-form-item>
|
||||
<n-form-item label="风格词权重" path="style_weight" style="margin-left: 10px;">
|
||||
<n-input-number :min="1" :max="4" :step="0.05" :precision="2" style="width: 150px;"
|
||||
v-model:value="formValue.style_weight" placeholder="输入风格词权重" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
<n-form-item label="图片分辨率" path="resolution">
|
||||
<n-input-number :show-button="false" v-model:value="formValue.width" placeholder="输入迭代步数" />
|
||||
<span style="margin: 0 10px;"> * </span>
|
||||
<n-input-number :show-button="false" v-model:value="formValue.height" placeholder="输入迭代步数" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button attr-type="button" type="primary" @click="SaveSDConfig">
|
||||
保存设置
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="ADetailer setting" tab="ADetailer 设置">
|
||||
<SDADetailerSetting />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { NForm, NFormItem, NInput, NButton, useMessage, NInputNumber, NSelect, NTabs, NTabPane, NCheckbox } from "naive-ui"
|
||||
import SDADetailerSetting from "../Components/SDADetailerSetting.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NButton, NInputNumber, NSelect, NTabs, NTabPane, SDADetailerSetting, NCheckbox
|
||||
},
|
||||
setup() {
|
||||
let formValue = ref({
|
||||
webui_api_url: window.config.webui_api_url,
|
||||
type: null,
|
||||
prompt: null,
|
||||
negative_prompt: null,
|
||||
denoising_strength: 1,
|
||||
sampler_name: null,
|
||||
steps: 15,
|
||||
width: 0,
|
||||
height: 0,
|
||||
adetailer: false,
|
||||
style_weight: 1,
|
||||
cfg_scale: 1
|
||||
})
|
||||
|
||||
let modelOption = ref([{
|
||||
label: "文生图",
|
||||
value: "txt2img"
|
||||
}, {
|
||||
label: "图生图",
|
||||
value: "img2img"
|
||||
}]);
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.InitSDConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
formValue.value = value.data;
|
||||
})
|
||||
})
|
||||
|
||||
let message = useMessage();
|
||||
/**
|
||||
* 保存SD设置
|
||||
*/
|
||||
async function SaveSDConfig() {
|
||||
// 保存SD配置
|
||||
await window.api.SaveSDConfig(toRaw(formValue.value), (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
return;
|
||||
} else if (value.code == 1) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
window.api.getSettingDafultData((value) => {
|
||||
window.config = value;
|
||||
})
|
||||
return
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "未知错误" });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
formValue,
|
||||
SaveSDConfig,
|
||||
modelOption,
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<NSpace vertical>
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
保存中
|
||||
</template>
|
||||
<n-form ref="formRef" :model="formValue" :rules="rules">
|
||||
<n-form-item path="draft_path" label="剪映草稿地址">
|
||||
<n-input v-model:value="formValue.draft_path" @keydown.enter.prevent />
|
||||
</n-form-item>
|
||||
<n-form-item path="project_path" label="项目地址(帧,图片等输出的地址)">
|
||||
<div>
|
||||
<n-input style="width: 400px;" v-model:value="formValue.project_path" @keydown.enter.prevent />
|
||||
</div>
|
||||
<n-button color="#e5a84b" @click="SelectProjectFolder" style="margin-left: 10px;">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item path="project_name" label="项目名称">
|
||||
<n-input v-model:value="formValue.project_name" @keydown.enter.prevent />
|
||||
</n-form-item>
|
||||
<n-form-item path="system_setting" label="系统设置">
|
||||
<n-form-item path="task_number" label="后台并行任务数(量力而行)">
|
||||
<n-input-number v-model:value="formValue.task_number" @keydown.enter.prevent :min="1" :max="10"
|
||||
placeholder="输入后台并行任务数" />
|
||||
</n-form-item>
|
||||
<n-form-item label="系统主题" style="margin-left: 20px;">
|
||||
<n-switch :rail-style="railStyle" v-model:value="formValue.theme" size="medium"
|
||||
checked-value="dark" unchecked-value="light" @update:value="ChangeMode">
|
||||
<template #checked>
|
||||
暗
|
||||
</template>
|
||||
<template #unchecked>
|
||||
亮
|
||||
</template>
|
||||
</n-switch>
|
||||
</n-form-item>
|
||||
<n-form-item label="人物场景选择模式" style="margin-left: 20px;">
|
||||
<n-select size="small" placeholder="请选择" :options="character_select_model_options"
|
||||
v-model:value="formValue.character_select_model" style="width: 120px;" />
|
||||
</n-form-item>
|
||||
</n-form-item>
|
||||
<n-form-item path="gpt_setting" label="GPT设置">
|
||||
<n-form-item path="gpt_business" label="GPT接口服务商">
|
||||
<n-select v-model:value="formValue.gpt_business" :options="gpt_options" placeholder="GPT接口服务商"
|
||||
style="width: 200px;" />
|
||||
</n-form-item>
|
||||
<n-button type="info" @click="openGptBuyUrl">
|
||||
购买
|
||||
</n-button>
|
||||
<n-form-item style="width: 300px; margin-left: 30px;" path="gpt_key" label="GPT Key">
|
||||
<n-input type="password" show-password-on="mousedown" v-model:value="formValue.gpt_key"
|
||||
@keydown.enter.prevent placeholder="GPT Key" />
|
||||
</n-form-item>
|
||||
<n-form-item style="margin-left: 30px;" path="gpt_model" label="GPT模型">
|
||||
<n-select v-model:value="formValue.gpt_model" :options="gpt_model_options" placeholder="GPT模型"
|
||||
style="width: 160px;" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-left: 30px;" path="gpt_count" label="推理上下文行数">
|
||||
<n-input-number v-model:value="formValue.gpt_count" placeholder="GPT模型" style="width: 150px;"
|
||||
:show-button="false" :min="1" :max="100" />
|
||||
</n-form-item>
|
||||
<n-button color="#e18a3b" @click="addGptOption" style="margin-left: 10px;">
|
||||
添加GPT
|
||||
</n-button>
|
||||
<n-button color="#779649" @click="TestGPTConnection" :loading="loading" style="margin-left: 10px;">
|
||||
测试链接
|
||||
</n-button>
|
||||
<n-button color="#ba5b49" @click="AddGptPrompt" style="margin-left: 10px;">
|
||||
添加GPT提示词
|
||||
</n-button>
|
||||
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_setting" label="翻译设置">
|
||||
<n-form-item path="translation_business" label="翻译服务商">
|
||||
<n-select v-model:value="formValue.translation_business" :options="translation_options"
|
||||
@update:value="SwitchTranslate" placeholder="翻译服务商" style="width: 200px;" />
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_app_id" label="APP ID" style="width: 300px; margin-left: 30px;">
|
||||
<n-input type="password" show-password-on="mousedown"
|
||||
v-model:value="formValue.translation_app_id" @keydown.enter.prevent placeholder="APP ID" />
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_secret" label="产品密钥" style="width: 300px; margin-left: 30px;">
|
||||
<n-input type="password" show-password-on="mousedown"
|
||||
v-model:value="formValue.translation_secret" placeholder="请输入产品密钥"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_secret" label="自动翻译" style="width: 300px; margin-left: 30px;">
|
||||
<n-checkbox v-model:checked="formValue.translation_auto" placeholder="请输入产品密钥"></n-checkbox>
|
||||
</n-form-item>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button
|
||||
:disabled="formValue.draft_path == null || formValue.draft_path == '' || formValue.project_path == null || formValue.project_path == ''"
|
||||
round type="primary" @click="handleValidateButtonClick">
|
||||
保存
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRaw, onMounted, defineComponent, h } from "vue"
|
||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, NSpace, NSpin, useMessage, NSelect, NCheckbox, NSwitch, NIcon } from "naive-ui"
|
||||
import AddGptOption from "./Components/AddGptOption.vue";
|
||||
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
|
||||
import AddGptPrompts from './Components/AddGptPrompts.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSpace, NSpin, NSelect, NCheckbox, NSwitch, FolderOpen, NIcon
|
||||
},
|
||||
setup() {
|
||||
let formRef = ref(null);
|
||||
let message = useMessage()
|
||||
let formValue = ref({
|
||||
draft_path: window.config.draft_path,
|
||||
project_path: window.config.project_path,
|
||||
project_name: window.config.project_name,
|
||||
gpt_business: window.config.gpt_business,
|
||||
gpt_key: window.config.gpt_key,
|
||||
gpt_model: window.config.gpt_model,
|
||||
gpt_count: window.config.gpt_count,
|
||||
task_number: window.config.task_number,
|
||||
translation_business: window.config.translation_business,
|
||||
translation_app_id: window.config.translation_app_id,
|
||||
translation_secret: window.config.translation_secret,
|
||||
translation_auto: window.config.translation_auto,
|
||||
theme: window.config.theme,
|
||||
character_select_model: window.config.character_select_model
|
||||
});
|
||||
let show = ref(false)
|
||||
let gpt_options = ref([]);
|
||||
let gpt_model_options = ref([]);
|
||||
let character_select_model_options = ref([])
|
||||
let dialog = useDialog();
|
||||
let loading = ref(false);
|
||||
|
||||
/**
|
||||
* 加载GPT的配置信息
|
||||
*/
|
||||
async function InitGptOptions() {
|
||||
debugger;
|
||||
await window.api.getGptBusinessOption("all", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
gpt_options.value = value.data;
|
||||
})
|
||||
|
||||
await window.api.getGptModelOption("all", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
gpt_model_options.value = value.data;
|
||||
})
|
||||
|
||||
await window.mj.GetTagSelectModel(value => {
|
||||
character_select_model_options.value = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await InitGptOptions();
|
||||
})
|
||||
|
||||
|
||||
let translation_options = [{
|
||||
label: "百度翻译",
|
||||
value: "https://fanyi-api.baidu.com/api/trans/vip/translate"
|
||||
}, {
|
||||
label: "火山引擎",
|
||||
value: "https://translate.volcengineapi.com?"
|
||||
},
|
||||
{
|
||||
label: "腾讯翻译",
|
||||
value: "https://tmt.tencentcloudapi.com"
|
||||
},
|
||||
{
|
||||
label: "阿里翻译",
|
||||
value: "https://mt.cn-hangzhou.aliyuncs.com"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
// 切换翻译服务商事件
|
||||
function SwitchTranslate(value, option) {
|
||||
// 将之前的数据全部删除
|
||||
formValue.value.translation_app_id = null;
|
||||
formValue.value.translation_secret = null;
|
||||
}
|
||||
|
||||
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 = {
|
||||
draft_path: ruleObj("必填剪映草稿地址"),
|
||||
project_path: ruleObj("必填项目地址"),
|
||||
project_name: ruleObj("必填项目名称"),
|
||||
task_number: ruleObj("必填后台并行任务数"),
|
||||
gpt_business: ruleObj("必填GPT接口服务商"),
|
||||
gpt_key: ruleObj("必填GPT Key"),
|
||||
gpt_model: ruleObj("必填GPT模型"),
|
||||
gpt_count: ruleObj("必填自动推理上下文行数"),
|
||||
translation_secret: ruleObj("必填产品密钥"),
|
||||
translation_app_id: ruleObj("必填APP ID"),
|
||||
translation_business: ruleObj("必填翻译服务商"),
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存配置信息
|
||||
*/
|
||||
function handleValidateButtonClick(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate((errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存文件
|
||||
window.api.ModifySampleSetting(JSON.stringify(toRaw(formValue.value)), (value) => {
|
||||
if (value.code == 1) {
|
||||
window.api.getSettingDafultData((value) => {
|
||||
window.config = value;
|
||||
})
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: value.message })
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: value.message })
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
let railStyle = (focused,
|
||||
checked) => {
|
||||
const style = {};
|
||||
if (checked) {
|
||||
style.background = "#f3a694";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #f3a694";
|
||||
}
|
||||
} else {
|
||||
style.background = "#775039";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #775039";
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开购买GPT的地址
|
||||
*/
|
||||
async function openGptBuyUrl() {
|
||||
window.api.openGptBuyUrl(toRaw(formValue.value.gpt_business));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @param {*} value 主题名称
|
||||
*/
|
||||
async function ChangeMode(value) {
|
||||
debugger
|
||||
console.log(value)
|
||||
const isDarkMode = await window.darkMode.toggle(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加GPT地址或者模型
|
||||
*/
|
||||
async function addGptOption() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 600;
|
||||
let dialogHeight = 450;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
title: "添加GPT地址或者模型",
|
||||
content: () => h(AddGptOption, {}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 刷新界面数据
|
||||
await InitGptOptions();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试当前GPT是不是可以链接成功
|
||||
*/
|
||||
async function TestGPTConnection() {
|
||||
loading.value = true;
|
||||
// 测试链接
|
||||
await window.api.TestGPTConnection(JSON.stringify(formValue.value), (value) => {
|
||||
loading.value = false;
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "GPT链接失败,错误信息: " + value.message })
|
||||
return;
|
||||
}
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "gpt链接测试成功,可以正常使用!" })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择项目文件夹
|
||||
*/
|
||||
async function SelectProjectFolder() {
|
||||
await window.api.selectFolder({ default_paht: toRaw(formValue.value).project_path }, (value) => {
|
||||
if (value.length > 0) {
|
||||
formValue.value.project_path = value[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加GPT提示词预设
|
||||
*/
|
||||
async function AddGptPrompt() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 600;
|
||||
let dialogHeight = window.innerHeight * 0.9;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
title: "添加GPT提示词预设",
|
||||
content: () => h(AddGptPrompts, { height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px; padding-right : 3px `,
|
||||
maskClosable: false,
|
||||
onClose: () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
formRef,
|
||||
ChangeMode,
|
||||
openGptBuyUrl,
|
||||
railStyle,
|
||||
handleValidateButtonClick,
|
||||
rules,
|
||||
SwitchTranslate,
|
||||
translation_options,
|
||||
gpt_model_options,
|
||||
gpt_options,
|
||||
show,
|
||||
formValue,
|
||||
addGptOption,
|
||||
TestGPTConnection,
|
||||
loading,
|
||||
SelectProjectFolder,
|
||||
AddGptPrompt,
|
||||
character_select_model_options,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,520 @@
|
||||
<template>
|
||||
<div style="color: red;">注意:<br>所有的配置都是基于1440 * 1080。会根据分辨率自动计算匹配</div>
|
||||
<n-tabs type="line" animated default-value="GeneralSettings">
|
||||
<n-tab-pane name="GeneralSettings" tab="通用设置">
|
||||
<n-form ref="formRef" :model="generalSetting" :rules="rules" label-placement="left" label-width="auto"
|
||||
require-mark-placement="right-hanging" :size="size">
|
||||
<n-form-item label="视频分辨率" path="video_resolution">
|
||||
<n-input-number :show-button="false" v-model:value="generalSetting.video_resolution_x"
|
||||
placeholder="视频X" />
|
||||
<div style="font-size: 30px; margin: 0 20px;">*</div>
|
||||
<n-input-number :show-button="false" v-model:value="generalSetting.video_resolution_y"
|
||||
placeholder="视频Y" />
|
||||
</n-form-item>
|
||||
|
||||
<n-tooltip trigger="hover" placement="top-start" style="background-color: aliceblue;" :show-arrow="false">
|
||||
<template #trigger>
|
||||
<n-form-item label="图片关键帧" path="keyframe">
|
||||
<n-select style="width: 150px; margin-right: 10px;"
|
||||
v-model:value="generalSetting.keyframe.name" :options="keyframeOptions"></n-select>
|
||||
<div style="margin-left: 10px;">上下:</div>
|
||||
<n-input-number :show-button="false" style="width: 100px;"
|
||||
v-model:value="generalSetting.keyframe.up_down" placeholder="上下偏移量" />
|
||||
<div style="margin-left: 10px;">左右:</div>
|
||||
<n-input-number :show-button="false" style="width: 100px;"
|
||||
v-model:value="generalSetting.keyframe.left_right" placeholder="上下偏移量" />
|
||||
<!-- <n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<div style="display: flex; margin-left: 10px;">
|
||||
<div style="margin-left: 10px; margin: auto;">大小(0-4):</div>
|
||||
<n-input-number :min="0" :max="4" :step="0.1" :show-button="false" style="width: 100px;"
|
||||
v-model:value="generalSetting.keyframe.scale" placeholder="放大缩小倍率" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
注意:放大倍率会先将图片全部铺满然后在进行放大。防止出现黑边
|
||||
</n-tooltip> -->
|
||||
|
||||
</n-form-item>
|
||||
</template>
|
||||
<div style="color: red;">设置的值越大,图片移动的速度越快(若是图片的尺寸不够,也会被放的越大)</div>
|
||||
</n-tooltip>
|
||||
<n-form-item label="帧率" path="frameRate">
|
||||
<n-input-number v-model:value="generalSetting.frameRate" placeholder="输入帧率" />
|
||||
</n-form-item>
|
||||
<n-form-item label="码率" path="bitRate">
|
||||
<n-input-number v-model:value="generalSetting.bitRate" placeholder="输入视频码率" />
|
||||
</n-form-item>
|
||||
<n-form-item label="配音声量大小" path="audioSoundSize">
|
||||
<n-input-number v-model:value="generalSetting.audioSoundSize" placeholder="配音声量大小" />
|
||||
</n-form-item>
|
||||
<n-form-item label="背景音乐声量大小" path="backgroundMusicSoundSize">
|
||||
<n-input-number v-model:value="generalSetting.backgroundMusicSoundSize" placeholder="背景音乐声量大小" />
|
||||
</n-form-item>
|
||||
<div style="display: flex;">
|
||||
<n-button round type="primary" @click="SaveGeneralSetting">
|
||||
保存
|
||||
</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="AssSetting" tab="字幕设置">
|
||||
<div style="color: red;"> 注意: 当前有些字体读取不能准确获取。可以在后面的下面的编辑列表中进行简单的修改</div>
|
||||
<!-- 字体 字号 颜色 透明度 位置 -->
|
||||
<n-form :model="assSetting" style="display: flex; flex-wrap: wrap;">
|
||||
<n-form-item style=" margin-right: 20px;" label="字体" path="fontName">
|
||||
<n-select style="width: 300px;" v-model:value="assSetting.fontName" :options="fontNameOptions"
|
||||
filterable />
|
||||
<n-button secondary color="#8a2be2" @click="GetSystemInstallFontName"> 更新字体列表 </n-button>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="字号(80差不多)">
|
||||
<n-input-number style="width: 200px;" v-model:value="assSetting.fontSize" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="颜色">
|
||||
<n-color-picker style="width: 200px;" v-model:value="assSetting.fontColor" :show-alpha="false" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="透明度(100不透明)">
|
||||
<n-input-number style="width: 200px;" v-model:value="assSetting.transparent" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-right: 20px;" label="位置">
|
||||
<n-input-number style="width: 60px; margin-right: 10px;" v-model:value="assSetting.positionX"
|
||||
placeholder="X" :show-button="false" />
|
||||
<n-input-number style="width: 60px;" v-model:value="assSetting.positionY" placeholder="Y"
|
||||
:show-button="false" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div>
|
||||
<n-button type="info" @click="SaveAssConfig"> 保存信息 </n-button>
|
||||
</div>
|
||||
|
||||
<n-data-table :columns="assSettingColumns" :data="generalSetting.assConfig" :pagination="false"
|
||||
:bordered="false" />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="WaterMarkSetting" tab="水印设置">
|
||||
<div style="color: red;"> 注意: 当前有些字体读取不能准确获取。可以在后面的下面的编辑列表中进行简单的修改</div>
|
||||
<!-- 字体 字号 颜色 透明度 位置 -->
|
||||
<n-form :model="wmSetting" style="display: flex; flex-wrap: wrap;">
|
||||
<n-form-item style=" margin-right: 20px;" label="水印字体(要换行输入\N)" path="fontName">
|
||||
<n-input style="width: 400px;" v-model:value="wmSetting.showText" />
|
||||
</n-form-item>
|
||||
<n-form-item style=" margin-right: 20px;" label="字体" path="fontName">
|
||||
<n-select style="width: 300px;" v-model:value="wmSetting.fontName" :options="fontNameOptions"
|
||||
filterable />
|
||||
<n-button secondary color="#8a2be2" @click="GetSystemInstallFontName"> 更新字体列表 </n-button>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="字号(30差不多)">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.fontSize" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="颜色">
|
||||
<n-color-picker style="width: 200px;" v-model:value="wmSetting.fontColor" :show-alpha="false" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="透明度(100不透明)">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.transparent" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-right: 20px;" label="位置(X,Y)">
|
||||
<n-input-number style="width: 60px; margin-right: 10px;" v-model:value="wmSetting.positionX"
|
||||
placeholder="X" :show-button="false" />
|
||||
<n-input-number style="width: 60px;" v-model:value="wmSetting.positionY" placeholder="Y"
|
||||
:show-button="false" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div>
|
||||
<n-button type="info" @click="SaveWaterMarkConfig"> 保存信息 </n-button>
|
||||
</div>
|
||||
|
||||
<n-data-table :columns="wmColumns" :data="generalSetting.watermarkConfig" :pagination="false"
|
||||
:bordered="false" />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="backgroundSetting" tab="背景音乐设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">设置背景乐对应的文件夹,选择对应的分类,可以随机匹配音乐</div>
|
||||
</div>
|
||||
<BackgroundMusic></BackgroundMusic>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { NForm, NFormItem, NInput, NButton, useMessage, NCard, NTabs, NTabPane, NInputNumber, NSelect, NColorPicker, NDataTable, useDialog, NTooltip } from "naive-ui"
|
||||
import ModifyVideoAssSetting from '../Components/ModifyVideoAssSetting.vue'
|
||||
import BackgroundMusic from './Components/BackgroundMusic.vue'
|
||||
|
||||
/**
|
||||
* 创建字幕设置表格的列表
|
||||
*/
|
||||
const createAssSettingColumns = ({
|
||||
DeleteAssSetting,
|
||||
EditAssSetting,
|
||||
DeleteWaterMarkSetting
|
||||
}, isWater = false) => {
|
||||
let columnData = [];
|
||||
if (isWater) {
|
||||
columnData.push(
|
||||
{
|
||||
title: "显示值",
|
||||
key: "showText"
|
||||
})
|
||||
}
|
||||
let return_data = columnData.concat([
|
||||
{
|
||||
title: "字体名称",
|
||||
key: "fontName"
|
||||
},
|
||||
{
|
||||
title: "字号",
|
||||
key: "fontSize"
|
||||
},
|
||||
{
|
||||
title: "字体颜色",
|
||||
key: "fontColor"
|
||||
},
|
||||
{
|
||||
title: "透明度",
|
||||
key: "transparent"
|
||||
},
|
||||
{
|
||||
title: "字体颜色",
|
||||
key: "fontColor",
|
||||
render(row) {
|
||||
return h(NColorPicker, {
|
||||
disabled: true,
|
||||
value: row.fontColor
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "位置X",
|
||||
key: "positionX",
|
||||
},
|
||||
{
|
||||
title: "位置Y",
|
||||
key: "positionY",
|
||||
},
|
||||
]);
|
||||
if (isWater) {
|
||||
return_data.push({
|
||||
title: "Action",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return [h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
style: "margin-right:5px",
|
||||
tertiary: true,
|
||||
type: 'info',
|
||||
size: "small",
|
||||
onClick: () => EditAssSetting(row, "watermarkConfig")
|
||||
},
|
||||
{ default: () => "编辑" }
|
||||
), h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
type: 'error',
|
||||
size: "small",
|
||||
onClick: () => DeleteWaterMarkSetting(row)
|
||||
},
|
||||
{ default: () => "删除" }
|
||||
)]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return_data.push({
|
||||
title: "Action",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return [h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
style: "margin-right:5px",
|
||||
tertiary: true,
|
||||
type: 'info',
|
||||
size: "small",
|
||||
onClick: () => EditAssSetting(row, "assConfig")
|
||||
},
|
||||
{ default: () => "编辑" }
|
||||
), h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
type: 'error',
|
||||
size: "small",
|
||||
onClick: () => DeleteAssSetting(row)
|
||||
},
|
||||
{ default: () => "删除" }
|
||||
)];
|
||||
}
|
||||
})
|
||||
}
|
||||
return return_data;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, useDialog,
|
||||
NInputNumber, NButton, NCard, NTabPane, NTabs,
|
||||
NSelect, NColorPicker, NDataTable, BackgroundMusic, NTooltip
|
||||
},
|
||||
setup() {
|
||||
let modifyRef = ref(null)
|
||||
let dialog = useDialog();
|
||||
let assSetting = ref({
|
||||
fontName: null,
|
||||
fontSize: 0,
|
||||
fontColor: "#3B7114",
|
||||
transparent: 0,
|
||||
positionX: 0,
|
||||
positionY: 0
|
||||
})
|
||||
let wmSetting = ref({
|
||||
showText: null,
|
||||
fontName: null,
|
||||
fontSize: 0,
|
||||
fontColor: "#3B7114",
|
||||
transparent: 0,
|
||||
positionX: 0,
|
||||
positionY: 0
|
||||
})
|
||||
let generalSetting = ref({
|
||||
video_resolution_x: 0,
|
||||
video_resolution_y: 0,
|
||||
keyframe: {
|
||||
name: null,
|
||||
up_down: 0,
|
||||
left_right: 0,
|
||||
scale: 0
|
||||
},
|
||||
frameRate: 0,
|
||||
bitRate: 0,
|
||||
audioSoundSize: 0,
|
||||
backgroundMusicSoundSize: 0,
|
||||
assConfig: [],
|
||||
watermarkConfig: []
|
||||
})
|
||||
let message = useMessage();
|
||||
let fontNameOptions = ref([]);
|
||||
let keyframeOptions = ref([]);
|
||||
|
||||
/**
|
||||
* 获取视频设置的基本信息
|
||||
*/
|
||||
async function GetVideoConfigMessage() {
|
||||
// 获取基础数据
|
||||
await window.api.GetVideoConfigMessage((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
}
|
||||
else if (value.code == 1) {
|
||||
// 基础设置
|
||||
generalSetting.value = value.data;
|
||||
generalSetting.value.keyframe = generalSetting.value.keyframe ? generalSetting.value.keyframe : {}
|
||||
// 字体下拉列表显示值
|
||||
fontNameOptions.value = value.data.font_name_list
|
||||
//
|
||||
} else {
|
||||
message.error("未知错误");
|
||||
}
|
||||
})
|
||||
|
||||
await window.api.GetKeyFrameOptions(value => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
keyframeOptions.value = value.data.filter(item => item.value != "KFTypeScale");
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取基础数据
|
||||
await GetVideoConfigMessage();
|
||||
})
|
||||
/**
|
||||
* 保存基本配置
|
||||
*/
|
||||
async function SaveGeneralSetting() {
|
||||
debugger
|
||||
await window.api.SaveGeneralSetting(toRaw(generalSetting.value), (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
}
|
||||
else if (value.code == 1) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "未知错误" });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前系统已经安装了的字体文件
|
||||
*/
|
||||
async function GetSystemInstallFontName() {
|
||||
await window.api.GetSystemInstallFontName(async (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
} else if (value.code == 1) {
|
||||
message.success("保存成功");
|
||||
// 刷新数据
|
||||
await GetVideoConfigMessage();
|
||||
} else {
|
||||
message.error("未知错误")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存字幕设置
|
||||
*/
|
||||
async function SaveAssConfig() {
|
||||
window.api.SaveAssConfig(["assConfig", toRaw(assSetting.value)], async (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 1) {
|
||||
// 刷新数据,将上面的数据清零。
|
||||
await GetVideoConfigMessage();
|
||||
// message.success("保存成功");
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else if (value.code == 0) {
|
||||
// message.error(value.message);
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else {
|
||||
// message.error("未知错误");
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "未知错误" })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对应行的字幕设置
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function DeleteAssSetting(row) {
|
||||
await window.api.DeleteVideoConfig(['assConfig', row.id], async (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await GetVideoConfigMessage();
|
||||
return
|
||||
}
|
||||
message.error(value.message);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存水印设置
|
||||
*/
|
||||
async function SaveWaterMarkConfig() {
|
||||
window.api.SaveAssConfig(["watermarkConfig", toRaw(wmSetting.value)], async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 1) {
|
||||
// 刷新数据,将上面的数据清零。
|
||||
await GetVideoConfigMessage();
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ value: 0, message: "未知错误" })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的行
|
||||
*/
|
||||
async function DeleteWaterMarkSetting(row) {
|
||||
debugger
|
||||
await window.api.DeleteVideoConfig(['watermarkConfig', row.id], async (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await GetVideoConfigMessage();
|
||||
return
|
||||
}
|
||||
message.error(value.message);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据
|
||||
*/
|
||||
async function EditAssSetting(row, type) {
|
||||
// ModifyVideoAssSetting
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
content: () => h(ModifyVideoAssSetting, { ref: modifyRef, initData: row, type: type }),
|
||||
style: `width : 700px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
debugger;
|
||||
let ss = modifyRef.value.wmSetting
|
||||
console.log(ss)
|
||||
|
||||
await window.api.SaveAssConfig([type, toRaw(modifyRef.value.wmSetting)], async (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("修改成功")
|
||||
// 刷新数据,将上面的数据清零。
|
||||
await GetVideoConfigMessage();
|
||||
})
|
||||
},
|
||||
onNegativeClick: async () => {
|
||||
await GetVideoConfigMessage();
|
||||
},
|
||||
onClose: async () => {
|
||||
await GetVideoConfigMessage();
|
||||
},
|
||||
onEsc: async () => {
|
||||
await GetVideoConfigMessage();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
generalSetting,
|
||||
SaveGeneralSetting,
|
||||
assSetting,
|
||||
wmSetting,
|
||||
GetSystemInstallFontName,
|
||||
fontNameOptions,
|
||||
SaveAssConfig,
|
||||
SaveWaterMarkConfig,
|
||||
modifyRef,
|
||||
keyframeOptions,
|
||||
assSettingColumns: createAssSettingColumns({
|
||||
DeleteAssSetting,
|
||||
EditAssSetting,
|
||||
DeleteWaterMarkSetting
|
||||
}),
|
||||
wmColumns: createAssSettingColumns({ DeleteAssSetting, EditAssSetting, DeleteWaterMarkSetting }, true)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import App from './App.vue'
|
||||
import { Home } from '@vicons/ionicons5';
|
||||
const app = createApp(App);
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
component: () => import('./components/Home/Home.vue'),
|
||||
children: [
|
||||
|
||||
{ path: "/global_setting", name: "global_setting", component: () => import('./components/Setting/Setting.vue') },
|
||||
{ path: "/clip_setting", name: "clip_setting", component: () => import('./components/Setting/ClipSetting.vue') },
|
||||
{ path: "/getframe", name: "getframe", component: () => import('./components/Backstep/GetFrame.vue') },
|
||||
{ path: "/pushBackPrompt", name: "pushBackPrompt", component: () => import('./components/Backstep/PushBackPrompt.vue') },
|
||||
{ path: "/regenerate", name: "regenerate", component: () => import('./components/Backstep/ReGenerate.vue') },
|
||||
{ path: "/align_draft", name: "align_draft", component: () => import('./components/Clip/AlignDraft.vue') },
|
||||
{ path: "/add_draft", name: "add_draft", component: () => import('./components/Clip/AddDraft.vue') },
|
||||
{ path: "/VideoGenerate", name: "VideoGenerate", component: () => import('./components/Backstep/VideoGenerate.vue') },
|
||||
{ path: "/sd_setting", name: "sd_setting", component: () => import('./components/Setting/SDSetting.vue') },
|
||||
{ path: "/copywriting", name: "copywriting", component: () => import('./components/Backstep/CopyWriting.vue') },
|
||||
{ path: "/videogeneratesetting", name: "videogeneratesetting", component: () => import('./components/Setting/VideoGenerateSetting.vue') },
|
||||
{ path: "/ShowMessage", name: "ShowMessage", component: () => import('./components/Home/ShowMessage.vue') },
|
||||
{ path: "/sdoriginal", name: "sdoriginal", component: () => import('./components/Original/MainPage.vue') },
|
||||
{ path: "/mj_setting", name: "mj_setting", component: () => import('./components/Setting/MJSetting.vue') },
|
||||
]
|
||||
},
|
||||
{ path: "/ReDrawImage", component: () => import('./components/Home/ReDrawImageWD.vue'), },
|
||||
]
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app')
|
||||
@@ -0,0 +1,7 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #333; color: white; }
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
body { background: #ddd; color: black; }
|
||||
}
|
||||
Reference in New Issue
Block a user