添加软件基础配置

This commit is contained in:
lq1405 2024-10-18 12:46:58 +08:00
parent 9618ae6b14
commit 11a23c92df
21 changed files with 482 additions and 22 deletions

View File

@ -62,6 +62,20 @@ export default [
}
]
},
{
name: 'options',
path: '/options',
icon: 'Tool',
access: 'canOptions',
routes: [
{
name: 'laitoolOptions',
path: '/options/laitoolOptions',
component: './Options/LaitoolOptions/LaitoolOptions/index',
access: 'canLaitoolOptions',
}
]
},
{
path: '/roleManagement',
name: 'roleManagement',

View File

@ -17,6 +17,9 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
isSuperAdmin: false,
isAdminOrSuperAdmin: false,
canOptions: false,
canLaitoolOptions: false,
canMachineManagement: false,
canAddMachine: true,
canEditMachine: false,
@ -61,6 +64,9 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
isAdmin: true,
isAdminOrSuperAdmin: true,
canOptions: true,
canLaitoolOptions: true,
canMachineManagement: true,
canEditMachine: true,
canDeleteMachine: true,
@ -81,6 +87,9 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
isSuperAdmin: true,
isAdminOrSuperAdmin: true,
canOptions: true,
canLaitoolOptions: true,
canMachineManagement: true,
canEditMachine: true,
canDeleteMachine: true,

View File

@ -52,7 +52,6 @@ export async function getInitialState(): Promise<{
// 如果不是登录页面,执行
const { location } = history;
debugger;
if (location.pathname !== loginPath && !location.pathname.startsWith('/user/register')) {
let currentUserString = localStorage.getItem('userInfo');
let currentUser = currentUserString ? JSON.parse(currentUserString) : null;
@ -184,7 +183,6 @@ export function rootContainer(container: React.ReactNode) {
*/
export const request = {
...errorConfig,
prefix: "https://localhost:44362",
timeout: 60000,
};

View File

@ -7,6 +7,9 @@ export default {
'menu.prompt.prompt-type': '提示词类型',
'menu.prompt.prompt-management': '提示词管理',
'menu.options': '配置管理',
'menu.options.laitoolOptions': 'Laitool配置',
'menu.roleManagement': '角色管理',
'menu.userManagement': '用户管理',

View File

@ -0,0 +1,101 @@
import { GetOptions, getOptionsStringValue, SaveOptions } from '@/services/services/options/optionsTool';
import { useOptionsStore } from '@/store/options';
import { useSoftStore } from '@/store/software';
import { Button, Card, Form, Input } from 'antd';
import TextArea from 'antd/es/input/TextArea';
import { message } from 'antd/lib';
import React, { useEffect } from 'react';
const BasicOptions: React.FC = () => {
const { setTopSpinning, setTopSpinTip } = useSoftStore();
const [messageApi, messageHolder] = message.useMessage();
const { laitoolOptions, setLaitoolOptions } = useOptionsStore();
const [form] = Form.useForm();
useEffect(() => {
setTopSpinning(true);
setTopSpinTip("加载信息中");
// 这边加载所有的配音数据
GetOptions("software").then((res) => {
setLaitoolOptions(res);
form.setFieldsValue({
LaitoolHomePage: getOptionsStringValue(res, 'LaitoolHomePage', ""),
LaitoolUpdateContent: getOptionsStringValue(res, 'LaitoolUpdateContent', ""),
LaitoolNotice: getOptionsStringValue(res, 'LaitoolNotice', ""),
LaitoolVersion: getOptionsStringValue(res, 'LaitoolVersion', ""),
});
}
).catch((err: any) => {
messageApi.error(err.message);
}).finally(() => {
console.log('finally');
setTopSpinning(false);
});
}, []);
async function onFinish(values: any): Promise<void> {
setTopSpinning(true);
setTopSpinTip("正在保存通用设置");
try {
// 这边保存所有的配音数据
await SaveOptions(values);
// 判断Option中的key是不是在属性上
for (let key in values) {
setLaitoolOptions(laitoolOptions.map((item: OptionModel.Option) => {
if (item.key === key) {
item.value = values[key]
}
return item
}));
}
messageApi.success('设置成功');
} catch (error: any) {
messageApi.error(error.message);
} finally {
setTopSpinning(false);
}
}
return (
<div>
<Card title="通用设置" bordered={false}>
<Form name="trigger" form={form} layout="vertical" autoComplete="off" onFinish={onFinish}>
<Form.Item
label="软件版本号"
name="LaitoolVersion"
>
<Input placeholder='请输入版本号' />
</Form.Item>
<Form.Item
label="首页内容"
name="LaitoolHomePage"
>
<TextArea autoSize={{ minRows: 3, maxRows: 6 }} placeholder="支持HTML和网址网址用iframe可以内嵌所有的网页" />
</Form.Item>
<Form.Item
label="更新内容"
name="LaitoolUpdateContent"
>
<TextArea autoSize={{ minRows: 3, maxRows: 6 }} placeholder="支持HTML和网址网址用iframe" />
</Form.Item>
<Form.Item
label="通知"
name="LaitoolNotice"
>
<TextArea autoSize={{ minRows: 3, maxRows: 6 }} placeholder="支持HTML和网址网址用iframe" />
</Form.Item>
<Form.Item>
<Button color="primary" variant="filled" htmlType="submit"></Button>
</Form.Item>
</Form>
</Card>
{messageHolder}
</div>
);
};
export default BasicOptions;

View File

@ -0,0 +1,64 @@
import { getOptionsStringValue, SaveOptions } from '@/services/services/options/optionsTool';
import { useOptionsStore } from '@/store/options';
import { useSoftStore } from '@/store/software';
import { Button, Card, Col, Form, Input, message, Row } from 'antd';
import TextArea from 'antd/es/input/TextArea';
import React, { useEffect } from 'react';
const DubSettingTTsOptions: React.FC = () => {
const [form] = Form.useForm();
const { ttsOptions, setTTsOptions } = useOptionsStore();
const { topSpinning, setTopSpinning, topSpinTip, setTopSpinTip } = useSoftStore();
const [messageApi, messageHolder] = message.useMessage();
async function onFinish(values: any): Promise<void> {
console.log('Received values of form: ', values);
setTopSpinning(true);
setTopSpinTip("正在保存EdgeTTs配置");
try {
await SaveOptions(values);
setTTsOptions(ttsOptions.map((item: OptionModel.Option) => {
if (item.key === "EdgeTTsRoles") {
item.value = values.edgeTTsRoles
}
return item
}));
messageApi.success('设置成功');
} catch (error: any) {
messageApi.error(error.message);
} finally {
setTopSpinning(false);
}
}
useEffect(() => {
console.log("DubSettingTTsOptions", ttsOptions)
form.setFieldsValue({ edgeTTsRoles: getOptionsStringValue(ttsOptions, 'EdgeTTsRoles', "{}") })
}, [ttsOptions])
return (
<Card title="配置" >
<Form form={form} name="advanced_search" onFinish={onFinish} layout="vertical">
<Row gutter={24}>
<Col span={8}>
<Form.Item
label="语音合成角色"
name="edgeTTsRoles"
>
<TextArea placeholder="请输入EdgeTTs合成角色JSON格式" autoSize={{ minRows: 6, maxRows: 6 }} />
</Form.Item>
</Col>
</Row>
<Form.Item wrapperCol={{ offset: 0, span: 16 }}>
<Button color="primary" variant="filled" htmlType="submit">
EdgeTTs配置
</Button>
</Form.Item>
</Form>
{messageHolder}
</Card>
);
};
export default DubSettingTTsOptions;

View File

@ -0,0 +1,51 @@
import { Card, Collapse, CollapseProps, Form } from 'antd';
import React, { useEffect } from 'react';
import DubSettingTTsOptions from './DubSettingTTsOptions';
import { useOptionsStore } from '@/store/options';
import { useSoftStore } from '@/store/software';
import { GetOptions } from '@/services/services/options/optionsTool';
const DubSetting: React.FC = () => {
const { ttsOptions, setTTsOptions } = useOptionsStore();
const { setTopSpinning, setTopSpinTip } = useSoftStore();
const onChange = (key: string | string[]) => {
console.log(key);
};
useEffect(() => {
setTopSpinning(true);
setTopSpinTip("加载信息中");
// 这边加载所有的配音数据
GetOptions('tts').then((res) => {
setTTsOptions(res);
}
).catch((err) => {
console.log(err);
}).finally(() => {
console.log('finally');
setTopSpinning(false);
});
}, []);
const items: CollapseProps['items'] = [
{
key: '1',
label: '配置',
children: <p></p>,
},
{
key: '2',
label: <strong>Edge TTS</strong>,
children: <DubSettingTTsOptions />,
},
];
return (
<Collapse items={items} bordered={false} ghost onChange={onChange} />
);
};
export default DubSetting;

View File

@ -0,0 +1,38 @@
import TemplateContainer from '@/pages/TemplateContainer';
import { useModel } from '@umijs/max';
import { Tabs, TabsProps, theme } from 'antd';
import React from 'react';
import DubSetting from '../DubSetting';
import BasicOptions from '../BasicOptions';
const LaitoolOptions: React.FC = () => {
const { initialState } = useModel('@@initialState');
const [tabKey, setTabKey] = React.useState<string | undefined>(undefined);
const items = [{
label: `软件设置`,
key: "software",
children: <BasicOptions />,
style: undefined,
}, {
label: `配音设置`,
key: "dub",
children: <DubSetting />,
style: undefined,
destroyInactiveTabPane : true
}]
const onChange = (key: string) => {
setTabKey(key);
};
return (
<TemplateContainer title={false} navTheme={initialState?.settings?.navTheme ?? "realDark"}>
<Tabs defaultActiveKey="1" destroyInactiveTabPane={true} items={items} onChange={onChange} />
</TemplateContainer>
);
};
export default LaitoolOptions;

View File

@ -115,7 +115,7 @@ const PromptManagement: React.FC = () => {
width: 220,
render: (dom, ent) => <>
<Button size='middle' style={{ marginRight: "5px" }} type="primary" onClick={() => {
debugger
setEditData(ent)
setType("edit")
setOpen(true)

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Card, Spin } from 'antd';
import { useSoftStore } from '@/store/software';
@ -7,9 +7,10 @@ interface TemplateContainerProps {
children: React.ReactNode;
navTheme: string;
style?: React.CSSProperties;
title?: React.ReactNode | false;
}
const TemplateContainer: React.FC<TemplateContainerProps> = ({ children, navTheme, style }) => {
const TemplateContainer: React.FC<TemplateContainerProps> = ({ children, navTheme, style, title }) => {
const { topSpinning, topSpinTip } = useSoftStore();
@ -20,7 +21,7 @@ const TemplateContainer: React.FC<TemplateContainerProps> = ({ children, navThem
return (
<Spin spinning={topSpinning} tip={topSpinTip}>
<PageContainer>
<PageContainer title={title}>
<Card
style={{
...style,

View File

@ -27,8 +27,6 @@ const UserManagement: React.FC = () => {
totalBoundaryShowSizeChanger: true,
},
});
const [modal, contextHolder] = Modal.useModal();
const [loading, setLoading] = useState<boolean>(true);
const [userId, setUserId] = useState<number>(0);
const [openModal, setOpenModal] = useState<boolean>(false);

View File

@ -38,7 +38,7 @@ const refreshToken = async () => {
"deviceInfo": "2"
},
});
debugger
if (response.code == 1) {
localStorage.setItem('token', response.data);
return true;
@ -53,7 +53,7 @@ const refreshToken = async () => {
const retryRequest = async (url: string, opts: RequestOptions) => {
if (url) {
try {
debugger
const response = await request(url, {
...opts,
headers: {
@ -96,7 +96,7 @@ export const errorConfig: RequestConfig = {
if (opts?.skipErrorHandler) throw error;
// 我们的 errorThrower 抛出的错误。
if (error.name === 'BizError') {
debugger
} else if (error.response) {
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
const { status } = error.response;
@ -150,13 +150,13 @@ export const errorConfig: RequestConfig = {
// history.push('/user/login'); // 重定向到登录页面
}
} else if (error.request) {
debugger
// 请求已经成功发起,但没有收到响应
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
message.error('None response! Please retry.');
} else {
debugger
// 发送请求时出了点问题
message.error('Request error, please retry.');
}
@ -170,7 +170,7 @@ export const errorConfig: RequestConfig = {
// 拦截请求配置,进行个性化处理。
// 添加校验头
// config.baseURL = 'https://localhost:44362';
config.baseURL = 'http://101.35.233.173:5000';
config.baseURL = window.location.origin.includes('localhost') ? 'https://localhost:44362' : window.location.origin;
const headers = {
...config.headers, // 保留已有的请求头
'Authorization': `Bearer ${localStorage.getItem('token')}`, // 添加新的请求头

View File

@ -0,0 +1,5 @@
export enum OptionType {
String = 1,
JSON = 2,
Number = 3,
}

View File

@ -1,9 +1,7 @@
// @ts-ignore
/* eslint-disable */
import { request, useModel } from '@umijs/max';
import CryptoJS from 'crypto-js';
import { TokenStorage } from '../define/tokenStorage';
import { errorMessage, successMessage } from './response';
import { getCurrentUser } from './user';
import forge from 'node-forge';
@ -124,7 +122,7 @@ export async function login(body: API.LoginParams, options?: { [key: string]: an
export async function UserRegistr(params: UserModel.UserRegisterParams): Promise<void> {
let publicKey = await getPublicKey();
// 加密密码
debugger;
let secPassword = encryptPassword(publicKey.publicKey, params.password);
let bodyData = {
userName: params.userName,

View File

@ -0,0 +1,135 @@
import { OptionType } from "@/services/enum/optionEnum";
import { isEmpty } from "lodash";
/**
*
*
* @template T -
* @param {OptionModel.Option[]} options -
* @param {string} keyName -
* @param {T} [defaultValue] -
* @returns {T} -
* @throws {Error} -
* @throws {Error} -
* @throws {Error} -
*/
export function getOptionsValue<T>(options: OptionModel.Option[], keyName: string, defaultValue?: T): T {
const option = options.find(x => x.key === keyName);
if (!option) {
throw new Error(`Option with key '${keyName}' not found`);
}
if (isEmpty(option.value) && defaultValue) {
return defaultValue;
}
try {
switch (option.type) {
case OptionType.String:
return option.value as T;
case OptionType.JSON:
return JSON.parse(option.value) as T;
case OptionType.Number:
return Number(option.value) as T;
default:
throw new Error(`Unsupported option type: ${option.type}`);
}
} catch (error: any) {
throw new Error(`Error processing option '${keyName}': ${error.message}`);
}
}
/**
*
*
* @param options -
* @param keyName -
* @param defaultValue -
* @returns
* @throws
* @throws
* @throws
*/
export function getOptionsStringValue(options: OptionModel.Option[], keyName: string, defaultValue?: string): string {
// if (options.length === 0) {
// throw new Error("Options array is empty");
// }
const option = options.find(x => x.key === keyName);
if (!option) {
if (defaultValue !== undefined) {
return defaultValue;
}
throw new Error(`Option with key '${keyName}' not found`);
}
if (isEmpty(option.value)) {
return defaultValue ?? "";
}
try {
switch (option.type) {
case OptionType.String:
case OptionType.JSON:
return String(option.value);
case OptionType.Number:
return String(Number(option.value));
default:
throw new Error(`Unsupported option type: ${option.type}`);
}
} catch (error) {
if (error instanceof Error) {
throw new Error(`Error processing option '${keyName}': ${error.message}`);
}
throw error;
}
}
import { request } from "@umijs/max";
/**
* TTS
* @returns {Promise<OptionModel.Option[]>} OptionModel.Option Promise
* @throws {Error} 1
*/
export async function GetOptions(optionsKey: string): Promise<OptionModel.Option[]> {
let res = await request<ApiResponse.SuccessItem<OptionModel.Option[]>>(`/lms/LaitoolOptions/GetAllOptions/${optionsKey}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
console.log("GetTTSOptions", res)
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
/**
*
* @param {string} key
* @param {string} value
* @returns {Promise<void>} Promise
* @throws {Error} 1
*/
export async function SaveOptions(options: object): Promise<void> {
let data: { key: string; value: any; }[] = [];
Object.entries(options).reduce((acc, [key, value]) => {
data.push({ key: key, value: value });
return "";
}, {});
console.log("SaveOptionsDataParams", data)
let res = await request<ApiResponse.SuccessItem<boolean>>('/lms/LaitoolOptions/ModifyOptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: data
});
if (res.code != 1) {
throw new Error(res.message);
}
}

View File

@ -12,7 +12,7 @@ import { errorMessage } from './response';
*/
export async function getPromptSample(typeId: string, pageSize: number | undefined, current: number | undefined, options?: { [key: string]: any }): Promise<API.SuccessItem | API.ErrorItem> {
try {
debugger
return await request(`/api/Prompt/GetPromptString/${typeId}/${pageSize}/${current}`, {
method: 'GET',
...(options || {}),
@ -24,7 +24,7 @@ export async function getPromptSample(typeId: string, pageSize: number | undefin
export async function getPromptDetail(id: string): Promise<API.SuccessItem | API.ErrorItem> {
try {
debugger
return await request(`/api/Prompt/GetPromptDetailById/${id}`, {
method: 'GET',
});
@ -122,7 +122,7 @@ export async function addPromptType(data: Prompt.AddPromptType): Promise<API.Suc
*/
export async function editPromptType(data: Prompt.AddPromptType): Promise<API.SuccessItem | API.ErrorItem> {
try {
debugger
let res = await request('/api/Prompt/ModifyPromptType', {
method: 'POST',
data: {

View File

@ -18,6 +18,13 @@ declare namespace AccessType {
isAdminOrSuperAdmin: boolean;
//#endregion
//#region 软件配置项操作权限
/** 是不是可以操作配置型 */
canOptions : boolean;
/** 是不是可以操作软件配置项 */
canLaitoolOptions : boolean;
//#endregion
//#region 机器权限
/** 是不是显示机器码管理的菜单 */
canMachineManagement: boolean

15
src/services/typing/options/dub.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
declare namespace DubModel {
//#region EdgeTTs
/** Edge TTS 的配音角色数据 */
type EdgeTTsRole = {
value: string,
gender: string | 'Female' | "Male",
label: string,
lang: string,
}
//#endregion
}

View File

@ -0,0 +1,9 @@
declare namespace OptionModel {
type Option = {
key: string;
value: string;
type: OptionType;
}
}

13
src/store/options.ts Normal file
View File

@ -0,0 +1,13 @@
import { create } from 'zustand';
export const useOptionsStore = create((set: (arg0: any) => any) => ({
/**EdgeTTS role List */
ttsOptions: [] as OptionModel.Option[],
setTTsOptions: (value: string) => set({ ttsOptions: value }),
/** Laitool 基础配置 */
laitoolOptions: [] as OptionModel.Option[],
setLaitoolOptions: (value: string) => set({ laitoolOptions: value }),
})) as any;

View File

@ -11,4 +11,5 @@ export const useSoftStore = create((set: (arg0: any) => any) => ({
topSpinTip: "加载中...",
setTopSpinTip: (tip: string) => set({ topSpinTip: tip }),
})) as any;