Compare commits

..

10 Commits

57 changed files with 2466 additions and 769 deletions

View File

@ -41,7 +41,7 @@ export default [
path: '/userCenter',
name: 'userCenter',
icon: 'User',
component: './User/UserCenter/index',
component: './User/UserCenter/UserCenter.tsx',
},
{
name: 'prompt',
@ -59,6 +59,21 @@ export default [
name: 'prompt-management',
path: '/prompt/prompt-management',
component: './Prompt/PromptManagement/index',
access: 'canPrompt',
}
]
},
{
name: 'options',
path: '/options',
icon: 'Tool',
access: 'canOptions',
routes: [
{
name: 'laitoolOptions',
path: '/options/laitoolOptions',
component: './Options/LaitoolOptions/LaitoolOptions/index',
access: 'canLaitoolOptions',
}
]
},
@ -74,7 +89,7 @@ export default [
name: 'userManagement',
icon: 'User',
access: 'canUserManagement',
component: './User/UserManagement/index',
component: './User/UserManage/UserManagement/index',
},
{
path: '/machineManagement',
@ -83,6 +98,13 @@ export default [
access: 'canMachineManagement',
component: './Machine/MachineManagement/index',
},
{
path: '/sofrwareControlManagement',
name: 'sofrwareControlManagement',
icon: 'Reconciliation',
access: 'canSofrwareControlManagement',
component: './Software/SofrwareControl/SofrwareControlManagement',
},
{
path: '/',
redirect: '/welcome',

View File

@ -1,6 +1,6 @@
{
"name": "ant-design-pro",
"version": "6.0.0",
"name": "lms",
"version": "1.0.1",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {

View File

@ -3,8 +3,6 @@
* */
export default function access(initialState: { currentUser?: API.CurrentUser } | undefined): AccessType.AccessType {
const { currentUser } = initialState ?? {};
console.log("currentUser", currentUser);
console.log("userRole", currentUser?.roleNames);
let access = {
canPrompt: false,
@ -17,11 +15,17 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
isSuperAdmin: false,
isAdminOrSuperAdmin: false,
canOptions: false,
canLaitoolOptions: false,
canApplySoftwareControl: false,
canSofrwareControlManagement: false,
canMachineManagement: false,
canAddMachine: true,
canEditMachine: false,
canDeleteMachine: false,
canUpgradeMachine: false,
canUpgradeMachine: true,
canDisableMachine: true
} as AccessType.AccessType;
@ -45,7 +49,7 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
access = {
...access,
canUserManagement: true,
canMachineManagement: true,
canUpgradeMachine: true
}
@ -54,6 +58,7 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
if (currentUser?.roleNames?.includes("Admin")) {
access = {
...access,
canPrompt: true,
canUserManagement: true,
canEditUser: true,
@ -61,10 +66,16 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
isAdmin: true,
isAdminOrSuperAdmin: true,
canOptions: true,
canLaitoolOptions: true,
canMachineManagement: true,
canEditMachine: true,
canDeleteMachine: true,
canUpgradeMachine: true,
canApplySoftwareControl: true,
canSofrwareControlManagement: true
}
}
@ -81,11 +92,18 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
isSuperAdmin: true,
isAdminOrSuperAdmin: true,
canOptions: true,
canLaitoolOptions: true,
canMachineManagement: true,
canEditMachine: true,
canDeleteMachine: true,
canUpgradeMachine: true,
canApplySoftwareControl: true,
canSofrwareControlManagement: true
};
}
console.log("accsee", access);
return access;
}

View File

@ -6,7 +6,7 @@ import type { RunTimeLayoutConfig } from '@umijs/max';
import { history, Link, request as q } from '@umijs/max';
import defaultSettings from '../config/defaultSettings';
import { errorConfig } from './requestErrorConfig';
import { GetUserInfo, getCurrentUser as queryCurrentUser } from './services/services/user';
import { UserInfo, getCurrentUser as queryCurrentUser } from './services/services/user';
import React, { useEffect, useState } from 'react';
import { TokenStorage } from './services/define/tokenStorage';
import { App, ConfigProvider } from 'antd';
@ -46,19 +46,17 @@ export async function getInitialState(): Promise<{
};
const GetUsrInfo = async (id: number) => {
const userInfo = await GetUserInfo(id);
const userInfo = await UserInfo.GetUserInfo(id);
return userInfo;
}
// 如果不是登录页面,执行
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;
let token = localStorage.getItem('token') ?? null;
if (token == null || currentUser == null) {
console.log('没有登录重定向到登录页面123')
history.push('/user/login');
return {
fetchUserInfo,
@ -184,7 +182,6 @@ export function rootContainer(container: React.ReactNode) {
*/
export const request = {
...errorConfig,
prefix: "https://localhost:44362",
timeout: 60000,
};

View File

@ -0,0 +1,15 @@
import React from 'react';
import Icon from '@ant-design/icons'
import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon';
const DiceIcon: React.FC<AntdIconProps> = (props) => {
let ico = <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" width="1.5em"
height="1.5em" ><path d="M440.88 129.37L288.16 40.62a64.14 64.14 0 0 0-64.33 0L71.12 129.37a4 4 0 0 0 0 6.9L254 243.85a4 4 0 0 0 4.06 0L440.9 136.27a4 4 0 0 0-.02-6.9zM256 152c-13.25 0-24-7.16-24-16s10.75-16 24-16s24 7.16 24 16s-10.75 16-24 16z" fill="currentColor"></path><path d="M238 270.81L54 163.48a4 4 0 0 0-6 3.46v173.92a48 48 0 0 0 23.84 41.39L234 479.48a4 4 0 0 0 6-3.46V274.27a4 4 0 0 0-2-3.46zM96 368c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm96-32c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24z" fill="currentColor"></path><path d="M458 163.51L274 271.56a4 4 0 0 0-2 3.45V476a4 4 0 0 0 6 3.46l162.15-97.23A48 48 0 0 0 464 340.86V167a4 4 0 0 0-6-3.49zM320 424c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm96 32c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24zm0-88c-8.84 0-16-10.75-16-24s7.16-24 16-24s16 10.75 16 24s-7.16 24-16 24z" fill="currentColor"></path></svg>;
return (
<Icon {...props} component={() => (
ico
)} />
);
};
export default DiceIcon;

View File

@ -49,7 +49,6 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, childre
/** 此方法会跳转到 redirect 参数所在的位置 */
const redirect = urlParams.get('redirect');
// Note: There may be security issues, please note
console.log('没有登录重定向到登录页面12333333333333333')
if (window.location.pathname !== '/user/login' && !redirect) {
history.replace({
pathname: '/user/login',

View File

@ -15,7 +15,7 @@ const clearCache = () => {
caches.delete(key);
});
})
.catch((e) => console.log(e));
.catch((e) => console.error(e));
}
};

View File

@ -7,11 +7,15 @@ export default {
'menu.prompt.prompt-type': '提示词类型',
'menu.prompt.prompt-management': '提示词管理',
'menu.options': '配置管理',
'menu.options.laitoolOptions': 'Laitool配置',
'menu.roleManagement': '角色管理',
'menu.userManagement': '用户管理',
'menu.machineManagement': '机器码管理',
'menu.sofrwareControlManagement': '软件控制管理',
'menu.more-blocks': '更多区块',
'menu.home': '首页',

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { Form, Input, Button, FormInstance, Spin, message, Select, DatePicker, InputNumber } from 'antd';
import moment from 'moment';
import { AddMachineData } from '@/services/services/machine';
import { useModel } from '@umijs/max';
import { useAccess, useModel } from '@umijs/max';
interface AddMachineModalProps {
setFormRef: (form: FormInstance) => void;
@ -13,6 +13,7 @@ const AddMachineForm: React.FC<AddMachineModalProps> = ({ setFormRef }) => {
const [loading, setLoading] = useState<boolean>(false);
const [messageApi, messageHolder] = message.useMessage();
const { initialState } = useModel('@@initialState');
const access = useAccess();
useEffect(() => {
setFormRef(form);
@ -28,9 +29,8 @@ const AddMachineForm: React.FC<AddMachineModalProps> = ({ setFormRef }) => {
}, [form, setFormRef]);
const onFinish = async (values: MachineModel.AddMachineParams) => {
if (values.useStatus == 0 && !values.deactivationTime) {
messageApi.error("试用机器码需要设置停用时间")
return;
if (values.userId == null) {
messageApi.error("请填写所属用户ID");
}
setLoading(true);
try {
@ -60,48 +60,12 @@ const AddMachineForm: React.FC<AddMachineModalProps> = ({ setFormRef }) => {
>
<Input />
</Form.Item>
<Form.Item<MachineModel.AddMachineParams>
label="使用状态"
name="useStatus"
rules={[{ required: true, message: 'Please input the role name!' }]}
>
<Select onChange={(value) => {
if (value == 1) {
form.setFieldsValue({ deactivationTime: null })
} else {
const currentDate = new Date();
const nextDayDate = new Date(currentDate);
nextDayDate.setDate(currentDate.getDate() + 1);
form.setFieldsValue({ deactivationTime: moment(nextDayDate) })
}
}}>
<Select.Option value={0}></Select.Option>
<Select.Option value={1}></Select.Option>
</Select>
</Form.Item>
<Form.Item<MachineModel.AddMachineParams>
label="状态"
name="status"
rules={[{ required: true, message: 'Please input the role name!' }]}
>
<Select >
<Select.Option value={0}></Select.Option>
<Select.Option value={1}></Select.Option>
</Select>
</Form.Item>
<Form.Item<MachineModel.AddMachineParams>
label="停用时间"
name="deactivationTime"
>
<DatePicker showTime />
</Form.Item>
<Form.Item<MachineModel.AddMachineParams>
label="所属用户ID"
name="userId"
rules={[{ required: true, message: 'Please input the role name!' }]}
>
<InputNumber style={{ width: 200 }} keyboard={false} min={0} changeOnWheel={false} controls={false} />
<InputNumber disabled={!access.isAdminOrSuperAdmin} style={{ width: 200 }} keyboard={false} min={0} changeOnWheel={false} controls={false} />
</Form.Item>
<Form.Item<MachineModel.AddMachineParams>
label="备注"

View File

@ -4,13 +4,13 @@ import TemplateContainer from "@/pages/TemplateContainer";
import { DeactivationMachine, MachinePermanent, QueryMachineList } from "@/services/services/machine";
import { FormatDate } from "@/util/time";
import { useAccess, useModel } from "@umijs/max";
import { Button, Form, Input, message, Modal, Select, SelectProps, Spin, Table, Tag } from "antd";
import { Button, Dropdown, Form, Input, Menu, message, Modal, Select, SelectProps, Spin, Table, Tag } from "antd";
import { ColumnsType, TablePaginationConfig } from "antd/es/table";
import { FilterValue, SorterResult, TableCurrentDataSource } from "antd/es/table/interface";
import { delay, set } from "lodash";
import { useEffect, useState } from "react";
import ModifyMachine from "../ModifyMachine";
import { PlusOutlined } from "@ant-design/icons";
import { DownOutlined, EditOutlined, MenuOutlined, MoreOutlined, PlusOutlined, SafetyCertificateOutlined, StopOutlined } from "@ant-design/icons";
import AddMachineForm from "../AddMachineForm";
const MachineManagement: React.FC = () => {
@ -35,6 +35,7 @@ const MachineManagement: React.FC = () => {
const [openAddModal, setOpenAddModal] = useState<boolean>(false);
const [spinning, setSpinning] = useState<boolean>(false);
const [spinTip, setSpinTip] = useState<string>('');
const [modal, modalHolder] = Modal.useModal();
useEffect(() => {
QueryMachineList(tableParams, form.getFieldsValue())
@ -57,12 +58,23 @@ const MachineManagement: React.FC = () => {
}, []);
async function SetMachinePermanent(id: string): Promise<void> {
setSpinning(true);
setSpinTip('正在设置为永久。。。');
try {
//
let cofirmRes = await modal.confirm({
title: '激活提示',
content: '即将同步软件控制权限信息至绑定机器码,会消耗一次授权次数,是否继续?',
okText: '确定',
cancelText: '取消',
});
if (!cofirmRes) {
messageApi.warning("取消操作");
return;
}
setSpinning(true);
setSpinTip('正在激活/同步。。。');
await MachinePermanent(id);
messageApi.success('设置为永久成功');
messageApi.success('激活同步信息成功');
setSpinning(false);
// 重新加载数据
await QueryMachineBasic(form.getFieldsValue(), tableParams.pagination);
@ -166,16 +178,14 @@ const MachineManagement: React.FC = () => {
render: (text) => FormatDate(text),
width: '160px',
},
{
title: '使用状态',
dataIndex: 'useStatus',
render: (text, record) => <Tag color={record.useStatus === 1 ? 'green' : 'red'}>{record.useStatus === 1 ? '永久' : '试用'}</Tag>,
width: '100px',
},
{
title: '状态',
dataIndex: 'status',
render: (text, record) => <Tag color={record.status === 1 ? 'blue' : 'red'}>{record.status === 1 ? '激活' : '冻结'}</Tag>,
render: (text, record) => (
<Tag color={record.status === 1 ? 'blue' : 'red'}>
{record.status === 1 ? '激活' : '冻结'}
</Tag>
),
width: '100px',
},
{
@ -190,17 +200,45 @@ const MachineManagement: React.FC = () => {
},
{
title: '操作',
width: '200px',
render: (text, record) => (
<div>
<Button hidden={!access.canEditMachine} style={{ marginRight: 5 }} type="primary" size="small"
onClick={() => { setOpenModal(true); setFormRef(form); setId(record.id) }}></Button>
<Button hidden={!access.canUpgradeMachine} type="primary" style={{ marginRight: 5 }} size="small"
onClick={async () => await SetMachinePermanent(record.id)}></Button>
<Button hidden={!access.canDisableMachine} type="primary" danger size="small"
onClick={async () => await ChangeDeactivationMachine(record.id)}></Button>
</div>
),
width: 100,
render: (text, record) => {
const menuItems = [
{
key: "edit",
icon: <EditOutlined />,
hidden: !access.isAdminOrSuperAdmin,
label: "编辑",
onClick: () => {
setOpenModal(true);
setFormRef(form);
setId(record.id);
},
},
{
key: "permanent",
icon: <SafetyCertificateOutlined />,
hidden: !access.canUpgradeMachine,
style: {
color: '#4caaff'
},
label: "激活",
onClick: async () => await SetMachinePermanent(record.id),
},
{
key: "disable",
icon: <StopOutlined />,
hidden: !access.canDisableMachine,
danger: true,
label: "停用",
onClick: async () => await ChangeDeactivationMachine(record.id),
},
].filter(Boolean);
return (
<Dropdown menu={{ items: menuItems }} trigger={["hover"]}>
<Button type="text" color="primary" variant="filled" icon={<MenuOutlined />} />
</Dropdown>
);
},
},
];
@ -268,6 +306,7 @@ const MachineManagement: React.FC = () => {
<AddMachineForm setFormRef={setFormRef} />
</Modal>
{messageHolder}
{modalHolder}
</TemplateContainer>
);
};

View File

@ -66,7 +66,6 @@ const ModifyMachine: React.FC<ModifyMachineProps> = ({ id, setFormRef, open }) =
};
const onOk = (value: DatePickerProps['value'] | RangePickerProps['value']) => {
console.log('onOk: ', value);
};
return (
@ -99,7 +98,7 @@ const ModifyMachine: React.FC<ModifyMachineProps> = ({ id, setFormRef, open }) =
label="机器码"
name="machineId"
>
<Input disabled={initialState?.currentUser?.roleNames?.includes("Admin") || initialState?.currentUser?.roleNames.includes("Super Admin")} />
<Input disabled={!(initialState?.currentUser?.roleNames?.includes("Admin") || initialState?.currentUser?.roleNames.includes("Super Admin"))} />
</Form.Item>
</Col>
@ -109,7 +108,7 @@ const ModifyMachine: React.FC<ModifyMachineProps> = ({ id, setFormRef, open }) =
name="useStatus"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Select>
<Select disabled>
<Select.Option value={0}></Select.Option>
<Select.Option value={1}></Select.Option>
</Select>
@ -121,7 +120,7 @@ const ModifyMachine: React.FC<ModifyMachineProps> = ({ id, setFormRef, open }) =
name="status"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Select>
<Select disabled>
<Select.Option value={0}></Select.Option>
<Select.Option value={1}></Select.Option>
</Select>
@ -143,10 +142,8 @@ const ModifyMachine: React.FC<ModifyMachineProps> = ({ id, setFormRef, open }) =
name="deactivationTime"
>
<DatePicker
showTime
showTime disabled
onChange={(value, dateString) => {
console.log('Selected Time: ', value);
console.log('Formatted Selected Time: ', dateString);
}}
onOk={onOk}
/>

View File

@ -0,0 +1,98 @@
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 SimpleOptions: 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(() => {
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>
<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>
{messageHolder}
</div>
);
};
export default SimpleOptions;

View File

@ -0,0 +1,78 @@
import React, { useEffect } from 'react';
import { GetOptions, getOptionsStringValue, SaveOptions } from '@/services/services/options/optionsTool';
import { Button, Col, Form, Input, InputNumber, message, Row, Space } from 'antd';
import { useSoftStore } from '@/store/software';
type TrailOptionsProps = {
LaiToolTrialDays: number;
}
const TrailOptions: React.FC = () => {
const [form] = Form.useForm();
const { setTopSpinning, setTopSpinTip } = useSoftStore();
const [messageApi, messageHolder] = message.useMessage();
useEffect(() => {
setTopSpinning(true);
setTopSpinTip("加载信息中");
GetOptions("trial").then((res) => {
form.setFieldsValue({
LaiToolTrialDays: getOptionsStringValue(res, 'LaiToolTrialDays', ""),
});
}).catch((err: any) => {
messageApi.error(err.message);
}).finally(() => {
setTopSpinning(false);
});
}, []);
const formStyle: React.CSSProperties = {
maxWidth: 'none',
padding: 24,
};
const onFinish = async (values: TrailOptionsProps) => {
setTopSpinning(true);
setTopSpinTip("正在保存试用设置");
try {
await SaveOptions(values);
messageApi.success('保存软件试用设置成功');
} catch (error: any) {
messageApi.error(error.message);
} finally {
setTopSpinning(false);
}
};
return (
<div>
<Form form={form} layout="vertical" name="advanced_search" style={formStyle} onFinish={onFinish}>
<Row gutter={24} >
<Col span={8} >
<Form.Item
label="软件最大试用天数"
name="LaiToolTrialDays"
rules={[{ required: true, message: '请输入软件最大试用天数' }]}
>
<InputNumber min={1} max={10} />
</Form.Item>
</Col>
<Col span={8} >
</Col>
<Col span={8} >
</Col>
</Row>
<Form.Item>
<Button color="primary" variant="filled" htmlType="submit"></Button>
</Form.Item>
</Form>
{messageHolder}
</div>
);
};
export default TrailOptions;

View File

@ -0,0 +1,29 @@
import { Card, Collapse, CollapseProps, Form } from 'antd';
import React, { useEffect } from 'react';
import SimpleOptions from './SimpleOptions';
import TrailOptions from './TrialOptions';
const DubSetting: React.FC = () => {
const onChange = (key: string | string[]) => {
};
const items: CollapseProps['items'] = [
{
key: 'simpleOptions',
label: '通用配置',
children: <SimpleOptions></SimpleOptions>,
}, {
key: 'trailOptions',
label: '试用设置',
children: <TrailOptions />,
}
];
return (
<Collapse items={items} bordered={false} ghost onChange={onChange} />
);
};
export default DubSetting;

View File

@ -0,0 +1,62 @@
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> {
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(() => {
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,49 @@
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[]) => {
};
useEffect(() => {
setTopSpinning(true);
setTopSpinTip("加载信息中");
// 这边加载所有的配音数据
GetOptions('tts').then((res) => {
setTTsOptions(res);
}
).catch((err) => {
console.error(err);
}).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

@ -1,153 +0,0 @@
import { addPrompt, getPromptDetail, modifyPrompt } from '@/services/services/prompt';
import { Button, Col, Form, FormProps, Input, InputNumber, message, Row, Select, Space, Switch } from 'antd';
import React from 'react';
import react, { useEffect, useState } from 'react';
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
interface PromptManagementProps {
type: string; // 接收的类型
promptType: Prompt.PromptTypeListItem[] | undefined; // 提示词类型
id: string | undefined; // 提示词id
}
const PromptManagement: React.FC<PromptManagementProps> = ({ type, promptType, id }) => {
const [form] = Form.useForm();
const [promptTypeOptions, setPromptTypeOptions] = useState<{ label: string, value: string }[]>([]);
const [data, setData] = useState<Prompt.AddPrompt>();
// 使用 useEffect 设置表单初始值
useEffect(() => {
form.resetFields();
if (type == 'edit') {
// 在编辑的时候,初始化数据
getPromptDetail(id ?? "").then((res: API.SuccessItem | API.ErrorItem) => {
if (res.code === 1) {
const fetchedData = res.data;
setData(fetchedData);
form.setFieldsValue({
...fetchedData,
status: fetchedData.status === 'enable',
});
}
});
} else {
form.resetFields();
}
let ops: react.SetStateAction<{ label: string; value: string; }[]> = []
promptType?.forEach(item => {
ops.push({ label: item.name, value: item.id })
})
setPromptTypeOptions(ops)
}, [type, id, form, promptType]);
const modifyCode = (value: string) => {
let code = promptType?.find(item => item.id == value)?.code
form.setFieldsValue({ promptTypeCode: code })
}
const onFinish: FormProps<Prompt.AddPrompt>['onFinish'] = async (values) => {
values.status = values.status ? "enable" : "disable";
if (type == "add") {
// 添加
let addRes = await addPrompt(values)
if (addRes.code != 1) {
message.error("添加失败," + addRes.message);
return
}
message.success("添加成功");
} else {
// 修改
let editRes = await modifyPrompt({ ...values, id: data?.id });
if (editRes.code != 1) {
message.error("修改失败," + editRes.message);
return
}
message.success("修改成功");
}
};
const onFinishFailed: FormProps<Prompt.AddPrompt>['onFinishFailed'] = (errorInfo) => {
console.log('Failed:', errorInfo);
};
return (<>
<Form
form={form}
preserve={false}
{...formItemLayout}
labelAlign="right"
variant="filled"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
initialValues={data}>
<Row>
<Col flex="auto">
<Form.Item<Prompt.AddPrompt> label="名称" name="name" rules={[{ required: true },]}>
<Input placeholder="请输入提示词名称" />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="类型" name="promptTypeId" rules={[{ required: true }]} >
<Select options={promptTypeOptions} allowClear onChange={modifyCode} >
</Select>
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="备注" name="remark">
<Input placeholder="请输入提示词备注" />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="编码" name="promptTypeCode">
<Input placeholder="请输入提示词类型编码" disabled={true} />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="创建者" >
<Input placeholder="请输入提示词创建者" disabled={true} value={data?.createUser?.nickname} />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="修改者">
<Input placeholder="请输入提示词修改者" disabled={true} value={data?.updateUser?.nickname} />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="修改时间" name="updateTime">
<Input placeholder="请输入提示词修改时间" disabled={true} />
</Form.Item>
</Col>
<Col flex="auto" style={{ marginLeft: "20px" }}>
<Form.Item<Prompt.AddPrompt> label="描述" name="description">
<Input placeholder="请输入提示词描述" />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="版本" name="version">
<InputNumber style={{ width: "100%" }} placeholder="请输入提示词版本" />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="状态" name="status">
<Switch checkedChildren="启用" unCheckedChildren="停用" defaultChecked />
</Form.Item>
<Form.Item<Prompt.AddPrompt> label="提示词设定" name="promptString" rules={[{ required: true }]}>
<Input.TextArea autoSize={
{ minRows: 6, maxRows: 6 }
} placeholder="请输入提示词设定" />
</Form.Item>
</Col>
</Row>
<Form.Item style={{ display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }} >
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form >
</>)
}
export default PromptManagement;

View File

@ -1,112 +0,0 @@
import { addPromptType, editPromptType } from '@/services/services/prompt';
import { Button, Col, Form, FormProps, Input, InputNumber, message, Row, Select, Space, Switch } from 'antd';
import React from 'react';
import react, { useEffect, useState } from 'react';
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
interface PromptManagementProps {
type: string; // Replace 'string' with the actual type of the 'type' prop
data?: Prompt.AddPromptType; // 初始化的提示词数据
}
const PromptManagement: React.FC<PromptManagementProps> = ({ type, data }) => {
const [form] = Form.useForm();
// 使用 useEffect 设置表单初始值
useEffect(() => {
form.resetFields();
if (type === 'edit' && data) {
form.setFieldsValue(data);
} else {
}
data?.status == "enable" ? form.setFieldsValue({ status: true }) : form.setFieldsValue({ status: false });
}, [type, data, form]);
const onFinish: FormProps<Prompt.AddPromptType>['onFinish'] = async (values) => {
// 处理values
values.status = values.status ? "enable" : "disable";
if (type == "add") {
let addRes = await addPromptType(values);
if (addRes.code != 1) {
message.error("添加失败," + addRes.message);
return
}
message.success("添加成功");
} else {
let editRes = await editPromptType({ ...values, id: data?.id });
if (editRes.code != 1) {
message.error("修改失败," + editRes.message);
return
}
message.success("修改成功");
}
};
const onFinishFailed: FormProps<Prompt.AddPromptType>['onFinishFailed'] = (errorInfo) => {
console.log('Failed:', errorInfo);
};
return (<>
<Form
preserve={false}
form={form}
{...formItemLayout}
labelAlign="right"
variant="filled"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
clearOnDestroy={true}
initialValues={data}>
<Row>
<Col flex="auto">
<Form.Item<Prompt.AddPromptType> label="名称" name="name" rules={[{ required: true }]}>
<Input placeholder="请输入提示词类型名称" />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="编码" name="code" rules={[{ required: true }]}>
<Input placeholder="请输入提示词描述" />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="创建者" >
<Input disabled={true} placeholder="请输入提示词创建者" value={data?.createUser?.nickname} />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="修改者" >
<Input disabled={true} placeholder="请输入提示词修改者" value={data?.updateUser?.nickname} />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="修改时间" name="updateTime">
<Input disabled={true} placeholder="请输入提示词修改时间" />
</Form.Item>
</Col>
<Col flex="auto" style={{ marginLeft: "20px" }}>
<Form.Item<Prompt.AddPromptType> label="状态" name="status">
<Switch disabled={type === "add"} checkedChildren="启用" unCheckedChildren="停用" defaultChecked />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="提示词设定" name="remark">
<Input.TextArea autoSize={
{ minRows: 6, maxRows: 6 }
} placeholder="请输入提示词设定" />
</Form.Item>
</Col>
</Row>
<Form.Item style={{ display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }} >
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form >
</>)
}
export default PromptManagement;

View File

@ -0,0 +1,162 @@
import { AddPrompt, GetPromptInfo, ModifyPrompt } from '@/services/services/prompt';
import { Button, Col, Form, FormInstance, FormProps, Input, InputNumber, message, Row, Select, Space, Spin, Switch } from 'antd';
import React, { version } from 'react';
import react, { useEffect, useState } from 'react';
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
interface PromptManagementProps {
type: string; // 接收的类型
promptTypeOptions: Prompt.PromptTypeOptions[] | undefined; // 提示词类型
setFormRef: (form: FormInstance) => void;
id: string | undefined; // 提示词id
open: boolean; // 是否打开
}
const PromptManagement: React.FC<PromptManagementProps> = ({ type, promptTypeOptions, setFormRef, id, open }) => {
const [form] = Form.useForm();
const [data, setData] = useState<Prompt.PromptItem>();
const [spinning, setSpinning] = useState<boolean>(true);
const [spinTip, setSpinTip] = useState<string>('加载中数据中。。。');
const [messageApi, messageHolder] = message.useMessage();
useEffect(() => {
setFormRef(form);
setData(undefined);
}, [form, setFormRef]);
// 使用 useEffect 设置表单初始值
useEffect(() => {
setSpinning(true);
setSpinTip('加载中数据中。。。');
if (type == 'edit') {
// 在编辑的时候,初始化数据
if (id !== undefined && open) {
GetPromptInfo(id).then((res: Prompt.PromptItem) => {
setData(res);
form.setFieldsValue(res);
setSpinning(false)
}).catch((error: any) => {
messageApi.error(error.message);
}).finally(() => {
setSpinning(false);
});
}
data?.status == "enable" ? form.setFieldsValue({ status: true }) : form.setFieldsValue({ status: false });
} else {
setSpinning(false);
form.resetFields();
form.setFieldsValue({ status: true, version: 1 });
}
}, [type, id, form, promptTypeOptions, open]);
const onFinish: FormProps<Prompt.PromptItem>['onFinish'] = async (values) => {
setSpinning(true);
setSpinTip("正在修改数据。。。");
try {
values.status = values.status ? "enable" : "disable";
if (type == "add") {
// 添加
let promptId = await AddPrompt(values)
setData({ ...values, id: promptId });
messageApi.success("添加提示词成功");
} else {
// 修改
if (id == undefined) {
messageApi.error("未知提示词ID");
} else {
await ModifyPrompt(id, values);
messageApi.success("修改提示词数据成功");
}
}
} catch (error: any) {
messageApi.error(error.message);
} finally {
setSpinning(false);
}
};
return (<>
<Spin spinning={spinning} tip={spinTip}>
{messageHolder}
<Form
form={form}
preserve={false}
{...formItemLayout}
labelAlign="right"
variant="filled"
onFinish={onFinish}
initialValues={data}>
<Row>
<Col flex="auto">
<Form.Item<Prompt.PromptItem> label="名称" name="name" rules={[{ required: true },]}>
<Input placeholder="请输入提示词名称" />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="类型" name="promptTypeId" rules={[{ required: true }]} >
<Select options={promptTypeOptions?.map(item => {
return {
label: item.name,
value: item.id
}
})} allowClear >
</Select>
</Form.Item>
<Form.Item<Prompt.PromptItem> label="备注" name="remark">
<Input placeholder="请输入提示词备注" />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="创建者" >
<Input placeholder="请输入提示词创建者" disabled={true} value={data?.createdUser?.nickName} />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="修改者" >
<Input placeholder="请输入提示词修改者" disabled={true} value={data?.updatedUser?.nickName} />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="修改时间" name="updateTime">
<Input placeholder="请输入提示词修改时间" disabled={true} />
</Form.Item>
</Col>
<Col flex="auto" style={{ marginLeft: "20px" }}>
<Form.Item<Prompt.PromptItem> label="描述" name="description">
<Input placeholder="请输入提示词描述" />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="版本" name="version">
<InputNumber style={{ width: "100%" }} placeholder="请输入提示词版本" />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="状态" name="status">
<Switch checkedChildren="启用" unCheckedChildren="停用" defaultChecked />
</Form.Item>
<Form.Item<Prompt.PromptItem> label="提示词设定" name="promptString" rules={[{ required: true }]}>
<Input.TextArea autoSize={
{ minRows: 6, maxRows: 6 }
} placeholder="请输入提示词设定" />
</Form.Item>
</Col>
</Row>
<Form.Item style={{ display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }} >
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form >
</Spin>
</>)
}
export default PromptManagement;

View File

@ -1,157 +1,199 @@
import { PageContainer } from '@ant-design/pro-components';
import { useModel } from '@umijs/max';
import { Card, Form, GetProp, Input, message, Modal, Table, TablePaginationConfig, TableProps, theme } from 'antd';
import { Card, Form, GetProp, Input, message, Modal, Select, Table, TablePaginationConfig, TableProps, theme, Tooltip } from 'antd';
import React, { useEffect, useState } from 'react';
import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProTable, TableDropdown } from '@ant-design/pro-components';
import { Button, Dropdown, Space, Tag } from 'antd';
import { useRef } from 'react';
import { ColumnsType, SorterResult } from 'antd/es/table/interface';
import qs from 'qs';
import { getPromptSample, getPrompyType } from '@/services/services/prompt';
import ManagePrompt from '../ManagePrompt/index';
export const waitTimePromise = async (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
export const waitTime = async (time: number = 100) => {
await waitTimePromise(time);
};
interface TableParams {
pagination?: TablePaginationConfig;
sortField?: SorterResult<any>['field'];
sortOrder?: SorterResult<any>['order'];
filters?: Parameters<GetProp<TableProps, 'onChange'>>[1];
}
interface TableParams {
pagination?: TablePaginationConfig;
sortField?: SorterResult<any>['field'];
sortOrder?: SorterResult<any>['order'];
filters?: Parameters<GetProp<TableProps, 'onChange'>>[1];
}
const getRandomuserParams = (params: TableParams) => ({
results: params.pagination?.pageSize,
page: params.pagination?.current,
...params,
});
import { DeletePrompt, GetPromptTypeOptions, QueryPromptCollection, QueryPromptypeCollection } from '@/services/services/prompt';
import ManagePrompt from './ManagePrompt/index';
import { useFormReset } from '@/hooks/useFormReset';
import TextArea from 'antd/es/input/TextArea';
import { useSoftStore } from '@/store/software';
import TemplateContainer from '@/pages/TemplateContainer';
import { PlusOutlined } from '@ant-design/icons';
const PromptManagement: React.FC = () => {
const { token } = theme.useToken();
const { initialState } = useModel('@@initialState');
const [data, setData] = useState<[Prompt.PromptListItem][]>();
const [data, setData] = useState<Prompt.PromptItem[]>();
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const [open, setOpen] = React.useState<boolean>(false);
const [type, setType] = useState<string>("add");
const [editData, setEditData] = useState<Prompt.AddPrompt>();
const [promptType, setPromptType] = useState<Prompt.PromptTypeListItem[]>();
const [formKey, setFormKey] = useState(Date.now().toString());
const [promptId, setPromptId] = useState<string>();
const [promptTypeOptions, setPromptTypeOptions] = useState<Prompt.PromptTypeOptions[]>();
const [messageApi, messageHolder] = message.useMessage();
const { setFormRef, resetForm } = useFormReset();
const [modal, modalHolder] = Modal.useModal();
const { setTopSpinTip, setTopSpinning } = useSoftStore();
const [tableParams, setTableParams] = useState<TableParams>({
const [tableParams, setTableParams] = useState<TableModel.TableParams>({
pagination: {
current: 1,
pageSize: 10,
showQuickJumper: true,
totalBoundaryShowSizeChanger: true,
},
});
const columns: ColumnsType<Prompt.PromptListItem> = [
const columns: ColumnsType<Prompt.PromptItem> = [
{
title: '名称',
dataIndex: 'name',
sorter: true,
width: '120px',
width: '220px',
},
{
title: 'Gender',
dataIndex: 'gender',
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
width: '200',
title: '提示词类型',
dataIndex: 'promptType',
width: '200px',
render: (_, record) => {
return record.promptType?.name;
},
},
{
title: 'Email',
dataIndex: 'email',
title: '状态',
dataIndex: 'status',
width: '70px',
render: (dom, en) => <>
<Tag color={en.status == "enable" ? "green" : "red"}> {en.status == "enable" ? '启用' : "停用"} </Tag>
</>
},
{
title: '提示词设定',
dataIndex: 'promptString',
ellipsis: {
showTitle: false,
},
render: (promptString) => (
<span style={{ cursor: "pointer" }} onClick={() => showPrompt(promptString)}>
{promptString}
</span>
),
},
{
title: '描述',
dataIndex: 'description',
width: '200px',
ellipsis: {
showTitle: false,
},
render: (remark) => (
<Tooltip placement="topLeft" title={remark}>
{remark}
</Tooltip>
),
},
{
title: '备注',
dataIndex: 'remark',
width: '200px',
ellipsis: {
showTitle: false,
},
render: (remark) => (
<Tooltip placement="topLeft" title={remark}>
{remark}
</Tooltip>
),
},
{
title: "操作",
dataIndex: 'option',
width: '200px',
render: (_, record) => <>
<Button size='middle' style={{ marginRight: "5px" }} type="primary" onClick={() => {
debugger
setEditData(record)
setType("edit")
setOpen(true)
}}></Button>,
<Button size='middle' type="primary" danger></Button>,
setPromptId(record.id)
}}></Button>
<Button size='middle' type="primary" danger onClick={async () => await DeletePromptHandle(record.id)}></Button>
</>
}
];
async function DeletePromptHandle(id: string) {
try {
if (id == null) {
messageApi.error("未知提示词ID");
}
let confirmed = await modal.confirm({
title: "删除提示词",
content: "确定删除提示词吗?",
okText: "确认",
cancelText: "取消"
});
if (confirmed) {
setTopSpinning(true);
setTopSpinTip("正在删除数据。。。")
await DeletePrompt(id);
messageApi.success("删除提示词成功");
setTopSpinning(false);
await fetchData();
} else {
messageApi.info("取消删除操作");
}
} catch (error: any) {
messageApi.error(error.message);
} finally {
setTopSpinning(false);
}
}
function showPrompt(content: string) {
modal.info({
width: 800,
title: '',
footer: null,
icon: null,
closable: true,
closeIcon: true,
content: (
<TextArea style={{ marginTop: "10px" }} defaultValue={content} autoSize>
</TextArea >)
})
}
const fetchData = async () => {
debugger
setLoading(true);
try {
let param: Prompt.PromptQueryCondition = {
...form.getFieldsValue(),
page: tableParams.pagination?.current,
pageSize: tableParams.pagination?.pageSize
}
let promptRes = await QueryPromptCollection(param);
let promptRes = await getPromptSample("all", tableParams.pagination?.pageSize, tableParams.pagination?.current)
if (promptRes.code == 1) {
message.success("获取提示词设置成功")
setData(promptRes.data)
setLoading(false);
setData(promptRes.collection);
setTableParams({
...tableParams,
pagination: {
...tableParams.pagination,
total: promptRes.data.count,
// 200 is mock data, you should read it from server
// total: data.totalCount,
total: promptRes.total,
},
});
} else {
message.success("获取提示词成功")
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
message.error("获取提示词设置失败")
}
};
}
useEffect(() => {
fetchData();
// 加载提示词类型
getPrompyType(100, 1).then(res => {
if (res.code == 1) {
setPromptType(res.data)
} else {
message.error("获取提示词类型失败")
}
GetPromptTypeOptions().then((res: Prompt.PromptTypeOptions[]) => {
setPromptTypeOptions(res);
}).catch((error: any) => {
messageApi.error(error.message);
});
}, [
tableParams.pagination?.current,
tableParams.pagination?.pageSize,
tableParams?.sortOrder,
tableParams?.sortField,
JSON.stringify(tableParams.filters),
]);
}, [tableParams.pagination?.current,
tableParams.pagination?.pageSize]);
const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter) => {
const handleTableChange = (pagination: any) => {
setTableParams({
pagination,
filters,
sortOrder: Array.isArray(sorter) ? undefined : sorter.order,
sortField: Array.isArray(sorter) ? undefined : sorter.field,
});
// `dataSource` is useless since `pageSize` changed
@ -161,7 +203,7 @@ const PromptManagement: React.FC = () => {
};
return (
<PageContainer>
<TemplateContainer navTheme={initialState?.settings?.navTheme ?? "realDark"}>
<Card
style={{
borderRadius: 8,
@ -180,32 +222,50 @@ const PromptManagement: React.FC = () => {
<Form
layout='inline'
form={form}
onFinish={fetchData}
>
<Form.Item label="名称">
<Form.Item label="名称" name="name">
<Input placeholder="请输入查询提示词的名称" />
</Form.Item>
<Form.Item label="提示词类型" name="promptTypeId">
<Select allowClear style={{ width: 200 }} options={promptTypeOptions?.map(item => {
return {
label: item.name,
value: item.id
}
})} placeholder="请选择提示词类型" />
</Form.Item>
<Form.Item label="状态" name="status" >
<Select style={{ width: 200 }} placeholder="请选择提示词状态" allowClear >
<Select.Option value="enable"></Select.Option>
<Select.Option value="disable"></Select.Option>
</Select>
</Form.Item>
<Form.Item label="备注" name="remark">
<Input placeholder="请输入提示词备注" />
</Form.Item>
<Form.Item >
<Button type="primary"></Button>
<Button type="default" onClick={async () => {
form.resetFields();
await fetchData();
}}></Button>
</Form.Item>
<Form.Item >
<Button type="default"></Button>
<Button type="primary" htmlType='submit'></Button>
</Form.Item>
<Form.Item style={{ display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }}>
<div >
<Button type="primary" style={{ marginBottom: 10 }} onClick={() => {
<Button icon={<PlusOutlined />} type="primary" style={{ marginBottom: 10 }} onClick={() => {
setOpen(true)
setType("add")
setFormKey(Date.now().toString()); // 每次打开 Modal 时更新 formKey强制子组件重新渲染
}}>
</Button>
</div>
</Form.Item>
</Form>
</div>
<Table
columns={columns}
@ -223,7 +283,7 @@ const PromptManagement: React.FC = () => {
onCancel={async () => {
setOpen(false)
await fetchData()
setFormKey(Date.now().toString()); // 每次打开 Modal 时更新 formKey强制子组件重新渲染
resetForm(); // 每次打开 Modal 时更新 formKey强制子组件重新渲染
}}
width={800}
footer={null}
@ -231,9 +291,11 @@ const PromptManagement: React.FC = () => {
forceRender={true}
destroyOnClose={true}
>
<ManagePrompt key={formKey} type={type} id={editData?.id} promptType={promptType} />
<ManagePrompt setFormRef={setFormRef} type={type} id={promptId} promptTypeOptions={promptTypeOptions} open={open} />
</Modal>
</PageContainer>
{messageHolder}
{modalHolder}
</TemplateContainer>
);
};

View File

@ -0,0 +1,141 @@
import { AddPromptType, EditPromptType, GetPromptTypeInfo } from '@/services/services/prompt';
import createSoftStore, { useSoftStore } from '@/store/software';
import { Button, Col, Form, FormInstance, FormProps, Input, InputNumber, message, Row, Select, Space, Spin, Switch } from 'antd';
import React from 'react';
import react, { useEffect, useState } from 'react';
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
interface PromptManagementProps {
type: string; // Replace 'string' with the actual type of the 'type' prop
setFormRef: (form: FormInstance) => void;
id: string | undefined;
open: boolean;
}
const PromptManagement: React.FC<PromptManagementProps> = ({ type, setFormRef, id, open }) => {
const [form] = Form.useForm();
const [data, setData] = useState<Prompt.PromptTypeItem>();
const [messageApi, messageHolder] = message.useMessage();
const [spinning, setSpinning] = useState<boolean>(true);
const [spinTip, setSpinTip] = useState<string>('加载中数据中。。。');
useEffect(() => {
setFormRef(form);
setData(undefined);
}, [form, setFormRef]);
// 使用 useEffect 设置表单初始值
useEffect(() => {
if (type === 'edit') {
setSpinning(true);
// 远程加载数据
if (id !== undefined && open) {
GetPromptTypeInfo(id).then((res) => {
setData(res);
form.setFieldsValue(res);
messageApi.success('数据加载成功');
}).catch((error: any) => {
messageApi.error(error.message);
}).finally(() => {
setSpinning(false);
});
}
} else {
setSpinning(false);
}
data?.status == "enable" ? form.setFieldsValue({ status: true }) : form.setFieldsValue({ status: false });
}, [type, form, open, setFormRef, id]);
const onFinish: FormProps<Prompt.AddPromptType>['onFinish'] = async (values) => {
try {
setSpinning(true);
setSpinTip("正在修改数据。。。");
// 处理values
values.status = values.status ? "enable" : "disable";
if (type == "add") {
let addRes = await AddPromptType(values);
messageApi.success(addRes);
} else if (type == "edit") {
let res = await EditPromptType({ ...values, id: data?.id });
messageApi.success(res);
} else {
messageApi.error("未知操作类型");
}
} catch (error: any) {
messageApi.error(error.message);
} finally {
setSpinning(false);
}
};
const onFinishFailed: FormProps<Prompt.AddPromptType>['onFinishFailed'] = (errorInfo) => {
};
return (<>
{messageHolder}
<Spin spinning={spinning} tip={spinTip}>
<Form
preserve={false}
form={form}
{...formItemLayout}
labelAlign="right"
variant="filled"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
clearOnDestroy={true}
initialValues={data}>
<Row>
<Col flex="auto">
<Form.Item<Prompt.AddPromptType> label="名称" name="name" rules={[{ required: true }]}>
<Input placeholder="请输入提示词类型名称" />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="编码" name="code" rules={[{ required: true }]}>
<Input placeholder="请输入提示词描述" />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="创建者" >
<Input disabled={true} placeholder="请输入提示词创建者" value={data?.createdUser?.nickName} />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="修改者" >
<Input disabled={true} placeholder="请输入提示词修改者" value={data?.updatedUser?.nickName} />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="修改时间" name="updateTime">
<Input disabled={true} placeholder="请输入提示词修改时间" />
</Form.Item>
</Col>
<Col flex="auto" style={{ marginLeft: "20px" }}>
<Form.Item<Prompt.AddPromptType> label="状态" name="status">
<Switch checkedChildren="启用" unCheckedChildren="停用" defaultChecked />
</Form.Item>
<Form.Item<Prompt.AddPromptType> label="备注" name="remark">
<Input.TextArea autoSize={
{ minRows: 6, maxRows: 6 }
} placeholder="请输入备注" />
</Form.Item>
</Col>
</Row>
<Form.Item style={{ display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }} >
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form >
</Spin>
</>)
}
export default PromptManagement;

View File

@ -1,17 +1,14 @@
import { PageContainer } from '@ant-design/pro-components';
import { useModel } from '@umijs/max';
import { Card, Form, GetProp, Input, message, Modal, Table, TablePaginationConfig, TableProps, theme } from 'antd';
import { Card, Form, GetProp, Input, message, Modal, Select, Table, TablePaginationConfig, TableProps, theme } from 'antd';
import React, { useEffect, useState } from 'react';
import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProTable, TableDropdown } from '@ant-design/pro-components';
import { Button, Dropdown, Space, Tag } from 'antd';
import { useRef } from 'react';
import { ColumnsType, SorterResult } from 'antd/es/table/interface';
import qs from 'qs';
import { getPromptSample, getPrompyType } from '@/services/services/prompt';
import ManagePromptType from '../ManagePromptType';
import { set } from 'lodash';
import { Button, Tag } from 'antd';
import { ColumnsType } from 'antd/es/table/interface';
import ManagePromptType from './ManagePromptType';
import TemplateContainer from '@/pages/TemplateContainer';
import { DeletePromptType, QueryPromptypeCollection } from '@/services/services/prompt';
import { useFormReset } from '@/hooks/useFormReset';
import { useSoftStore } from '@/store/software';
import { PlusOutlined } from '@ant-design/icons';
export const waitTimePromise = async (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
@ -25,67 +22,56 @@ export const waitTime = async (time: number = 100) => {
};
interface TableParams {
pagination?: TablePaginationConfig;
sortField?: SorterResult<any>['field'];
sortOrder?: SorterResult<any>['order'];
filters?: Parameters<GetProp<TableProps, 'onChange'>>[1];
}
interface TableParams {
pagination?: TablePaginationConfig;
sortField?: SorterResult<any>['field'];
sortOrder?: SorterResult<any>['order'];
filters?: Parameters<GetProp<TableProps, 'onChange'>>[1];
}
const getRandomuserParams = (params: TableParams) => ({
results: params.pagination?.pageSize,
page: params.pagination?.current,
...params,
});
const PromptManagement: React.FC = () => {
const { token } = theme.useToken();
const { initialState } = useModel('@@initialState');
const [data, setData] = useState<[Prompt.PromptTypeListItem][]>();
const [data, setData] = useState<Prompt.PromptTypeItem[]>();
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const [open, setOpen] = React.useState<boolean>(false);
const [type, setType] = useState<string>("add");
const [editData, setEditData] = useState<Prompt.AddPromptType>();
const [tableParams, setTableParams] = useState<TableParams>({
const [promptTypeId, setPromptTypeId] = useState<string>();
const [messageApi, messageHolder] = message.useMessage();
const [modal, modalHolder] = Modal.useModal();
const { setFormRef, resetForm } = useFormReset();
const { setTopSpinning, setTopSpinTip } = useSoftStore();
const [tableParams, setTableParams] = useState<TableModel.TableParams>({
pagination: {
current: 1,
pageSize: 10,
showQuickJumper: true,
totalBoundaryShowSizeChanger: true,
},
});
const fetchData = async () => {
setLoading(true);
let promptRes = await getPrompyType(tableParams.pagination?.pageSize, tableParams.pagination?.current)
if (promptRes.code == 1) {
message.success("获取提示词类型成功")
setData(promptRes.data)
setLoading(false);
try {
let params = {
...form.getFieldsValue(),
pageSize: tableParams.pagination?.pageSize ?? 10,
page: tableParams.pagination?.current ?? 1,
} as Prompt.PromptTypeQueryCondition;
let promptRes = await QueryPromptypeCollection(params)
setData(promptRes.collection);
setTableParams({
...tableParams,
pagination: {
...tableParams.pagination,
total: promptRes.data.count,
total: promptRes.total,
},
});
} else {
message.success("获取提示词类型成功")
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
message.error("获取提示词类型失败")
}
};
const columns: ColumnsType<Prompt.PromptTypeListItem> = [
const columns: ColumnsType<Prompt.PromptTypeItem> = [
{
title: '编码',
dataIndex: 'code',
@ -102,7 +88,7 @@ const PromptManagement: React.FC = () => {
dataIndex: 'status',
width: '100px',
render: (dom, en) => <>
<Tag color={en.status == "enable" ? "green" : "red"}></Tag>
<Tag color={en.status == "enable" ? "green" : "red"}> {en.status == "enable" ? '启用' : "停用"} </Tag>
</>
},
{
@ -115,16 +101,61 @@ const PromptManagement: React.FC = () => {
width: 220,
render: (dom, ent) => <>
<Button size='middle' style={{ marginRight: "5px" }} type="primary" onClick={() => {
debugger
setEditData(ent)
setType("edit")
setPromptTypeId(ent.id)
setOpen(true)
}}></Button>,
<Button size='middle' type="primary" danger></Button>,
}}></Button>
<Button size='middle' type="primary" danger onClick={async () => await DeletePromptTypeHandle(ent.id)}></Button>
</>
},
}
];
async function DeletePromptTypeHandle(id: string) {
setTopSpinning(true);
setTopSpinTip("正在删除数据。。。")
try {
// 调用删除的方法
let res = await DeletePromptType(id, false);
if (res.code == 6001) {
// 提示词,删除失败是不是删除对应的关联的提示词数据
const confirmed = await modal.confirm({
title: "删除提示词类型提醒",
content: (
<div>
<span></span>
<br />
<span></span>
</div>
),
okText: "确认",
cancelText: "取消"
})
if (confirmed) {
// 开始删除
let confirmDelete = await DeletePromptType(id, true);
if (confirmDelete.code != 1) {
throw new Error(confirmDelete.message);
}
}
else {
messageApi.error("取消删除");
return;
}
} else if (res.code == 1) {
// 删除成功
} else {
throw new Error(res.message)
}
message.success("删除成功")
setTopSpinning(false);
await fetchData()
} catch (error: any) {
message.error(error.message)
} finally {
setTopSpinning(false);
}
}
useEffect(() => {
fetchData();
@ -136,12 +167,10 @@ const PromptManagement: React.FC = () => {
JSON.stringify(tableParams.filters),
]);
const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter) => {
const handleTableChange = (pagination: TablePaginationConfig) => {
setTableParams({
pagination,
filters,
sortOrder: Array.isArray(sorter) ? undefined : sorter.order,
sortField: Array.isArray(sorter) ? undefined : sorter.field,
});
// `dataSource` is useless since `pageSize` changed
@ -151,7 +180,7 @@ const PromptManagement: React.FC = () => {
};
return (
<PageContainer>
<TemplateContainer navTheme={initialState?.settings?.navTheme ?? "realDark"}>
<Card
style={{
borderRadius: 8,
@ -170,32 +199,42 @@ const PromptManagement: React.FC = () => {
<Form
layout='inline'
form={form}
onFinish={fetchData}
>
<Form.Item label="名称">
<Input placeholder="请输入查询提示词的名称" />
<Form.Item<Prompt.PromptTypeQueryCondition> label="名称" name="name">
<Input placeholder="请输入查询提示词类型的名称" />
</Form.Item>
<Form.Item >
<Button type="primary"></Button>
<Form.Item<Prompt.PromptTypeQueryCondition> label="编码" name="code">
<Input placeholder="请输入查询提示词类型的编码" />
</Form.Item>
<Form.Item<Prompt.PromptTypeQueryCondition> label="状态" name="status">
<Select placeholder="请选择查询提示词类型的状态" style={{ width: 200 }}>
<Select.Option value="enable"></Select.Option>
<Select.Option value="disable"></Select.Option>
</Select>
</Form.Item>
<Form.Item<Prompt.PromptTypeQueryCondition> label="备注" name="remark">
<Input placeholder="请输入查询提示词类型的备注" />
</Form.Item>
<Form.Item >
<Button type="default"></Button>
</Form.Item>
<Form.Item >
<Button type="primary" htmlType='submit' ></Button>
</Form.Item>
<Form.Item style={{ display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }}>
<div >
<Button type="primary" style={{ marginBottom: 10 }} onClick={() => {
<Button type="primary" icon={<PlusOutlined />} style={{ marginBottom: 10 }} onClick={() => {
setOpen(true)
setType("add")
}}>
</Button>
</div>
</Form.Item>
</Form>
</div>
<Table
columns={columns}
rowKey={(record) => record.id}
@ -212,7 +251,8 @@ const PromptManagement: React.FC = () => {
onCancel={async () => {
setOpen(false)
await fetchData()
setEditData(undefined)
setPromptTypeId(undefined)
resetForm();
}}
width={800}
footer={null}
@ -220,9 +260,11 @@ const PromptManagement: React.FC = () => {
forceRender={true}
destroyOnClose
>
<ManagePromptType type={type} data={editData} />
<ManagePromptType type={type} setFormRef={setFormRef} id={promptTypeId} open={open} />
</Modal>
</PageContainer>
{messageHolder}
{modalHolder}
</TemplateContainer>
);
};

View File

@ -15,7 +15,6 @@ const AddRoleForm: React.FC<AddRoleModalProps> = ({ setFormRef }) => {
}, [form, setFormRef]);
const onFinish = async (values: any) => {
console.log('Success:', values);
setLoading(true);
try {
await AddRole(values.name, values.remark);

View File

@ -41,7 +41,6 @@ const ManageRoleModal: React.FC<ManageRoleModalProps> = ({ roleId, setFormRef })
}, [roleId]);
async function onFinish(values: RoleModel.Collection): Promise<void> {
console.log("onFinish", values);
setLoading(true);
setSpinTip("更新中...");
try {

View File

@ -1,5 +1,3 @@
import { useModel } from '@/.umi/plugin-model';
import { PageContainer } from '@ant-design/pro-components';
import { Button, Card, Form, Input, message, Modal, Table } from 'antd';
import React, { useEffect, useState, useRef } from 'react';
import TemplateContainer from '@/pages/TemplateContainer';
@ -8,14 +6,13 @@ import { ColumnsType, TablePaginationConfig } from 'antd/es/table';
import { DeleteRoleById, QueryRoleList } from '@/services/services/role';
import { FormatDate } from '@/util/time';
import ManageRoleModal from '../ManageRoleModal';
import { isEmpty, set } from 'lodash';
import { useFormReset } from '@/hooks/useFormReset';
import AddRoleForm from '../AddRoleForm';
import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface';
import { useModel } from '@umijs/max';
const RoleManagement: React.FC = () => {
const { initialState } = useModel('@@initialState');
const [data, setData] = useState<RoleModel.Collection[]>(); // 数据
const [form] = Form.useForm();
@ -27,6 +24,7 @@ const RoleManagement: React.FC = () => {
const [modal, contextHolder] = Modal.useModal();
const [modalTitle, setModalTitle] = useState<string>("编辑角色");
const [type, setType] = useState<string>("edit");
const [messageApi, messageHolder] = message.useMessage();
const [tableParams, setTableParams] = useState<TableModel.TableParams>({
pagination: {
@ -40,7 +38,6 @@ const RoleManagement: React.FC = () => {
// 初始化加载数据
QueryRoleList(tableParams, form.getFieldsValue())
.then((res) => {
debugger;
setData(res.collection);
setTableParams({
pagination: {
@ -51,9 +48,10 @@ const RoleManagement: React.FC = () => {
setLoading(false);
})
.catch((error) => {
message.error(error.message);
messageApi.error(error.message);
}).finally(() => {
setLoading(false);
});
})
}, []);
async function modalCancel() {
@ -64,7 +62,7 @@ const RoleManagement: React.FC = () => {
let res = await QueryRoleList(tableParams, form.getFieldsValue());
setData(res.collection);
} catch (error: any) {
message.error(error.message);
messageApi.error(error.message);
} finally {
setLoading(false);
}
@ -82,7 +80,7 @@ const RoleManagement: React.FC = () => {
}
})
} catch (error: any) {
message.error(error.message);
messageApi.error(error.message);
} finally {
setLoading(false);
}
@ -101,9 +99,9 @@ const RoleManagement: React.FC = () => {
try {
await DeleteRoleById(roleId);
await QueryRoleByName(form.getFieldsValue());
message.success("删除角色成功");
messageApi.success("删除角色成功");
} catch (error: any) {
message.error(error.message);
messageApi.error(error.message);
}
},
onCancel: async () => {
@ -191,7 +189,7 @@ const RoleManagement: React.FC = () => {
}
})
} catch (error: any) {
message.error(error.message);
messageApi.error(error.message);
} finally {
setLoading(false);
}
@ -234,6 +232,7 @@ const RoleManagement: React.FC = () => {
}
</Modal>
{contextHolder}
{messageHolder}
</TemplateContainer>
)
}

View File

@ -0,0 +1,337 @@
import React, { useEffect, useState } from 'react';
import type { FC } from 'react';
import TemplateContainer from '@/pages/TemplateContainer';
import { useModel } from '@umijs/max';
import { Button, Dropdown, Form, Input, message, Modal, Select, Table, TableProps, Tag } from 'antd';
import { FilterValue, SorterResult, TableCurrentDataSource, TablePaginationConfig } from 'antd/es/table/interface';
import { Software, SoftwareControl } from '@/services/services/software';
import moment from 'moment';
import { DeleteOutlined, EditOutlined, MenuOutlined, PlusSquareOutlined } from '@ant-design/icons';
import { GetOptions, getOptionsStringValue } from '@/services/services/options/optionsTool';
import { useSoftStore } from '@/store/software';
interface SoftwareControlManagementProps {
// Add your props here
}
const SoftwareControlManagement: FC<SoftwareControlManagementProps> = () => {
const { initialState } = useModel('@@initialState');
const [messageApi, messageHolder] = message.useMessage();
const [loading, setLoading] = React.useState<boolean>(false);
const [modalApi, modalHolder] = Modal.useModal();
const [form] = Form.useForm();
const [softwareBasicInfo, setSoftwareBasicInfo] = useState<SoftwareModel.SoftwareBasicInfo[]>();
const [softwareOptions, setSoftwareOptions] = useState<any>([]);
const [data, setData] = React.useState<SoftwareModel.SoftwareControlBase[]>([]);
const [tableParams, setTableParams] = useState<TableModel.TableParams>({
pagination: {
current: 1,
pageSize: 10,
showQuickJumper: true,
totalBoundaryShowSizeChanger: true,
},
});
const { setTopSpinning, setTopSpinTip } = useSoftStore();
const columns: TableProps<SoftwareModel.SoftwareControlBase>['columns'] = [
{
title: '软件代码',
dataIndex: 'software',
width: 100,
key: 'softwareCode',
render: (software) => <span> {software.softwareCode}</span >
},
{
title: '软件名称',
dataIndex: 'software',
key: 'softwareName',
render: (software) => <span>{software.softwareName}</span>,
},
{
title: '所属用户ID',
dataIndex: 'user',
key: 'userId',
render: (user) => <span>{user.id}</span>,
},
{
title: '所属用户名称',
dataIndex: 'user',
key: 'userName',
render: (user) => <span>{user.nickName}</span>,
},
{
title: '创建者',
dataIndex: 'createdUser',
key: 'createdUserNickName',
render: (createdUser) => <span>{createdUser.nickName}</span>,
},
{
title: '更新者',
dataIndex: 'updatedUser',
key: 'updatedUserNickName',
render: (updatedUser) => <span>{updatedUser.nickName}</span>,
},
{
title: '更新时间',
dataIndex: 'updatedTime',
key: 'updatedTime',
width: 200,
render: (updatedTime) => updatedTime ? moment(updatedTime).format('YYYY-MM-DD HH:mm:ss') : 'null',
},
{
title: '到期时间',
dataIndex: 'expirationTime',
key: 'expirationTime',
width: 200,
render: (expirationTime) => expirationTime ? moment(expirationTime).format('YYYY-MM-DD HH:mm:ss') : 'null',
},
{
title: '是否永久',
dataIndex: 'isForever',
key: 'isForever',
width: 100,
render: (isForever) => isForever ? <Tag color="green"></Tag> : <Tag color="red"></Tag>,
},
{
title: '操作',
key: 'action',
width: 100,
render: (_, record) => (
<Dropdown
menu={{
items: [
{
key: 'edit',
label: '编辑',
icon: <EditOutlined />,
onClick: () => {
// 编辑
messageApi.warning("暂不支持编辑");
}
},
{
key: 'addTrail',
label: '添加试用',
style: { color: '#faad14' },
icon: <PlusSquareOutlined />,
onClick: async () => {
// 添加试用
await AddSoftwareControlExpirationTime(record.id, 1, true);
}
},
{
key: 'addMouth',
label: '添加月付',
style: { color: '#38a2fc' },
icon: <PlusSquareOutlined />,
onClick: async () => {
// 延长到期时间
await AddSoftwareControlExpirationTime(record.id, 30);
}
},
{
key: 'addQuarterly',
label: '添加季付',
style: { color: '#38a2fc' },
icon: <PlusSquareOutlined />,
onClick: async () => {
// 延长到期时间
await AddSoftwareControlExpirationTime(record.id, 90);
}
},
{
key: 'addHalfYear',
label: '添加半年',
style: { color: '#38a2fc' },
icon: <PlusSquareOutlined />,
onClick: async () => {
// 延长到期时间
await AddSoftwareControlExpirationTime(record.id, 180);
}
},
{
key: 'addYear',
label: '添加年付',
style: { color: '#38a2fc' },
icon: <PlusSquareOutlined />,
onClick: async () => {
// 延长到期时间
await AddSoftwareControlExpirationTime(record.id, 365);
}
},
{
key: 'addForever',
label: '永久',
style: { color: '#38a2fc' },
icon: <PlusSquareOutlined />,
onClick: async () => {
// 延长到期时间
await AddSoftwareControlExpirationTime(record.id, 0);
}
},
{
key: 'delete',
label: '停用权限',
danger: true,
icon: <DeleteOutlined />,
onClick: async () => {
await DeleteSoftwareControl(record.id);
}
},
],
}}
>
<Button type="text" color="primary" variant="filled" icon={<MenuOutlined />} />
</Dropdown>
),
}
];
async function DeleteSoftwareControl(id: string) {
try {
const confirmed = await modalApi.confirm({
title: "确认停用",
content: "确定停用吗,重置到期时间和永久选项"
});
if (confirmed) {
setLoading(true);
await SoftwareControl.AddSoftwareControlExpirationTime(id, 0, false);
// 重新查询
await QueryUserSoftwareControlCollection(tableParams, form.getFieldsValue());
messageApi.success("停用成功");
} else {
messageApi.info("取消停用");
}
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
}
}
async function AddSoftwareControlExpirationTime(id: string, days: number, isTry = false) {
try {
if (isTry) {
setTopSpinning(true);
setTopSpinTip("加载信息中");
let LaiToolTrialDays = 1;
let res = await GetOptions("trial");
days = Number(getOptionsStringValue(res, 'LaiToolTrialDays', "") || LaiToolTrialDays);
setTopSpinning(false);
}
const confirmed = await modalApi.confirm({
title: "确认添加",
content: `确认添加 ${days == 0 ? "永久" : days + " 天"} 吗?`
});
if (confirmed) {
setLoading(true);
await SoftwareControl.AddSoftwareControlExpirationTime(id, days, days == 0, isTry);
// 重新查询
await QueryUserSoftwareControlCollection(tableParams, form.getFieldsValue());
if (days == 0) {
messageApi.success("添加永久成功");
} else {
messageApi.success("添加 " + days + " 天成功");
}
} else {
messageApi.info("取消添加");
}
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
setTopSpinning(false);
}
}
async function QueryUserSoftwareControlCollection(tableParams: TableModel.TableParams, options?: SoftwareModel.SoftwareControlQueryParams) {
try {
debugger
setLoading(true);
let res = await SoftwareControl.GetUserSoftwareControlCollection(tableParams, options ?? {});
setData(res.collection);
setTableParams({
pagination: {
...tableParams.pagination,
total: res.total
}
})
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
}
}
useEffect(() => {
QueryUserSoftwareControlCollection(tableParams, form.getFieldsValue()).then();
Software.GetSoftwareBaseCollection().then((res) => {
setSoftwareBasicInfo(res);
let options = []
for (let i = 0; i < res.length; i++) {
const element = res[i];
let option = {
label: element.isUse == false ? element.softwareName + "(未启用)" : element.softwareName,
value: element.id
}
options.push(option);
}
setSoftwareOptions(options);
}).catch((error) => {
messageApi.error(error.message);
})
}, []);
async function TableChangeHandle(pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<SoftwareModel.SoftwareControlBase> | SorterResult<SoftwareModel.SoftwareControlBase>[], extra: TableCurrentDataSource<SoftwareModel.SoftwareControlBase>): Promise<void> {
await QueryUserSoftwareControlCollection({ pagination }, form.getFieldsValue());
setTableParams({
pagination: {
...tableParams.pagination,
current: pagination.current,
pageSize: pagination.pageSize
}
})
}
async function QuerySoftwareControlByCondition(values: any) {
await QueryUserSoftwareControlCollection(tableParams, values);
}
return (
<TemplateContainer navTheme={initialState?.settings?.navTheme ?? "realDark"}>
<Form
layout='inline'
form={form}
onFinish={QuerySoftwareControlByCondition}
>
<Form.Item label="用户ID" name='userId' style={{ marginBottom: 5 }}>
<Input placeholder="请输入用户ID" />
</Form.Item>
<Form.Item label="软件" name='softwareId' style={{ marginBottom: 5 }}>
<Select placeholder="请选择用户名称" style={{ width: 200 }} options={softwareOptions} />
</Form.Item>
<Form.Item label="是否永久" name='isForever' style={{ marginBottom: 5 }}>
<Select placeholder="请选择是否永久" style={{ width: 200 }} options={[{ label: "是", value: true }, { label: "否", value: false }]} />
</Form.Item>
<Form.Item label="备注" name='remark' style={{ marginBottom: 5 }}>
<Input placeholder="请输入备注" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit"></Button>
</Form.Item>
</Form>
<div>
<Table<SoftwareModel.SoftwareControlBase> columns={columns} dataSource={data} rowKey={(record) => record.id} pagination={tableParams.pagination} onChange={TableChangeHandle} loading={loading} />
</div>
{messageHolder}
{modalHolder}
</TemplateContainer>
);
};
export default SoftwareControlManagement;

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

@ -130,7 +130,6 @@ const Login: React.FC = () => {
setUserLoginState({
status: 'error',
});
console.log(error);
message.error(error.message);
}
};

View File

@ -19,7 +19,6 @@ const Register: React.FC = () => {
}, []);
const onFinish = async (values: UserModel.UserRegisterParams) => {
console.log('Received values of form: ', values);
// 判断两次密码是否一致
if (values.password !== values.confirm) {
messageApi.warning('两次密码不一致!');

View File

@ -1,17 +1,20 @@
import TemplateContainer from '@/pages/TemplateContainer';
import { GetUserAgentInfo, GetUserInfo } from '@/services/services/user';
import { UserInfo } from '@/services/services/user';
import { RedoOutlined } from '@ant-design/icons';
import { useModel } from '@umijs/max';
import { Avatar, Button, Card, Col, Input, message, Modal, Row, Spin, Tag } from 'antd';
import { Avatar, Badge, Button, Card, Col, FloatButton, Input, message, Modal, Row, Spin, Tag } from 'antd';
import React, { useEffect, useState } from 'react';
import UserCenterUserInfo from '../UserCenterUserInfo';
import UserCenterAgentMessage from '../UserCenterAgentMessage';
import UserCenterUserInfo from './UserCenterUserInfo';
import UserCenterAgentMessage from './UserCenterAgentMessage';
import { useSoftStore } from '@/store/software';
import { SoftwareControl } from '@/services/services/software';
import UserSoftwareInfo from './UserSoftwareInfo';
const UserCenter: React.FC = () => {
const { initialState, setInitialState } = useModel('@@initialState');
const [messageApi, messageHolder] = message.useMessage();
const [modalApi, modalHolder] = Modal.useModal();
const [badgeCount, setBadgeCount] = useState(0);
const { setTopSpinTip, setTopSpinning } = useSoftStore();
const [userAgentUserInfo, setUserAgentUserInfo] = useState<UserModel.UserAgentInfo>();
@ -20,19 +23,68 @@ const UserCenter: React.FC = () => {
// 初始化加载用户信息
setTopSpinning(true);
setTopSpinTip("正在获取用户信息。。。");
GetUserInfo(initialState?.currentUser?.id).then(async (res) => {
UserInfo.GetUserInfo(initialState?.currentUser?.id).then(async (res) => {
setInitialState({ ...initialState, currentUser: res });
localStorage.setItem('userInfo', JSON.stringify(res));
let agentInfo = await GetUserAgentInfo();
let agentInfo = await UserInfo.GetUserAgentInfo();
setUserAgentUserInfo(agentInfo);
}).catch((error) => {
console.log(error)
messageApi.error(error.message);
}).finally(() => {
setTopSpinning(false);
})
// 加载当前用户可申请的的软件控制权限
SoftwareControl.GetUserSoftwareControlCount(initialState?.currentUser?.id).then((res) => {
setBadgeCount(res);
}).catch((error) => {
messageApi.error(error.message);
})
}
}, [])
/**
*
*/
async function ApplyUserSoftwareControlHandle() {
let userID = initialState?.currentUser?.id;
if (!userID) {
messageApi.error("用户信息不存在");
return;
}
// 重新获取用户关联信息
const userCanApplyCount = await SoftwareControl.GetUserSoftwareControlCount(userID);
if (userCanApplyCount <= 0) {
messageApi.warning("您已经没有可申请的软件控制权限了");
return;
}
// 开始调用申请接口
setTopSpinning(true);
setTopSpinTip("正在申请软件控制权限。。。");
try {
await SoftwareControl.ApplyUserSoftwareControl(userID);
// 提示成功
messageApi.success("申请成功");
setBadgeCount(0);
} catch (error: any) {
messageApi.error(error.message);
} finally {
setTopSpinning(false);
}
}
/**
*
*/
async function ShowSoftwareControlHandle() {
modalApi.info({
title: "用户软件控制权限",
content: <UserSoftwareInfo userId={initialState?.currentUser?.id} />,
width: 800,
footer: null,
closable: true,
});
}
function renderTitie() {
return (
<div style={{ display: 'flex' }}>
@ -81,6 +133,18 @@ const UserCenter: React.FC = () => {
</div>
<strong style={{ fontSize: 24, color: "goldenrod" }}>{initialState?.currentUser?.affiliateCode}</strong>
</Col>
<Col span={3} style={{ minWidth: 100 }}>
<div>
<span></span>
</div>
{
badgeCount > 0 ? <Badge count={badgeCount}>
<Button variant="filled" color="default" onClick={ApplyUserSoftwareControlHandle}> </Button>
</Badge> : <Button variant="filled" color="default" onClick={ApplyUserSoftwareControlHandle}> </Button>
}
<Button color="primary" variant="filled" style={{ marginLeft: 10 }} onClick={ShowSoftwareControlHandle} > </Button>
</Col>
</Row>
</Card>
</div>

View File

@ -5,7 +5,7 @@ import renderTitle from '../UserRenderList';
import { isEmpty } from 'lodash';
import { useModel } from '@umijs/max';
import { useSoftStore } from '@/store/software';
import { EnableAgent, GetUserAgentInfo, GetUserInfo } from '@/services/services/user';
import { UserInfo } from '@/services/services/user';
type UserCenterAgentMessageProps = {
userAgentUserInfo: UserModel.UserAgentInfo | undefined;
@ -28,15 +28,15 @@ const UserCenterAgentMessage: React.FC<UserCenterAgentMessageProps> = ({ userAge
setTopSpinTip("正在启用代理。。。");
// 开始调用启用代理的接口
try {
await EnableAgent();
await UserInfo.EnableAgent();
messageApi.success("启用代理成功");
// 冲i性能加载用户信息
if (initialState?.currentUser?.id) {
let res = await GetUserInfo(initialState?.currentUser?.id);
let res = await UserInfo.GetUserInfo(initialState?.currentUser?.id);
localStorage.setItem('userInfo', JSON.stringify(res));
setInitialState({ ...initialState, currentUser: res });
// 重新加载代理信息
let agentInfo = await GetUserAgentInfo();
let agentInfo = await UserInfo.GetUserAgentInfo();
setUserAgentUserInfo(agentInfo);
}
} catch (error: any) {

View File

@ -0,0 +1,97 @@
import React, { useEffect, useState } from 'react';
import { message, Space, Table, Tag } from 'antd';
import type { TablePaginationConfig, TableProps } from 'antd';
import { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface';
import { SoftwareControl } from '@/services/services/software';
import { useModel } from '@umijs/max';
import moment from 'moment';
const columns: TableProps<SoftwareModel.SoftwareControlBase>['columns'] = [
{
title: '软件代码',
dataIndex: 'software',
width: 100,
key: 'softwareCode',
render: (software) => <span> {software.softwareCode}</span >
},
{
title: '软件名称',
dataIndex: 'software',
key: 'softwareName',
render: (software) => <span>{software.softwareName}</span>,
},
{
title: '到期时间',
dataIndex: 'expirationTime',
key: 'expirationTime',
width: 200,
render: (expirationTime) => expirationTime ? moment(expirationTime).format('YYYY-MM-DD HH:mm:ss') : 'null',
},
{
title: '是否永久',
dataIndex: 'isForever',
key: 'isForever',
width: 100,
render: (isForever) => isForever ? <Tag color="green"></Tag> : <Tag color="red"></Tag>,
}
];
interface UserSoftwareInfoProps {
userId?: number;
}
const UserSoftwareInfo: React.FC<UserSoftwareInfoProps> = ({ userId }) => {
const [data, setData] = React.useState<SoftwareModel.SoftwareControlBase[]>([]);
const [loading, setLoading] = React.useState<boolean>(false);
const [messageApi, messageHolder] = message.useMessage();
const [tableParams, setTableParams] = useState<TableModel.TableParams>({
pagination: {
current: 1,
pageSize: 10,
showQuickJumper: true,
totalBoundaryShowSizeChanger: true,
},
});
async function QueryUserSoftwareControlCollection() {
try {
if (userId == null) {
messageApi.error("用户ID不能为空");
}
setLoading(true);
let res = await SoftwareControl.GetUserSoftwareControlCollection(tableParams, {
userId: userId
});
setData(res.collection);
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
}
}
useEffect(() => {
QueryUserSoftwareControlCollection().then();
}, []);
async function TableChangeHandle(pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<SoftwareModel.SoftwareControlBase> | SorterResult<SoftwareModel.SoftwareControlBase>[], extra: TableCurrentDataSource<SoftwareModel.SoftwareControlBase>): Promise<void> {
await QueryUserSoftwareControlCollection();
setTableParams({
pagination: {
...tableParams.pagination,
current: pagination.current,
pageSize: pagination.pageSize,
}
});
}
return (
<div>
<Table<SoftwareModel.SoftwareControlBase> columns={columns} dataSource={data} rowKey={(record) => record.id} pagination={tableParams.pagination} onChange={TableChangeHandle} loading={loading} />
{messageHolder}
</div>
);
};
export default UserSoftwareInfo;

View File

@ -1,5 +1,5 @@
import { QueryRoleOption } from '@/services/services/role';
import { GetUserInfo, UpdatedUserInfo } from '@/services/services/user';
import { UserInfo } from '@/services/services/user';
import { FormatDate } from '@/util/time';
import { useModel } from '@umijs/max';
import { Button, Col, Form, FormInstance, Input, InputNumber, message, Row, Select, SelectProps, Spin, Tag } from 'antd';
@ -38,7 +38,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
setLoading(false);
})
GetUserInfo(userId).then((res) => {
UserInfo.GetUserInfo(userId).then((res) => {
let tempRes = {
...res,
createdDate: FormatDate(res.createdDate)
@ -55,7 +55,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
setLoading(true);
setSpinTip("修改中...");
try {
await UpdatedUserInfo(values);
await UserInfo.UpdatedUserInfo(values);
messageApi.success("用户修改成功");
} catch (error: any) {
messageApi.error(error.message);
@ -113,7 +113,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="用户名称"
name="userName"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请填写用户名称!' }]}
>
<Input />
</Form.Item>
@ -123,7 +123,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="用户昵称"
name="nickName"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请填写用户昵称!' }]}
>
<Input />
</Form.Item>
@ -132,7 +132,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="邮箱"
name="email"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请填写邮箱!' }]}
>
<Input />
</Form.Item>
@ -149,7 +149,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="角色名称"
name="roleNames"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请选择角色名称!' }]}
>
<Select
mode="multiple"
@ -165,7 +165,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="可激活设备"
name="allDeviceCount"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请设置可激活设置!' }]}
>
<InputNumber min={0} step="1" />
</Form.Item>
@ -174,7 +174,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="代理分成"
name="agentPercent"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请设置代理分成!' }]}
>
<InputNumber min={0.1} max={0.7} step="0.01" />
</Form.Item>
@ -183,7 +183,7 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="免费换绑次数"
name="freeCount"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请设置免费换绑次数!' }]}
>
<InputNumber min={1} max={10} step="1" />
</Form.Item>
@ -192,11 +192,20 @@ const ModifyUser: React.FC<ModifyUserProps> = ({ userId, setFormRef, open }) =>
<Form.Item
label="注册时间"
name="createdDate"
rules={[{ required: true, message: 'Please input your username!' }]}
rules={[{ required: true, message: '请选择注册时间!' }]}
>
<Input disabled={true} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="备注"
name="remark"
rules={[{ required: true, message: '请输入备注!' }]}
>
<Input />
</Form.Item>
</Col>
</Row>
<Form.Item wrapperCol={{ offset: 4, span: 3 }}>

View File

@ -0,0 +1,58 @@
import React, { useState } from 'react';
import { Form, Input, Button, message, Tooltip } from 'antd';
import DiceIcon from '@/components/Icon/DiceIcon';
import { generateRandomPassword } from '@/util/password';
import { set, toArray } from 'lodash';
import { useSoftStore } from '@/store/software';
import { UserInfo } from '@/services/services/user';
interface ResetUserPasswordProps {
userId: number;
}
const ResetUserPassword: React.FC<ResetUserPasswordProps> = ({ userId }) => {
const [newPassword, setNewPassword] = useState<string>('');
const [messageApi, messageHolder] = message.useMessage();
const { setTopSpinning, setTopSpinTip } = useSoftStore();
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '16px' }}>
<Input
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
placeholder="请输入新密码"
/>
<Button
icon={<DiceIcon />}
color="primary"
variant="filled"
onClick={() => {
let newPw = generateRandomPassword();
console.log(newPw);
setNewPassword(newPw);
}}
/>
<Tooltip title="确认重置用户密码">
<Button type="primary" onClick={async () => {
setTopSpinning(true);
setTopSpinTip('正在重置密码。。。');
// 调用重置密码的方法
try {
await UserInfo.ResetUserPassword(userId, newPassword);
} catch (error: any) {
messageApi.error('密码重置失败, ' + error.message);
} finally {
setTopSpinning(false);
}
messageApi.success('密码重置成功 ' + newPassword);
// setPasswordModel(false);
}}>
</Button>
</Tooltip>
{messageHolder}
</div>
);
};
export default ResetUserPassword;

View File

@ -1,14 +1,20 @@
import { useFormReset } from "@/hooks/useFormReset";
import TemplateContainer from "@/pages/TemplateContainer";
import { QueryRoleOption } from "@/services/services/role";
import { QueryUserList } from "@/services/services/user";
import { UserInfo } from "@/services/services/user";
import { FormatDate } from "@/util/time";
import { useAccess, useModel } from "@umijs/max";
import { Button, Form, Input, InputNumber, message, Modal, Select, SelectProps, Table, Tag } from "antd";
import { Button, Dropdown, Form, Input, InputNumber, message, Modal, Select, SelectProps, Table, Tag, Tooltip } from "antd";
import { ColumnsType, TablePaginationConfig } from "antd/es/table";
import { FilterValue, SorterResult, TableCurrentDataSource } from "antd/es/table/interface";
import { useEffect, useState } from "react";
import ModifyUser from "../ModifyUser";
import Icon, { DeleteOutlined, EditOutlined, KeyOutlined, MenuOutlined, SyncOutlined } from "@ant-design/icons";
import { SoftwareControl } from "@/services/services/software";
import { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon";
import DiceIcon from "@/components/Icon/DiceIcon";
import { generateRandomPassword } from "@/util/password";
import ResetUserPassword from "./ResetUserPassword";
const UserManagement: React.FC = () => {
type TagRender = SelectProps['tagRender'];
@ -27,12 +33,11 @@ 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);
const [roleNames, setRoleNames] = useState<SelectProps['options']>([]);
const [modal, modaltHolder] = Modal.useModal();
@ -48,7 +53,7 @@ const UserManagement: React.FC = () => {
messageApi.error(error.message);
})
QueryUserList(tableParams, form.getFieldsValue())
UserInfo.QueryUserList(tableParams, form.getFieldsValue())
.then((res) => {
setData(res.collection);
setTableParams({
@ -71,7 +76,7 @@ const UserManagement: React.FC = () => {
setLoading(true);
try {
let tableParamsParams = pagination ? { pagination } : tableParams;
let res = await QueryUserList(tableParamsParams, params ?? form.getFieldsValue());
let res = await UserInfo.QueryUserList(tableParamsParams, params ?? form.getFieldsValue());
setData(res.collection);
setTableParams({
pagination: {
@ -89,7 +94,7 @@ const UserManagement: React.FC = () => {
async function handleTableChange(pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<UserModel.UserCollection> | SorterResult<UserModel.UserCollection>[], extra: TableCurrentDataSource<UserModel.UserCollection>): Promise<void> {
setLoading(true);
try {
let queryUser = await QueryUserList({ pagination }, form.getFieldsValue());
let queryUser = await UserInfo.QueryUserList({ pagination }, form.getFieldsValue());
setData(queryUser.collection);
setTableParams({
pagination: {
@ -136,6 +141,44 @@ const UserManagement: React.FC = () => {
);
};
async function ResetUserPasswordFunc(userId: number) {
modal.info({
title: '重置密码',
width: 500,
closable: true,
content: <ResetUserPassword userId={userId} />,
footer: null
});
}
/**
*
* @param userId ID
* @returns
*/
async function ApplySoftwareControlHandle(userId: number) {
if (!access.isAdminOrSuperAdmin) {
messageApi.error("您没有权限执行此操作");
return;
}
try {
setLoading(true);
let userCanApplyCount = await SoftwareControl.GetUserSoftwareControlCount(userId);
if (userCanApplyCount <= 0) {
messageApi.warning("用户已经没有可申请的软件控制权限了");
return;
}
// 开始申请
await SoftwareControl.ApplyUserSoftwareControl(userId);
messageApi.success("用户软件控制权限申请成功");
} catch (error: any) {
messageApi.error(error.message);
} finally {
setLoading(false);
}
}
const columns: ColumnsType<UserModel.UserCollection> = [
{
title: 'ID',
@ -174,32 +217,85 @@ const UserManagement: React.FC = () => {
title: '创建时间',
dataIndex: 'createdDate',
render: (text, record) => FormatDate(record.createdDate),
width: '200px',
width: '160px',
},
{
title: '上次登录时间',
dataIndex: 'lastLoginDate',
render: (text, record) => FormatDate(record.lastLoginDate),
width: '200px',
width: '160px',
},
{
title: '上次登录IP',
dataIndex: 'lastLoginIp',
width: '200px',
width: '120px',
},
{
title: '备注',
dataIndex: 'remark',
hidden: !access.isAdminOrSuperAdmin,
ellipsis: {
showTitle: false,
},
render: (remark) => (
<Tooltip placement="topLeft" title={remark}>
{remark}
</Tooltip>
),
},
{
title: '操作',
width: '120px',
width: '80px',
hidden: !access.isAdminOrSuperAdmin,
render: (text, record) => (
<div style={{ display: "flex" }}>
<Button hidden={!access.canEditUser} size='small' style={{ marginRight: 5 }} type="primary" onClick={() => {
setUserId(record.id);
setOpenModal(true);
}}></Button>
<Button hidden={!access.canDeleteUser} danger size='small' type="primary" onClick={() => {
messageApi.error("暂不支持删除用户");
}} ></Button>
</div>
<Dropdown
menu={{
items: [
{
key: 'edit',
label: '编辑',
icon: <EditOutlined />,
hidden: !access.canEditUser,
primary: true,
onClick: () => {
setUserId(record.id);
setOpenModal(true);
}
},
{
key: 'resetPassword',
label: '重置密码',
icon: <KeyOutlined />,
hidden: !access.isSuperAdmin,
onClick: async () => {
await ResetUserPasswordFunc(record.id);
}
},
{
key: 'apply',
label: '初始化权限',
icon: <SyncOutlined />,
hidden: !access.canApplySoftwareControl,
primary: true,
onClick: async () => {
await ApplySoftwareControlHandle(record.id);
}
},
{
key: 'delete',
label: '删除',
icon: <DeleteOutlined />,
hidden: !access.canDeleteUser,
danger: true,
onClick: () => {
messageApi.error("暂不支持删除用户");
}
}
].filter(item => !item.hidden)
}}
>
<Button color="primary" variant="filled" icon={<MenuOutlined />} />
</Dropdown>
),
},
];
@ -245,6 +341,16 @@ const UserManagement: React.FC = () => {
</Form.Item> :
null
}
<Form.Item>
</Form.Item>
{
access.isAdminOrSuperAdmin ?
<Form.Item label="备注" name='remark' style={{ marginBottom: 5 }}>
<Input placeholder="请输入备注" />
</Form.Item> :
null
}
<Form.Item>
<Button type="primary" htmlType='submit'></Button>
@ -263,6 +369,7 @@ const UserManagement: React.FC = () => {
<ModifyUser setFormRef={setFormRef} open={openModal} userId={userId} />
</Modal>
{messageHolder}
{modaltHolder}
</TemplateContainer>
);
};

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,38 +96,30 @@ export const errorConfig: RequestConfig = {
if (opts?.skipErrorHandler) throw error;
// 我们的 errorThrower 抛出的错误。
if (error.name === 'BizError') {
debugger
} else if (error.response) {
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
const { status } = error.response;
if (status === 401) {
console.log('401 error', url);
if (url.toLowerCase().includes('/refreshtoken')) {
console.log('refresh token error');
message.error('登录已过期,请重新登录');
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
localStorage.removeItem('userInfo');
history.push('/user/login');
} else {
console.log('normal 401 error try to refresh token');
// 尝试刷新 token
const refreshSuccess = await refreshToken();
console.log('refresh token result', refreshSuccess);
if (refreshSuccess) {
console.log('refresh token success, retry request');
// 刷新成功,重试原请求
// 这里需要实现重试逻辑,可能需要修改您的请求库
message.success('已重新获取授权,正在重试请求');
// 刷新成功,重试原请求
try {
console.log('retry request', opts);
const retryResponse = await retryRequest(url, opts);
// 如果重试成功,返回重试的响应
console.log('retry success', retryResponse);
return retryResponse;
} catch (retryError) {
console.log('retry error', retryError);
message.error('重试请求失败,请重新登录');
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
@ -136,7 +128,6 @@ export const errorConfig: RequestConfig = {
}
// 重试原请求的逻辑
} else {
console.log('refresh token failed');
// 刷新失败,重定向到登录页面
message.error('授权已过期,请重新登录');
localStorage.removeItem('token');
@ -150,13 +141,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 +161,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

@ -65,7 +65,6 @@ export class TokenStorage {
const request = objectStore.put({ id: 'token', value: token });
transaction.oncomplete = () => {
console.log('Token saved');
resolve();
};
@ -86,7 +85,6 @@ export class TokenStorage {
request.onsuccess = (event) => {
const result = (event.target as IDBRequest).result;
if (result) {
console.log('Token:', result.value);
let claims: any = null;
if (result.value) {
const decodedToken = jwtDecode(result.value);
@ -100,7 +98,6 @@ export class TokenStorage {
}
resolve(claims);
} else {
console.log('Token not found');
resolve(null);
}
};
@ -122,10 +119,8 @@ export class TokenStorage {
request.onsuccess = (event) => {
const result = (event.target as IDBRequest).result;
if (result) {
console.log('Token:', result.value);
resolve(result.value);
} else {
console.log('Token not found');
reject(new Error("Token not found"));
}
};
@ -145,7 +140,6 @@ export class TokenStorage {
const request = objectStore.delete('token');
transaction.oncomplete = () => {
console.log('Token deleted');
resolve();
};

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,14 +122,14 @@ 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,
email: params.email ?? '',
password: secPassword,
tokenId: publicKey.token,
affiliateCode : params.affiliateCode
affiliateCode: params.affiliateCode
}
let res = await request<ApiResponse.SuccessItem<string>>('/lms/User/Register', {
method: 'POST',

View File

@ -17,7 +17,6 @@ async function QueryMachineList(tableParams: TableModel.TableParams, userParams:
let res = await request<ApiResponse.SuccessItem<MachineModel.QueryMachineData>>(`/lms/Machine/QueryMachineCollection?${query}`, {
method: 'GET',
});
console.log(QueryMachineList, res)
if (res.code != 1) {
throw new Error(res.message);
}
@ -59,7 +58,6 @@ async function GetMachineInfo(id: string): Promise<MachineModel.MachineInfo> {
let res = await request<ApiResponse.SuccessItem<MachineModel.MachineInfo>>(`/lms/Machine/GetMachineDetail/${id}`, {
method: 'GET',
});
console.log("GetMachineInfo", res)
if (res.code != 1) {
throw new Error(res.message);
}
@ -77,7 +75,6 @@ async function ModifyMachineData(id: string, params: MachineModel.ModifyMachineP
...params,
deactivationTime: deactivationTimeString
}
console.log("ModifyMachineData", params)
let res = await request<ApiResponse.SuccessItem<null>>(`/lms/Machine/ModifyMachine/${id}`, {
method: 'POST',
data: data
@ -95,8 +92,12 @@ async function AddMachineData(params: MachineModel.AddMachineParams) {
let deactivationTimeString = params.deactivationTime ? params.deactivationTime.toISOString() : undefined;
let data = {
...params,
deactivationTime: deactivationTimeString
deactivationTime: deactivationTimeString,
useStatus: 0, // 这边设置默认就是试用,然后状态时激活,不需要用户再次设置
status: 1,
}
debugger
console.log(data)
let res = await request<ApiResponse.SuccessItem<null>>(`/lms/Machine/AddMachine`, {
method: 'POST',
data: data

View File

@ -0,0 +1,133 @@
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 | undefined {
// 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 ?? undefined;
}
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',
}
});
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.toString() });
return "";
}, {});
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

@ -1,79 +1,94 @@
import { request } from '@umijs/max';
import { errorMessage } from './response';
import { objectToQueryString } from './common';
//#region 提示词数据相关
/**
*
* @param typeId Idall
* @param pageSize
* @param current
* @param options
*
* @param param
* @returns
*/
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 || {}),
});
} catch (error: any) {
return errorMessage(error.toString())
export async function QueryPromptCollection(param: Prompt.PromptQueryCondition): Promise<Prompt.PromptResponse> {
let query = objectToQueryString(param)
let res = await request<ApiResponse.SuccessItem<Prompt.PromptResponse>>(`/lms/Prompt/QueryPromptStringCollection/?${query}`, {
method: 'GET',
});
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
/**
*
* @param id ID
*/
export async function GetPromptInfo(id: string): Promise<Prompt.PromptItem> {
let res = await request<ApiResponse.SuccessItem<Prompt.PromptItem>>(`/lms/Prompt/GetPromptInfo/${id}`, {
method: 'GET',
});
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
/**
*
* @param data
* @returns
*/
export async function AddPrompt(data: Prompt.PromptItem): Promise<string> {
let res = await request('/lms/Prompt/AddPrompt', {
method: 'POST',
data: {
name: data.name,
promptTypeId: data.promptTypeId,
promptString: data.promptString,
description: data.description,
remark: data.remark,
status: data.status,
version: data.version,
}
})
if (res.code != 1) {
throw new Error(res.message);
}
return res.data as string;
}
/**
*
* @param id ID
* @param params
*/
export async function ModifyPrompt(id: string, params: Prompt.PromptItem): Promise<void> {
let res = await request(`/lms/Prompt/ModifyPrompt/${id}`, {
method: "POST",
data: {
...params
}
})
if (res.code != 1) {
throw new Error(res.message);
}
}
export async function getPromptDetail(id: string): Promise<API.SuccessItem | API.ErrorItem> {
try {
debugger
return await request(`/api/Prompt/GetPromptDetailById/${id}`, {
method: 'GET',
});
} catch (error: any) {
return errorMessage(error.toString())
/**
*
* @param id ID
*/
export async function DeletePrompt(id: string): Promise<void> {
let res = await request(`/lms/Prompt/DeletePrompt/${id}`, {
method: 'DELETE',
})
if (res.code != 1) {
throw new Error(res.message);
}
}
export async function addPrompt(data: Prompt.AddPrompt): Promise<API.SuccessItem | API.ErrorItem> {
try {
return await request('/api/Prompt/AddPromptString', {
method: 'POST',
data: {
name: data.name,
promptTypeId: data.promptTypeId,
promptTypeCode: data.promptTypeCode,
promptString: data.promptString,
description: data.description,
remark: data.remark,
}
});
} catch (error: any) {
return errorMessage(error.toString())
}
}
export async function modifyPrompt(data: Prompt.AddPrompt): Promise<API.SuccessItem | API.ErrorItem> {
try {
return await request('/api/Prompt/ModifyPromptString', {
method: 'POST',
data: {
id: data.id,
name: data.name,
promptTypeId: data.promptTypeId,
promptTypeCode: data.promptTypeCode,
promptString: data.promptString,
description: data.description,
remark: data.remark,
status: data.status,
version: data.version,
}
});
} catch (error: any) {
return errorMessage(error.toString())
}
}
//#endregion
@ -85,60 +100,101 @@ export async function modifyPrompt(data: Prompt.AddPrompt): Promise<API.SuccessI
* @param current
* @returns
*/
export async function getPrompyType(pageSize: number | undefined, current: number | undefined): Promise<API.SuccessItem | API.ErrorItem> {
try {
return await request(`/api/Prompt/GetPromptType/${pageSize}/${current}`, {
method: 'GET',
});
} catch (error: any) {
return errorMessage(error.toString())
export async function QueryPromptypeCollection(param: Prompt.PromptTypeQueryCondition): Promise<Prompt.PromptTypeResponse> {
let query = objectToQueryString(param)
let res = await request<ApiResponse.SuccessItem<Prompt.PromptTypeResponse>>(`/lms/Prompt/QueryPromptypeCollection/?${query}`, {
method: 'GET',
});
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
/**
* ID和Name
* @returns
*/
export async function GetPromptTypeOptions(): Promise<Prompt.PromptTypeOptions[]> {
let res = await request<ApiResponse.SuccessItem<Prompt.PromptTypeOptions[]>>(`/lms/Prompt/GetPromptTypeOptions`, {
method: 'GET',
});
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
/**
* ID
* @param id ID
* @returns
*/
export async function GetPromptTypeInfo(id: string): Promise<Prompt.PromptTypeItem> {
let res = await request<ApiResponse.SuccessItem<Prompt.PromptTypeItem>>(`/lms/Prompt/GetPromptTypeInfo/${id}`, {
method: 'GET',
});
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
/**
*
* @param data
*/
export async function addPromptType(data: Prompt.AddPromptType): Promise<API.SuccessItem | API.ErrorItem> {
try {
let res = await request('/api/Prompt/AddPromptType', {
method: 'POST',
data: {
name: data.name,
code: data.code,
remark: data.remark,
}
})
return res
} catch (error: any) {
return errorMessage(error.toString())
export async function AddPromptType(data: Prompt.AddPromptType): Promise<string> {
let res = await request('/lms/Prompt/AddPromptType', {
method: 'POST',
data: {
name: data.name,
code: data.code,
status: data.status,
remark: data.remark,
}
})
if (res.code != 1) {
throw new Error(res.message);
}
return res.data as string;
}
/**
*
* @param data
*/
export async function editPromptType(data: Prompt.AddPromptType): Promise<API.SuccessItem | API.ErrorItem> {
try {
debugger
let res = await request('/api/Prompt/ModifyPromptType', {
method: 'POST',
data: {
id: data.id,
name: data.name,
code: data.code,
remark: data.remark,
status: data.status,
}
})
return res;
} catch (error: any) {
console.log(error)
return errorMessage("修改提示词数据失败")
export async function EditPromptType(data: Prompt.AddPromptType): Promise<string> {
let id = data.id;
if (id == null || id == undefined) {
throw new Error("修改提示词类型的ID不能为空")
}
let res = await request(`/lms/Prompt/ModityPromptType/${id}`, {
method: 'POST',
data: {
name: data.name,
code: data.code,
remark: data.remark,
status: data.status,
}
})
if (res.code != 1) {
throw new Error(res.message);
}
return res.data as string;
}
/**
*
* @param id ID
* @param deletePrompt
* @returns
*/
export async function DeletePromptType(id: string, deletePrompt: boolean): Promise<API.SuccessItem> {
let res = await request(`/lms/Prompt/DeletePromptType/${id}/${deletePrompt}`, {
method: 'DELETE',
})
return res;
}
//#endregion

View File

@ -21,7 +21,6 @@ export async function QueryRoleList(tableParams: TableModel.TableParams, rolePar
let res = await request<ApiResponse.SuccessItem<RoleModel.QueryRoleData>>(`/lms/Role/QueryRoleCollection?${query}`, {
method: 'GET',
});
console.log("QueryRoleList", res);
if (res.code != 1) {
throw new Error(res.message);
}
@ -35,7 +34,6 @@ export async function QueryRoleOption() {
let res = await request<ApiResponse.SuccessItem<string[]>>(`/lms/Role/QueryRoleOption`, {
method: 'GET',
});
console.log("QueryRoleOption", res);
if (res.code != 1) {
throw new Error(res.message);
}
@ -49,11 +47,9 @@ export async function QueryRoleOption() {
* @returns
*/
export async function GetRoleById(roleId: number): Promise<RoleModel.Collection> {
console.log("GetRoleById", roleId);
let res = await request<ApiResponse.SuccessItem<RoleModel.Collection>>(`/lms/Role/QueryRoleById/${roleId}`, {
method: 'GET',
});
console.log("GetRoleByIdRes", res);
if (res.code != 1) {
throw new Error(res.message);
}
@ -74,7 +70,6 @@ export async function UpdeteRole(roleId: number, roleName: string, roleRemark: s
remark: roleRemark
}
});
console.log("UpdeteRole", res);
if (res.code != 1) {
throw new Error(res.message);
}
@ -89,7 +84,6 @@ export async function DeleteRoleById(roleId: number): Promise<void> {
let res = await request<ApiResponse.SuccessItem<void>>(`/lms/Role/DeleteRole/${roleId}`, {
method: 'DELETE',
});
console.log("DeleteRole", res);
if (res.code != 1) {
throw new Error(res.message);
}
@ -111,7 +105,6 @@ export async function AddRole(roleNmae: string, roleRemark: string): Promise<voi
remark: roleRemark
}
});
console.log("AddRole", res);
if (res.code != 1) {
throw new Error(res.message);
}

View File

@ -0,0 +1,96 @@
import { request } from '@umijs/max';
import { objectToQueryString } from './common';
/**
*
* @param userId
* @returns
*/
async function GetUserSoftwareControlCount(userId: number) {
let res = await request<ApiResponse.SuccessItem<number>>(`/lms/SoftWare/GetUserSoftwareControlCount/${userId}`, {
method: 'GET',
});
if (res.code == 1) {
return res.data;
}
throw new Error(res.message);
}
/**
*
* @param userId
* @returns
*/
async function ApplyUserSoftwareControl(userId: number) {
let res = await request<ApiResponse.SuccessItem<boolean>>(`/lms/SoftWare/ApplySoftwareControl/${userId}`, {
method: 'POST',
});
if (res.code == 1) {
return res.data;
}
throw new Error(res.message);
}
/**
*
* @param tableParams
* @param queryParams
* @returns
*/
async function GetUserSoftwareControlCollection(tableParams: TableModel.TableParams, queryParams: SoftwareModel.SoftwareControlQueryParams) {
debugger
let data = {
...queryParams,
page: tableParams.pagination?.current,
pageSize: tableParams.pagination?.pageSize,
}
let query = objectToQueryString(data)
let res = await request<ApiResponse.SuccessItem<SoftwareModel.QuerySoftwareControlData>>(`/lms/SoftWare/GetSoftwareControlCollection?${query}`, {
method: 'GET',
});
if (res.code != 1) {
throw new Error(res.message);
}
return res.data;
}
async function AddSoftwareControlExpirationTime(id: string, days: number, isForever: boolean, isTry = false) {
let res = await request<ApiResponse.SuccessItem<null>>(`/lms/SoftWare/ModifySoftwareControlValidity/${id}`, {
method: 'POST',
data: {
expirationTime: days,
isForever: isForever,
isTry: isTry
}
});
if (res.code != 1) {
throw new Error(res.message);
}
}
export const SoftwareControl = {
GetUserSoftwareControlCount,
ApplyUserSoftwareControl,
GetUserSoftwareControlCollection,
AddSoftwareControlExpirationTime
}
/**
*
* @returns
*/
async function GetSoftwareBaseCollection() {
let res = await request<ApiResponse.SuccessItem<SoftwareModel.SoftwareBasicInfo[]>>(`/lms/SoftWare/GetSoftwareBaseCollection`, {
method: 'GET',
});
if (res.code == 1) {
return res.data;
}
throw new Error(res.message);
}
export const Software = {
GetSoftwareBaseCollection
}

View File

@ -74,7 +74,6 @@ async function QueryUserList(tableParams: TableModel.TableParams, userParams: Us
if (res.code != 1) {
throw new Error(res.message);
}
console.log("QueryUserList", res);
return res.data;
}
@ -92,6 +91,10 @@ async function EnableAgent() {
}
}
/**
*
* @returns
*/
async function GetUserAgentInfo(): Promise<UserModel.UserAgentInfo> {
let res = await request<ApiResponse.SuccessItem<UserModel.UserAgentInfo>>(`/lms/User/GetUserAgentInfo`, {
method: 'GET',
@ -102,10 +105,28 @@ async function GetUserAgentInfo(): Promise<UserModel.UserAgentInfo> {
return res.data;
}
export {
/**
*
* @param userId
* @param newPassword
*/
async function ResetUserPassword(userId: number, newPassword: string): Promise<void> {
let res = await request<ApiResponse.SuccessItem<null>>(`/lms/User/ResetPassword/${userId}`, {
method: 'post',
data: {
newPassword: newPassword,
}
});
if (res.code != 1) {
throw new Error(res.message);
}
}
export const UserInfo = {
QueryUserList,
GetUserInfo,
UpdatedUserInfo,
EnableAgent,
GetUserAgentInfo,
ResetUserPassword,
}

View File

@ -18,6 +18,13 @@ declare namespace AccessType {
isAdminOrSuperAdmin: boolean;
//#endregion
//#region 软件配置项操作权限
/** 是不是可以操作配置型 */
canOptions: boolean;
/** 是不是可以操作软件配置项 */
canLaitoolOptions: boolean;
//#endregion
//#region 机器权限
/** 是不是显示机器码管理的菜单 */
canMachineManagement: boolean
@ -32,5 +39,14 @@ declare namespace AccessType {
/** 是不是有停用机器码的权限 */
canDisableMachine: boolean;
//#endregion
//#region 软件控制权限
/** 是否可以同步用户软件控制权限 */
canApplySoftwareControl: boolean;
/** 是否可以查看软件控制管理 */
canSofrwareControlManagement: boolean;
//#endregion
}
}

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;
}
}

View File

@ -1,25 +1,6 @@
declare namespace Prompt {
type PromptListItem = {
key: string;
id: string;
name: string;
promptTypeId: string;
promptTypeCode: string;
promptString: string;
description?: string;
remark?: string;
createUser: API.SubUserResponse;
createTime: Date;
updateUser: API.SubUserResponse;
updateTime: Date;
status: string;
version: number;
count: number;
}
type AddPrompt = {
id?: string;
name: string;
@ -36,23 +17,89 @@ declare namespace Prompt {
version: number;
}
//#region 提示词相关
type PromptTypeListItem = {
key: string;
/** 查询提示词预设的参数模型结构 */
type PromptQueryCondition = {
page: number;
pageSize: number;
name?: string;
promptTypeId?: string;
status?: string;
remark?: string;
}
type PromptItem = {
id: string;
name: string;
promptTypeId: string;
promptTypeCode: string;
promptString: string;
description?: string;
remark?: string;
createdUser: UserModel.UserBasic;
createTime: Date;
updatedUser: UserModel.UserBasic;
updateTime: Date;
status: string;
version: number;
promptType: PromptTypeBasic;
}
/** 返回的提示词类型的完整数据数据结构 */
type PromptResponse = {
collection: PromptItem[];
total: number;
current: number;
}
//#endregion
//#region 提示词类型相关
interface PromptTypeBasic {
id: string;
name: string;
code: string;
remark?: string;
status: string;
createUser: API.SubUserResponse;
createTime: Date;
updateUser: API.SubUserResponse;
updateTime: Date;
coubt: number;
label: string;
value: string;
}
type PromptTypeOptions = {
id: string;
name: string;
}
/** 提示词类型的collection的数据结构 */
interface PromptTypeItem extends PromptTypeBasic {
id: string;
name: string;
code: string;
remark?: string;
status: string;
createdUser: UserModel.UserBasic;
createTime: Date;
updatedUser: UserModel.UserBasic;
updateTime: Date;
}
/** 查询提示词的参数模型结构 */
type PromptTypeQueryCondition = {
page: number;
pageSize: number;
name?: string;
code?: string;
status?: string;
remark?: string;
}
/** 返回的提示词类型的完整数据数据结构 */
type PromptTypeResponse = {
collection: PromptTypeItem[];
total: number;
current: number;
}
type AddPromptType = {
id?: string;
@ -64,4 +111,5 @@ declare namespace Prompt {
updateUser: API.SubUserResponse;
updateTime: Date;
}
//#endregion
}

47
src/services/typing/software.d.ts vendored Normal file
View File

@ -0,0 +1,47 @@
declare namespace SoftwareModel {
interface SoftwareBasicInfo {
id: string;
softwareName: string;
softwareCode: string;
isUse: boolean;
}
interface SoftwareBase {
id: string;
softwareName: string;
softwareCode: string;
createdUserId: number;
updatedUserId: number;
createdTime: Date;
updatedTime: Date;
remark: string;
isUse: boolean;
}
interface SoftwareControlBase {
id: string;
user: UserModel.UserBasic;
software: SoftwareBase;
createdUser: UserModel.UserBasic;
createdTime: Date;
updatedUser: UserModel.UserBasic;
updatedTime: Date;
remark: string;
expirationTime: string;
isForever: boolean;
}
type SoftwareControlQueryParams = {
userId?: number;
softwareId?: string;
isForever?: boolean;
remark?: string
};
type QuerySoftwareControlData = {
collection: SoftwareControlBase[];
current: number;
total: number;
}
}

View File

@ -29,21 +29,26 @@ declare namespace UserModel {
affiliateMoney: number,
}
//#region 用户基本的数据信息
interface UserInfo {
interface UserBasic {
id: number;
nickName: string;
userName: string;
avatar?: string;
email: string;
phoneNumber?: string;
}
//#region 用户基本的数据信息
interface UserInfo extends UserBasic {
roleNames: string[];
allDeviceCount: number;
agentPercent: number;
freeCount: number;
affiliateCode: string;
createdDate: Date;
options: userOptions
options: userOptions;
remark?: string;
}
type userOptions = {

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

@ -1,14 +1,26 @@
import { create } from 'zustand';
import defaultSettings from '../../config/defaultSettings';
export const useSoftStore = create((set: (arg0: any) => any) => ({
interface SoftStoreState {
token: string | null;
currentUser: any;
settings: typeof defaultSettings;
topSpinning: boolean;
topSpinTip: string;
setTopSpinning: (spinning: boolean) => void;
setTopSpinTip: (tip: string) => void;
}
const createSoftStore = (initialState: Partial<SoftStoreState> = {}) => create<SoftStoreState>((set) => ({
token: null,
currentUser: null,
settings: defaultSettings,
topSpinning: false,
setTopSpinning: (spinning: boolean) => set({ topSpinning: spinning }),
topSpinTip: "加载中...",
setTopSpinning: (spinning: boolean) => set({ topSpinning: spinning }),
setTopSpinTip: (tip: string) => set({ topSpinTip: tip }),
})) as any;
...initialState, // 合并初始状态
}));
export const useSoftStore = createSoftStore();
export default createSoftStore;

25
src/util/password.ts Normal file
View File

@ -0,0 +1,25 @@
export function generateRandomPassword(): string {
const upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lowerCase = 'abcdefghijklmnopqrstuvwxyz';
const numbers = '0123456789';
const special = '@$!%*?.&';
let password = '';
// Ensure at least one character from each category
password += upperCase[Math.floor(Math.random() * upperCase.length)];
password += lowerCase[Math.floor(Math.random() * lowerCase.length)];
password += numbers[Math.floor(Math.random() * numbers.length)];
password += special[Math.floor(Math.random() * special.length)];
// Fill the rest randomly
const allChars = upperCase + lowerCase + numbers + special;
for (let i = password.length; i < 12; i++) {
password += allChars[Math.floor(Math.random() * allChars.length)];
}
// Shuffle the password
password = password.split('').sort(() => Math.random() - 0.5).join('');
return password;
}