From 5834254a8f76be6192bb2caffd8a967b49530dc9 Mon Sep 17 00:00:00 2001
From: zhengw <247276359@qq.com>
Date: Fri, 7 Apr 2023 17:38:15 +0800
Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91:=20=E7=99=BB=E5=BD=95?=
=?UTF-8?q?=E7=AD=89=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
config/config.ts | 20 +-
config/defaultSettings.ts | 4 +-
config/routes.ts | 84 ++--
package.json | 2 +-
src/app.tsx | 65 ++-
src/components/Footer/index.tsx | 25 +-
.../RightContent/AvatarDropdown.tsx | 61 ++-
.../SearchBarPlugin/index.module.scss | 36 ++
src/components/SearchBarPlugin/index.tsx | 62 +++
src/global.less | 51 ++-
src/locales/zh-CN.ts | 2 +-
src/locales/zh-CN/pages.ts | 6 +-
src/models/demo.ts | 8 +
src/pages/404.tsx | 4 +-
src/pages/ChatLogs/ChatBar.tsx | 271 ++++++++++++
src/pages/ChatLogs/index.module.scss | 109 +++++
src/pages/ChatLogs/index.tsx | 161 +++++++
src/pages/DepartmentsList/index.module.scss | 42 ++
src/pages/DepartmentsList/index.tsx | 407 ++++++++++++++++++
src/pages/TableList/index.tsx | 27 +-
src/pages/User/Login/index.tsx | 109 +++--
src/pages/Welcome.tsx | 4 +
src/requestErrorConfig.ts | 3 +-
src/services/ajax.ts | 33 ++
src/services/ant-design-pro/typings.d.ts | 7 +-
src/services/utils.ts | 17 +
src/window.d.ts | 8 +
27 files changed, 1475 insertions(+), 153 deletions(-)
create mode 100644 src/components/SearchBarPlugin/index.module.scss
create mode 100644 src/components/SearchBarPlugin/index.tsx
create mode 100644 src/models/demo.ts
create mode 100644 src/pages/ChatLogs/ChatBar.tsx
create mode 100644 src/pages/ChatLogs/index.module.scss
create mode 100644 src/pages/ChatLogs/index.tsx
create mode 100644 src/pages/DepartmentsList/index.module.scss
create mode 100644 src/pages/DepartmentsList/index.tsx
create mode 100644 src/services/ajax.ts
create mode 100644 src/services/utils.ts
create mode 100644 src/window.d.ts
diff --git a/config/config.ts b/config/config.ts
index acf2afd..752cba6 100644
--- a/config/config.ts
+++ b/config/config.ts
@@ -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
diff --git a/config/defaultSettings.ts b/config/defaultSettings.ts
index 9fac66a..3393bdb 100644
--- a/config/defaultSettings.ts
+++ b/config/defaultSettings.ts
@@ -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;
diff --git a/config/routes.ts b/config/routes.ts
index 436b0e3..98eab3b 100644
--- a/config/routes.ts
+++ b/config/routes.ts
@@ -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',
},
-
];
diff --git a/package.json b/package.json
index 96084e3..e91c1df 100644
--- a/package.json
+++ b/package.json
@@ -92,4 +92,4 @@
"engines": {
"node": ">=12.0.0"
}
-}
+}
\ No newline at end of file
diff --git a/src/app.tsx b/src/app.tsx
index 024f261..51b63b1 100644
--- a/src/app.tsx
+++ b/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: () => [, ],
+ // actionsRender: () => [, ],
+ actionsRender: () => [],
avatarProps: {
- src: initialState?.currentUser?.avatar,
+ src: initialState?.currentUser?.avatar
+ ? '/api/' + initialState?.currentUser?.avatar
+ : 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
title: ,
render: (_, avatarChildren) => {
return {avatarChildren};
},
},
- waterMarkProps: {
- content: initialState?.currentUser?.name,
- },
+ // waterMarkProps: {
+ // content: initialState?.currentUser?.login_name,
+ // },
+ siderWidth: 200,
footerRender: () => ,
onPageChange: () => {
const { location } = history;
@@ -91,6 +97,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
width: '331px',
},
],
+ layout: 'side',
links: isDev
? [
@@ -99,6 +106,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
,
]
: [],
+ suppressSiderWhenMenuEmpty: true,
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible:
unAccessible
,
@@ -108,7 +116,14 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
return (
<>
{children}
- }
+ type="primary"
+ onClick={() => {
+ window.scrollTo(0, 0);
+ }}
+ >
+ {/*
+ /> */}
>
);
},
@@ -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],
};
diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx
index 03ac146..76a0188 100644
--- a/src/components/Footer/index.tsx
+++ b/src/components/Footer/index.tsx
@@ -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 (
,
- 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,
},
]}
diff --git a/src/components/RightContent/AvatarDropdown.tsx b/src/components/RightContent/AvatarDropdown.tsx
index c4c6415..4c47480 100644
--- a/src/components/RightContent/AvatarDropdown.tsx
+++ b/src/components/RightContent/AvatarDropdown.tsx
@@ -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 {currentUser?.name};
+ return {currentUser?.login_name};
};
export const AvatarDropdown: React.FC = ({ 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 = ({ 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 = ({ 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 = ({ menu, childre
];
return (
-
- {children}
-
+ <>
+
+ {children}
+
+ setVisible(false)}
+ onOk={() => {
+ flushSync(() => {
+ setInitialState((s) => ({ ...s, currentUser: undefined }));
+ });
+ loginOut();
+ }}
+ >
+ 确认退出账号?
+
+ >
);
};
diff --git a/src/components/SearchBarPlugin/index.module.scss b/src/components/SearchBarPlugin/index.module.scss
new file mode 100644
index 0000000..58ab4ee
--- /dev/null
+++ b/src/components/SearchBarPlugin/index.module.scss
@@ -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;
+ }
+ }
+}
diff --git a/src/components/SearchBarPlugin/index.tsx b/src/components/SearchBarPlugin/index.tsx
new file mode 100644
index 0000000..86ea901
--- /dev/null
+++ b/src/components/SearchBarPlugin/index.tsx
@@ -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 = (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(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 (
+
+
+ {props.children}
+
+
+
+
{searchBarText}
+ {searchBarText == '展开' ?
:
}
+
+
+
+ );
+};
+
+export const SearchBottonsCardPlugin: React.FC = (props) => {
+ return {props.children}
;
+};
diff --git a/src/global.less b/src/global.less
index a9a0c51..a3db108 100644
--- a/src/global.less
+++ b/src/global.less
@@ -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;
}
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts
index 7691489..60c9690 100644
--- a/src/locales/zh-CN.ts
+++ b/src/locales/zh-CN.ts
@@ -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 开发,快速构建标准页面',
diff --git a/src/locales/zh-CN/pages.ts b/src/locales/zh-CN/pages.ts
index 7fa751f..7783c22 100644
--- a/src/locales/zh-CN/pages.ts
+++ b/src/locales/zh-CN/pages.ts
@@ -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': '验证码错误',
diff --git a/src/models/demo.ts b/src/models/demo.ts
new file mode 100644
index 0000000..6685c81
--- /dev/null
+++ b/src/models/demo.ts
@@ -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 };
+};
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 0263687..df82c46 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -6,10 +6,10 @@ const NoFoundPage: React.FC = () => (
history.push('/')}>
- Back Home
+ 返回首页
}
/>
diff --git a/src/pages/ChatLogs/ChatBar.tsx b/src/pages/ChatLogs/ChatBar.tsx
new file mode 100644
index 0000000..94d2c38
--- /dev/null
+++ b/src/pages/ChatLogs/ChatBar.tsx
@@ -0,0 +1,271 @@
+import { PlayCircleOutlined } from '@ant-design/icons';
+import styles from './index.module.scss';
+export const ChatBar: React.FC = () => {
+ return (
+
+
+
+

+
+
+
name
+
+ 通过 message.useMessage 创建支持读取 context 的
+ contextHolder。请注意,我们推荐通过顶层注册的方式代替 message
+ 静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+
+
+
+
+
name
+
+ 通过 message.useMessage 创建支持读取 context 的
+ contextHolder。请注意,我们推荐通过顶层注册的方式代替 message
+ 静态方法,因为静态方法无法消费上下文,因而 ConfigProvider 的数据也不会生效。
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+
+
+

+
+
+
+ );
+};
+
+interface IChatTimeProps {
+ msgtime: number;
+}
+
+export const ChatTime: React.FC = (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 (
+
+ );
+};
+
+export const ChatRevoke: React.FC = () => {
+ return (
+
+
+ "xxxxx" 撤回了一条消息
+
+
+ );
+};
+
+export const ChatAgreeOrNot: React.FC = () => {
+ return (
+
+
+ 对方[不]同意存档会话内容,你将无法继续提供服务
+
+
+ );
+};
+
+// 语音
+export const ChatVoice = () => {
+ return (
+
+
+
+

+
+
+ );
+};
+// 视频
+export const ChatVideo = () => {
+ return (
+
+
+
+

+
+
+ );
+};
+
+// 名片
+export const ChatCard = () => {
+ return (
+
+
+
name
+
+
+
+

+
+
腾讯
+
+
corpname
+
corpname2
+
个人名片
+
+
+
+

+
+
+ );
+};
+
+// 表情
+export const ChatEmotion = () => {
+ return (
+
+
+
name
+
+

+
+
+
+

+
+
+ );
+};
+// 文件
+export const ChatFile = () => {
+ return (
+
+
+
name
+
+

+
+
+
+

+
+
+ );
+};
diff --git a/src/pages/ChatLogs/index.module.scss b/src/pages/ChatLogs/index.module.scss
new file mode 100644
index 0000000..08c8688
--- /dev/null
+++ b/src/pages/ChatLogs/index.module.scss
@@ -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;
+ }
+}
diff --git a/src/pages/ChatLogs/index.tsx b/src/pages/ChatLogs/index.tsx
new file mode 100644
index 0000000..5420bbd
--- /dev/null
+++ b/src/pages/ChatLogs/index.tsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ param.page_count = size;
+ setParam({ ...param });
+ }}
+ showTotal={(total, range) => {
+ return 共{total}条;
+ }}
+ onChange={(page, pageSize) => {
+ param.curr_page = page;
+ setParam({ ...param });
+ }}
+ />
+
+ );
+};
+
+export default ChatLogs;
diff --git a/src/pages/DepartmentsList/index.module.scss b/src/pages/DepartmentsList/index.module.scss
new file mode 100644
index 0000000..fab244d
--- /dev/null
+++ b/src/pages/DepartmentsList/index.module.scss
@@ -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;
+ }
+}
diff --git a/src/pages/DepartmentsList/index.tsx b/src/pages/DepartmentsList/index.tsx
new file mode 100644
index 0000000..5bbc2ef
--- /dev/null
+++ b/src/pages/DepartmentsList/index.tsx
@@ -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 (
+
+
+
+
{
+ console.log(selectedKeys);
+ if (selectedKeys.length) {
+ setDepartmentsID(Number(selectedKeys[0]));
+ }
+ }}
+ titleRender={(nodeData: any) => {
+ // console.log(nodeData);
+ return (
+
+
+ {nodeData.name}
+
+
+
{
+ e.stopPropagation();
+ setOpen(true);
+ }}
+ className={styles.edit}
+ />
+ {
+ setPopOpen(-1);
+ e?.stopPropagation();
+ }}
+ onConfirm={(e) => {
+ e?.stopPropagation();
+ console.log(nodeData.id);
+ }}
+ >
+ {
+ e.stopPropagation();
+ setPopOpen(nodeData.id);
+ }}
+ className={styles.del}
+ />
+
+
+
+ );
+ }}
+ >
+
+
setOpen(false)}
+ centered
+ onOk={() => {}}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ >
+
+
+
{
+ param.page_count = size;
+ setParam({ ...param });
+ }}
+ showTotal={(total, range) => {
+ return 共{total}条;
+ }}
+ onChange={(page, pageSize) => {
+ param.curr_page = page;
+ setParam({ ...param });
+ }}
+ />
+
+
+
+ );
+};
+
+export default DepartmentsList;
diff --git a/src/pages/TableList/index.tsx b/src/pages/TableList/index.tsx
index c201007..6691441 100644
--- a/src/pages/TableList/index.tsx
+++ b/src/pages/TableList/index.tsx
@@ -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 (
+
+
+
+ {counter}
+
headerTitle={intl.formatMessage({
id: 'pages.searchTable.title',
diff --git a/src/pages/User/Login/index.tsx b/src/pages/User/Login/index.tsx
index ef48307..28d7449 100644
--- a/src/pages/User/Login/index.tsx
+++ b/src/pages/User/Login/index.tsx
@@ -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="Ant Design"
- subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
+ title="scrm.antd"
+ subTitle={
}
initialValues={{
autoLogin: true,
}}
- actions={[
-
,
-
,
- ]}
+ // actions={[
+ //
,
+ //
,
+ // ]}
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 = () => {
+
);
};
diff --git a/src/pages/Welcome.tsx b/src/pages/Welcome.tsx
index d0c49f7..aced863 100644
--- a/src/pages/Welcome.tsx
+++ b/src/pages/Welcome.tsx
@@ -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 (
+ {count.counter}
{
// 拦截请求配置,进行个性化处理。
- const url = config?.url?.concat('?token = 123');
+ // const url = config?.url?.concat('?token = 123');
+ const url = config?.url;
return { ...config, url };
},
],
diff --git a/src/services/ajax.ts b/src/services/ajax.ts
new file mode 100644
index 0000000..11556aa
--- /dev/null
+++ b/src/services/ajax.ts
@@ -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;
+ });
+};
diff --git a/src/services/ant-design-pro/typings.d.ts b/src/services/ant-design-pro/typings.d.ts
index 13e5a68..41622ae 100644
--- a/src/services/ant-design-pro/typings.d.ts
+++ b/src/services/ant-design-pro/typings.d.ts
@@ -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;
diff --git a/src/services/utils.ts b/src/services/utils.ts
new file mode 100644
index 0000000..ac292d3
--- /dev/null
+++ b/src/services/utils.ts
@@ -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';
+ }
+};
diff --git a/src/window.d.ts b/src/window.d.ts
new file mode 100644
index 0000000..03294b0
--- /dev/null
+++ b/src/window.d.ts
@@ -0,0 +1,8 @@
+import { NotificationInstance } from 'antd/es/notification/interface';
+
+// 在window上挂载 antd Notification
+declare global {
+ interface Window {
+ NotificationCF: NotificationInstance;
+ }
+}