基本完成消息通知
parent
e71ca15397
commit
06a283390e
|
@ -21,6 +21,7 @@
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"echarts": "^5.4.1",
|
"echarts": "^5.4.1",
|
||||||
"echarts-for-react": "^3.0.2",
|
"echarts-for-react": "^3.0.2",
|
||||||
|
"event-source-polyfill": "^1.0.31",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -30,6 +31,9 @@
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/event-source-polyfill": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
|
@ -4033,6 +4037,12 @@
|
||||||
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.0.tgz",
|
||||||
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ=="
|
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/event-source-polyfill": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/event-source-polyfill/-/event-source-polyfill-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-b8O8/rg7NIW0iJ8i9MNDBZqPljHA+b7AjC3QFqH3dSyW6vgrl3oBgyIv5dw2fibh5enHHDkkPZG5PHza7U4NRw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/express": {
|
"node_modules/@types/express": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.15",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.15.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.15.tgz",
|
||||||
|
@ -7679,6 +7689,11 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/event-source-polyfill": {
|
||||||
|
"version": "1.0.31",
|
||||||
|
"resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz",
|
||||||
|
"integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA=="
|
||||||
|
},
|
||||||
"node_modules/eventemitter3": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
@ -20152,6 +20167,12 @@
|
||||||
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.0.tgz",
|
||||||
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ=="
|
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ=="
|
||||||
},
|
},
|
||||||
|
"@types/event-source-polyfill": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/event-source-polyfill/-/event-source-polyfill-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-b8O8/rg7NIW0iJ8i9MNDBZqPljHA+b7AjC3QFqH3dSyW6vgrl3oBgyIv5dw2fibh5enHHDkkPZG5PHza7U4NRw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/express": {
|
"@types/express": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.15",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.15.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.15.tgz",
|
||||||
|
@ -23080,6 +23101,11 @@
|
||||||
"resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
|
||||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
|
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
|
||||||
},
|
},
|
||||||
|
"event-source-polyfill": {
|
||||||
|
"version": "1.0.31",
|
||||||
|
"resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz",
|
||||||
|
"integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA=="
|
||||||
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"echarts": "^5.4.1",
|
"echarts": "^5.4.1",
|
||||||
"echarts-for-react": "^3.0.2",
|
"echarts-for-react": "^3.0.2",
|
||||||
|
"event-source-polyfill": "^1.0.31",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -49,5 +50,8 @@
|
||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/event-source-polyfill": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ export interface Notice {
|
||||||
noticeId: string;
|
noticeId: string;
|
||||||
noticeTargetId: string;
|
noticeTargetId: string;
|
||||||
noticeTime: string;
|
noticeTime: string;
|
||||||
|
alreadyRead: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
import React, {useState, useEffect} from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import {UploadOutlined, UserOutlined, BellOutlined, CloseOutlined} from '@ant-design/icons';
|
import {UploadOutlined, UserOutlined, BellOutlined, CloseOutlined} from '@ant-design/icons';
|
||||||
import {Layout, Menu, theme, Typography, Button, Dropdown, MenuProps, Popover} from 'antd';
|
import {Layout, Menu, theme, Typography, Button, Dropdown, MenuProps, Popover, Badge} from 'antd';
|
||||||
import {useAppDispatch, useAppSelector} from "../models/hooks";
|
import {useAppDispatch, useAppSelector} from "../models/hooks";
|
||||||
import {getStaff, getToken, setStaff, setToken, store} from "../models/store";
|
import {getStaff, getToken, setStaff, setToken, store} from "../models/store";
|
||||||
import {Link, Outlet, useLocation, useNavigate} from "react-router-dom";
|
import {Link, Outlet, useLocation, useNavigate} from "react-router-dom";
|
||||||
import Icon from '@ant-design/icons';
|
import Icon from '@ant-design/icons';
|
||||||
import {Logo} from "../assets/icons";
|
import {Logo} from "../assets/icons";
|
||||||
import axiosInstance from "../utils/axiosInstance";
|
import axiosInstance, {baseUrl} from "../utils/axiosInstance";
|
||||||
import {Department, Staff, Token} from "../models/Staff";
|
import {Department, Staff, Token} from "../models/Staff";
|
||||||
import MessageList from "./MessageList";
|
import MessageList from "./MessageList";
|
||||||
|
import {NativeEventSource, EventSourcePolyfill} from 'event-source-polyfill';
|
||||||
|
import {NoticeResponse} from "../models/Notice";
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
const {Title, Text, Paragraph} = Typography;
|
const {Title, Text, Paragraph} = Typography;
|
||||||
const {Header, Content, Footer, Sider} = Layout;
|
const {Header, Content, Footer, Sider} = Layout;
|
||||||
|
|
||||||
|
|
||||||
function HeaderBar(props: any) {
|
function HeaderBar(props: { messageCount: number, onMessageCountChange: (count: number) => void }) {
|
||||||
const {
|
const {
|
||||||
token: {colorBgContainer, colorPrimary},
|
token: {colorBgContainer, colorPrimary},
|
||||||
} = theme.useToken();
|
} = theme.useToken();
|
||||||
|
@ -90,7 +93,7 @@ function HeaderBar(props: any) {
|
||||||
boxShadow: '0px 6px 16px 0px rgba(0, 0, 0, 0.08)'
|
boxShadow: '0px 6px 16px 0px rgba(0, 0, 0, 0.08)'
|
||||||
}}>
|
}}>
|
||||||
<div style={{marginLeft: 20}}>
|
<div style={{marginLeft: 20}}>
|
||||||
<MessageList/>
|
<MessageList count={props.messageCount} onCountChange={props.onMessageCountChange}/>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
@ -121,18 +124,21 @@ function HeaderBar(props: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function MainMenu(props: any) {
|
function MainMenu(props: any) {
|
||||||
console.log(props)
|
const [key, setKey] = useState(1)
|
||||||
if (props.defaultSelectedKeys[0] === '')
|
useEffect(() => {
|
||||||
return null
|
setKey(key + 1)
|
||||||
else
|
}, [props]);
|
||||||
return (
|
return (
|
||||||
|
<div key={key} style={{width: '100%'}}>
|
||||||
<Menu
|
<Menu
|
||||||
style={{borderInlineEnd: 'unset'}}
|
style={{width: '100%', borderInlineEnd: 'unset'}}
|
||||||
mode="inline"
|
mode="inline"
|
||||||
selectedKeys={props.defaultSelectedKeys}
|
selectedKeys={props.defaultSelectedKeys}
|
||||||
items={props.items}
|
items={props.items}
|
||||||
/>
|
/>
|
||||||
)
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeView() {
|
function HomeView() {
|
||||||
|
@ -141,54 +147,54 @@ function HomeView() {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const [messageCount, setMessageCount] = useState([])
|
const [messageCount, setMessageCount] = useState(0)
|
||||||
|
const [reimbursementCount, setReimbursementCount] = useState(0)
|
||||||
|
const [badgeTop, setBadgeTop] = useState(66 + 3 * 44)
|
||||||
|
const [eventSource, setEventSource] = useState<EventSourcePolyfill | null>(null)
|
||||||
|
|
||||||
// 新建Sse连接
|
|
||||||
const createSseConnect = () => {
|
const createSseConnect = () => {
|
||||||
if (window.EventSource) {
|
if (eventSource === null) {
|
||||||
|
let source = new EventSourcePolyfill(baseUrl + 'common/notice/listen', {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + store.getState().token.accessToken
|
||||||
|
},
|
||||||
|
heartbeatTimeout: 21600000
|
||||||
|
})
|
||||||
|
|
||||||
let source = new EventSource('http://localhost:3000/api1/sse/createConnect?clientId=001')
|
|
||||||
|
|
||||||
// 监听打开事件
|
|
||||||
source.addEventListener('open', (e) => {
|
source.addEventListener('open', (e) => {
|
||||||
console.log("打开连接 onopen==>", e)
|
console.log("open==>", e)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听消息事件
|
source.addEventListener("init", (e) => {
|
||||||
source.addEventListener("message", (e) => {
|
console.log("init==>", e)
|
||||||
|
})
|
||||||
|
|
||||||
|
source.addEventListener("reimbursement", (e) => {
|
||||||
|
console.log("reimbursement==>", e)
|
||||||
|
// @ts-ignore
|
||||||
|
setReimbursementCount(reimbursementCount + Number(e.data))
|
||||||
|
})
|
||||||
|
|
||||||
|
source.addEventListener("notice", (e) => {
|
||||||
|
console.log("notice==>", e)
|
||||||
|
// @ts-ignore
|
||||||
|
setMessageCount(messageCount + Number(e.data))
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听错误事件
|
|
||||||
source.addEventListener("error", (e) => {
|
source.addEventListener("error", (e) => {
|
||||||
|
console.log("error==>", e)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 关闭连接
|
// 关闭连接
|
||||||
source.close = () => {
|
source.close = () => {
|
||||||
console.log("source.close")
|
console.log("source.close")
|
||||||
}
|
}
|
||||||
|
setEventSource(source)
|
||||||
} else {
|
|
||||||
alert("该浏览器不支持SSE")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取系统消息
|
const items = [{
|
||||||
const getSystemMessage = () => {
|
|
||||||
// 发送网络请求
|
|
||||||
axiosInstance.post(`http://localhost:3000/api1/sse/broadcast`).then(
|
|
||||||
response => {
|
|
||||||
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let items = [{
|
|
||||||
key: "/invoice/mine",
|
key: "/invoice/mine",
|
||||||
//icon: React.createElement(UserOutlined),
|
//icon: React.createElement(UserOutlined),
|
||||||
label: <Link to="/invoice/mine">我的发票</Link>
|
label: <Link to="/invoice/mine">我的发票</Link>
|
||||||
|
@ -203,7 +209,7 @@ function HomeView() {
|
||||||
}, {
|
}, {
|
||||||
key: "/reimbursement/approval",
|
key: "/reimbursement/approval",
|
||||||
//icon: React.createElement(UserOutlined),
|
//icon: React.createElement(UserOutlined),
|
||||||
label: <Link to="/reimbursement/approval">报销审批</Link>,
|
label: <Link to="/reimbursement/approval">报销审批</Link>
|
||||||
}, {
|
}, {
|
||||||
key: "/stat",
|
key: "/stat",
|
||||||
//icon: React.createElement(UserOutlined),
|
//icon: React.createElement(UserOutlined),
|
||||||
|
@ -236,21 +242,59 @@ function HomeView() {
|
||||||
navigate('/invoice/mine')
|
navigate('/invoice/mine')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reimbursementStatus: number[] = []
|
||||||
|
|
||||||
if (token.staffId === 'manager') {
|
if (token.staffId === 'manager') {
|
||||||
|
setBadgeTop(66 + 0 * 44)
|
||||||
setMenuItems(items.slice(3, 6))
|
setMenuItems(items.slice(3, 6))
|
||||||
|
reimbursementStatus.push(4)
|
||||||
} else if (staff.managingDepartment) {
|
} else if (staff.managingDepartment) {
|
||||||
|
setBadgeTop(66 + 3 * 44)
|
||||||
if (staff.managingDepartment.departmentId === 1) {
|
if (staff.managingDepartment.departmentId === 1) {
|
||||||
setMenuItems(items.slice(0, 5))
|
setMenuItems(items.slice(0, 5))
|
||||||
|
reimbursementStatus.push(3)
|
||||||
} else {
|
} else {
|
||||||
setMenuItems(items.slice(0, 4))
|
setMenuItems(items.slice(0, 4))
|
||||||
|
reimbursementStatus.push(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (staff.staffDepartments && staff.staffDepartments.find((value, index, obj) => value.departmentId === 1))
|
setBadgeTop(66 + 3 * 44)
|
||||||
|
if (staff.staffDepartments && staff.staffDepartments.find((value, index, obj) => value.departmentId === 1)) {
|
||||||
setMenuItems(items.slice(0, 5))
|
setMenuItems(items.slice(0, 5))
|
||||||
else
|
reimbursementStatus.push(2)
|
||||||
|
} else
|
||||||
setMenuItems(items.slice(0, 2))
|
setMenuItems(items.slice(0, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
axiosInstance({
|
||||||
|
url: 'common/notice',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
pageNum: 0,
|
||||||
|
pageSize: 100
|
||||||
|
}
|
||||||
|
}).then(response => {
|
||||||
|
const data: NoticeResponse = response.data
|
||||||
|
let unreadCount = 0
|
||||||
|
for (const notice of data.records)
|
||||||
|
if (!notice.alreadyRead) unreadCount++
|
||||||
|
setMessageCount(unreadCount)
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
axiosInstance({
|
||||||
|
url: 'approval/reimbursement?'+ qs.stringify({
|
||||||
|
pageNum: 0,
|
||||||
|
pageSize: 1,
|
||||||
|
reimbursementStatus: reimbursementStatus
|
||||||
|
}, {skipNulls: true, arrayFormat: 'indices'}),
|
||||||
|
method: 'get'
|
||||||
|
}).then(response => {
|
||||||
|
setReimbursementCount(response.data.total)
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
})
|
})
|
||||||
|
@ -261,7 +305,7 @@ function HomeView() {
|
||||||
navigate("/login")
|
navigate("/login")
|
||||||
}
|
}
|
||||||
getStaffInfo()
|
getStaffInfo()
|
||||||
|
createSseConnect()
|
||||||
}, []);
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDefaultSelectedKeys([location.pathname])
|
setDefaultSelectedKeys([location.pathname])
|
||||||
|
@ -299,10 +343,26 @@ function HomeView() {
|
||||||
marginLeft: '4px'
|
marginLeft: '4px'
|
||||||
}}>智能财务报销系统</span>
|
}}>智能财务报销系统</span>
|
||||||
</div>
|
</div>
|
||||||
<MainMenu items={menuItems} defaultSelectedKeys={defaultSelectedKeys}/>
|
<div style={{width: '100%', display: "flex"}}>
|
||||||
|
<MainMenu items={menuItems} defaultSelectedKeys={defaultSelectedKeys}/>
|
||||||
|
<div style={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 14,
|
||||||
|
top: badgeTop,
|
||||||
|
height: 44,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
}}>
|
||||||
|
<Badge count={reimbursementCount}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</Sider>
|
</Sider>
|
||||||
<Layout>
|
<Layout>
|
||||||
<HeaderBar/>
|
<HeaderBar messageCount={messageCount} onMessageCountChange={(count) => setMessageCount(count)}/>
|
||||||
<Content style={{margin: '0', overflowY: "auto"}}>
|
<Content style={{margin: '0', overflowY: "auto"}}>
|
||||||
<Outlet/>
|
<Outlet/>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
@ -7,14 +7,16 @@ import 'dayjs/locale/zh-cn'
|
||||||
import axiosInstance from "../utils/axiosInstance";
|
import axiosInstance from "../utils/axiosInstance";
|
||||||
import {setStaff, setToken} from "../models/store";
|
import {setStaff, setToken} from "../models/store";
|
||||||
import {Staff} from "../models/Staff";
|
import {Staff} from "../models/Staff";
|
||||||
|
import {invoiceTypeNameMap} from "../models/Invoice";
|
||||||
|
|
||||||
const {Text, Title, Paragraph} = Typography
|
const {Text, Title, Paragraph} = Typography
|
||||||
const relativeTime = require('dayjs/plugin/relativeTime')
|
const relativeTime = require('dayjs/plugin/relativeTime')
|
||||||
dayjs.locale('zh-cn')
|
dayjs.locale('zh-cn')
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
function MessageList() {
|
function MessageList(props: { count: number, onCountChange: (count: number) => void}) {
|
||||||
const [list, setList] = useState<Notice[]>([])
|
const [list, setList] = useState<Notice[]>([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const getTitle = (notice: Notice) => {
|
const getTitle = (notice: Notice) => {
|
||||||
switch (notice.data.noticeType) {
|
switch (notice.data.noticeType) {
|
||||||
|
@ -27,7 +29,7 @@ function MessageList() {
|
||||||
return '报销单状态更新'
|
return '报销单状态更新'
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
return '发票被打回'
|
return '发票被撤回'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,22 +56,29 @@ function MessageList() {
|
||||||
switch (notice.data.noticeType) {
|
switch (notice.data.noticeType) {
|
||||||
case 0:
|
case 0:
|
||||||
if (notice.data.approvalResult === 5)
|
if (notice.data.approvalResult === 5)
|
||||||
return '报销单审批未通过'
|
return <Paragraph>{
|
||||||
|
notice.data.reimbursement?.reimbursementId + '号报销单(' + notice.data.reimbursement?.reimbursementDepartureName
|
||||||
|
+ (notice.data.reimbursement?.roundTrip ? " ⇌ " : " → ") + notice.data.reimbursement?.reimbursementDestinationName + ')'
|
||||||
|
+ '『' + notice.data.approvalStaff?.staffName + '』审批不通过'
|
||||||
|
}<br/>{'审批意见:' + notice.data.approvalOpinion}</Paragraph>
|
||||||
else
|
else
|
||||||
return <Paragraph>{
|
return <Paragraph>{
|
||||||
notice.data.reimbursement?.reimbursementId + '号报销单(' + notice.data.reimbursement?.reimbursementDepartureName
|
notice.data.reimbursement?.reimbursementId + '号报销单(' + notice.data.reimbursement?.reimbursementDepartureName
|
||||||
+ (notice.data.reimbursement?.roundTrip ? " ⇌ " : " → ") + notice.data.reimbursement?.reimbursementDestinationName+')'
|
+ (notice.data.reimbursement?.roundTrip ? " ⇌ " : " → ") + notice.data.reimbursement?.reimbursementDestinationName + ')'
|
||||||
+ getApprovalStep(notice.data.approvalResult)+'『'+notice.data.approvalStaff?.staffName+'』审批通过'
|
+ getApprovalStep(notice.data.approvalResult) + '『' + notice.data.approvalStaff?.staffName + '』审批通过'
|
||||||
}<br/>{'审批意见:'+notice.data.approvalOpinion}</Paragraph>
|
}<br/>{'审批意见:' + notice.data.approvalOpinion}</Paragraph>
|
||||||
|
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
return '发票被打回'
|
if (notice.data.invoice)
|
||||||
|
return <Paragraph>{
|
||||||
|
'一张' + invoiceTypeNameMap.get(notice.data.invoice?.invoiceKind) + '(¥' + notice.data.invoice?.invoiceAmount / 100. + ')被『' + notice.data.rejectStaff?.staffName + '』撤回'
|
||||||
|
}<br/>{'撤回原因:' + notice.data.rejectOpinion}</Paragraph>
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMessageListOpen = () => {
|
const onMessageListOpen = () => {
|
||||||
|
setLoading(true)
|
||||||
axiosInstance({
|
axiosInstance({
|
||||||
url: 'common/notice',
|
url: 'common/notice',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
@ -81,6 +90,7 @@ function MessageList() {
|
||||||
console.log(response.data)
|
console.log(response.data)
|
||||||
const data: NoticeResponse = response.data
|
const data: NoticeResponse = response.data
|
||||||
setList(data.records)
|
setList(data.records)
|
||||||
|
setLoading(false)
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
})
|
})
|
||||||
|
@ -163,6 +173,7 @@ function MessageList() {
|
||||||
<List
|
<List
|
||||||
style={{width: 400}}
|
style={{width: 400}}
|
||||||
itemLayout="horizontal"
|
itemLayout="horizontal"
|
||||||
|
loading={loading}
|
||||||
dataSource={list}
|
dataSource={list}
|
||||||
renderItem={(item) => (
|
renderItem={(item) => (
|
||||||
<div style={{
|
<div style={{
|
||||||
|
@ -180,22 +191,36 @@ function MessageList() {
|
||||||
whiteSpace: "unset",
|
whiteSpace: "unset",
|
||||||
textAlign: "unset"
|
textAlign: "unset"
|
||||||
}}>
|
}}>
|
||||||
<Dropdown menu={{
|
<Dropdown disabled={item.alreadyRead} menu={{
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: (<span>标为已读</span>),
|
label: (<span>标为已读</span>),
|
||||||
}],
|
}],
|
||||||
onClick: ({key}) => {
|
onClick: ({key}) => {
|
||||||
|
axiosInstance({
|
||||||
|
url: 'common/notice/' + item.noticeId + '/read',
|
||||||
|
method: 'put',
|
||||||
|
}).then(response => {
|
||||||
|
props.onCountChange(props.count-1)
|
||||||
|
setList(list.map((value, index, array) => {
|
||||||
|
if (value === item)
|
||||||
|
value.alreadyRead = true
|
||||||
|
return value
|
||||||
|
}))
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}} placement="top" arrow>
|
}} placement="top" arrow>
|
||||||
<div>
|
<div>
|
||||||
<Badge status="processing"/>
|
<Badge status="processing" style={{
|
||||||
|
display: item.alreadyRead ? 'none' : 'unset',
|
||||||
|
marginRight: 10
|
||||||
|
}}/>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
marginLeft: 10
|
|
||||||
}}>{getTitle(item)} </span>
|
}}>{getTitle(item)} </span>
|
||||||
<Divider type="vertical"/>
|
<Divider type="vertical"/>
|
||||||
<Text>{
|
<Text>{
|
||||||
|
@ -210,15 +235,30 @@ function MessageList() {
|
||||||
<Button style={{
|
<Button style={{
|
||||||
marginLeft: -30
|
marginLeft: -30
|
||||||
}} shape='circle'
|
}} shape='circle'
|
||||||
type='text'><CloseOutlined/></Button>
|
type='text' onClick={() => {
|
||||||
|
axiosInstance({
|
||||||
|
url: 'common/notice/' + item.noticeId,
|
||||||
|
method: 'delete',
|
||||||
|
}).then(response => {
|
||||||
|
setList(list.filter(value => value !== item))
|
||||||
|
if(!item.alreadyRead) props.onCountChange(props.count-1)
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
}}>
|
||||||
|
<CloseOutlined/>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
} title="我的消息" arrowPointAtCenter onOpenChange={onMessageListOpen}>
|
} title="我的消息" arrowPointAtCenter onOpenChange={onMessageListOpen}>
|
||||||
<Button type="text" shape="circle">
|
<Badge count={props.count} size="small" offset={[-10, 10]}>
|
||||||
<BellOutlined/>
|
<Button type="text" shape="circle" size="large">
|
||||||
</Button>
|
<BellOutlined/>
|
||||||
|
</Button>
|
||||||
|
</Badge>
|
||||||
</Popover>
|
</Popover>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,10 @@ function LoginView() {
|
||||||
refreshToken: response.data.refreshToken,
|
refreshToken: response.data.refreshToken,
|
||||||
clientSecret: response.data.clientSecret
|
clientSecret: response.data.clientSecret
|
||||||
}))
|
}))
|
||||||
//models.commit('setStaff', response.data.data)
|
if (values.staffId === 'manager')
|
||||||
navigate('/')
|
navigate('/reimbursement/approval')
|
||||||
|
else
|
||||||
|
navigate('/invoice/mine')
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
setAlertMessage(error.response.data.msg)
|
setAlertMessage(error.response.data.msg)
|
||||||
|
@ -40,7 +42,7 @@ function LoginView() {
|
||||||
console.log('Failed:', errorInfo);
|
console.log('Failed:', errorInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
const afterClose = () =>{
|
const afterClose = () => {
|
||||||
setShowAlert(false)
|
setShowAlert(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +100,7 @@ function LoginView() {
|
||||||
<Input.Password prefix={<LockOutlined/>}
|
<Input.Password prefix={<LockOutlined/>}
|
||||||
placeholder="密码"/>
|
placeholder="密码"/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{showAlert&&
|
{showAlert &&
|
||||||
<Alert
|
<Alert
|
||||||
style={{marginBottom: 20, height: 32}}
|
style={{marginBottom: 20, height: 32}}
|
||||||
message={alertMessage}
|
message={alertMessage}
|
||||||
|
@ -107,7 +109,7 @@ function LoginView() {
|
||||||
showIcon
|
showIcon
|
||||||
afterClose={afterClose}
|
afterClose={afterClose}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit" style={{width: '100%'}}>
|
<Button type="primary" htmlType="submit" style={{width: '100%'}}>
|
||||||
登录
|
登录
|
||||||
|
|
Loading…
Reference in New Issue