开发: 登录等功能
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import { defineConfig } from '@umijs/max';
|
||||
import { join } from 'path';
|
||||
import defaultSettings from './defaultSettings';
|
||||
import proxy from './proxy';
|
||||
import routes from './routes';
|
||||
|
||||
const { REACT_APP_ENV = 'dev' } = process.env;
|
||||
@@ -14,7 +13,10 @@ export default defineConfig({
|
||||
* @doc https://umijs.org/docs/api/config#hash
|
||||
*/
|
||||
hash: true,
|
||||
|
||||
// hash 路由
|
||||
// history: {
|
||||
// type: 'hash',
|
||||
// },
|
||||
/**
|
||||
* @name 兼容性设置
|
||||
* @description 设置 ie11 不一定完美兼容,需要检查自己使用的所有依赖
|
||||
@@ -54,7 +56,14 @@ export default defineConfig({
|
||||
* @doc 代理介绍 https://umijs.org/docs/guides/proxy
|
||||
* @doc 代理配置 https://umijs.org/docs/api/config#proxy
|
||||
*/
|
||||
proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
|
||||
// proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
|
||||
proxy: {
|
||||
'/api/': {
|
||||
target: 'http://192.168.1.219:8183/',
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/api': '' },
|
||||
},
|
||||
},
|
||||
/**
|
||||
* @name 快速热更新配置
|
||||
* @description 一个不错的热更新组件,更新时可以保留 state
|
||||
@@ -76,11 +85,12 @@ export default defineConfig({
|
||||
* @name layout 插件
|
||||
* @doc https://umijs.org/docs/max/layout-menu
|
||||
*/
|
||||
title: 'Ant Design Pro',
|
||||
title: 'SCRM',
|
||||
layout: {
|
||||
locale: true,
|
||||
locale: false,
|
||||
...defaultSettings,
|
||||
},
|
||||
|
||||
/**
|
||||
* @name moment2dayjs 插件
|
||||
* @description 将项目中的 moment 替换为 dayjs
|
||||
|
@@ -15,7 +15,7 @@ const Settings: ProLayoutProps & {
|
||||
fixedHeader: false,
|
||||
fixSiderbar: true,
|
||||
colorWeak: false,
|
||||
title: 'Ant Design Pro',
|
||||
title: 'SCRM',
|
||||
pwa: true,
|
||||
logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
|
||||
iconfontUrl: '',
|
||||
@@ -23,7 +23,7 @@ const Settings: ProLayoutProps & {
|
||||
// 参见ts声明,demo 见文档,通过token 修改样式
|
||||
//https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F
|
||||
},
|
||||
splitMenus: true
|
||||
splitMenus: true,
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @name umi 的路由配置
|
||||
* @description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置
|
||||
* @param path path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。
|
||||
@@ -22,49 +22,75 @@ export default [
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/welcome',
|
||||
// name: 'welcome',
|
||||
// icon: 'smile',
|
||||
// component: './Welcome',
|
||||
// },
|
||||
{
|
||||
path: '/welcome',
|
||||
name: 'welcome',
|
||||
icon: 'smile',
|
||||
component: './Welcome',
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'admin',
|
||||
path: '/departments',
|
||||
name: 'scrm',
|
||||
icon: 'crown',
|
||||
access: 'canAdmin',
|
||||
// access: 'canAdmin',
|
||||
routes: [
|
||||
{
|
||||
path: '/admin',
|
||||
redirect: '/admin/sub-page',
|
||||
path: '/departments',
|
||||
redirect: '/departments/page/list',
|
||||
},
|
||||
{
|
||||
path: '/admin/sub-page',
|
||||
name: 'sub-page',
|
||||
component: './Admin',
|
||||
path: '/departments/page',
|
||||
name: '部门管理',
|
||||
// hideInBreadcrumb: true,
|
||||
// component: './Admin',
|
||||
routes: [
|
||||
{
|
||||
path: '/departments/page',
|
||||
redirect: '/departments/page/list',
|
||||
},
|
||||
{
|
||||
name: '部门员工',
|
||||
icon: 'table',
|
||||
path: '/departments/page/list',
|
||||
component: './DepartmentsList',
|
||||
},
|
||||
{
|
||||
name: '聊天记录',
|
||||
icon: 'table',
|
||||
path: '/departments/page/list2',
|
||||
component: './ChatLogs',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/admin/sub-page2',
|
||||
name: 'sub-page2',
|
||||
component: './Admin',
|
||||
}
|
||||
// {
|
||||
// path: '/departments/sub-page2',
|
||||
// name: '商品',
|
||||
// // component: './TableList',
|
||||
// routes: [
|
||||
// {
|
||||
// name: '列表',
|
||||
// icon: 'table',
|
||||
// path: '/departments/sub-page2/list',
|
||||
// component: './TableList',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: 'list.table-list',
|
||||
icon: 'table',
|
||||
path: '/list',
|
||||
component: './TableList',
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'list.table-list',
|
||||
// icon: 'table',
|
||||
// path: '/list',
|
||||
// component: './TableList',
|
||||
// },
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/welcome',
|
||||
redirect: '/departments',
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
layout: false,
|
||||
component: './404',
|
||||
},
|
||||
|
||||
];
|
||||
|
@@ -92,4 +92,4 @@
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
}
|
||||
}
|
65
src/app.tsx
65
src/app.tsx
@@ -1,15 +1,14 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import { Question, SelectLang } from '@/components/RightContent';
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
import { LinkOutlined, VerticalAlignTopOutlined } from '@ant-design/icons';
|
||||
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
|
||||
import { SettingDrawer } from '@ant-design/pro-components';
|
||||
import type { RunTimeLayoutConfig } from '@umijs/max';
|
||||
import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
|
||||
import { history, Link } from '@umijs/max';
|
||||
import defaultSettings from '../config/defaultSettings';
|
||||
import { errorConfig } from './requestErrorConfig';
|
||||
import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
|
||||
import { App, FloatButton } from 'antd';
|
||||
import React from 'react';
|
||||
import defaultSettings from '../config/defaultSettings';
|
||||
import { AvatarDropdown, AvatarName } from './components/RightContent/AvatarDropdown';
|
||||
import { errorConfig } from './requestErrorConfig';
|
||||
import { post } from './services/ajax';
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
const loginPath = '/user/login';
|
||||
|
||||
@@ -24,9 +23,7 @@ export async function getInitialState(): Promise<{
|
||||
}> {
|
||||
const fetchUserInfo = async () => {
|
||||
try {
|
||||
const msg = await queryCurrentUser({
|
||||
skipErrorHandler: true,
|
||||
});
|
||||
const msg = await post({ url: '/User/LoginStatus' });
|
||||
return msg.data;
|
||||
} catch (error) {
|
||||
history.push(loginPath);
|
||||
@@ -51,18 +48,27 @@ export async function getInitialState(): Promise<{
|
||||
|
||||
// ProLayout 支持的api https://procomponents.ant.design/components/layout
|
||||
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
|
||||
// console.log(initialState?.settings);
|
||||
|
||||
const { notification } = App.useApp();
|
||||
window.NotificationCF = notification;
|
||||
|
||||
return {
|
||||
actionsRender: () => [<Question key="doc" />, <SelectLang key="SelectLang" />],
|
||||
// actionsRender: () => [<Question key="doc" />, <SelectLang key="SelectLang" />],
|
||||
actionsRender: () => [],
|
||||
avatarProps: {
|
||||
src: initialState?.currentUser?.avatar,
|
||||
src: initialState?.currentUser?.avatar
|
||||
? '/api/' + initialState?.currentUser?.avatar
|
||||
: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
|
||||
title: <AvatarName />,
|
||||
render: (_, avatarChildren) => {
|
||||
return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
|
||||
},
|
||||
},
|
||||
waterMarkProps: {
|
||||
content: initialState?.currentUser?.name,
|
||||
},
|
||||
// waterMarkProps: {
|
||||
// content: initialState?.currentUser?.login_name,
|
||||
// },
|
||||
siderWidth: 200,
|
||||
footerRender: () => <Footer />,
|
||||
onPageChange: () => {
|
||||
const { location } = history;
|
||||
@@ -91,6 +97,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
width: '331px',
|
||||
},
|
||||
],
|
||||
layout: 'side',
|
||||
links: isDev
|
||||
? [
|
||||
<Link key="openapi" to="/umi/plugin/openapi" target="_blank">
|
||||
@@ -99,6 +106,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
</Link>,
|
||||
]
|
||||
: [],
|
||||
suppressSiderWhenMenuEmpty: true,
|
||||
menuHeaderRender: undefined,
|
||||
// 自定义 403 页面
|
||||
// unAccessible: <div>unAccessible</div>,
|
||||
@@ -108,7 +116,14 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<SettingDrawer
|
||||
<FloatButton
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
></FloatButton>
|
||||
{/* <SettingDrawer
|
||||
disableUrlParams
|
||||
enableDarkTheme
|
||||
settings={initialState?.settings}
|
||||
@@ -118,7 +133,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
settings,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
</>
|
||||
);
|
||||
},
|
||||
@@ -126,11 +141,27 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
||||
};
|
||||
};
|
||||
|
||||
export function rootContainer(container: any) {
|
||||
return React.createElement(App, null, container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name request 配置,可以配置错误处理
|
||||
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
|
||||
* @doc https://umijs.org/docs/max/request#配置
|
||||
*/
|
||||
const authHeaderInterceptor = (url: string, options: RequestConfig) => {
|
||||
const authHeader = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
};
|
||||
return {
|
||||
url: `${url}`,
|
||||
options: { ...options, interceptors: true, headers: authHeader, method: 'POST' },
|
||||
};
|
||||
};
|
||||
|
||||
export const request = {
|
||||
...errorConfig,
|
||||
requestInterceptors: [authHeaderInterceptor],
|
||||
};
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { GithubOutlined } from '@ant-design/icons';
|
||||
import { DefaultFooter } from '@ant-design/pro-components';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import React from 'react';
|
||||
@@ -7,34 +6,20 @@ const Footer: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const defaultMessage = intl.formatMessage({
|
||||
id: 'app.copyright.produced',
|
||||
defaultMessage: '蚂蚁集团体验技术部出品',
|
||||
defaultMessage: '2021 - 2028 福州晨丰科技有限公司',
|
||||
});
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<DefaultFooter
|
||||
style={{
|
||||
background: 'none',
|
||||
}}
|
||||
copyright={`${currentYear} ${defaultMessage}`}
|
||||
copyright={`${defaultMessage}`}
|
||||
links={[
|
||||
{
|
||||
key: 'Ant Design Pro',
|
||||
title: 'Ant Design Pro',
|
||||
href: 'https://pro.ant.design',
|
||||
blankTarget: true,
|
||||
},
|
||||
{
|
||||
key: 'github',
|
||||
title: <GithubOutlined />,
|
||||
href: 'https://github.com/ant-design/ant-design-pro',
|
||||
blankTarget: true,
|
||||
},
|
||||
{
|
||||
key: 'Ant Design',
|
||||
title: 'Ant Design',
|
||||
href: 'https://ant.design',
|
||||
key: 'scrm.antd',
|
||||
title: 'scrm.antd',
|
||||
href: '/',
|
||||
blankTarget: true,
|
||||
},
|
||||
]}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { outLogin } from '@/services/ant-design-pro/api';
|
||||
import { post } from '@/services/ajax';
|
||||
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { useEmotionCss } from '@ant-design/use-emotion-css';
|
||||
import { history, useModel } from '@umijs/max';
|
||||
import { Spin } from 'antd';
|
||||
import { App, Modal, Spin } from 'antd';
|
||||
import { stringify } from 'querystring';
|
||||
import type { MenuInfo } from 'rc-menu/lib/interface';
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import HeaderDropdown from '../HeaderDropdown';
|
||||
|
||||
@@ -17,15 +17,23 @@ export type GlobalHeaderRightProps = {
|
||||
export const AvatarName = () => {
|
||||
const { initialState } = useModel('@@initialState');
|
||||
const { currentUser } = initialState || {};
|
||||
return <span className="anticon">{currentUser?.name}</span>;
|
||||
return <span className="anticon">{currentUser?.login_name}</span>;
|
||||
};
|
||||
|
||||
export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, children }) => {
|
||||
/**
|
||||
* 退出登录,并且将当前的 url 保存
|
||||
*/
|
||||
|
||||
const { notification } = App.useApp();
|
||||
const loginOut = async () => {
|
||||
await outLogin();
|
||||
const res = await post({ url: '/User/LoginOut' });
|
||||
if (res.err_code == 0) {
|
||||
notification.success({
|
||||
message: `退出登录`,
|
||||
description: `退出登录成功`,
|
||||
});
|
||||
}
|
||||
const { search, pathname } = window.location;
|
||||
const urlParams = new URL(window.location.href).searchParams;
|
||||
/** 此方法会跳转到 redirect 参数所在的位置 */
|
||||
@@ -56,15 +64,13 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, childre
|
||||
};
|
||||
});
|
||||
const { initialState, setInitialState } = useModel('@@initialState');
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const onMenuClick = useCallback(
|
||||
(event: MenuInfo) => {
|
||||
const { key } = event;
|
||||
if (key === 'logout') {
|
||||
flushSync(() => {
|
||||
setInitialState((s) => ({ ...s, currentUser: undefined }));
|
||||
});
|
||||
loginOut();
|
||||
setVisible(true);
|
||||
return;
|
||||
}
|
||||
history.push(`/account/${key}`);
|
||||
@@ -90,7 +96,7 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, childre
|
||||
|
||||
const { currentUser } = initialState;
|
||||
|
||||
if (!currentUser || !currentUser.name) {
|
||||
if (!currentUser || !currentUser.login_name) {
|
||||
return loading;
|
||||
}
|
||||
|
||||
@@ -120,14 +126,31 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, childre
|
||||
];
|
||||
|
||||
return (
|
||||
<HeaderDropdown
|
||||
menu={{
|
||||
selectedKeys: [],
|
||||
onClick: onMenuClick,
|
||||
items: menuItems,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</HeaderDropdown>
|
||||
<>
|
||||
<HeaderDropdown
|
||||
menu={{
|
||||
selectedKeys: [],
|
||||
onClick: onMenuClick,
|
||||
items: menuItems,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</HeaderDropdown>
|
||||
<Modal
|
||||
width={300}
|
||||
open={visible}
|
||||
title={'退出登录'}
|
||||
centered
|
||||
onCancel={() => setVisible(false)}
|
||||
onOk={() => {
|
||||
flushSync(() => {
|
||||
setInitialState((s) => ({ ...s, currentUser: undefined }));
|
||||
});
|
||||
loginOut();
|
||||
}}
|
||||
>
|
||||
确认退出账号?
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
36
src/components/SearchBarPlugin/index.module.scss
Normal file
36
src/components/SearchBarPlugin/index.module.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
.searchBarContent {
|
||||
position: relative;
|
||||
height: auto;
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
& {
|
||||
display: block;
|
||||
height: 94px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.searchBar {
|
||||
display: none;
|
||||
|
||||
// border-top: 2px solid #eee;
|
||||
|
||||
& > div {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
color: #209bfa;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
& {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
62
src/components/SearchBarPlugin/index.tsx
Normal file
62
src/components/SearchBarPlugin/index.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { getDevice } from '@/services/utils';
|
||||
|
||||
import { DownOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
interface IProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SearchBarPlugin: React.FC<IProps> = (props) => {
|
||||
// const [device, setDevice] = useState(window.innerWidth);
|
||||
const isPhone = getDevice() === 'phone';
|
||||
const [searchBarText, setSearchBarText] = useState('展开');
|
||||
// useEffect(() => {
|
||||
// const handleResize = (e: any) => {
|
||||
// const currDevice = e && e.target && e.target.innerWidth;
|
||||
// console.log(currDevice)
|
||||
// if(currDevice !== device){
|
||||
// setDevice(currDevice);
|
||||
// }
|
||||
// }
|
||||
// window.addEventListener('optimizedResize', handleResize);
|
||||
// return () => window.removeEventListener('optimizedResize', handleResize);
|
||||
// }, [device]);
|
||||
const searchBarContentRef = useRef<any>(null);
|
||||
const changeSearchbar = () => {
|
||||
if (searchBarText === '展开') {
|
||||
setSearchBarText('收起');
|
||||
searchBarContentRef.current.style.height = `${
|
||||
searchBarContentRef.current.children[0].clientHeight + 10
|
||||
}px`;
|
||||
searchBarContentRef.current.style.transition = 'height .4s';
|
||||
} else {
|
||||
setSearchBarText('展开');
|
||||
searchBarContentRef.current.style.height = `${isPhone ? 76 : 94}px`;
|
||||
searchBarContentRef.current.style.transition = 'height .4s';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ background: '#fff', padding: 16, paddingBottom: 0, borderRadius: 6 }}>
|
||||
<div
|
||||
className={styles.searchBarContent}
|
||||
style={{ height: isPhone ? 76 : 'auto' }}
|
||||
ref={searchBarContentRef}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
<div className={styles.searchBar}>
|
||||
<div onClick={changeSearchbar}>
|
||||
<div style={{ display: 'inline-block', verticalAlign: 'middle' }}>{searchBarText}</div>
|
||||
{searchBarText == '展开' ? <DownOutlined /> : <UpOutlined />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SearchBottonsCardPlugin: React.FC<IProps> = (props) => {
|
||||
return <div style={{ background: '#fff', paddingBottom: 16 }}>{props.children}</div>;
|
||||
};
|
@@ -7,6 +7,7 @@ body,
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.colorWeak {
|
||||
@@ -16,6 +17,16 @@ body,
|
||||
.ant-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ant-pro-page-container {
|
||||
min-height: calc(100vh - 55px - 60px);
|
||||
}
|
||||
|
||||
.ant-pro-global-footer {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
|
||||
left: unset;
|
||||
}
|
||||
@@ -35,19 +46,47 @@ ol {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.ant-pro-page-container-children-content,
|
||||
.ant-page-header {
|
||||
padding-left: 24px !important;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
|
||||
input {
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: #000; // 改变了字体颜色
|
||||
}
|
||||
|
||||
// 部门 树 css
|
||||
.department-tree .ant-tree-node-content-wrapper {
|
||||
display: flex;
|
||||
flex-shrink: 0 !important;
|
||||
min-width: 0;
|
||||
flex: 1 !important;
|
||||
|
||||
>.ant-tree-title {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ant-table {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
&-thead > tr,
|
||||
&-tbody > tr {
|
||||
> th,
|
||||
> td {
|
||||
|
||||
&-thead>tr,
|
||||
&-tbody>tr {
|
||||
|
||||
>th,
|
||||
>td {
|
||||
white-space: pre;
|
||||
> span {
|
||||
|
||||
>span {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ export default {
|
||||
'layout.user.link.help': '帮助',
|
||||
'layout.user.link.privacy': '隐私',
|
||||
'layout.user.link.terms': '条款',
|
||||
'app.copyright.produced': '蚂蚁集团体验技术部出品',
|
||||
'app.copyright.produced': '2021 - 2028 福州晨丰科技有限公司',
|
||||
'app.preview.down.block': '下载此页面到本地项目',
|
||||
'app.welcome.link.fetch-blocks': '获取全部区块',
|
||||
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
|
||||
|
@@ -1,12 +1,12 @@
|
||||
export default {
|
||||
'pages.layouts.userLayout.title': 'Ant Design 是西湖区最具影响力的 Web 设计规范',
|
||||
'pages.login.accountLogin.tab': '账户密码登录',
|
||||
'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/ant.design)',
|
||||
'pages.login.accountLogin.errorMessage': '错误的用户名和密码',
|
||||
'pages.login.failure': '登录失败,请重试!',
|
||||
'pages.login.success': '登录成功!',
|
||||
'pages.login.username.placeholder': '用户名: admin or user',
|
||||
'pages.login.username.placeholder': '请输入用户名',
|
||||
'pages.login.username.required': '用户名是必填项!',
|
||||
'pages.login.password.placeholder': '密码: ant.design',
|
||||
'pages.login.password.placeholder': '请输入密码',
|
||||
'pages.login.password.required': '密码是必填项!',
|
||||
'pages.login.phoneLogin.tab': '手机号登录',
|
||||
'pages.login.phoneLogin.errorMessage': '验证码错误',
|
||||
|
8
src/models/demo.ts
Normal file
8
src/models/demo.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export default () => {
|
||||
const [counter, setCounter] = useState(0);
|
||||
const increment = useCallback(() => setCounter((c) => c + 1), []);
|
||||
const decrement = useCallback(() => setCounter((c) => c - 1), []);
|
||||
return { counter, increment, decrement };
|
||||
};
|
@@ -6,10 +6,10 @@ const NoFoundPage: React.FC = () => (
|
||||
<Result
|
||||
status="404"
|
||||
title="404"
|
||||
subTitle="Sorry, the page you visited does not exist."
|
||||
subTitle="抱歉,你访问的页面不存在"
|
||||
extra={
|
||||
<Button type="primary" onClick={() => history.push('/')}>
|
||||
Back Home
|
||||
返回首页
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
271
src/pages/ChatLogs/ChatBar.tsx
Normal file
271
src/pages/ChatLogs/ChatBar.tsx
Normal file
@@ -0,0 +1,271 @@
|
||||
import { PlayCircleOutlined } from '@ant-design/icons';
|
||||
import styles from './index.module.scss';
|
||||
export const ChatBar: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.chatLeft}>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.content}>
|
||||
通过 message.useMessage 创建支持读取 context 的
|
||||
contextHolder。请注意,我们推荐通过顶层注册的方式代替 message
|
||||
静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.content}>
|
||||
通过 message.useMessage 创建支持读取 context 的
|
||||
contextHolder。请注意,我们推荐通过顶层注册的方式代替 message
|
||||
静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IChatTimeProps {
|
||||
msgtime: number;
|
||||
}
|
||||
|
||||
export const ChatTime: React.FC<IChatTimeProps> = (props) => {
|
||||
function padWith(value: string | number) {
|
||||
return `${value}`.padStart(2, '0');
|
||||
}
|
||||
|
||||
const weekStr = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
|
||||
const formatTime = () => {
|
||||
const now = new Date();
|
||||
const msgtime = new Date(props.msgtime);
|
||||
console.log(msgtime.toLocaleString());
|
||||
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const dayDiff =
|
||||
(Date.parse(
|
||||
`${now.getFullYear()}-${padWith(now.getMonth() + 1)}-${padWith(now.getDate())} 00:00:00`,
|
||||
) -
|
||||
props.msgtime) /
|
||||
(24 * 60 * 60 * 1000) +
|
||||
1;
|
||||
|
||||
if (now.getFullYear() == msgtime.getFullYear()) {
|
||||
if (
|
||||
now.getFullYear() == msgtime.getFullYear() &&
|
||||
now.getMonth() == msgtime.getMonth() &&
|
||||
now.getDate() == msgtime.getDate()
|
||||
) {
|
||||
// 当天
|
||||
return `${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
} else if (
|
||||
yesterday.getFullYear() == msgtime.getFullYear() &&
|
||||
yesterday.getMonth() == msgtime.getMonth() &&
|
||||
yesterday.getDate() == msgtime.getDate()
|
||||
) {
|
||||
// 昨天
|
||||
return `昨天 ${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
} else if (dayDiff < 7) {
|
||||
// 星期
|
||||
return `星期${weekStr[msgtime.getDay()]} ${padWith(msgtime.getHours())}:${padWith(
|
||||
msgtime.getMinutes(),
|
||||
)}`;
|
||||
} else {
|
||||
// 超过7天
|
||||
return `${padWith(msgtime.getMonth() + 1)}月${padWith(msgtime.getDate())}日 ${padWith(
|
||||
msgtime.getHours(),
|
||||
)}:${padWith(msgtime.getMinutes())}`;
|
||||
}
|
||||
} else {
|
||||
// 跨年
|
||||
return `${msgtime.getFullYear()}年${padWith(msgtime.getMonth() + 1)}月${padWith(
|
||||
msgtime.getDate(),
|
||||
)}日 ${padWith(msgtime.getHours())}:${padWith(msgtime.getMinutes())}`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{formatTime()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChatRevoke: React.FC = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
"xxxxx" 撤回了一条消息
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChatAgreeOrNot: React.FC = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
background: '#DADADA',
|
||||
color: '#fff',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
lineHeight: 1,
|
||||
height: 26,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
对方[不]同意存档会话内容,你将无法继续提供服务
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 语音
|
||||
export const ChatVoice = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.content} style={{ cursor: 'pointer' }}>
|
||||
<span style={{ lineHeight: 1, marginRight: 4 }}>2"</span>
|
||||
<svg viewBox="0 0 1024 1024" width="18" height="18">
|
||||
<path
|
||||
d="M913.568627 518.334145C913.568627 483.422393 894.958532 451.162604 864.748584 433.706729 834.538616 416.250852 797.318425 416.250852 767.108477 433.706729 736.89851 451.162604 718.288414 483.422393 718.288414 518.334145 718.288414 553.245895 736.89851 585.505684 767.108477 602.961559 797.318425 620.417436 834.538616 620.417436 864.748584 602.961559 894.958532 585.505684 913.568627 553.245895 913.568627 518.334145L913.568627 518.334145ZM581.566143 215.288946C581.566143 215.288946 593.269057 203.098427 620.669932 203.098427 650.6546 203.098427 674.950626 227.414165 674.950626 257.423171 674.950626 290.452615 648.917554 305.250663 636.758739 309.031723 568.451875 353.599725 523.116685 430.436655 523.116685 518.116824 523.116685 605.970743 568.603889 682.938067 637.149606 727.440872 650.915177 731.852057 674.950626 743.064634 674.950626 778.940765 674.950626 808.949772 650.6546 833.265507 620.669932 833.265507 593.269057 833.265507 581.566143 821.07499 581.566143 821.07499 481.233659 757.536806 414.403283 645.801687 414.403283 518.181916 414.403283 390.562145 481.233659 278.805502 581.566143 215.288946L581.566143 215.288946ZM348.376225 14.135304C348.376225 14.135304 365.941399 7.529394 392.690949 7.529394 428.668185 7.529394 457.82781 36.73442 457.82781 72.719107 457.82781 98.186537 406.282822 128.195544 388.54391 137.474179 297.265513 229.695819 240.704994 356.381112 240.704994 496.452082 240.704994 650.321426 309.055287 787.849957 416.661363 881.418878 440.740242 890.980031 457.82781 914.426591 457.82781 941.914889 457.82781 977.899576 428.668185 1007.104602 392.690949 1007.104602 363.314276 1007.104602 345.336511 998.13011 345.336511 998.13011 202.12236 882.635664 110.431373 705.732666 110.431373 507.316947 110.431373 307.532423 203.446734 129.564556 348.376225 14.135304L348.376225 14.135304Z"
|
||||
fill="#000000"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// 视频
|
||||
export const ChatVideo = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div className={styles.video}>
|
||||
<PlayCircleOutlined style={{ color: '#fff', fontSize: 40 }} />
|
||||
<span className={styles.videoTime}>0:46</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 名片
|
||||
export const ChatCard = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div
|
||||
className={styles.content}
|
||||
style={{ cursor: 'pointer', flexDirection: 'column', minWidth: 300 }}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div className={styles.chatAvatar} style={{ marginRight: 12 }}>
|
||||
<img
|
||||
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.corpname}>腾讯</div>
|
||||
</div>
|
||||
<div>corpname</div>
|
||||
<div style={{ color: '#999' }}>corpname2</div>
|
||||
<div style={{ color: '#999', borderTop: '1px solid #ddd' }}>个人名片</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 表情
|
||||
export const ChatEmotion = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div
|
||||
className={styles.content}
|
||||
style={{ cursor: 'pointer', flexDirection: 'column', minWidth: 300 }}
|
||||
>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// 文件
|
||||
export const ChatFile = () => {
|
||||
return (
|
||||
<div className={styles.chatRight}>
|
||||
<div className={styles.chatContentBox}>
|
||||
<div className={styles.name}>name</div>
|
||||
<div
|
||||
className={styles.content}
|
||||
style={{ cursor: 'pointer', flexDirection: 'column', minWidth: 300 }}
|
||||
>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chatAvatar}>
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
109
src/pages/ChatLogs/index.module.scss
Normal file
109
src/pages/ChatLogs/index.module.scss
Normal file
@@ -0,0 +1,109 @@
|
||||
.chatLeft {
|
||||
display: flex;
|
||||
|
||||
.chatContentBox {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
max-width: 60%;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.chatAvatar {
|
||||
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: cover;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-bottom: 8px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
word-break: break-all;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: -6px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #fff;
|
||||
transform: rotate(45deg);
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover::before {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
}
|
||||
|
||||
.chatRight {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
.name {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chatContentBox {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
justify-content: end;
|
||||
max-width: 60%;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #95ec69;
|
||||
|
||||
&::before {
|
||||
right: -6px;
|
||||
left: initial;
|
||||
background-color: #95ec69;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover::before {
|
||||
background-color: #89d961;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 176px;
|
||||
height: 144px;
|
||||
background-color: #333;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.videoTime {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
161
src/pages/ChatLogs/index.tsx
Normal file
161
src/pages/ChatLogs/index.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { SearchBarPlugin, SearchBottonsCardPlugin } from '@/components/SearchBarPlugin';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { Button, Col, Form, Input, Pagination, Row, Select, Table } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { ChatBar, ChatTime } from './ChatBar';
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
age: number;
|
||||
address: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const ChatLogs: React.FC = () => {
|
||||
const [param, setParam] = useState({
|
||||
curr_page: 1,
|
||||
page_count: 20,
|
||||
});
|
||||
|
||||
const data: DataType[] = [
|
||||
{
|
||||
key: '1',
|
||||
firstName: 'John',
|
||||
lastName: 'Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer'],
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
firstName: 'Jim',
|
||||
lastName: 'Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser'],
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
firstName: 'Joe',
|
||||
lastName: 'Black',
|
||||
age: 32,
|
||||
address: 'Sydney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
];
|
||||
|
||||
// const { notification } = App.useApp();
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<SearchBarPlugin>
|
||||
<Form>
|
||||
<Row gutter={{ xs: 0, sm: 16 }}>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
{ value: 'disabled', label: 'Disabled', disabled: true },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</SearchBarPlugin>
|
||||
<SearchBottonsCardPlugin>
|
||||
<Row justify={'center'}>
|
||||
<Button type="primary">搜索</Button>
|
||||
</Row>
|
||||
</SearchBottonsCardPlugin>
|
||||
<ChatTime msgtime={1680761136755}></ChatTime>
|
||||
<ChatBar></ChatBar>
|
||||
<Table
|
||||
size="middle"
|
||||
dataSource={data}
|
||||
style={{ padding: 16, borderRadius: 6, background: '#fff', marginTop: 16 }}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
>
|
||||
<Table.Column title="ID" dataIndex={'key'}></Table.Column>
|
||||
<Table.Column title="firstName" dataIndex={'firstName'}></Table.Column>
|
||||
</Table>
|
||||
|
||||
<Pagination
|
||||
style={{
|
||||
background: '#fff',
|
||||
borderRadius: 6,
|
||||
marginTop: 16,
|
||||
padding: 16,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
current={param.curr_page}
|
||||
pageSize={param.page_count}
|
||||
total={1000}
|
||||
pageSizeOptions={[10, 20, 50, 100]}
|
||||
onShowSizeChange={(current, size) => {
|
||||
param.page_count = size;
|
||||
setParam({ ...param });
|
||||
}}
|
||||
showTotal={(total, range) => {
|
||||
return <span style={{ lineHeight: 1 }}>共{total}条</span>;
|
||||
}}
|
||||
onChange={(page, pageSize) => {
|
||||
param.curr_page = page;
|
||||
setParam({ ...param });
|
||||
}}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatLogs;
|
42
src/pages/DepartmentsList/index.module.scss
Normal file
42
src/pages/DepartmentsList/index.module.scss
Normal file
@@ -0,0 +1,42 @@
|
||||
.departmentItem {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
|
||||
.name {
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btnsBox {
|
||||
display: none;
|
||||
|
||||
.edit {
|
||||
width: 24px;
|
||||
color: #1890ff;
|
||||
|
||||
&:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
}
|
||||
|
||||
.del {
|
||||
width: 24px;
|
||||
color: #ff4d4f;
|
||||
|
||||
&:hover {
|
||||
color: #ff7875;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .btnsBox {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
407
src/pages/DepartmentsList/index.tsx
Normal file
407
src/pages/DepartmentsList/index.tsx
Normal file
@@ -0,0 +1,407 @@
|
||||
import { SearchBarPlugin, SearchBottonsCardPlugin } from '@/components/SearchBarPlugin';
|
||||
import { post } from '@/services/ajax';
|
||||
import { DeleteOutlined, FormOutlined } from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
Pagination,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Select,
|
||||
Table,
|
||||
Tree,
|
||||
} from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
age: number;
|
||||
address: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const DepartmentsList: React.FC = () => {
|
||||
const [param, setParam] = useState({
|
||||
curr_page: 1,
|
||||
page_count: 20,
|
||||
});
|
||||
|
||||
const [departmentID, setDepartmentsID] = useState(1);
|
||||
|
||||
const departments = [
|
||||
{
|
||||
id: 1,
|
||||
name: '福州晨丰科技有限公司',
|
||||
parent_id: 0,
|
||||
sort: 2147483447,
|
||||
department_leader: 'chenf,LiXiang,CFRS',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
name: '总经办',
|
||||
parent_id: 1,
|
||||
sort: 2147483447,
|
||||
department_leader: 'chenf',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '研发部',
|
||||
parent_id: 1,
|
||||
sort: 2147483247,
|
||||
department_leader: 'yangxb',
|
||||
children: [
|
||||
{
|
||||
id: 18,
|
||||
name: '.Net组',
|
||||
parent_id: 3,
|
||||
sort: 100005000,
|
||||
department_leader: 'xief',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: '平台组',
|
||||
parent_id: 3,
|
||||
sort: 100004000,
|
||||
department_leader: 'yangxb',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: 'CAD组',
|
||||
parent_id: 3,
|
||||
sort: 100003000,
|
||||
department_leader: 'chenx',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
name: '测试组',
|
||||
parent_id: 3,
|
||||
sort: 100001000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
name: '是的防守对方',
|
||||
parent_id: 3,
|
||||
sort: 100000000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
name: '12',
|
||||
parent_id: 3,
|
||||
sort: 99999000,
|
||||
department_leader: '',
|
||||
children: [
|
||||
{
|
||||
id: 25,
|
||||
name: '3',
|
||||
parent_id: 24,
|
||||
sort: 100000000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 181,
|
||||
name: '.Net组',
|
||||
parent_id: 3,
|
||||
sort: 100005000,
|
||||
department_leader: 'xief',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 191,
|
||||
name: '平台组',
|
||||
parent_id: 3,
|
||||
sort: 100004000,
|
||||
department_leader: 'yangxb',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 201,
|
||||
name: 'CAD组',
|
||||
parent_id: 3,
|
||||
sort: 100003000,
|
||||
department_leader: 'chenx',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 211,
|
||||
name: '测试组',
|
||||
parent_id: 3,
|
||||
sort: 100001000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: 231,
|
||||
name: '是的防守对方是的防守对方是的防守对方是的防守对方',
|
||||
parent_id: 3,
|
||||
sort: 100000000,
|
||||
department_leader: '',
|
||||
children: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const data: DataType[] = [
|
||||
{
|
||||
key: '2',
|
||||
firstName: 'Jim',
|
||||
lastName: 'Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser'],
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
firstName: 'Joe',
|
||||
lastName: 'Black',
|
||||
age: 32,
|
||||
address: 'Sydney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher'],
|
||||
},
|
||||
];
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [popOpen, setPopOpen] = useState(-1);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 260,
|
||||
flexShrink: 0,
|
||||
minHeight: 'calc(100vh - 55px - 60px - 96px)',
|
||||
maxHeight: 'calc(100vh - 55px - 60px)',
|
||||
overflow: 'auto',
|
||||
marginRight: 12,
|
||||
background: '#fff',
|
||||
position: 'sticky',
|
||||
top: 64,
|
||||
padding: '12px 0',
|
||||
}}
|
||||
>
|
||||
<Tree
|
||||
className={'department-tree'}
|
||||
blockNode
|
||||
selectedKeys={[departmentID]}
|
||||
defaultExpandAll
|
||||
style={{}}
|
||||
treeData={departments}
|
||||
fieldNames={{ title: 'name', key: 'id' }}
|
||||
onSelect={(selectedKeys) => {
|
||||
console.log(selectedKeys);
|
||||
if (selectedKeys.length) {
|
||||
setDepartmentsID(Number(selectedKeys[0]));
|
||||
}
|
||||
}}
|
||||
titleRender={(nodeData: any) => {
|
||||
// console.log(nodeData);
|
||||
return (
|
||||
<div className={styles.departmentItem}>
|
||||
<div className={styles.name} title={nodeData.name}>
|
||||
{nodeData.name}
|
||||
</div>
|
||||
<div className={styles.btnsBox}>
|
||||
<FormOutlined
|
||||
title="修改"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpen(true);
|
||||
}}
|
||||
className={styles.edit}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确认删除?"
|
||||
open={popOpen == nodeData.id}
|
||||
onCancel={(e) => {
|
||||
setPopOpen(-1);
|
||||
e?.stopPropagation();
|
||||
}}
|
||||
onConfirm={(e) => {
|
||||
e?.stopPropagation();
|
||||
console.log(nodeData.id);
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined
|
||||
title="删除"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPopOpen(nodeData.id);
|
||||
}}
|
||||
className={styles.del}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
></Tree>
|
||||
</div>
|
||||
<Modal
|
||||
open={open}
|
||||
title="修改部门"
|
||||
onCancel={() => setOpen(false)}
|
||||
centered
|
||||
onOk={() => {}}
|
||||
>
|
||||
<Form>
|
||||
<Form.Item label="名称">
|
||||
<Input type="text"></Input>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<div style={{ flexGrow: 1, minWidth: 0 }}>
|
||||
<SearchBarPlugin>
|
||||
<Form>
|
||||
<Row gutter={{ xs: 0, sm: 16 }}>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Select
|
||||
defaultValue="lucy"
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => {}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
{ value: 'Yiminghe', label: 'yiminghe' },
|
||||
{ value: 'disabled', label: 'Disabled', disabled: true },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item label="用户名">
|
||||
<Input autoComplete="off"></Input>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</SearchBarPlugin>
|
||||
<SearchBottonsCardPlugin>
|
||||
<Row justify={'center'}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
post({
|
||||
url: '/Departments/List',
|
||||
}).then((res) => {});
|
||||
}}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</Row>
|
||||
</SearchBottonsCardPlugin>
|
||||
<Table
|
||||
size="middle"
|
||||
dataSource={data}
|
||||
style={{ padding: 16, borderRadius: 6, background: '#fff', marginTop: 16 }}
|
||||
pagination={false}
|
||||
bordered={true}
|
||||
>
|
||||
<Table.Column title="ID" dataIndex={'key'}></Table.Column>
|
||||
<Table.Column title="firstName" dataIndex={'firstName'}></Table.Column>
|
||||
<Table.Column
|
||||
width={90}
|
||||
title="操作"
|
||||
render={(_text, record: any) => {
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
console.log(record);
|
||||
}}
|
||||
>
|
||||
{record.firstName}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
></Table.Column>
|
||||
</Table>
|
||||
|
||||
<Pagination
|
||||
style={{
|
||||
background: '#fff',
|
||||
borderRadius: 6,
|
||||
marginTop: 16,
|
||||
padding: 16,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
current={param.curr_page}
|
||||
pageSize={param.page_count}
|
||||
total={1000}
|
||||
pageSizeOptions={[10, 20, 50, 100]}
|
||||
onShowSizeChange={(current, size) => {
|
||||
param.page_count = size;
|
||||
setParam({ ...param });
|
||||
}}
|
||||
showTotal={(total, range) => {
|
||||
return <span style={{ lineHeight: 1 }}>共{total}条</span>;
|
||||
}}
|
||||
onChange={(page, pageSize) => {
|
||||
param.curr_page = page;
|
||||
setParam({ ...param });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DepartmentsList;
|
@@ -10,7 +10,7 @@ import {
|
||||
ProFormTextArea,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { FormattedMessage, useIntl } from '@umijs/max';
|
||||
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';
|
||||
@@ -241,8 +241,33 @@ const TableList: React.FC = () => {
|
||||
},
|
||||
];
|
||||
|
||||
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',
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import { login } from '@/services/ant-design-pro/api';
|
||||
import { IAjaxReturn, post } from '@/services/ajax';
|
||||
import { getFakeCaptcha } from '@/services/ant-design-pro/login';
|
||||
import {
|
||||
AlipayCircleOutlined,
|
||||
@@ -16,11 +16,12 @@ import {
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import { useEmotionCss } from '@ant-design/use-emotion-css';
|
||||
import { FormattedMessage, history, SelectLang, useIntl, useModel, Helmet } from '@umijs/max';
|
||||
import { Alert, message, Tabs } from 'antd';
|
||||
import Settings from '../../../../config/defaultSettings';
|
||||
import { FormattedMessage, Helmet, history, SelectLang, useIntl, useModel } from '@umijs/max';
|
||||
import { Alert, App, message, Tabs } from 'antd';
|
||||
import { stringify as qsStringify } from 'qs';
|
||||
import React, { useState } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import Settings from '../../../../config/defaultSettings';
|
||||
|
||||
const ActionIcons = () => {
|
||||
const langClassName = useEmotionCss(({ token }) => {
|
||||
@@ -113,33 +114,34 @@ const Login: React.FC = () => {
|
||||
});
|
||||
}
|
||||
};
|
||||
const { notification } = App.useApp();
|
||||
|
||||
window.NotificationCF = notification;
|
||||
|
||||
const handleSubmit = async (values: API.LoginParams) => {
|
||||
try {
|
||||
// 登录
|
||||
const msg = await login({ ...values, type });
|
||||
if (msg.status === 'ok') {
|
||||
const defaultLoginSuccessMessage = intl.formatMessage({
|
||||
id: 'pages.login.success',
|
||||
defaultMessage: '登录成功!',
|
||||
const data = {
|
||||
user: values.username,
|
||||
password: values.password,
|
||||
};
|
||||
|
||||
post({ url: '/User/Login', data: qsStringify(data) }).then((res: IAjaxReturn) => {
|
||||
if (res && res.err_code == 0) {
|
||||
notification.success({
|
||||
message: '登录成功',
|
||||
description: '登录成功',
|
||||
});
|
||||
flushSync(() => {
|
||||
// 登录成功!
|
||||
setInitialState((s) => ({
|
||||
...s,
|
||||
currentUser: res.data,
|
||||
currentAuthority: 'admin',
|
||||
}));
|
||||
});
|
||||
message.success(defaultLoginSuccessMessage);
|
||||
await fetchUserInfo();
|
||||
const urlParams = new URL(window.location.href).searchParams;
|
||||
history.push(urlParams.get('redirect') || '/');
|
||||
return;
|
||||
}
|
||||
console.log(msg);
|
||||
// 如果失败去设置用户错误信息
|
||||
setUserLoginState(msg);
|
||||
} catch (error) {
|
||||
const defaultLoginFailureMessage = intl.formatMessage({
|
||||
id: 'pages.login.failure',
|
||||
defaultMessage: '登录失败,请重试!',
|
||||
});
|
||||
console.log(error);
|
||||
message.error(defaultLoginFailureMessage);
|
||||
}
|
||||
});
|
||||
};
|
||||
const { status, type: loginType } = userLoginState;
|
||||
|
||||
@@ -154,32 +156,50 @@ const Login: React.FC = () => {
|
||||
- {Settings.title}
|
||||
</title>
|
||||
</Helmet>
|
||||
<Lang />
|
||||
{/* <Lang /> */}
|
||||
<div
|
||||
style={{
|
||||
flex: '1',
|
||||
padding: '32px 0',
|
||||
}}
|
||||
>
|
||||
{/* <ChatTime msgtime={1680713999000}></ChatTime>
|
||||
<ChatTime msgtime={1680763041000}></ChatTime>
|
||||
<ChatTime msgtime={1680710399000}></ChatTime>
|
||||
<ChatTime msgtime={1680623999000}></ChatTime>
|
||||
<ChatTime msgtime={1680537599000}></ChatTime>
|
||||
<ChatTime msgtime={1680451199000}></ChatTime>
|
||||
<ChatTime msgtime={1680331041000}></ChatTime>
|
||||
<ChatTime msgtime={1680195599000}></ChatTime>
|
||||
<ChatTime msgtime={1680244641000}></ChatTime>
|
||||
<ChatTime msgtime={1680191999000}></ChatTime>
|
||||
<ChatTime msgtime={1680158241000}></ChatTime>
|
||||
<ChatRevoke></ChatRevoke>
|
||||
<ChatAgreeOrNot />
|
||||
<ChatVoice></ChatVoice>
|
||||
<ChatVideo></ChatVideo>
|
||||
<ChatCard></ChatCard>
|
||||
<ChatEmotion></ChatEmotion>
|
||||
<ChatBar></ChatBar> */}
|
||||
<LoginForm
|
||||
contentStyle={{
|
||||
minWidth: 280,
|
||||
maxWidth: '75vw',
|
||||
}}
|
||||
logo={<img alt="logo" src="/logo.svg" />}
|
||||
title="Ant Design"
|
||||
subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
|
||||
title="scrm.antd"
|
||||
subTitle={<div style={{ height: 30 }}></div>}
|
||||
initialValues={{
|
||||
autoLogin: true,
|
||||
}}
|
||||
actions={[
|
||||
<FormattedMessage
|
||||
key="loginWith"
|
||||
id="pages.login.loginWith"
|
||||
defaultMessage="其他登录方式"
|
||||
/>,
|
||||
<ActionIcons key="icons" />,
|
||||
]}
|
||||
// actions={[
|
||||
// <FormattedMessage
|
||||
// key="loginWith"
|
||||
// id="pages.login.loginWith"
|
||||
// defaultMessage="其他登录方式"
|
||||
// />,
|
||||
// <ActionIcons key="icons" />,
|
||||
// ]}
|
||||
onFinish={async (values) => {
|
||||
await handleSubmit(values as API.LoginParams);
|
||||
}}
|
||||
@@ -196,13 +216,13 @@ const Login: React.FC = () => {
|
||||
defaultMessage: '账户密码登录',
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: 'mobile',
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.login.phoneLogin.tab',
|
||||
defaultMessage: '手机号登录',
|
||||
}),
|
||||
},
|
||||
// {
|
||||
// key: 'mobile',
|
||||
// label: intl.formatMessage({
|
||||
// id: 'pages.login.phoneLogin.tab',
|
||||
// defaultMessage: '手机号登录',
|
||||
// }),
|
||||
// },
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -246,7 +266,7 @@ const Login: React.FC = () => {
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.login.password.placeholder',
|
||||
defaultMessage: '密码: ant.design',
|
||||
defaultMessage: '请输入密码',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
@@ -364,6 +384,7 @@ const Login: React.FC = () => {
|
||||
</LoginForm>
|
||||
</div>
|
||||
<Footer />
|
||||
<div style={{ height: 12 }}></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -86,8 +86,12 @@ const InfoCard: React.FC<{
|
||||
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,
|
||||
|
@@ -89,7 +89,8 @@ export const errorConfig: RequestConfig = {
|
||||
requestInterceptors: [
|
||||
(config: RequestOptions) => {
|
||||
// 拦截请求配置,进行个性化处理。
|
||||
const url = config?.url?.concat('?token = 123');
|
||||
// const url = config?.url?.concat('?token = 123');
|
||||
const url = config?.url;
|
||||
return { ...config, url };
|
||||
},
|
||||
],
|
||||
|
33
src/services/ajax.ts
Normal file
33
src/services/ajax.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { request } from '@umijs/max';
|
||||
|
||||
export interface IAjaxReturn {
|
||||
err_code: number;
|
||||
err_msg: string;
|
||||
count?: number;
|
||||
[propname: string]: any;
|
||||
}
|
||||
|
||||
export interface IPost {
|
||||
url: string;
|
||||
data?: any;
|
||||
showError?: boolean;
|
||||
}
|
||||
|
||||
export const post = async (param: IPost) => {
|
||||
const { url, data, showError = true } = param;
|
||||
return await request('/api' + url, {
|
||||
data: data,
|
||||
}).then((res: IAjaxReturn) => {
|
||||
if (res.err_code != 0 && showError) {
|
||||
if (window.NotificationCF) {
|
||||
window.NotificationCF.error({
|
||||
message: `${res.err_msg}`,
|
||||
description: `${res.err_code}:${res.err_msg}`,
|
||||
});
|
||||
} else {
|
||||
console.log('window.NotificationCF未设置');
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
};
|
7
src/services/ant-design-pro/typings.d.ts
vendored
7
src/services/ant-design-pro/typings.d.ts
vendored
@@ -3,9 +3,12 @@
|
||||
|
||||
declare namespace API {
|
||||
type CurrentUser = {
|
||||
name?: string;
|
||||
login_name?: string;
|
||||
state?: string | number;
|
||||
avatar?: string;
|
||||
userid?: string;
|
||||
user_id?: string;
|
||||
user_name?: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
signature?: string;
|
||||
title?: string;
|
||||
|
17
src/services/utils.ts
Normal file
17
src/services/utils.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
interface IGetDevice {
|
||||
(): 'phone' | 'tablet' | 'desktop';
|
||||
}
|
||||
|
||||
export const getDevice: IGetDevice = () => {
|
||||
const width = window.innerWidth;
|
||||
const isPhone =
|
||||
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
|
||||
|
||||
if (width < 680 || isPhone) {
|
||||
return 'phone';
|
||||
} else if (width < 1280 && width > 680) {
|
||||
return 'tablet';
|
||||
} else {
|
||||
return 'desktop';
|
||||
}
|
||||
};
|
8
src/window.d.ts
vendored
Normal file
8
src/window.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NotificationInstance } from 'antd/es/notification/interface';
|
||||
|
||||
// 在window上挂载 antd Notification
|
||||
declare global {
|
||||
interface Window {
|
||||
NotificationCF: NotificationInstance;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user