V 3.2.4(2025.02.24)

1. 新增MJ(API,代理模式)国内转发接口(不包括反推),解决部分用户不能访问外部网络
2. 新增 FLUX-API 国内转发接口,解决部分用户不能访问外部网络(可能出现超时报错)
3. 动态设置FLUX模型,统一后台管理
4. 新增图转视频国内(Kling,Luma,Runway)转发接口,解决部分用户不能访问外部网络
5. 新增支持 Luma 慢速模型
6. 优化MJ设置及MJ请求,减少出错
7. MJ代理模式取消账号生图速度校验
注意:本次更新需要重新设置MJ设置(代理账号不用)
This commit is contained in:
2025-02-24 17:25:00 +08:00
parent 6cf1e7df5b
commit 03d3ff7e9d
49 changed files with 1823 additions and 1122 deletions
+30
View File
@@ -0,0 +1,30 @@
import { define } from "@/define/define"
import { OptionKeyName } from "@/define/enum/option";
import { ValidateJson } from "@/define/Tools/validate";
import axios from "axios";
import { isEmpty } from "lodash";
/**
* 远程的FLUX模型列表
* @returns
*/
async function GetRemoteFluxModelList() {
debugger
let res = await axios.get(define.lms + `/lms/LaitoolOptions/GetSimpleOptions/LaitoolFluxApiModelList`);
if (res.data.code != 1) {
throw new Error(res.data.message)
}
let data = res.data.data.filter(item => item.key == "LaitoolFluxApiModelList")
if (data.length != 1) {
throw new Error("获取远程FLUX API模型列表失败")
}
if (isEmpty(data[0].value) || !ValidateJson(data[0].value)) {
throw new Error("FLUX API模型列表数据异常,请联系管理员!")
}
return JSON.parse(data[0].value)
}
let SDFluxCommon = {
GetRemoteFluxModelList
}
export default SDFluxCommon
+48 -1
View File
@@ -1,6 +1,7 @@
// @ts-nocheck
import { OptionKeyName, OptionType } from '@/define/enum/option'
import { useOptionStore } from '@/stores/option'
import SDFluxCommon from './SDFluxCommon'
/**
* 初始化特殊字符数据,用于文案的相关处理
@@ -90,8 +91,54 @@ async function InitTTSGlobalSetting() {
}
}
/**
* 初始化模型列表
* @returns
*/
async function InitFluxModelList(isMust: boolean = false): Promise<Array<{ label: string, value: string }>> {
debugger
let initData = []
if (!isMust) {
let FLUX_APIModelListData = await window.options.GetOptionByKey(OptionKeyName.FLUX_APIModelList);
if (FLUX_APIModelListData.code == 0) {
window.api.showGlobalMessageDialog(FLUX_APIModelListData);
return initData;
}
if (FLUX_APIModelListData.data == null) {
initData = await SDFluxCommon.GetRemoteFluxModelList()
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.FLUX_APIModelList,
JSON.stringify(initData),
OptionType.JOSN
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog(saveRes)
return initData
} else {
return initData
}
} else {
return JSON.parse(FLUX_APIModelListData.data.value)
}
} else {
initData = await SDFluxCommon.GetRemoteFluxModelList()
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.FLUX_APIModelList,
JSON.stringify(initData),
OptionType.JOSN
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog(saveRes)
return initData
} else {
return initData
}
}
}
let InitCommon = {
InitSpecialCharacters,
InitTTSGlobalSetting
InitTTSGlobalSetting,
InitFluxModelList
}
export default InitCommon
@@ -95,6 +95,8 @@ async function GetBookTaskDetailOption() {
}
if (ValidateJson(videoMessage.value.lumaOptions)) {
lumaOptions.value = JSON.parse(videoMessage.value.lumaOptions)
lumaOptions.value.request_model =
lumaOptions.value.request_model == null ? 'fast' : lumaOptions.value.request_model
lumaOptions.value.image_url = videoMessage.value.imageUrl
}
if (ValidateJson(videoMessage.value.klingOptions)) {
@@ -30,6 +30,16 @@
<n-form-item-gi :span="12">
<n-image width="100" :src="lumaOptions.image_end_url"></n-image>
</n-form-item-gi>
<n-form-item-gi :span="4" label="速度模式">
<n-select
v-model:value="lumaOptions.request_model"
:options="[
{ label: 'FAST', value: 'fast' },
{ label: 'RELAX', value: 'relax' }
]"
placeholder="请选择速度"
></n-select>
</n-form-item-gi>
</n-grid>
</n-form>
</div>
@@ -37,7 +47,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { NForm, NImage, NGrid, NFormItemGi, NInput, NCheckbox } from 'naive-ui'
import { NForm, NImage, NSelect, NGrid, NFormItemGi, NInput, NCheckbox } from 'naive-ui'
let props = defineProps({
lumaOptions: undefined
@@ -9,7 +9,7 @@
pane-wrapper-style="margin: 0 -4px"
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;"
>
<n-tab-pane name="mj_setting" tab="MJ设置"> <MJSetting /> </n-tab-pane>
<n-tab-pane name="mj_setting" tab="MJ设置"> <MJSettingHome /> </n-tab-pane>
<n-tab-pane name="sd_setting" tab="SD设置"> <SDSetting /> </n-tab-pane>
<n-tab-pane name="export_video_setting" tab="导出视频设置">
<VideoGenerateSetting />
@@ -28,7 +28,7 @@
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NTabPane, NTabs, NCard } from 'naive-ui'
import MJSetting from '../../Setting/MJSetting.vue'
import MJSettingHome from '../../Setting/MJSetting/MJSettingHome.vue'
import SDSetting from '../../Setting/SDSetting.vue'
import VideoGenerateSetting from '../../Setting/VideoGenerateSetting.vue'
import WatermarkSetting from '../../Setting/WatermarkSetting.vue'
@@ -39,7 +39,7 @@ export default defineComponent({
NTabPane,
NTabs,
NCard,
MJSetting,
MJSettingHome,
SDSetting,
VideoGenerateSetting,
WatermarkSetting,
@@ -1,522 +0,0 @@
<template>
<div style="overflow: auto; height: 100%">
<n-card title="基础设置" size="medium" hoverable style="min-width: 850px">
<n-form :model="mjSetting" ref="sampleRef" :rules="sampleRules">
<div style="display: flex; align-items: center">
<n-form-item label="出图模式" style="width: 150px" path="requestModel">
<n-select
:options="request_model_options"
v-model:value="mjSetting.requestModel"
placeholder="请选择出图模式"
></n-select>
</n-form-item>
<n-form-item
label="选择生图机器人"
path="selectRobot"
style="width: 120px; margin-left: 10px"
>
<n-select
:options="select_robot_options"
v-model:value="mjSetting.selectRobot"
@update:value="UpdateSelectRobot"
></n-select>
</n-form-item>
<n-form-item label="机器人模型" style="width: 120px; margin-left: 10px" path="imageModel">
<n-select
placeholder="请选择机器人模型"
:options="image_model_options"
v-model:value="mjSetting.imageModel"
></n-select>
</n-form-item>
<n-form-item label="生图尺寸" style="width: 120px; margin-left: 10px" path="imageScale">
<n-select
placeholder="请选择生图尺寸"
:options="image_scale_options"
v-model:value="mjSetting.imageScale"
></n-select>
</n-form-item>
<n-form-item label="命令后缀" style="width: 160px; margin-left: 10px" path="imageSuffix">
<n-input v-model:value="mjSetting.imageSuffix" placeholder="请输入后缀命令"></n-input>
</n-form-item>
<n-form-item label="生图任务量" style="width: 100px; margin-left: 10px" path="taskCount">
<n-input-number
v-model:value="mjSetting.taskCount"
:show-button="false"
placeholder="MJ并发出图数量"
:min="1"
:max="20"
></n-input-number>
</n-form-item>
<n-form-item label="间隔时间(s)" style="width: 100px; margin-left: 10px" path="spaceTime">
<n-input-number
v-model:value="mjSetting.spaceTime"
:show-button="false"
placeholder="请输入间隔时间(s)"
:min="1"
:max="30"
></n-input-number>
</n-form-item>
</div>
</n-form>
</n-card>
<n-card title="API模式设置" hoverable style="margin-top: 10px; min-width: 850px" size="medium">
<n-form :model="mjSetting.apiSetting">
<div style="display: flex">
<n-form-item
label="选择出图的API"
style="width: 160px; margin-left: 10px"
path="mjApiUrl"
>
<n-select
:options="mj_api_options"
v-model:value="mjSetting.apiSetting.mjApiUrl"
placeholder="选择出图的中转API"
/>
</n-form-item>
<n-form-item style="margin-left: 10px" path="mj_api_url">
<n-button type="success" @click="openGptBuyUrl">购买</n-button>
</n-form-item>
<n-form-item
label="选择出图速度模式"
style="width: 160px; margin-left: 10px"
path="mjSpeed"
>
<n-select
:options="mj_speed_options"
v-model:value="mjSetting.apiSetting.mjSpeed"
placeholder="选择出图速度模式"
/>
</n-form-item>
<n-form-item label="输入API密钥" style="width: 160px; margin-left: 10px" path="apiKey">
<n-input
type="password"
placeholder="请输入密钥"
show-password-on="mousedown"
v-model:value="mjSetting.apiSetting.apiKey"
></n-input>
</n-form-item>
</div>
</n-form>
</n-card>
<n-card title="代理模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-button type="info" @click="AddMultiMjAccount()">账号管理</n-button>
</n-card>
<n-card title="浏览器模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-form :model="mjSetting.browserSetting">
<div style="display: flex; margin-top: 10px">
<n-form-item>
<n-button type="info" @click="OpenDiscordWindow">打开登录MJ</n-button>
</n-form-item>
<n-form-item label="服务器ID" style="width: 200px; margin-left: 5px" path="serviceId">
<n-input
v-model:value="mjSetting.browserSetting.serviceId"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
<n-form-item label="频道ID" style="width: 200px; margin-left: 10px" path="channelId">
<n-input
v-model:value="mjSetting.browserSetting.channelId"
placeholder="登录MJ后切换频道自动获取"
/>
</n-form-item>
<n-form-item label="用户token" style="width: 300px; margin-left: 10px" path="token">
<n-input
v-model:value="mjSetting.browserSetting.token"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
</div>
<n-form-item label="用户Agent" style="width: 810px" path="userAgent">
<n-input
v-model:value="mjSetting.browserSetting.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.browserSetting.userAgentCustom"
style="width: 100px"
>
自定义
</n-checkbox>
</template>
开启自定义需要手动填写userAgent可以填入浏览器的userAgent
</n-tooltip>
</n-form-item>
</n-form>
</n-card>
<n-button type="info" round @click="SaveMjSetting" style="margin-top: 10px"
>保存MJ配置</n-button
>
</div>
</template>
<script setup>
import { defineComponent, ref, onMounted, computed, toRaw, h } from 'vue'
import {
NButton,
useMessage,
useDialog,
NDataTable,
NForm,
NFormItem,
NInput,
NDivider,
NCheckbox,
NSelect,
NTooltip,
NIcon,
NInputNumber,
NCard,
NSpin
} from 'naive-ui'
import { DEFINE_STRING } from '../../../../define/define_string'
import { Reload } from '@vicons/ionicons5'
import { isEmpty, max, min } from 'lodash'
import { MJImageType } from '../../../../define/enum/mjEnum'
import AddMultiRemoteMj from './Components/AddMultiRemoteMj.vue'
let message = useMessage()
let dialog = useDialog()
let sampleRef = ref(null)
let select_robot_options = ref([
{
label: 'MJ',
value: 'mj'
},
{
label: 'NIJI',
value: 'niji'
}
])
let mj_speed_options = ref([
{
label: 'RELAXED',
value: 'relaxed'
},
{
label: 'FAST',
value: 'fast'
}
])
let mjSetting = ref({
id: null,
imageModel: null,
imageScale: null,
imageSuffix: null,
requestModel: null,
type: null,
selectRobot: null,
spaceTime: 6,
taskCount: 3,
createTime: null,
updateTime: null,
version: null,
remoteSetting: {
accountId: null,
channelId: null,
coreSize: 3,
guildId: null,
mjBotChannelId: null,
nijiBotChannelId: null,
queueSize: 6,
remark: null, // 机器码
remixAutoSubmit: false,
timeoutMinutes: 6,
userAgent: null,
userToken: null
},
apiSetting: {
id: null,
mjApiUrl: null,
mjSpeed: null,
apiKey: null
},
browserSetting: {
id: null,
serviceId: null,
channelId: null,
mjBotId: null,
nijBotId: null,
token: null,
userAgent: null,
userAgentCustom: false
}
})
let image_scale_options = ref([])
let image_model_options = ref([])
let tmp_image_model_options = []
let request_model_options = ref([])
let mj_api_options = ref([])
let computedSuffix = () => {
let text = image_model_options.value.findIndex((item) => {
return item.value == mjSetting.value.imageModel
})
if (text == -1) {
return
}
let sc = image_scale_options.value.findIndex((item) => {
return item.value == mjSetting.value.imageScale
})
let dd = ` --${image_model_options.value[text].text} --ar ${image_scale_options.value[sc].text}`
mjSetting.value.imageSuffix = dd
return dd
}
// 初始化现在设置的数据
async function InitData() {
// 获取到的是当前的配置信息
let res = await window.setting.GetMJSettingTreeData()
if (res.code == 0) {
message.error(res.message)
return
}
if (!res.data.apiSetting) {
delete res.data.apiSetting
}
if (!res.data.remoteSetting) {
delete res.data.remoteSetting
}
if (!res.data.browserSetting) {
delete res.data.browserSetting
}
mjSetting.value = Object.assign(mjSetting.value, res.data)
console.log(res)
await window.mj.GetMJImageScale((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
image_scale_options.value = value.data
if (image_scale_options.value.length > 0 && mjSetting.value.imageScale == null) {
mjSetting.value.imageScale = image_scale_options.value[0].value
}
})
await window.mj.GetMJImageRobotModel((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
image_model_options.value = value.data
tmp_image_model_options = value.data
if (image_model_options.value.length > 0 && mjSetting.value.imageModel == null) {
mjSetting.value.imageModel = image_model_options.value[0].value
}
})
await window.mj.GetMJGenerateCategory((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
request_model_options.value = value.data.filter((item) => !item.disable)
if (request_model_options.value.length > 0 && mjSetting.value.requestModel == null) {
mjSetting.value.requestModel = request_model_options.value[0].value
}
})
await window.api.getGptBusinessOption('all', (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
mj_api_options.value = value.data.filter((item) => item.mj_url)
if (mj_api_options.value.length > 0 && mjSetting.value.apiSetting.mjApiUrl == null) {
mjSetting.value.apiSetting.mjApiUrl = mj_api_options.value[0].value
}
})
}
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.browserSetting.serviceId = value.data.serviceID
mjSetting.value.browserSetting.channelId = value.data.channelID
} else if (value.type == DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.TOKEN) {
mjSetting.value.browserSetting.token = value.data.authorization
if (!mjSetting.value.browserSetting.userAgentCustom) {
mjSetting.value.browserSetting.userAgent = value.data.userAgent
}
} else {
message.error('未知的数据类型')
}
})
await InitData()
if (mjSetting.value.apiSetting.mjSpeed == null) {
mjSetting.value.apiSetting.mjSpeed = mj_speed_options.value[0].value
}
})
/**
* 打开discord窗口
*/
async function OpenDiscordWindow() {
await window.api.OpenDiscordWindow((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
})
}
// 基础配置的必填检测
let sampleRules = {
imageModel: [{ required: true, message: '请选择机器人模型', trigger: 'blur' }],
imageScale: [{ required: true, message: '请选择生图尺寸', trigger: 'blur' }],
requestModel: [{ required: true, message: '请选择出图模式', trigger: 'blur' }],
selectRobot: [{ required: true, message: '请选择生图机器人', trigger: 'blur' }]
}
/**
* 保存MJ配置
*/
async function SaveMjSetting(e) {
// 在保存的时候要进行数据保存
e.preventDefault()
sampleRef.value?.validate(async (errors) => {
if (errors) {
message.error('请检查必填字段')
return
}
// mjSetting.value.imageSuffix = image_suffix.value
// 通过选择的请求模式判断哪些是要校验的
let request_model = mjSetting.value.requestModel
if (request_model == MJImageType.API_MJ) {
// API模式,检查必填数据
if (
mjSetting.value.apiSetting == null ||
mjSetting.value.apiSetting.mjApiUrl == null ||
mjSetting.value.apiSetting.mjSpeed == null ||
mjSetting.value.apiSetting.apiKey == null
) {
message.error('请检查API模式设置的必填字段')
return
}
}
if (request_model == MJImageType.REMOTE_MJ) {
// 检查是不是有代理账号
let remoteMjRes = await window.setting.GetRemoteMJSettings()
if (remoteMjRes.code == 0) {
message.error('获取代理MJ配置失败')
return
}
if (remoteMjRes.data.length <= 0) {
message.error('请先添加代理模式的配置')
return
}
}
if (request_model == MJImageType.BROWSER_MJ) {
// 浏览器模式,检查必填数据
if (
mjSetting.value.browserSetting == null ||
mjSetting.value.browserSetting.serviceId == null ||
mjSetting.value.browserSetting.channelId == null ||
mjSetting.value.browserSetting.token == null ||
mjSetting.value.browserSetting.userAgent == null
) {
message.error('请检查浏览器模式设置的必填字段')
return
}
}
// 开始保存数据
let res = await window.setting.SaveMJSettingTreeData(toRaw(mjSetting.value))
if (res.code == 0) {
window.api.showGlobalMessageDialog({ code: 0, message: res.message })
return
}
mjSetting.value = res.data
// 判断当前的模式
if (mjSetting.value.requestModel == MJImageType.REMOTE_MJ) {
window.api.showGlobalMessageDialog({
code: 1,
message: `数据保存成功,当前模式为代理模式`
})
} else {
window.api.showGlobalMessageDialog({ code: 1, message: '添加成功' })
}
})
}
/**
* 更新选择的机器人时触发的方法
*/
async function UpdateSelectRobot(value) {
// 刷新数据
image_model_options.value = tmp_image_model_options.filter((item) => {
return item.type == value
})
// 判断当前选中的机器人是不是在当前的数据中
if (
image_model_options.value.findIndex((item) => {
return item.value == mjSetting.value.imageModel
}) == -1
) {
mjSetting.value.imageModel =
image_model_options.value.length > 0 ? image_model_options.value[0].value : null
}
// 刷新命令后缀
mjSetting.value.imageSuffix = computedSuffix()
}
/**
* 打开购买GPT的地址
*/
async function openGptBuyUrl() {
let tmp_gb = mj_api_options.value.filter(
(item) => item.value == mjSetting.value.apiSetting.mjApiUrl
)
if (tmp_gb.length == 0) {
message.error('当前选择的服务商没有购买地址!')
return
}
let buy_url = tmp_gb[0].buy_url
if (isEmpty(buy_url)) {
message.error('当前选择的服务商没有购买地址!')
} else {
window.api.openGptBuyUrl(buy_url)
}
}
/**
* 添加多个账号,这样可以同时跑多个账号
*/
async function AddMultiMjAccount() {
let dW = window.innerWidth * 0.9
let dH = window.innerHeight * 0.9
dialog.create({
showIcon: false,
title: '管理代理模式MJ账号',
content: () => h(AddMultiRemoteMj, { height: dH }),
style: `min-width : 600px; width : ${dW}px; height : ${dH}px; padding-right : 5px;`,
maskClosable: false
})
}
</script>
@@ -0,0 +1,95 @@
<template>
<div class="mj-api-setting">
<n-card title="API模式设置" hoverable style="margin-top: 10px; min-width: 850px" size="medium">
<n-form :model="optionStore.MJ_GlobalSetting.mj_apiSetting">
<div style="display: flex">
<n-form-item
label="选择出图的API"
style="width: 160px; margin-left: 10px"
path="mjApiUrl"
>
<n-select
:options="mjAPIURLOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.mjApiUrl"
placeholder="选择出图的中转API"
/>
</n-form-item>
<n-form-item style="margin-left: 10px" path="mj_api_url">
<n-button type="success" @click="openGptBuyUrl">购买</n-button>
</n-form-item>
<n-form-item
label="选择出图速度模式"
style="width: 160px; margin-left: 10px"
path="mjSpeed"
>
<n-select
:options="mjImageSpeedOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.mjSpeed"
placeholder="选择出图速度模式"
/>
</n-form-item>
<n-form-item label="输入API密钥" style="width: 160px; margin-left: 10px" path="apiKey">
<n-input
type="password"
placeholder="请输入密钥"
show-password-on="mousedown"
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.apiKey"
></n-input>
</n-form-item>
<n-form-item
style="width: 120px; margin-left: 30px"
path="useTransfer"
label="是否国内转发"
>
<n-select
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.useTransfer"
:options="[
{ label: '是', value: true },
{ label: '否', value: false }
]"
style="width: 100px"
/>
</n-form-item>
</div>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { NCard, NForm, NFormItem, NInput, NSelect, NButton, useMessage } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
import MJDefine from '@/main/Service/MJ/mjDefine'
import { isEmpty } from 'lodash'
let optionStore = useOptionStore()
let message = useMessage()
let mjAPIURLOptions = ref([])
let mjImageSpeedOptions = ref([])
onMounted(() => {
mjAPIURLOptions.value = MJDefine.GetMJAPIUrlOptions()
mjImageSpeedOptions.value = MJDefine.GetMJSpeedOptions()
})
/**
* 打开购买GPT的地址
*/
async function openGptBuyUrl() {
let selectAPIUrl = mjAPIURLOptions.value.filter(
(item) => item.value == optionStore.MJ_GlobalSetting.mj_apiSetting.mjApiUrl
)
if (selectAPIUrl.length == 0) {
message.error('当前选择的服务商没有购买地址!')
return
}
let buy_url = selectAPIUrl[0].buy_url
if (isEmpty(buy_url)) {
message.error('当前选择的服务商没有购买地址!')
} else {
window.api.OpenUrl(buy_url)
}
}
</script>
@@ -0,0 +1,70 @@
<template>
<div class="mj-browser-setting">
<n-card title="浏览器模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-form :model="optionStore.MJ_GlobalSetting.mj_browserSetting">
<div style="display: flex; margin-top: 10px">
<n-form-item>
<n-button type="info" @click="OpenDiscordWindow">打开登录MJ</n-button>
</n-form-item>
<n-form-item label="服务器ID" style="width: 200px; margin-left: 5px" path="serviceId">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.serviceId"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
<n-form-item label="频道ID" style="width: 200px; margin-left: 10px" path="channelId">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.channelId"
placeholder="登录MJ后切换频道自动获取"
/>
</n-form-item>
<n-form-item label="用户token" style="width: 300px; margin-left: 10px" path="token">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.token"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
</div>
<n-form-item label="用户Agent" style="width: 810px" path="userAgent">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.userAgent"
placeholder="登录MJ后切换服务器自动获取(可自定义)"
style="margin-right: 10px"
/>
<n-tooltip trigger="hover" style="background-color: aliceblue; color: black">
<template #trigger>
<n-checkbox
v-model:checked="optionStore.MJ_GlobalSetting.mj_browserSetting.customUserAgent"
style="width: 100px"
>
自定义
</n-checkbox>
</template>
开启自定义需要手动填写userAgent可以填入浏览器的userAgent
</n-tooltip>
</n-form-item>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { NCard, NForm, NFormItem, NTooltip, NCheckbox, NInput, NButton } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
let optionStore = useOptionStore()
/**
* 打开discord窗口
*/
async function OpenDiscordWindow() {
await window.api.OpenDiscordWindow((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
})
}
</script>
@@ -0,0 +1,48 @@
<template>
<div class="mj-remote-setting">
<n-card title="代理模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-form inline>
<n-form-item style="width: 120px" path="useTransfer" label="是否国内转发">
<n-select
v-model:value="optionStore.MJ_GlobalSetting.mj_remoteSimpleSetting.useTransfer"
:options="[
{ label: '是', value: true },
{ label: '否', value: false }
]"
style="width: 100px"
/>
</n-form-item>
<n-form-item style="width: 120px; margin-left: 30px" path="useTransfer">
<n-button type="info" @click="AddMultiMjAccount">账号管理</n-button>
</n-form-item>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { ref, h } from 'vue'
import { NCard, NButton, useDialog, NForm, NFormItem, NSelect } from 'naive-ui'
import AddMultiRemoteMj from '../Components/AddMultiRemoteMj.vue'
import { useOptionStore } from '@/stores/option'
let optionStore = useOptionStore()
let dialog = useDialog()
/**
* 添加多个账号这样可以同时跑多个账号
*/
async function AddMultiMjAccount() {
let dW = window.innerWidth * 0.9
let dH = window.innerHeight * 0.9
dialog.create({
showIcon: false,
title: '管理代理模式MJ账号',
content: () => h(AddMultiRemoteMj, { height: dH }),
style: `min-width : 600px; width : ${dW}px; height : ${dH}px; padding-right : 5px;`,
maskClosable: false
})
}
</script>
@@ -0,0 +1,147 @@
<template>
<div class="mj-setting-home">
<MJSimpleSetting />
<MJAPISetting />
<MJRemoteSetting />
<MJBrowserSetting />
<n-button type="info" @click="SaveMjSetting" style="margin-top: 10px">保存MJ配置</n-button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useMessage, NButton } from 'naive-ui'
import { useSoftwareStore } from '@/stores/software'
import { useOptionStore } from '@/stores/option'
import { TimeDelay } from '@/define/Tools/time'
import { OptionKeyName, OptionType } from '@/define/enum/option'
import MJSimpleSetting from './MJSimpleSetting.vue'
import MJAPISetting from './MJAPISetting.vue'
import MJRemoteSetting from './MJRemoteSetting.vue'
import MJBrowserSetting from './MJBrowserSetting.vue'
import { MJImageType } from '@/define/enum/mjEnum'
import { isEmpty } from 'lodash'
let softwareStore = useSoftwareStore()
let optionStore = useOptionStore()
let message = useMessage()
onMounted(async () => {
await InitMJSettingData()
})
/**
* 初始化MJSetting数据
*/
async function InitMJSettingData() {
try {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在加载数据,请稍等...'
let mjRes = await window.options.GetOptionByKey(OptionKeyName.MJ_GlobalSetting)
if (mjRes.code == 0) {
window.api.showGlobalMessageDialog({
code: 0,
message: '加载或者是初始化MJSetting信息失败,失败原因:' + mjRes.message
})
return
}
if (mjRes.data == null) {
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.MJ_GlobalSetting,
JSON.stringify(optionStore.MJ_GlobalSetting),
OptionType.JSON
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog({
code: 0,
message: '加载或者是初始化MJSetting信息失败,失败原因:' + saveRes.message
})
return
} else {
message.success('初始化MJSetting信息成功')
return
}
} else {
optionStore.MJ_GlobalSetting = JSON.parse(mjRes.data.value)
message.success('加载MJSetting信息成功')
}
await TimeDelay(1000)
} catch (error) {
window.api.showGlobalMessageDialog({
code: 0,
message: '加载或者是初始化MJSetting信息失败,失败原因:' + error.toString()
})
} finally {
softwareStore.spin.spinning = false
}
}
/**
* 保存MJ配置
*/
async function SaveMjSetting() {
//
debugger
//
let request_model = optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel
if (request_model == MJImageType.API_MJ) {
// API
if (
optionStore.MJ_GlobalSetting.mj_simpleSetting == null ||
isEmpty(optionStore.MJ_GlobalSetting.mj_apiSetting.mjApiUrl) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_apiSetting.mjSpeed) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_apiSetting.apiKey)
) {
message.error('请检查API模式设置的必填字段')
return
}
}
if (request_model == MJImageType.REMOTE_MJ) {
//
let remoteMjRes = await window.setting.GetRemoteMJSettings()
if (remoteMjRes.code == 0) {
message.error('获取代理MJ配置失败')
return
}
if (remoteMjRes.data.length <= 0) {
message.error('请先添加代理模式的配置')
return
}
}
if (request_model == MJImageType.BROWSER_MJ) {
//
if (
optionStore.MJ_GlobalSetting.mj_browserSetting == null ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.serviceId) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.channelId) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.token) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.userAgent)
) {
message.error('请检查浏览器模式设置的必填字段')
return
}
}
optionStore.MJ_GlobalSetting.mj_simpleSetting.type =
optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.MJ_GlobalSetting,
JSON.stringify(optionStore.MJ_GlobalSetting),
OptionType.JSON
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog(saveRes)
} else {
window.api.showGlobalMessageDialog({
code: 1,
message: '保存数据成功'
})
}
}
</script>
@@ -0,0 +1,141 @@
<template>
<div class="mj-simple-setting">
<n-card title="基础设置" size="medium" hoverable style="min-width: 850px">
<n-form
:model="optionStore.MJ_GlobalSetting.mj_simpleSetting"
ref="sampleRef"
:rules="sampleRules"
>
<div style="display: flex; align-items: center">
<n-form-item label="出图模式" style="width: 150px" path="requestModel">
<n-select
:options="mjRequestModelOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel"
placeholder="请选择出图模式"
></n-select>
</n-form-item>
<n-form-item
label="选择生图机器人"
path="selectRobot"
style="width: 120px; margin-left: 10px"
>
<n-select
:options="mjRobotOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.selectRobot"
@update:value="UpdateSelectRobot"
></n-select>
</n-form-item>
<n-form-item label="机器人模型" style="width: 120px; margin-left: 10px" path="imageModel">
<n-select
placeholder="请选择机器人模型"
:options="mjRobotModelOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.imageModel"
@update:value="UpdateRobotModel"
></n-select>
</n-form-item>
<n-form-item label="生图尺寸" style="width: 120px; margin-left: 10px" path="imageScale">
<n-select
placeholder="请选择生图尺寸"
:options="mjImageScaleOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.imageScale"
@update:value="UpdateImageScale"
></n-select>
</n-form-item>
<n-form-item label="命令后缀" style="width: 160px; margin-left: 10px" path="imageSuffix">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.imageSuffix"
placeholder="请输入后缀命令"
></n-input>
</n-form-item>
<n-form-item label="生图任务量" style="width: 100px; margin-left: 10px" path="taskCount">
<n-input-number
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.taskCount"
:show-button="false"
placeholder="MJ并发出图数量"
:min="1"
:max="20"
></n-input-number>
</n-form-item>
<n-form-item label="间隔时间(s)" style="width: 100px; margin-left: 10px" path="spaceTime">
<n-input-number
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.spaceTime"
:show-button="false"
placeholder="请输入间隔时间(s)"
:min="1"
:max="30"
></n-input-number>
</n-form-item>
</div>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { NCard, NForm, NFormItem, NInputNumber, NSelect, NInput } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
import MJDefine from '@/main/Service/MJ/mjDefine'
let optionStore = useOptionStore()
let mjRequestModelOptions = ref([])
let mjRobotOptions = ref([])
let mjRobotModelOptions = ref([])
let mjImageScaleOptions = ref([])
//
let sampleRules = {
imageModel: [{ required: true, message: '请选择机器人模型', trigger: 'blur' }],
imageScale: [{ required: true, message: '请选择生图尺寸', trigger: 'blur' }],
requestModel: [{ required: true, message: '请选择出图模式', trigger: 'blur' }],
selectRobot: [{ required: true, message: '请选择生图机器人', trigger: 'blur' }]
}
onMounted(() => {
mjRequestModelOptions.value = MJDefine.GetMJRequestModelOptions()
mjRobotOptions.value = MJDefine.GetMJRobotOptions()
mjRobotModelOptions.value = MJDefine.GetMJRobotModelOptions(
optionStore.MJ_GlobalSetting.mj_simpleSetting.selectRobot
)
mjImageScaleOptions.value = MJDefine.GetMJImageScaleOptions()
})
let computedSuffix = () => {
let text = mjRobotModelOptions.value.findIndex((item) => {
return item.value == optionStore.MJ_GlobalSetting.mj_simpleSetting.imageModel
})
if (text == -1) {
return
}
let sc = mjImageScaleOptions.value.findIndex((item) => {
return item.value == optionStore.MJ_GlobalSetting.mj_simpleSetting.imageScale
})
let dd = ` --${mjRobotModelOptions.value[text].text} --ar ${mjImageScaleOptions.value[sc].text}`
optionStore.MJ_GlobalSetting.mj_simpleSetting.imageSuffix = dd
return dd
}
/**
* 更新选择的机器人时触发的方法
*/
function UpdateSelectRobot() {
mjRobotModelOptions.value = MJDefine.GetMJRobotModelOptions(
optionStore.MJ_GlobalSetting.mj_simpleSetting.selectRobot
)
optionStore.MJ_GlobalSetting.mj_simpleSetting.imageModel =
mjRobotModelOptions.value.length > 0 ? mjRobotModelOptions.value[0].value : undefined
//
computedSuffix()
}
function UpdateImageScale() {
computedSuffix()
}
function UpdateRobotModel() {
computedSuffix()
}
</script>
+192 -193
View File
@@ -111,18 +111,41 @@
placeholder="输入迭代步数"
/>
</n-form-item>
<n-form-item
style="width: 300px"
label="FLXU API模型(和GPT公用一个服务商)"
path="flux_model"
>
<n-select
placeholder="请选择FLUX API的生图模型"
v-model:value="formValue.flux_model"
:options="flux_model_options"
<div style="display: flex">
<n-form-item
style="width: 300px"
label="FLXU API模型(和GPT公用一个服务商)"
path="flux_model"
>
</n-select>
</n-form-item>
<n-select
placeholder="请选择FLUX API的生图模型"
v-model:value="formValue.flux_model"
:options="flux_model_options"
>
</n-select>
<n-button type="primary" style="margin-left: 10px" @click="GetFluxModelList"
>加载数据
</n-button>
</n-form-item>
<n-form-item
style="width: 150px; margin-left: 30px"
label="是否国内转发"
path="useTransfer"
>
<n-select
placeholder="请选择是否转换"
v-model:value="formValue.useTransfer"
:options="[
{ label: '是', value: true },
{ label: '否', value: false }
]"
>
</n-select>
</n-form-item>
<n-form-item style="margin-left: 30px">
<div style="color: red">注意使用国内转发可能会出现服务器超时错误错误码504</div>
</n-form-item>
</div>
<n-form-item>
<n-button attr-type="button" type="primary" @click="SaveSDConfig"> 保存设置 </n-button>
</n-form-item>
@@ -134,7 +157,7 @@
</n-tabs>
</template>
<script>
<script setup>
import { defineComponent, ref, h, onMounted, toRaw } from 'vue'
import {
NForm,
@@ -149,188 +172,164 @@ import {
NCheckbox
} from 'naive-ui'
import SDADetailerSetting from '../Components/SDADetailerSetting.vue'
import { FLxuAPIImageType } from '../../../../define/enum/image'
import InitCommon from '../../common/initCommon'
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,
sd_models: null,
lora: null,
flux_model: FLxuAPIImageType.FLUX
})
let flux_model_options = ref([])
let samplers_options = ref([])
let lora_options = ref([])
let sd_models_options = ref([])
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 samplers = value.data.sampler
samplers_options.value = []
for (let i = 0; i < samplers.length; i++) {
const element = samplers[i]
samplers_options.value.push({
label: element.name,
value: element.name
})
}
let loras = value.data.lora
lora_options.value = []
for (let i = 0; i < loras.length; i++) {
const element = loras[i]
lora_options.value.push({
label: element.name,
value: element.name
})
}
let sd_models = value.data.sd_model
sd_models_options.value = []
for (let i = 0; i < sd_models.length; i++) {
const element = sd_models[i]
sd_models_options.value.push({
label: element.title,
value: element.title
})
}
})
})
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: value.message })
}
})
}
/**
* 加载SD数据
*/
async function LoadSDServiceData() {
await window.sd.LoadSDServiceData(toRaw(formValue.value).webui_api_url, (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
// formValue.value = value.data
//
let samplers = value.data.sampler
samplers_options.value = []
for (let i = 0; i < samplers.length; i++) {
const element = samplers[i]
samplers_options.value.push({
label: element.name,
value: element.name
})
}
let loras = value.data.lora
lora_options.value = []
for (let i = 0; i < loras.length; i++) {
const element = loras[i]
lora_options.value.push({
label: element.name,
value: element.name
})
}
let sd_models = value.data.sd_model
sd_models_options.value = []
for (let i = 0; i < sd_models.length; i++) {
const element = sd_models[i]
sd_models_options.value.push({
label: element.title,
value: element.title
})
}
message.success('加载成功')
})
}
return {
formValue,
SaveSDConfig,
modelOption,
LoadSDServiceData,
samplers_options,
lora_options,
sd_models_options,
flux_model_options: [
{
label: 'FLUX',
value: FLxuAPIImageType.FLUX
},
{
label: 'FLUX DEV',
value: FLxuAPIImageType.FLUX_DEV
},
{
label: 'FLUX PRO',
value: FLxuAPIImageType.FLUX_PRO
},
{
label: 'FLUX PRO MAX',
value: FLxuAPIImageType.FLUX_PRO_MAX
},
{
label: 'FLUX SCHNELL',
value: FLxuAPIImageType.FLUX_SCHNELL
}
]
}
}
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,
sd_models: null,
lora: null,
flux_model: null,
useTransfer: false
})
let samplers_options = ref([])
let lora_options = ref([])
let sd_models_options = ref([])
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 samplers = value.data.sampler
samplers_options.value = []
for (let i = 0; i < samplers.length; i++) {
const element = samplers[i]
samplers_options.value.push({
label: element.name,
value: element.name
})
}
let loras = value.data.lora
lora_options.value = []
for (let i = 0; i < loras.length; i++) {
const element = loras[i]
lora_options.value.push({
label: element.name,
value: element.name
})
}
let sd_models = value.data.sd_model
sd_models_options.value = []
for (let i = 0; i < sd_models.length; i++) {
const element = sd_models[i]
sd_models_options.value.push({
label: element.title,
value: element.title
})
}
})
await InitFluxModelList()
})
/**
* 初始化FLUX模型列表
*/
async function InitFluxModelList(isMust = false) {
try {
flux_model_options.value = await InitCommon.InitFluxModelList(isMust)
if (!formValue.value.flux_model) {
formValue.value.flux_model =
flux_model_options.value.length > 0 ? flux_model_options.value[0].value : undefined
}
} catch (error) {
message.error('加载FLUX模型列表失败')
}
}
async function GetFluxModelList() {
await InitFluxModelList(true)
message.success('加载远程 FLUX API 模型列表成功')
}
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: value.message })
}
})
}
/**
* 加载SD数据
*/
async function LoadSDServiceData() {
await window.sd.LoadSDServiceData(toRaw(formValue.value).webui_api_url, (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
// formValue.value = value.data
//
let samplers = value.data.sampler
samplers_options.value = []
for (let i = 0; i < samplers.length; i++) {
const element = samplers[i]
samplers_options.value.push({
label: element.name,
value: element.name
})
}
let loras = value.data.lora
lora_options.value = []
for (let i = 0; i < loras.length; i++) {
const element = loras[i]
lora_options.value.push({
label: element.name,
value: element.name
})
}
let sd_models = value.data.sd_model
sd_models_options.value = []
for (let i = 0; i < sd_models.length; i++) {
const element = sd_models[i]
sd_models_options.value.push({
label: element.title,
value: element.title
})
}
message.success('加载成功')
})
}
</script>
+1 -1
View File
@@ -52,6 +52,7 @@ import { useOptionStore } from '@/stores/option'
let message = useMessage()
let optionStore = useOptionStore()
let roleOptions = ref([])
async function SwitchTTSOptions(key) {
console.log('SwitchTTSOptions', key)
@@ -65,7 +66,6 @@ async function SwitchTTSOptions(key) {
console.log('SwitchTTSOptions', roleOptions.value)
}
let roleOptions = ref([])
onMounted(async () => {
//
await SwitchTTSOptions('edge-tts')
+1 -1
View File
@@ -14,7 +14,7 @@
</n-form>
<EdgeTTS v-if="optionStore.TTS_GlobalSetting.selectModel == 'edge-tts'" />
<!-- <AzureTTS
:azureTTS="settingStore.ttsSetting.azureTTS"
:azureTTS=""
v-else-if="optionStore.TTS_GlobalSetting.selectModel == 'azure-tts'"
/> -->
</div>
+1 -1
View File
@@ -86,7 +86,7 @@ const routes = [
{
path: '/mj_setting',
name: 'mj_setting',
component: () => import('./components/Setting/MJSetting.vue')
component: () => import('./components/Setting/MJSetting/MJSettingHome.vue')
},
{
path: '/video_setting',