style: help menu
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
const child_process = require('child_process');
|
||||
|
||||
export const getBranchInfo = () => {
|
||||
const latestCommit = child_process
|
||||
.execSync('git rev-parse HEAD')
|
||||
.toString()
|
||||
.trim();
|
||||
const versionTag = child_process
|
||||
.execSync(`git tag --contains ${latestCommit}`)
|
||||
.toString()
|
||||
.trim();
|
||||
return { version: versionTag, commitId: latestCommit };
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
const users = [
|
||||
{ id: 0, name: 'Umi', nickName: 'U', gender: 'MALE' },
|
||||
{ id: 1, name: 'Fish', nickName: 'B', gender: 'FEMALE' },
|
||||
];
|
||||
|
||||
export default {
|
||||
'GET /api/v1/queryUserList': (req: any, res: any) => {
|
||||
res.json({
|
||||
success: true,
|
||||
data: { list: users },
|
||||
errorCode: 0,
|
||||
});
|
||||
},
|
||||
'PUT /api/v1/user/': (req: any, res: any) => {
|
||||
res.json({
|
||||
success: true,
|
||||
errorCode: 0,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { IApi } from '@umijs/max';
|
||||
|
||||
export default (api: IApi) => {
|
||||
api.modifyHTML(($) => {
|
||||
console.log('pllugins=========modifyHTML', $);
|
||||
return $;
|
||||
});
|
||||
api.onStart(() => {
|
||||
|
||||
@@ -168,12 +168,12 @@ export const gaugeItemConfig = {
|
||||
},
|
||||
rich: {
|
||||
value: {
|
||||
fontSize: 20,
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
color: 'rgba(0, 0, 0, 0.88)'
|
||||
},
|
||||
unit: {
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
color: 'rgba(0, 0, 0, 0.88)',
|
||||
fontWeight: '500',
|
||||
padding: [0, 0, 0, 2]
|
||||
|
||||
@@ -59,8 +59,9 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
|
||||
e.target.value = e.target.value?.trim?.();
|
||||
if (!inputRef.current?.input?.value) {
|
||||
setIsFocus(false);
|
||||
props.onBlur?.(e);
|
||||
}
|
||||
props.onChange?.(e);
|
||||
props.onBlur?.(e);
|
||||
};
|
||||
|
||||
const handleInput = (e: any) => {
|
||||
|
||||
+16
-13
@@ -273,7 +273,8 @@ body {
|
||||
}
|
||||
|
||||
.ant-pro-sider-logo {
|
||||
padding-left: 20px;
|
||||
padding-left: 18px;
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.ant-pro-sider-logo-collapsed {
|
||||
@@ -320,20 +321,12 @@ body {
|
||||
|
||||
.ant-pro-layout {
|
||||
height: 100vh;
|
||||
// .ant-pro-layout-content {
|
||||
// padding-block: var(--layout-content-blockpadding);
|
||||
// padding-inline: var(--layout-content-inlinepadding);
|
||||
// }
|
||||
|
||||
.ant-pro-page-container-children-container {
|
||||
padding-block: var(--layout-content-blockpadding);
|
||||
padding-inline: var(--layout-content-inlinepadding);
|
||||
}
|
||||
|
||||
.ant-pro-sider-logo {
|
||||
padding-left: 22px;
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.ant-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -382,6 +375,18 @@ body {
|
||||
}
|
||||
}
|
||||
// ======== menu style end ============
|
||||
.ant-menu-submenu-popup {
|
||||
.ant-menu-sub {
|
||||
.ant-menu-item-only-child {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
|
||||
.anticon {
|
||||
font-size: var(--font-size-middle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-menu-container {
|
||||
.user-avatar.ant-menu-submenu {
|
||||
@@ -425,9 +430,7 @@ body {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
// background: url('@/assets/images/bg-11.png') center center no-repeat;
|
||||
// background-size: cover;
|
||||
background-color: #fff;
|
||||
background-color: var(--color-white-1);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
|
||||
@@ -21,15 +21,14 @@ export default function useContainerScroll(
|
||||
scrollHeight > clientHeight + scrollTop
|
||||
) {
|
||||
scroller.current.current.scrollTop = scrollHeight;
|
||||
toBottomFlag.current = false;
|
||||
// toBottomFlag.current = false;
|
||||
isWheeled.current = false;
|
||||
}
|
||||
if (
|
||||
} else if (
|
||||
!isWheeled.current &&
|
||||
scrollHeight > clientHeight + scrollTop &&
|
||||
scroller.current?.current
|
||||
) {
|
||||
scroller.current.current.scrollTop += 5;
|
||||
scroller.current.current.scrollTop += 10;
|
||||
window.requestAnimationFrame(scrollerRun);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
import avatarImg from '@/assets/images/avatar.png';
|
||||
import langConfigMap from '@/locales/lang-config-map';
|
||||
import {
|
||||
DiscordOutlined,
|
||||
GithubOutlined,
|
||||
GlobalOutlined,
|
||||
HomeOutlined,
|
||||
InfoCircleOutlined,
|
||||
LogoutOutlined,
|
||||
QuestionCircleOutlined,
|
||||
ReadOutlined,
|
||||
SettingOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { getAllLocales, history, setLocale } from '@umijs/max';
|
||||
@@ -62,6 +68,66 @@ export function getRightRenderContent(opts: {
|
||||
);
|
||||
}
|
||||
|
||||
const helpList = [
|
||||
{
|
||||
key: 'site',
|
||||
icon: <HomeOutlined />,
|
||||
label: 'GPUStack',
|
||||
url: 'https://gpustack.ai/'
|
||||
},
|
||||
{
|
||||
key: 'github',
|
||||
icon: <GithubOutlined />,
|
||||
label: 'GitHub'
|
||||
},
|
||||
{
|
||||
key: 'Discord',
|
||||
icon: <DiscordOutlined />,
|
||||
label: 'Discord'
|
||||
},
|
||||
{
|
||||
key: 'docs',
|
||||
icon: <ReadOutlined />,
|
||||
label: intl.formatMessage({ id: 'common.button.docs' })
|
||||
},
|
||||
{
|
||||
key: 'version',
|
||||
icon: <InfoCircleOutlined />,
|
||||
label: intl.formatMessage({ id: 'common.button.version' })
|
||||
}
|
||||
];
|
||||
|
||||
const helpMenu = {
|
||||
selectedKeys: [],
|
||||
className: collapsed
|
||||
? 'user-menu-container user-menu-collapsed'
|
||||
: 'user-menu-container',
|
||||
mode: 'vertical',
|
||||
expandIcon: false,
|
||||
inlineCollapsed: collapsed,
|
||||
triggerSubMenuAction: 'hover',
|
||||
items: [
|
||||
{
|
||||
key: 'help',
|
||||
icon: <QuestionCircleOutlined />,
|
||||
label: (
|
||||
<span>{intl?.formatMessage?.({ id: 'common.button.help' })}</span>
|
||||
),
|
||||
children: helpList.map((item) => ({
|
||||
key: item.key,
|
||||
label: (
|
||||
<span className="flex flex-center">
|
||||
{item.icon}
|
||||
<a className="m-l-8" href="#" target="_blank">
|
||||
{item.label}
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
}))
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const langMenu = {
|
||||
selectedKeys: [],
|
||||
className: collapsed
|
||||
@@ -84,7 +150,9 @@ export function getRightRenderContent(opts: {
|
||||
key,
|
||||
label: (
|
||||
<span className="flex flex-center">
|
||||
<span>{_.get(langConfigMap, [key, 'icon'])}</span>
|
||||
<span className="font-size-14">
|
||||
{_.get(langConfigMap, [key, 'icon'])}
|
||||
</span>
|
||||
<span className="m-l-8">
|
||||
{_.get(langConfigMap, [key, 'label'])}
|
||||
</span>
|
||||
@@ -139,7 +207,7 @@ export function getRightRenderContent(opts: {
|
||||
label: (
|
||||
<span className="flex flex-center">
|
||||
<SettingOutlined />
|
||||
<span>
|
||||
<span className="m-l-8" style={{ marginLeft: 8 }}>
|
||||
{intl?.formatMessage?.({ id: 'common.button.settings' })}
|
||||
</span>
|
||||
</span>
|
||||
@@ -153,7 +221,7 @@ export function getRightRenderContent(opts: {
|
||||
label: (
|
||||
<span className="flex flex-center">
|
||||
<LogoutOutlined />
|
||||
<span>
|
||||
<span className="m-l-8" style={{ marginLeft: 8 }}>
|
||||
{intl?.formatMessage?.({ id: 'common.button.logout' })}
|
||||
</span>
|
||||
</span>
|
||||
@@ -169,6 +237,12 @@ export function getRightRenderContent(opts: {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Menu
|
||||
{...helpMenu}
|
||||
style={{
|
||||
width: collapsed ? 64 : `calc(${siderWidth}px - 16px)`
|
||||
}}
|
||||
></Menu>
|
||||
<Menu
|
||||
{...langMenu}
|
||||
style={{
|
||||
|
||||
@@ -187,5 +187,9 @@ export default {
|
||||
'common.form.password': 'Password',
|
||||
'common.form.username': 'Username',
|
||||
'common.login.rember': 'Remember me',
|
||||
'settings.company': 'SEAL'
|
||||
'settings.company': 'SEAL',
|
||||
'common.button.help': 'Help',
|
||||
'common.button.feedback': 'Feedback',
|
||||
'common.button.docs': 'Documentation',
|
||||
'common.button.version': 'Version'
|
||||
};
|
||||
|
||||
@@ -14,5 +14,15 @@ export default {
|
||||
'playground.prompt': 'Prompt',
|
||||
'playground.tokenusage': 'Token Usage',
|
||||
'models.openinplayground': 'Open in Playground',
|
||||
'playground.tokenoutput': 'Output'
|
||||
'playground.tokenoutput': 'Output',
|
||||
'playground.params.temperature.tips':
|
||||
'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
|
||||
'playground.params.maxtokens.tips':
|
||||
"The maximum number of tokens to generated. The total length of input tokens and generated tokens is limited by the model's context length.",
|
||||
'playground.params.topp.tips':
|
||||
'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered.',
|
||||
'playground.params.seed.tips':
|
||||
'Specify a random seed to ensure deterministic sampling. Using the same seed and parameters will produce the same results for repeated requests.',
|
||||
'playground.params.stop.tips':
|
||||
'A stop sequence is a predefined or user-specified text string that signals the AI to stop generating further tokens when these sequences appear.'
|
||||
};
|
||||
|
||||
@@ -180,5 +180,9 @@ export default {
|
||||
'common.form.password': '密码',
|
||||
'common.form.username': '用户名',
|
||||
'common.login.rember': '记住我',
|
||||
'settings.company': '数澈软件'
|
||||
'settings.company': '数澈软件',
|
||||
'common.button.help': '帮助',
|
||||
'common.button.feedback': '反馈',
|
||||
'common.button.docs': '文档',
|
||||
'common.button.version': '版本'
|
||||
};
|
||||
|
||||
@@ -14,5 +14,15 @@ export default {
|
||||
'playground.prompt': '提示',
|
||||
'playground.tokenusage': 'Token 使用量',
|
||||
'models.openinplayground': '打开试验场',
|
||||
'playground.tokenoutput': '输出'
|
||||
'playground.tokenoutput': '输出',
|
||||
'playground.params.temperature.tips':
|
||||
'控制随机性:降低温度会导致更少的随机完成。当温度接近零时,模型将变得确定性和重复性。',
|
||||
'playground.params.maxtokens.tips':
|
||||
'生成的最大 token 数。输入标记和生成的标记的总长度受模型上下文长度的限制。',
|
||||
'playground.params.topp.tips':
|
||||
'通过核采样控制多样性:0.5 表示考虑所有基于概率权重选项的一半。',
|
||||
'playground.params.seed.tips':
|
||||
'指定随机种子以确保确定性采样。使用相同的种子和参数对于重复请求将产生相同的结果。',
|
||||
'playground.params.stop.tips':
|
||||
'停止序列是一个预定义或用户指定的文本字符串,当这些序列出现时,它会提示 AI 停止生成后续的标记。'
|
||||
};
|
||||
|
||||
@@ -70,7 +70,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
<RowChildren key={`${item.id}_row`}>
|
||||
<Row style={{ width: '100%' }} align="middle">
|
||||
<Col span={4}>{item.name}</Col>
|
||||
<Col span={3}>{getWorkerIp(item)}</Col>
|
||||
<Col span={3}>{item.worker_name || '-'}</Col>
|
||||
<Col span={4}>
|
||||
<span>
|
||||
{item.source === 'huggingface'
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface ModelInstanceListItem {
|
||||
download_progress: number;
|
||||
model_id: number;
|
||||
model_name: string;
|
||||
worker_name: string;
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
|
||||
@@ -60,7 +60,7 @@ const renderWelCome = (intl: any) => {
|
||||
const LoginForm = () => {
|
||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
|
||||
const { initialState, setInitialState } = useModel('@@initialState');
|
||||
const { initialState, setInitialState } = useModel('@@initialState') || {};
|
||||
const intl = useIntl();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
PlusOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Col, Dropdown, Row, Space } from 'antd';
|
||||
import { Button, Col, Row, Space } from 'antd';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { Roles } from '../config';
|
||||
import '../style/chat-footer.less';
|
||||
@@ -22,6 +22,7 @@ interface ChatFooterProps {
|
||||
disabled?: boolean;
|
||||
feedback?: React.ReactNode;
|
||||
hasTokenResult?: boolean;
|
||||
selectedModel?: string;
|
||||
}
|
||||
|
||||
const ChatFooter: React.FC<ChatFooterProps> = (props) => {
|
||||
@@ -35,7 +36,8 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
|
||||
onView,
|
||||
feedback,
|
||||
disabled,
|
||||
hasTokenResult
|
||||
hasTokenResult,
|
||||
selectedModel
|
||||
} = props;
|
||||
useHotkeys(
|
||||
HotKeys.SUBMIT.join(','),
|
||||
@@ -58,15 +60,25 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
|
||||
<Row style={{ width: '100%' }}>
|
||||
<Col span={hasTokenResult ? 8 : 12}>
|
||||
<Space size={20}>
|
||||
<Dropdown
|
||||
{/* <Dropdown
|
||||
menu={{ items: MessageRoles, onClick: onNewMessage }}
|
||||
placement="topLeft"
|
||||
>
|
||||
<Button disabled={disabled} icon={<PlusOutlined />}>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={onNewMessage}
|
||||
>
|
||||
{intl.formatMessage({ id: 'playground.newMessage' })}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
|
||||
</Dropdown> */}
|
||||
<Button
|
||||
disabled={disabled}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={onNewMessage}
|
||||
>
|
||||
{intl.formatMessage({ id: 'playground.newMessage' })}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<DeleteOutlined></DeleteOutlined>}
|
||||
onClick={onClear}
|
||||
@@ -87,7 +99,11 @@ const ChatFooter: React.FC<ChatFooterProps> = (props) => {
|
||||
{intl.formatMessage({ id: 'playground.viewcode' })}
|
||||
</Button>
|
||||
{!disabled ? (
|
||||
<Button type="primary" disabled={disabled} onClick={onSubmit}>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={disabled || !selectedModel}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
{intl.formatMessage({ id: 'common.button.submit' })}
|
||||
<span className="m-l-5 opct-7">
|
||||
{platform.isMac ? (
|
||||
|
||||
@@ -56,11 +56,10 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
const setMessageId = () => {
|
||||
messageId.current = messageId.current + 1;
|
||||
};
|
||||
const handleNewMessage = (role: any) => {
|
||||
const handleNewMessage = (role?: any) => {
|
||||
messageList.push({
|
||||
// role:
|
||||
// _.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User,
|
||||
role: role.key,
|
||||
role:
|
||||
_.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User,
|
||||
content: '',
|
||||
uid: messageId.current + 1
|
||||
});
|
||||
@@ -142,11 +141,14 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
if (!messageList.length) {
|
||||
return;
|
||||
}
|
||||
const headItem = _.cloneDeep(_.get(messageList, '0'));
|
||||
headItem.content = '';
|
||||
headItem.role = Roles.User;
|
||||
headItem.uid = messageId.current + 1;
|
||||
setMessageList(headItem ? [headItem] : []);
|
||||
setMessageId();
|
||||
setMessageList([
|
||||
{
|
||||
role: Roles.User,
|
||||
content: '',
|
||||
uid: messageId.current
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
const handleView = () => {
|
||||
@@ -261,6 +263,7 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
onView={handleView}
|
||||
onStop={handleStopConversation}
|
||||
disabled={loading}
|
||||
selectedModel={parameters.model}
|
||||
hasTokenResult={!!tokenResult}
|
||||
feedback={<ReferenceParams usage={tokenResult}></ReferenceParams>}
|
||||
></ChatFooter>
|
||||
|
||||
@@ -2,8 +2,9 @@ import FieldWrapper from '@/components/seal-form/field-wrapper';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import { INPUT_WIDTH } from '@/constants';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form, InputNumber, Slider } from 'antd';
|
||||
import { Form, InputNumber, Slider, Tooltip } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { queryModelsList } from '../apis';
|
||||
@@ -100,13 +101,27 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
form.setFieldsValue(initialValues);
|
||||
setParams(initialValues);
|
||||
};
|
||||
const renderLabel = (args: { field: string; label: string }) => {
|
||||
const renderLabel = (args: {
|
||||
field: string;
|
||||
label: string;
|
||||
description: string;
|
||||
}) => {
|
||||
return (
|
||||
<span
|
||||
className={CustomLabelStyles.label}
|
||||
style={{ width: INPUT_WIDTH.mini }}
|
||||
>
|
||||
<span className="text">{args.label}</span>
|
||||
<span className="text">
|
||||
<span> {args.label}</span>
|
||||
{args.description && (
|
||||
<span className="m-l-5">
|
||||
<Tooltip title={args.description}>
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
||||
<InputNumber
|
||||
className="label-val"
|
||||
variant="outlined"
|
||||
@@ -150,7 +165,10 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
<FieldWrapper
|
||||
label={renderLabel({
|
||||
field: 'temperature',
|
||||
label: 'Temperature'
|
||||
label: 'Temperature',
|
||||
description: intl.formatMessage({
|
||||
id: 'playground.params.temperature.tips'
|
||||
})
|
||||
})}
|
||||
style={{ paddingInline: 0 }}
|
||||
variant="borderless"
|
||||
@@ -173,7 +191,10 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
<FieldWrapper
|
||||
label={renderLabel({
|
||||
field: 'max_tokens',
|
||||
label: 'Max Tokens'
|
||||
label: 'Max Tokens',
|
||||
description: intl.formatMessage({
|
||||
id: 'playground.params.maxtokens.tips'
|
||||
})
|
||||
})}
|
||||
style={{ paddingInline: 0 }}
|
||||
variant="borderless"
|
||||
@@ -196,7 +217,10 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
<FieldWrapper
|
||||
label={renderLabel({
|
||||
field: 'top_p',
|
||||
label: 'Top P'
|
||||
label: 'Top P',
|
||||
description: intl.formatMessage({
|
||||
id: 'playground.params.topp.tips'
|
||||
})
|
||||
})}
|
||||
style={{ paddingInline: 0 }}
|
||||
variant="borderless"
|
||||
@@ -219,7 +243,10 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
<FieldWrapper
|
||||
label={renderLabel({
|
||||
field: 'seed',
|
||||
label: 'Seed'
|
||||
label: 'Seed',
|
||||
description: intl.formatMessage({
|
||||
id: 'playground.params.seed.tips'
|
||||
})
|
||||
})}
|
||||
style={{ paddingInline: 0 }}
|
||||
variant="borderless"
|
||||
@@ -240,6 +267,9 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
>
|
||||
<SealInput.Input
|
||||
label="Stop Sequence"
|
||||
description={intl.formatMessage({
|
||||
id: 'playground.params.stop.tips'
|
||||
})}
|
||||
style={{ width: INPUT_WIDTH.mini }}
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Modal } from 'antd';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
|
||||
interface CreateFormProps {
|
||||
modalVisible: boolean;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const CreateForm: React.FC<PropsWithChildren<CreateFormProps>> = (props) => {
|
||||
const { modalVisible, onCancel } = props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
title="新建"
|
||||
width={420}
|
||||
centered={true}
|
||||
open={modalVisible}
|
||||
onCancel={() => onCancel()}
|
||||
footer={null}
|
||||
>
|
||||
{props.children}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateForm;
|
||||
@@ -1,138 +0,0 @@
|
||||
import {
|
||||
ProFormDateTimePicker,
|
||||
ProFormRadio,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
StepsForm
|
||||
} from '@ant-design/pro-components';
|
||||
import { Modal } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
export interface FormValueType extends Partial<API.UserInfo> {
|
||||
target?: string;
|
||||
template?: string;
|
||||
type?: string;
|
||||
time?: string;
|
||||
frequency?: string;
|
||||
}
|
||||
|
||||
export interface UpdateFormProps {
|
||||
onCancel: (flag?: boolean, formVals?: FormValueType) => void;
|
||||
onSubmit: (values: FormValueType) => Promise<void>;
|
||||
updateModalVisible: boolean;
|
||||
values: Partial<API.UserInfo>;
|
||||
}
|
||||
|
||||
const UpdateForm: React.FC<UpdateFormProps> = (props) => (
|
||||
<StepsForm
|
||||
stepsProps={{
|
||||
size: 'small'
|
||||
}}
|
||||
stepsFormRender={(dom, submitter) => {
|
||||
return (
|
||||
<Modal
|
||||
width={640}
|
||||
bodyStyle={{ padding: '32px 40px 48px' }}
|
||||
destroyOnClose
|
||||
title="规则配置"
|
||||
open={props.updateModalVisible}
|
||||
footer={submitter}
|
||||
onCancel={() => props.onCancel()}
|
||||
>
|
||||
{dom}
|
||||
</Modal>
|
||||
);
|
||||
}}
|
||||
onFinish={props.onSubmit}
|
||||
>
|
||||
<StepsForm.StepForm
|
||||
initialValues={{
|
||||
name: props.values.name,
|
||||
nickName: props.values.nickName
|
||||
}}
|
||||
title="基本信息"
|
||||
>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="name"
|
||||
label="规则名称"
|
||||
rules={[{ required: true, message: '请输入规则名称!' }]}
|
||||
/>
|
||||
<ProFormTextArea
|
||||
name="desc"
|
||||
width="md"
|
||||
label="规则描述"
|
||||
placeholder="请输入至少五个字符"
|
||||
rules={[
|
||||
{ required: true, message: '请输入至少五个字符的规则描述!', min: 5 }
|
||||
]}
|
||||
/>
|
||||
</StepsForm.StepForm>
|
||||
<StepsForm.StepForm
|
||||
initialValues={{
|
||||
target: '0',
|
||||
template: '0'
|
||||
}}
|
||||
title="配置规则属性"
|
||||
>
|
||||
<ProFormSelect
|
||||
width="md"
|
||||
name="target"
|
||||
label="监控对象"
|
||||
valueEnum={{
|
||||
0: '表一',
|
||||
1: '表二'
|
||||
}}
|
||||
/>
|
||||
<ProFormSelect
|
||||
width="md"
|
||||
name="template"
|
||||
label="规则模板"
|
||||
valueEnum={{
|
||||
0: '规则模板一',
|
||||
1: '规则模板二'
|
||||
}}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
name="type"
|
||||
width="md"
|
||||
label="规则类型"
|
||||
options={[
|
||||
{
|
||||
value: '0',
|
||||
label: '强'
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '弱'
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</StepsForm.StepForm>
|
||||
<StepsForm.StepForm
|
||||
initialValues={{
|
||||
type: '1',
|
||||
frequency: 'month'
|
||||
}}
|
||||
title="设定调度周期"
|
||||
>
|
||||
<ProFormDateTimePicker
|
||||
name="time"
|
||||
label="开始时间"
|
||||
rules={[{ required: true, message: '请选择开始时间!' }]}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="frequency"
|
||||
label="监控对象"
|
||||
width="xs"
|
||||
valueEnum={{
|
||||
month: '月',
|
||||
week: '周'
|
||||
}}
|
||||
/>
|
||||
</StepsForm.StepForm>
|
||||
</StepsForm>
|
||||
);
|
||||
|
||||
export default UpdateForm;
|
||||
@@ -1,270 +0,0 @@
|
||||
import services from '@/services/demo';
|
||||
import {
|
||||
ActionType,
|
||||
FooterToolbar,
|
||||
PageContainer,
|
||||
ProDescriptions,
|
||||
ProDescriptionsItemProps,
|
||||
ProTable
|
||||
} from '@ant-design/pro-components';
|
||||
import { Button, Divider, Drawer, message } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import CreateForm from './components/CreateForm';
|
||||
import UpdateForm, { FormValueType } from './components/UpdateForm';
|
||||
|
||||
const { addUser, queryUserList, deleteUser, modifyUser } =
|
||||
services.UserController;
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields: API.UserInfo) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
await addUser({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: FormValueType) => {
|
||||
const hide = message.loading('正在配置');
|
||||
try {
|
||||
await modifyUser(
|
||||
{
|
||||
userId: fields.id || ''
|
||||
},
|
||||
{
|
||||
name: fields.name || '',
|
||||
nickName: fields.nickName || '',
|
||||
email: fields.email || ''
|
||||
}
|
||||
);
|
||||
hide();
|
||||
|
||||
message.success('配置成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('配置失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除节点
|
||||
* @param selectedRows
|
||||
*/
|
||||
const handleRemove = async (selectedRows: API.UserInfo[]) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRows) return true;
|
||||
try {
|
||||
await deleteUser({
|
||||
userId: selectedRows.find((row) => row.id)?.id || ''
|
||||
});
|
||||
hide();
|
||||
message.success('删除成功,即将刷新');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const TableList: React.FC<unknown> = () => {
|
||||
const [createModalVisible, handleModalVisible] = useState<boolean>(false);
|
||||
const [updateModalVisible, handleUpdateModalVisible] =
|
||||
useState<boolean>(false);
|
||||
const [stepFormValues, setStepFormValues] = useState({});
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [row, setRow] = useState<API.UserInfo>();
|
||||
const [selectedRowsState, setSelectedRows] = useState<API.UserInfo[]>([]);
|
||||
const columns: ProDescriptionsItemProps<API.UserInfo>[] = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
tip: '名称是唯一的 key',
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '名称为必填项'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickName',
|
||||
valueType: 'text'
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'gender',
|
||||
hideInForm: true,
|
||||
valueEnum: {
|
||||
0: { text: '男', status: 'MALE' },
|
||||
1: { text: '女', status: 'FEMALE' }
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
valueType: 'option',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
<a
|
||||
onClick={() => {
|
||||
handleUpdateModalVisible(true);
|
||||
setStepFormValues(record);
|
||||
}}
|
||||
>
|
||||
配置
|
||||
</a>
|
||||
<Divider type="vertical" />
|
||||
<a href="">订阅警报</a>
|
||||
</>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: 'CRUD 示例'
|
||||
}}
|
||||
>
|
||||
<ProTable<API.UserInfo>
|
||||
headerTitle="查询表格"
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
search={{
|
||||
labelWidth: 120
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="1"
|
||||
type="primary"
|
||||
onClick={() => handleModalVisible(true)}
|
||||
>
|
||||
新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
const { data, success } = await queryUserList({
|
||||
...params,
|
||||
// FIXME: remove @ts-ignore
|
||||
// @ts-ignore
|
||||
sorter,
|
||||
filter
|
||||
});
|
||||
return {
|
||||
data: data?.list || [],
|
||||
success
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
rowSelection={{
|
||||
onChange: (_, selectedRows) => setSelectedRows(selectedRows)
|
||||
}}
|
||||
/>
|
||||
{selectedRowsState?.length > 0 && (
|
||||
<FooterToolbar
|
||||
extra={
|
||||
<div>
|
||||
已选择{' '}
|
||||
<a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
|
||||
项
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await handleRemove(selectedRowsState);
|
||||
setSelectedRows([]);
|
||||
actionRef.current?.reloadAndRest?.();
|
||||
}}
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<Button type="primary">批量审批</Button>
|
||||
</FooterToolbar>
|
||||
)}
|
||||
<CreateForm
|
||||
onCancel={() => handleModalVisible(false)}
|
||||
modalVisible={createModalVisible}
|
||||
>
|
||||
<ProTable<API.UserInfo, API.UserInfo>
|
||||
onSubmit={async (value) => {
|
||||
const success = await handleAdd(value);
|
||||
if (success) {
|
||||
handleModalVisible(false);
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
rowKey="id"
|
||||
type="form"
|
||||
columns={columns}
|
||||
/>
|
||||
</CreateForm>
|
||||
{stepFormValues && Object.keys(stepFormValues).length ? (
|
||||
<UpdateForm
|
||||
onSubmit={async (value) => {
|
||||
const success = await handleUpdate(value);
|
||||
if (success) {
|
||||
handleUpdateModalVisible(false);
|
||||
setStepFormValues({});
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCancel={() => {
|
||||
handleUpdateModalVisible(false);
|
||||
setStepFormValues({});
|
||||
}}
|
||||
updateModalVisible={updateModalVisible}
|
||||
values={stepFormValues}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Drawer
|
||||
width={600}
|
||||
open={!!row}
|
||||
onClose={() => {
|
||||
setRow(undefined);
|
||||
}}
|
||||
closable={false}
|
||||
>
|
||||
{row?.name && (
|
||||
<ProDescriptions<API.UserInfo>
|
||||
column={2}
|
||||
title={row?.name}
|
||||
request={async () => ({
|
||||
data: row || {}
|
||||
})}
|
||||
params={{
|
||||
id: row?.name
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
)}
|
||||
</Drawer>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableList;
|
||||
@@ -290,6 +290,11 @@ const Users: React.FC = () => {
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title={intl.formatMessage({ id: 'users.form.fullname' })}
|
||||
dataIndex="full_name"
|
||||
key="full_name"
|
||||
/>
|
||||
<Column
|
||||
title={intl.formatMessage({ id: 'common.table.createTime' })}
|
||||
dataIndex="created_at"
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/* eslint-disable */
|
||||
// 该文件由 OneAPI 自动生成,请勿手动修改!
|
||||
import { request } from '@umijs/max';
|
||||
|
||||
/** 此处后端没有提供注释 GET /api/v1/queryUserList */
|
||||
export async function queryUserList(
|
||||
params: {
|
||||
// query
|
||||
/** keyword */
|
||||
keyword?: string;
|
||||
/** current */
|
||||
current?: number;
|
||||
/** pageSize */
|
||||
pageSize?: number;
|
||||
},
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
return request<API.Result_PageInfo_UserInfo__>('/api/v1/queryUserList', {
|
||||
method: 'GET',
|
||||
params: {
|
||||
...params,
|
||||
},
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /api/v1/user */
|
||||
export async function addUser(
|
||||
body?: API.UserInfoVO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
return request<API.Result_UserInfo_>('/api/v1/user', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /api/v1/user/${param0} */
|
||||
export async function getUserDetail(
|
||||
params: {
|
||||
// path
|
||||
/** userId */
|
||||
userId?: string;
|
||||
},
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { userId: param0 } = params;
|
||||
return request<API.Result_UserInfo_>(`/api/v1/user/${param0}`, {
|
||||
method: 'GET',
|
||||
params: { ...params },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 PUT /api/v1/user/${param0} */
|
||||
export async function modifyUser(
|
||||
params: {
|
||||
// path
|
||||
/** userId */
|
||||
userId?: string;
|
||||
},
|
||||
body?: API.UserInfoVO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { userId: param0 } = params;
|
||||
return request<API.Result_UserInfo_>(`/api/v1/user/${param0}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...params },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 DELETE /api/v1/user/${param0} */
|
||||
export async function deleteUser(
|
||||
params: {
|
||||
// path
|
||||
/** userId */
|
||||
userId?: string;
|
||||
},
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { userId: param0 } = params;
|
||||
return request<API.Result_string_>(`/api/v1/user/${param0}`, {
|
||||
method: 'DELETE',
|
||||
params: { ...params },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/* eslint-disable */
|
||||
// 该文件由 OneAPI 自动生成,请勿手动修改!
|
||||
|
||||
import * as UserController from './UserController';
|
||||
export default {
|
||||
UserController,
|
||||
};
|
||||
Vendored
-68
@@ -1,68 +0,0 @@
|
||||
/* eslint-disable */
|
||||
// 该文件由 OneAPI 自动生成,请勿手动修改!
|
||||
|
||||
declare namespace API {
|
||||
interface PageInfo {
|
||||
/**
|
||||
1 */
|
||||
current?: number;
|
||||
pageSize?: number;
|
||||
total?: number;
|
||||
list?: Array<Record<string, any>>;
|
||||
}
|
||||
|
||||
interface PageInfo_UserInfo_ {
|
||||
/**
|
||||
1 */
|
||||
current?: number;
|
||||
pageSize?: number;
|
||||
total?: number;
|
||||
list?: Array<UserInfo>;
|
||||
}
|
||||
|
||||
interface Result {
|
||||
success?: boolean;
|
||||
errorMessage?: string;
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
|
||||
interface Result_PageInfo_UserInfo__ {
|
||||
success?: boolean;
|
||||
errorMessage?: string;
|
||||
data?: PageInfo_UserInfo_;
|
||||
}
|
||||
|
||||
interface Result_UserInfo_ {
|
||||
success?: boolean;
|
||||
errorMessage?: string;
|
||||
data?: UserInfo;
|
||||
}
|
||||
|
||||
interface Result_string_ {
|
||||
success?: boolean;
|
||||
errorMessage?: string;
|
||||
data?: string;
|
||||
}
|
||||
|
||||
type UserGenderEnum = 'MALE' | 'FEMALE';
|
||||
|
||||
interface UserInfo {
|
||||
id?: string;
|
||||
name?: string;
|
||||
/** nick */
|
||||
nickName?: string;
|
||||
/** email */
|
||||
email?: string;
|
||||
gender?: UserGenderEnum;
|
||||
}
|
||||
|
||||
interface UserInfoVO {
|
||||
name?: string;
|
||||
/** nick */
|
||||
nickName?: string;
|
||||
/** email */
|
||||
email?: string;
|
||||
}
|
||||
|
||||
type definitions_0 = null;
|
||||
}
|
||||
Reference in New Issue
Block a user