开发: 删除不用的文件
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
|
||||
import { Settings as LayoutSettings } from '@ant-design/pro-components';
|
||||
import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
|
||||
import { history, Link } from '@umijs/max';
|
||||
import { App } from 'antd';
|
||||
@@ -114,7 +114,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
childrenRender: (children) => {
|
||||
// if (initialState?.loading) return <PageLoading />;
|
||||
return (
|
||||
<>
|
||||
<div style={{ minHeight: 'calc(100vh - 55px - 60px)' }}>
|
||||
{children}
|
||||
{/* <FloatButton
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
@@ -134,7 +134,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
}));
|
||||
}}
|
||||
/> */}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
...initialState?.settings,
|
||||
|
@@ -1,20 +1,13 @@
|
||||
import { DefaultFooter } from '@ant-design/pro-components';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import React from 'react';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const defaultMessage = intl.formatMessage({
|
||||
id: 'app.copyright.produced',
|
||||
defaultMessage: '2021 - 2028 福州晨丰科技有限公司',
|
||||
});
|
||||
|
||||
return (
|
||||
<DefaultFooter
|
||||
style={{
|
||||
background: 'none',
|
||||
}}
|
||||
copyright={`${defaultMessage}`}
|
||||
copyright={`2021 - 2028 福州晨丰科技有限公司`}
|
||||
links={[
|
||||
{
|
||||
key: 'scrm.antd',
|
||||
|
@@ -18,10 +18,6 @@ body,
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ant-pro-page-container {
|
||||
min-height: calc(100vh - 55px - 60px);
|
||||
}
|
||||
|
||||
.ant-pro-global-footer {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, message, notification } from 'antd';
|
||||
import defaultSettings from '../config/defaultSettings';
|
||||
|
||||
@@ -23,7 +22,7 @@ const clearCache = () => {
|
||||
if (pwa) {
|
||||
// Notify user if offline now
|
||||
window.addEventListener('sw.offline', () => {
|
||||
message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' }));
|
||||
message.warning('当前处于离线状态');
|
||||
});
|
||||
|
||||
// Pop up a prompt on the page asking the user if they want to use the latest version
|
||||
@@ -62,12 +61,12 @@ if (pwa) {
|
||||
reloadSW();
|
||||
}}
|
||||
>
|
||||
{useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.ok' })}
|
||||
刷新
|
||||
</Button>
|
||||
);
|
||||
notification.open({
|
||||
message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }),
|
||||
description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }),
|
||||
message: '有新内容',
|
||||
description: '请点击“刷新”按钮或者手动刷新页面',
|
||||
btn,
|
||||
key,
|
||||
onClose: async () => null,
|
||||
|
@@ -1,45 +0,0 @@
|
||||
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Alert, Card, Typography } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const Admin: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<PageContainer
|
||||
content={intl.formatMessage({
|
||||
id: 'pages.admin.subPage.title',
|
||||
defaultMessage: 'This page can only be viewed by admin',
|
||||
})}
|
||||
>
|
||||
<Card>
|
||||
<Alert
|
||||
message={intl.formatMessage({
|
||||
id: 'pages.welcome.alertMessage',
|
||||
defaultMessage: 'Faster and stronger heavy-duty components have been released.',
|
||||
})}
|
||||
type="success"
|
||||
showIcon
|
||||
banner
|
||||
style={{
|
||||
margin: -12,
|
||||
marginBottom: 48,
|
||||
}}
|
||||
/>
|
||||
<Typography.Title level={2} style={{ textAlign: 'center' }}>
|
||||
<SmileTwoTone /> Ant Design Pro <HeartTwoTone twoToneColor="#eb2f96" /> You
|
||||
</Typography.Title>
|
||||
</Card>
|
||||
<p style={{ textAlign: 'center', marginTop: 24 }}>
|
||||
Want to add more pages? Please refer to{' '}
|
||||
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
|
||||
use block
|
||||
</a>
|
||||
。
|
||||
</p>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Admin;
|
@@ -70,6 +70,7 @@ export interface IGroup {
|
||||
|
||||
export interface IGroupMembers {
|
||||
avatar: string;
|
||||
staff_avatar?: string;
|
||||
|
||||
cust_id: string;
|
||||
gender: number;
|
||||
|
@@ -186,11 +186,12 @@ const ChatLogs: React.FC = () => {
|
||||
}).then((res) => {
|
||||
if (res.err_code == 0) {
|
||||
if (Array.isArray(res.data)) {
|
||||
setGroupMembersList(res.data);
|
||||
groupMembersObjRef.current = {};
|
||||
res.data.forEach((item: IGroupMembers) => {
|
||||
item.avatar = item.staff_avatar || item.avatar;
|
||||
groupMembersObjRef.current[item.user_id] = item;
|
||||
});
|
||||
setGroupMembersList(res.data);
|
||||
page(1);
|
||||
}
|
||||
}
|
||||
|
@@ -1,209 +0,0 @@
|
||||
import {
|
||||
ProFormDateTimePicker,
|
||||
ProFormRadio,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
StepsForm,
|
||||
} from '@ant-design/pro-components';
|
||||
import { FormattedMessage, useIntl } from '@umijs/max';
|
||||
import { Modal } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
export type FormValueType = {
|
||||
target?: string;
|
||||
template?: string;
|
||||
type?: string;
|
||||
time?: string;
|
||||
frequency?: string;
|
||||
} & Partial<API.RuleListItem>;
|
||||
|
||||
export type UpdateFormProps = {
|
||||
onCancel: (flag?: boolean, formVals?: FormValueType) => void;
|
||||
onSubmit: (values: FormValueType) => Promise<void>;
|
||||
updateModalOpen: boolean;
|
||||
values: Partial<API.RuleListItem>;
|
||||
};
|
||||
|
||||
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<StepsForm
|
||||
stepsProps={{
|
||||
size: 'small',
|
||||
}}
|
||||
stepsFormRender={(dom, submitter) => {
|
||||
return (
|
||||
<Modal
|
||||
width={640}
|
||||
bodyStyle={{ padding: '32px 40px 48px' }}
|
||||
destroyOnClose
|
||||
title={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleConfig',
|
||||
defaultMessage: '规则配置',
|
||||
})}
|
||||
open={props.updateModalOpen}
|
||||
footer={submitter}
|
||||
onCancel={() => {
|
||||
props.onCancel();
|
||||
}}
|
||||
>
|
||||
{dom}
|
||||
</Modal>
|
||||
);
|
||||
}}
|
||||
onFinish={props.onSubmit}
|
||||
>
|
||||
<StepsForm.StepForm
|
||||
initialValues={{
|
||||
name: props.values.name,
|
||||
desc: props.values.desc,
|
||||
}}
|
||||
title={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.basicConfig',
|
||||
defaultMessage: '基本信息',
|
||||
})}
|
||||
>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleName.nameLabel',
|
||||
defaultMessage: '规则名称',
|
||||
})}
|
||||
width="md"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.updateForm.ruleName.nameRules"
|
||||
defaultMessage="请输入规则名称!"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormTextArea
|
||||
name="desc"
|
||||
width="md"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleDesc.descLabel',
|
||||
defaultMessage: '规则描述',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleDesc.descPlaceholder',
|
||||
defaultMessage: '请输入至少五个字符',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.updateForm.ruleDesc.descRules"
|
||||
defaultMessage="请输入至少五个字符的规则描述!"
|
||||
/>
|
||||
),
|
||||
min: 5,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</StepsForm.StepForm>
|
||||
<StepsForm.StepForm
|
||||
initialValues={{
|
||||
target: '0',
|
||||
template: '0',
|
||||
}}
|
||||
title={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleProps.title',
|
||||
defaultMessage: '配置规则属性',
|
||||
})}
|
||||
>
|
||||
<ProFormSelect
|
||||
name="target"
|
||||
width="md"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.object',
|
||||
defaultMessage: '监控对象',
|
||||
})}
|
||||
valueEnum={{
|
||||
0: '表一',
|
||||
1: '表二',
|
||||
}}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="template"
|
||||
width="md"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleProps.templateLabel',
|
||||
defaultMessage: '规则模板',
|
||||
})}
|
||||
valueEnum={{
|
||||
0: '规则模板一',
|
||||
1: '规则模板二',
|
||||
}}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
name="type"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.ruleProps.typeLabel',
|
||||
defaultMessage: '规则类型',
|
||||
})}
|
||||
options={[
|
||||
{
|
||||
value: '0',
|
||||
label: '强',
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '弱',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</StepsForm.StepForm>
|
||||
<StepsForm.StepForm
|
||||
initialValues={{
|
||||
type: '1',
|
||||
frequency: 'month',
|
||||
}}
|
||||
title={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.schedulingPeriod.title',
|
||||
defaultMessage: '设定调度周期',
|
||||
})}
|
||||
>
|
||||
<ProFormDateTimePicker
|
||||
name="time"
|
||||
width="md"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.schedulingPeriod.timeLabel',
|
||||
defaultMessage: '开始时间',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.updateForm.schedulingPeriod.timeRules"
|
||||
defaultMessage="请选择开始时间!"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="frequency"
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.searchTable.updateForm.object',
|
||||
defaultMessage: '监控对象',
|
||||
})}
|
||||
width="md"
|
||||
valueEnum={{
|
||||
month: '月',
|
||||
week: '周',
|
||||
}}
|
||||
/>
|
||||
</StepsForm.StepForm>
|
||||
</StepsForm>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateForm;
|
@@ -1,422 +0,0 @@
|
||||
import { addRule, removeRule, rule, updateRule } from '@/services/ant-design-pro/api';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
|
||||
import {
|
||||
FooterToolbar,
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
ProDescriptions,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { FormattedMessage, useIntl, useModel } from '@umijs/max';
|
||||
import { Button, Drawer, Input, message } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import type { FormValueType } from './components/UpdateForm';
|
||||
import UpdateForm from './components/UpdateForm';
|
||||
|
||||
/**
|
||||
* @en-US Add node
|
||||
* @zh-CN 添加节点
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields: API.RuleListItem) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
await addRule({ ...fields });
|
||||
hide();
|
||||
message.success('Added successfully');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('Adding failed, please try again!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @en-US Update node
|
||||
* @zh-CN 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: FormValueType) => {
|
||||
const hide = message.loading('Configuring');
|
||||
try {
|
||||
await updateRule({
|
||||
name: fields.name,
|
||||
desc: fields.desc,
|
||||
key: fields.key,
|
||||
});
|
||||
hide();
|
||||
|
||||
message.success('Configuration is successful');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('Configuration failed, please try again!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete node
|
||||
* @zh-CN 删除节点
|
||||
*
|
||||
* @param selectedRows
|
||||
*/
|
||||
const handleRemove = async (selectedRows: API.RuleListItem[]) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRows) return true;
|
||||
try {
|
||||
await removeRule({
|
||||
key: selectedRows.map((row) => row.key),
|
||||
});
|
||||
hide();
|
||||
message.success('Deleted successfully and will refresh soon');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('Delete failed, please try again');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const TableList: React.FC = () => {
|
||||
/**
|
||||
* @en-US Pop-up window of new window
|
||||
* @zh-CN 新建窗口的弹窗
|
||||
* */
|
||||
const [createModalOpen, handleModalOpen] = useState<boolean>(false);
|
||||
/**
|
||||
* @en-US The pop-up window of the distribution update window
|
||||
* @zh-CN 分布更新窗口的弹窗
|
||||
* */
|
||||
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
||||
|
||||
const [showDetail, setShowDetail] = useState<boolean>(false);
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [currentRow, setCurrentRow] = useState<API.RuleListItem>();
|
||||
const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]);
|
||||
|
||||
/**
|
||||
* @en-US International configuration
|
||||
* @zh-CN 国际化配置
|
||||
* */
|
||||
const intl = useIntl();
|
||||
|
||||
const columns: ProColumns<API.RuleListItem>[] = [
|
||||
{
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.updateForm.ruleName.nameLabel"
|
||||
defaultMessage="Rule name"
|
||||
/>
|
||||
),
|
||||
dataIndex: 'name',
|
||||
tip: 'The rule name is the unique key',
|
||||
render: (dom, entity) => {
|
||||
return (
|
||||
<a
|
||||
onClick={() => {
|
||||
setCurrentRow(entity);
|
||||
setShowDetail(true);
|
||||
}}
|
||||
>
|
||||
{dom}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: <FormattedMessage id="pages.searchTable.titleDesc" defaultMessage="Description" />,
|
||||
dataIndex: 'desc',
|
||||
valueType: 'textarea',
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.titleCallNo"
|
||||
defaultMessage="Number of service calls"
|
||||
/>
|
||||
),
|
||||
dataIndex: 'callNo',
|
||||
sorter: true,
|
||||
hideInForm: true,
|
||||
renderText: (val: string) =>
|
||||
`${val}${intl.formatMessage({
|
||||
id: 'pages.searchTable.tenThousand',
|
||||
defaultMessage: ' 万 ',
|
||||
})}`,
|
||||
},
|
||||
{
|
||||
title: <FormattedMessage id="pages.searchTable.titleStatus" defaultMessage="Status" />,
|
||||
dataIndex: 'status',
|
||||
hideInForm: true,
|
||||
valueEnum: {
|
||||
0: {
|
||||
text: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.nameStatus.default"
|
||||
defaultMessage="Shut down"
|
||||
/>
|
||||
),
|
||||
status: 'Default',
|
||||
},
|
||||
1: {
|
||||
text: (
|
||||
<FormattedMessage id="pages.searchTable.nameStatus.running" defaultMessage="Running" />
|
||||
),
|
||||
status: 'Processing',
|
||||
},
|
||||
2: {
|
||||
text: (
|
||||
<FormattedMessage id="pages.searchTable.nameStatus.online" defaultMessage="Online" />
|
||||
),
|
||||
status: 'Success',
|
||||
},
|
||||
3: {
|
||||
text: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.nameStatus.abnormal"
|
||||
defaultMessage="Abnormal"
|
||||
/>
|
||||
),
|
||||
status: 'Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.titleUpdatedAt"
|
||||
defaultMessage="Last scheduled time"
|
||||
/>
|
||||
),
|
||||
sorter: true,
|
||||
dataIndex: 'updatedAt',
|
||||
valueType: 'dateTime',
|
||||
renderFormItem: (item, { defaultRender, ...rest }, form) => {
|
||||
const status = form.getFieldValue('status');
|
||||
if (`${status}` === '0') {
|
||||
return false;
|
||||
}
|
||||
if (`${status}` === '3') {
|
||||
return (
|
||||
<Input
|
||||
{...rest}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.searchTable.exception',
|
||||
defaultMessage: 'Please enter the reason for the exception!',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return defaultRender(item);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="Operating" />,
|
||||
dataIndex: 'option',
|
||||
valueType: 'option',
|
||||
render: (_, record) => [
|
||||
<a
|
||||
key="config"
|
||||
onClick={() => {
|
||||
handleUpdateModalOpen(true);
|
||||
setCurrentRow(record);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="pages.searchTable.config" defaultMessage="Configuration" />
|
||||
</a>,
|
||||
<a key="subscribeAlert" href="https://procomponents.ant.design/">
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.subscribeAlert"
|
||||
defaultMessage="Subscribe to alerts"
|
||||
/>
|
||||
</a>,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const { add, minus, counter } = useModel('demo', (ret) => ({
|
||||
add: ret.increment,
|
||||
minus: ret.decrement,
|
||||
counter: ret.counter,
|
||||
}));
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ marginRight: 12 }}
|
||||
>
|
||||
加+1
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
minus();
|
||||
}}
|
||||
style={{ marginRight: 12 }}
|
||||
>
|
||||
减-1
|
||||
</Button>
|
||||
<span>{counter}</span>
|
||||
</div>
|
||||
<ProTable<API.RuleListItem, API.PageParams>
|
||||
headerTitle={intl.formatMessage({
|
||||
id: 'pages.searchTable.title',
|
||||
defaultMessage: 'Enquiry form',
|
||||
})}
|
||||
actionRef={actionRef}
|
||||
rowKey="key"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="primary"
|
||||
onClick={() => {
|
||||
handleModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="New" />
|
||||
</Button>,
|
||||
]}
|
||||
request={rule}
|
||||
columns={columns}
|
||||
rowSelection={{
|
||||
onChange: (_, selectedRows) => {
|
||||
setSelectedRows(selectedRows);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{selectedRowsState?.length > 0 && (
|
||||
<FooterToolbar
|
||||
extra={
|
||||
<div>
|
||||
<FormattedMessage id="pages.searchTable.chosen" defaultMessage="Chosen" />{' '}
|
||||
<a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
|
||||
<FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
|
||||
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.totalServiceCalls"
|
||||
defaultMessage="Total number of service calls"
|
||||
/>{' '}
|
||||
{selectedRowsState.reduce((pre, item) => pre + item.callNo!, 0)}{' '}
|
||||
<FormattedMessage id="pages.searchTable.tenThousand" defaultMessage="万" />
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await handleRemove(selectedRowsState);
|
||||
setSelectedRows([]);
|
||||
actionRef.current?.reloadAndRest?.();
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.batchDeletion"
|
||||
defaultMessage="Batch deletion"
|
||||
/>
|
||||
</Button>
|
||||
<Button type="primary">
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.batchApproval"
|
||||
defaultMessage="Batch approval"
|
||||
/>
|
||||
</Button>
|
||||
</FooterToolbar>
|
||||
)}
|
||||
<ModalForm
|
||||
title={intl.formatMessage({
|
||||
id: 'pages.searchTable.createForm.newRule',
|
||||
defaultMessage: 'New rule',
|
||||
})}
|
||||
width="400px"
|
||||
open={createModalOpen}
|
||||
onOpenChange={handleModalOpen}
|
||||
onFinish={async (value) => {
|
||||
const success = await handleAdd(value as API.RuleListItem);
|
||||
if (success) {
|
||||
handleModalOpen(false);
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ProFormText
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: (
|
||||
<FormattedMessage
|
||||
id="pages.searchTable.ruleName"
|
||||
defaultMessage="Rule name is required"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
width="md"
|
||||
name="name"
|
||||
/>
|
||||
<ProFormTextArea width="md" name="desc" />
|
||||
</ModalForm>
|
||||
<UpdateForm
|
||||
onSubmit={async (value) => {
|
||||
const success = await handleUpdate(value);
|
||||
if (success) {
|
||||
handleUpdateModalOpen(false);
|
||||
setCurrentRow(undefined);
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCancel={() => {
|
||||
handleUpdateModalOpen(false);
|
||||
if (!showDetail) {
|
||||
setCurrentRow(undefined);
|
||||
}
|
||||
}}
|
||||
updateModalOpen={updateModalOpen}
|
||||
values={currentRow || {}}
|
||||
/>
|
||||
|
||||
<Drawer
|
||||
width={600}
|
||||
open={showDetail}
|
||||
onClose={() => {
|
||||
setCurrentRow(undefined);
|
||||
setShowDetail(false);
|
||||
}}
|
||||
closable={false}
|
||||
>
|
||||
{currentRow?.name && (
|
||||
<ProDescriptions<API.RuleListItem>
|
||||
column={2}
|
||||
title={currentRow?.name}
|
||||
request={async () => ({
|
||||
data: currentRow || {},
|
||||
})}
|
||||
params={{
|
||||
id: currentRow?.name,
|
||||
}}
|
||||
columns={columns as ProDescriptionsItemProps<API.RuleListItem>[]}
|
||||
/>
|
||||
)}
|
||||
</Drawer>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableList;
|
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import { IAjaxReturn, post } from '@/services/ajax';
|
||||
import { getFakeCaptcha } from '@/services/ant-design-pro/login';
|
||||
import {
|
||||
AlipayCircleOutlined,
|
||||
LockOutlined,
|
||||
@@ -16,8 +15,8 @@ import {
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import { useEmotionCss } from '@ant-design/use-emotion-css';
|
||||
import { FormattedMessage, Helmet, history, SelectLang, useIntl, useModel } from '@umijs/max';
|
||||
import { Alert, App, message, Tabs } from 'antd';
|
||||
import { FormattedMessage, Helmet, history, SelectLang, useModel } from '@umijs/max';
|
||||
import { Alert, App, Tabs } from 'antd';
|
||||
import { stringify as qsStringify } from 'qs';
|
||||
import React, { useState } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
@@ -101,8 +100,6 @@ const Login: React.FC = () => {
|
||||
};
|
||||
});
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
const userInfo = await initialState?.fetchUserInfo?.();
|
||||
if (userInfo) {
|
||||
@@ -148,13 +145,7 @@ const Login: React.FC = () => {
|
||||
return (
|
||||
<div className={containerClassName}>
|
||||
<Helmet>
|
||||
<title>
|
||||
{intl.formatMessage({
|
||||
id: 'menu.login',
|
||||
defaultMessage: '登录页',
|
||||
})}
|
||||
- {Settings.title}
|
||||
</title>
|
||||
<title>登录页 - {Settings.title}</title>
|
||||
</Helmet>
|
||||
{/* <Lang /> */}
|
||||
<div
|
||||
@@ -211,28 +202,17 @@ const Login: React.FC = () => {
|
||||
items={[
|
||||
{
|
||||
key: 'account',
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.login.accountLogin.tab',
|
||||
defaultMessage: '账户密码登录',
|
||||
}),
|
||||
label: '账户密码登录',
|
||||
},
|
||||
// {
|
||||
// key: 'mobile',
|
||||
// label: intl.formatMessage({
|
||||
// id: 'pages.login.phoneLogin.tab',
|
||||
// defaultMessage: '手机号登录',
|
||||
// }),
|
||||
// label: '手机号登录',
|
||||
// },
|
||||
]}
|
||||
/>
|
||||
|
||||
{status === 'error' && loginType === 'account' && (
|
||||
<LoginMessage
|
||||
content={intl.formatMessage({
|
||||
id: 'pages.login.accountLogin.errorMessage',
|
||||
defaultMessage: '账户或密码错误(admin/ant.design)',
|
||||
})}
|
||||
/>
|
||||
<LoginMessage content={'账户或密码错误(admin/ant.design)'} />
|
||||
)}
|
||||
{type === 'account' && (
|
||||
<>
|
||||
@@ -242,10 +222,7 @@ const Login: React.FC = () => {
|
||||
size: 'large',
|
||||
prefix: <UserOutlined />,
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.login.username.placeholder',
|
||||
defaultMessage: '用户名: admin or user',
|
||||
})}
|
||||
placeholder={'用户名'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -264,10 +241,7 @@ const Login: React.FC = () => {
|
||||
size: 'large',
|
||||
prefix: <LockOutlined />,
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.login.password.placeholder',
|
||||
defaultMessage: '请输入密码',
|
||||
})}
|
||||
placeholder={'请输入密码'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -292,10 +266,7 @@ const Login: React.FC = () => {
|
||||
prefix: <MobileOutlined />,
|
||||
}}
|
||||
name="mobile"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.login.phoneNumber.placeholder',
|
||||
defaultMessage: '手机号',
|
||||
})}
|
||||
placeholder={'手机号'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -325,21 +296,12 @@ const Login: React.FC = () => {
|
||||
captchaProps={{
|
||||
size: 'large',
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.login.captcha.placeholder',
|
||||
defaultMessage: '请输入验证码',
|
||||
})}
|
||||
placeholder={'请输入验证码'}
|
||||
captchaTextRender={(timing, count) => {
|
||||
if (timing) {
|
||||
return `${count} ${intl.formatMessage({
|
||||
id: 'pages.getCaptchaSecondText',
|
||||
defaultMessage: '获取验证码',
|
||||
})}`;
|
||||
return `${count} 获取验证码`;
|
||||
}
|
||||
return intl.formatMessage({
|
||||
id: 'pages.login.phoneLogin.getVerificationCode',
|
||||
defaultMessage: '获取验证码',
|
||||
});
|
||||
return '获取验证码';
|
||||
}}
|
||||
name="captcha"
|
||||
rules={[
|
||||
@@ -354,13 +316,13 @@ const Login: React.FC = () => {
|
||||
},
|
||||
]}
|
||||
onGetCaptcha={async (phone) => {
|
||||
const result = await getFakeCaptcha({
|
||||
phone,
|
||||
});
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
message.success('获取验证码成功!验证码为:1234');
|
||||
// const result = await getFakeCaptcha({
|
||||
// phone,
|
||||
// });
|
||||
// if (!result) {
|
||||
// return;
|
||||
// }
|
||||
// message.success('获取验证码成功!验证码为:1234');
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
@@ -1,96 +0,0 @@
|
||||
import { render, fireEvent, act } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TestBrowser } from '@@/testBrowser';
|
||||
|
||||
// @ts-ignore
|
||||
import { startMock } from '@@/requestRecordMock';
|
||||
|
||||
const waitTime = (time: number = 100) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, time);
|
||||
});
|
||||
};
|
||||
|
||||
let server: {
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
describe('Login Page', () => {
|
||||
beforeAll(async () => {
|
||||
server = await startMock({
|
||||
port: 8000,
|
||||
scene: 'login',
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server?.close();
|
||||
});
|
||||
|
||||
it('should show login form', async () => {
|
||||
const historyRef = React.createRef<any>();
|
||||
const rootContainer = render(
|
||||
<TestBrowser
|
||||
historyRef={historyRef}
|
||||
location={{
|
||||
pathname: '/user/login',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await rootContainer.findAllByText('Ant Design');
|
||||
|
||||
act(() => {
|
||||
historyRef.current?.push('/user/login');
|
||||
});
|
||||
|
||||
expect(rootContainer.baseElement?.querySelector('.ant-pro-form-login-desc')?.textContent).toBe(
|
||||
'Ant Design is the most influential web design specification in Xihu district',
|
||||
);
|
||||
|
||||
expect(rootContainer.asFragment()).toMatchSnapshot();
|
||||
|
||||
rootContainer.unmount();
|
||||
});
|
||||
|
||||
it('should login success', async () => {
|
||||
const historyRef = React.createRef<any>();
|
||||
const rootContainer = render(
|
||||
<TestBrowser
|
||||
historyRef={historyRef}
|
||||
location={{
|
||||
pathname: '/user/login',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await rootContainer.findAllByText('Ant Design');
|
||||
|
||||
const userNameInput = await rootContainer.findByPlaceholderText('Username: admin or user');
|
||||
|
||||
act(() => {
|
||||
fireEvent.change(userNameInput, { target: { value: 'admin' } });
|
||||
});
|
||||
|
||||
const passwordInput = await rootContainer.findByPlaceholderText('Password: ant.design');
|
||||
|
||||
act(() => {
|
||||
fireEvent.change(passwordInput, { target: { value: 'ant.design' } });
|
||||
});
|
||||
|
||||
await (await rootContainer.findByText('Login')).click();
|
||||
|
||||
// 等待接口返回结果
|
||||
await waitTime(5000);
|
||||
|
||||
await rootContainer.findAllByText('Ant Design Pro');
|
||||
|
||||
expect(rootContainer.asFragment()).toMatchSnapshot();
|
||||
|
||||
await waitTime(2000);
|
||||
|
||||
rootContainer.unmount();
|
||||
});
|
||||
});
|
@@ -1,168 +0,0 @@
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { useModel } from '@umijs/max';
|
||||
import { Card, theme } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* 每个单独的卡片,为了复用样式抽成了组件
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
const InfoCard: React.FC<{
|
||||
title: string;
|
||||
index: number;
|
||||
desc: string;
|
||||
href: string;
|
||||
}> = ({ title, href, index, desc }) => {
|
||||
const { useToken } = theme;
|
||||
|
||||
const { token } = useToken();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: token.colorBgContainer,
|
||||
boxShadow: token.boxShadow,
|
||||
borderRadius: '8px',
|
||||
fontSize: '14px',
|
||||
color: token.colorTextSecondary,
|
||||
lineHeight: '22px',
|
||||
padding: '16px 19px',
|
||||
minWidth: '220px',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '4px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
lineHeight: '22px',
|
||||
backgroundSize: '100%',
|
||||
textAlign: 'center',
|
||||
padding: '8px 16px 16px 12px',
|
||||
color: '#FFF',
|
||||
fontWeight: 'bold',
|
||||
backgroundImage:
|
||||
"url('https://gw.alipayobjects.com/zos/bmw-prod/daaf8d50-8e6d-4251-905d-676a24ddfa12.svg')",
|
||||
}}
|
||||
>
|
||||
{index}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
color: token.colorText,
|
||||
paddingBottom: 8,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
color: token.colorTextSecondary,
|
||||
textAlign: 'justify',
|
||||
lineHeight: '22px',
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
{desc}
|
||||
</div>
|
||||
<a href={href} target="_blank" rel="noreferrer">
|
||||
了解更多 {'>'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Welcome: React.FC = () => {
|
||||
const { token } = theme.useToken();
|
||||
const { initialState } = useModel('@@initialState');
|
||||
|
||||
const count = useModel('demo');
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<span>{count.counter}</span>
|
||||
<Card
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
}}
|
||||
bodyStyle={{
|
||||
backgroundImage:
|
||||
initialState?.settings?.navTheme === 'realDark'
|
||||
? 'background-image: linear-gradient(75deg, #1A1B1F 0%, #191C1F 100%)'
|
||||
: 'background-image: linear-gradient(75deg, #FBFDFF 0%, #F5F7FF 100%)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
backgroundPosition: '100% -30%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: '274px auto',
|
||||
backgroundImage:
|
||||
"url('https://gw.alipayobjects.com/mdn/rms_a9745b/afts/img/A*BuFmQqsB2iAAAAAAAAAAAAAAARQnAQ')",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
color: token.colorTextHeading,
|
||||
}}
|
||||
>
|
||||
欢迎使用 Ant Design Pro
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
color: token.colorTextSecondary,
|
||||
lineHeight: '22px',
|
||||
marginTop: 16,
|
||||
marginBottom: 32,
|
||||
width: '65%',
|
||||
}}
|
||||
>
|
||||
Ant Design Pro 是一个整合了 umi,Ant Design 和 ProComponents
|
||||
的脚手架方案。致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: 16,
|
||||
}}
|
||||
>
|
||||
<InfoCard
|
||||
index={1}
|
||||
href="https://umijs.org/docs/introduce/introduce"
|
||||
title="了解 umi"
|
||||
desc="umi 是一个可扩展的企业级前端应用框架,umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。"
|
||||
/>
|
||||
<InfoCard
|
||||
index={2}
|
||||
title="了解 ant design"
|
||||
href="https://ant.design"
|
||||
desc="antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。"
|
||||
/>
|
||||
<InfoCard
|
||||
index={3}
|
||||
title="了解 Pro Components"
|
||||
href="https://procomponents.ant.design"
|
||||
desc="ProComponents 是一个基于 Ant Design 做了更高抽象的模板组件,以 一个组件就是一个页面为开发理念,为中后台开发带来更好的体验。"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Welcome;
|
@@ -10,8 +10,12 @@
|
||||
display: inline-flex;
|
||||
flex: 1;
|
||||
margin-bottom: 12px;
|
||||
border-right: 1px solid #ddd;
|
||||
padding-left: 24px;
|
||||
border-right: 1px solid #ddd;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dataIconBox {
|
||||
@@ -41,8 +45,8 @@
|
||||
}
|
||||
|
||||
.dataCard {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user