完善消息通知,解决财务统计部分金额未除100

main
wuyize 2023-01-08 18:49:42 +08:00
parent 8c370ead0e
commit 90eaf4f871
6 changed files with 219 additions and 182 deletions

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect} from 'react'; import React, {useState, useEffect, useRef} 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, Badge} 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";
@ -149,6 +149,8 @@ function HomeView() {
const [messageCount, setMessageCount] = useState(0) const [messageCount, setMessageCount] = useState(0)
const [reimbursementCount, setReimbursementCount] = useState(0) const [reimbursementCount, setReimbursementCount] = useState(0)
const messageCountRef = useRef(messageCount);
const reimbursementCountRef = useRef(reimbursementCount);
const [badgeTop, setBadgeTop] = useState(66 + 3 * 44) const [badgeTop, setBadgeTop] = useState(66 + 3 * 44)
const [eventSource, setEventSource] = useState<EventSourcePolyfill | null>(null) const [eventSource, setEventSource] = useState<EventSourcePolyfill | null>(null)
@ -172,13 +174,15 @@ function HomeView() {
source.addEventListener("reimbursement", (e) => { source.addEventListener("reimbursement", (e) => {
console.log("reimbursement==>", e) console.log("reimbursement==>", e)
// @ts-ignore // @ts-ignore
setReimbursementCount(reimbursementCount + Number(e.data)) reimbursementCountRef.current+=Number(e.data)
setReimbursementCount(reimbursementCountRef.current )
}) })
source.addEventListener("notice", (e) => { source.addEventListener("notice", (e) => {
console.log("notice==>", e) console.log("notice==>", e)
// @ts-ignore // @ts-ignore
setMessageCount(messageCount + Number(e.data)) messageCountRef.current+=Number(e.data)
setMessageCount(messageCountRef.current)
}) })
source.addEventListener("error", (e) => { source.addEventListener("error", (e) => {
@ -278,7 +282,8 @@ function HomeView() {
let unreadCount = 0 let unreadCount = 0
for (const notice of data.records) for (const notice of data.records)
if (!notice.alreadyRead) unreadCount++ if (!notice.alreadyRead) unreadCount++
setMessageCount(unreadCount) messageCountRef.current = unreadCount
setMessageCount( messageCountRef.current )
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
}) })
@ -291,7 +296,8 @@ function HomeView() {
}, {skipNulls: true, arrayFormat: 'indices'}), }, {skipNulls: true, arrayFormat: 'indices'}),
method: 'get' method: 'get'
}).then(response => { }).then(response => {
setReimbursementCount(response.data.total) reimbursementCountRef.current=response.data.total
setReimbursementCount( reimbursementCountRef.current)
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
}) })
@ -347,7 +353,7 @@ function HomeView() {
<MainMenu items={menuItems} defaultSelectedKeys={defaultSelectedKeys}/> <MainMenu items={menuItems} defaultSelectedKeys={defaultSelectedKeys}/>
<div style={{ <div style={{
position: "absolute", position: "absolute",
right: 14, right: 18,
top: badgeTop, top: badgeTop,
height: 44, height: 44,
display: "flex", display: "flex",
@ -355,14 +361,15 @@ function HomeView() {
}}> }}>
<Badge count={reimbursementCount}/> <Badge count={reimbursementCount}/>
</div> </div>
</div> </div>
</Sider> </Sider>
<Layout> <Layout>
<HeaderBar messageCount={messageCount} onMessageCountChange={(count) => setMessageCount(count)}/> <HeaderBar messageCount={messageCount} onMessageCountChange={(count) => {
messageCountRef.current = count
setMessageCount(messageCountRef.current)
}}/>
<Content style={{margin: '0', overflowY: "auto"}}> <Content style={{margin: '0', overflowY: "auto"}}>
<Outlet/> <Outlet/>
</Content> </Content>

View File

@ -14,7 +14,7 @@ import qs from "qs";
const {Text, Paragraph} = Typography; const {Text, Paragraph} = Typography;
function InvoiceDetailModal(props: any) { function InvoiceDetailModal(props: { invoiceDetail: any, open: boolean, onClose: () => void, needRefresh?: () => void, isBase64?:boolean }) {
//const [open, setOpen] = useState(false) //const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [invoice, setInvoice] = useState(null as any) const [invoice, setInvoice] = useState(null as any)
@ -88,6 +88,7 @@ function InvoiceDetailModal(props: any) {
console.log(response.data) console.log(response.data)
setLoading(false) setLoading(false)
props.onClose() props.onClose()
if (props.needRefresh)
props.needRefresh() props.needRefresh()
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
@ -99,7 +100,7 @@ function InvoiceDetailModal(props: any) {
<Modal <Modal
centered centered
open={props.open} open={props.open}
title={"发票详情"+(props.invoiceDetail?.modified ? '(发票信息经过手动修改)' : '')} title={"发票详情" + (props.invoiceDetail?.modified ? '(发票信息经过手动修改)' : '')}
onCancel={handleCancel} onCancel={handleCancel}
footer={ footer={
<div style={{width: '100%', display: "flex", flexDirection: "row", justifyContent: "space-between"}}> <div style={{width: '100%', display: "flex", flexDirection: "row", justifyContent: "space-between"}}>
@ -139,11 +140,8 @@ function InvoiceDetailModal(props: any) {
</Form> </Form>
<Image <Image
width={'100%'} width={'100%'}
src={baseUrl + props.invoiceDetail?.invoiceFileUri} src={props.isBase64?('data:image/png;base64,'+props.invoiceDetail?.invoiceFileBase64):(baseUrl + props.invoiceDetail?.invoiceFileUri)}
/> />
{/* <div style={{ position:"absolute", left:0, top: 50, paddingRight: 20,width: '100%', display: "flex", flexDirection: "column", alignItems: "flex-end"}}>
</div>*/}
</Modal> </Modal>
) )

View File

@ -420,7 +420,7 @@ function InvoiceListView(props: { isManagement: boolean }) {
searchInvoiceContent(invoiceSearchOption) searchInvoiceContent(invoiceSearchOption)
}} invoiceDetail={invoiceDetail} open={detailModalOpen} onClose={() => { }} invoiceDetail={invoiceDetail} open={detailModalOpen} onClose={() => {
setDetailModalOpen(false) setDetailModalOpen(false)
}} showFooter={true}/> }}/>
</div> </div>
) )

12
src/pages/MessageList.css Normal file
View File

@ -0,0 +1,12 @@
.MessageList::-webkit-scrollbar {
width: 4px;
}
.MessageList::-webkit-scrollbar-thumb {
border-radius: 10px;
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
background: rgba(0,0,0,0.2);
}
.MessageList::-webkit-scrollbar-track {
border-radius: 0;
background: rgba(0,0,0,0);
}

View File

@ -7,16 +7,27 @@ 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"; import {InvoiceDetail, invoiceTypeNameMap} from "../models/Invoice";
import './MessageList.css';
import InvoiceDetailModal from "./Invoice/InvoiceDetailModal";
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(props: { count: number, onCountChange: (count: number) => void}) {
function MessageList(props: { count: number, onCountChange: (count: number) => void }) {
const [popoverOpen, setPopoverOpen] = useState(false)
const [list, setList] = useState<Notice[]>([]) const [list, setList] = useState<Notice[]>([])
const [initLoading, setInitLoading] = useState(false)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [showLoadMore, setShowLoadMore] = useState(false)
const [index, setIndex] = useState(0)
const [invoiceModalOpen, setInvoiceModalOpen] = useState(false)
const [invoiceDetail, setInvoiceDetail] = useState<any>()
const pageSize = 10
const getTitle = (notice: Notice) => { const getTitle = (notice: Notice) => {
switch (notice.data.noticeType) { switch (notice.data.noticeType) {
@ -78,102 +89,119 @@ function MessageList(props: { count: number, onCountChange: (count: number) => v
} }
const onMessageListOpen = () => { const onMessageListOpen = () => {
setLoading(true) setInitLoading(true)
axiosInstance({ axiosInstance({
url: 'common/notice', url: 'common/notice',
method: 'get', method: 'get',
params: { params: {
pageNum: 0, pageNum: 0,
pageSize: 10 pageSize: pageSize
} }
}).then(response => { }).then(response => {
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) setIndex(pageSize)
setShowLoadMore(pageSize < data.total)
setInitLoading(false)
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
}) })
} }
useEffect(() => { const onLoadMore = () => {
/*setList([ setLoading(true);
{ axiosInstance({
"noticeTargetId": "50", url: 'common/notice',
"noticeTime": "1981-10-04 06:37:59", method: 'get',
"data": { params: {
"noticeType": 1, pageNum: index,
"count": 16 pageSize: pageSize
},
"noticeId": "48"
},
{
"noticeTargetId": "45",
"noticeTime": "1980-05-09 20:10:06",
"data": {
"invoice": {
"invoiceId": 23,
"invoiceNo": "deserunt enim anim ex voluptate",
"invoiceCode": "6",
"invoiceDate": "2016-05-11",
"invoiceUploadTime": "2019-12-01 07:17:16",
"invoiceNote": "consequat laborum",
"invoiceKind": 82,
"invoiceAmount": 51,
"modified": false,
"invoiceFileBase64": "nulla enim",
"invoiceDeparture": "ea enim fugiat do",
"invoiceDestination": null,
"invoiceName": "号度前事她状报"
},
"rejectStaff": {
"staffName": "飞百亲治头油手",
"staffId": "49",
"staffPassword": "velit in incididunt",
"staffBase": "eiusmod voluptate Ut Lorem occaecat"
},
"rejectOpinion": "dolore exercitation irure do",
"noticeType": 1
},
"noticeId": "93"
},
{
"noticeTargetId": "32",
"noticeTime": "2014-12-30 09:02:43",
"data": {
"noticeType": 0,
"count": 6
},
"noticeId": "58"
},
{
"noticeTargetId": "16",
"noticeTime": "2014-01-18 21:30:34",
"data": {
"noticeType": 0,
"count": 93
},
"noticeId": "42"
},
{
"noticeTargetId": "9",
"noticeTime": "1997-12-03 08:45:39",
"data": {
"noticeType": 1,
"count": 2
},
"noticeId": "40"
} }
])*/ }).then(response => {
}, []); console.log(response.data)
const data: NoticeResponse = response.data
setList(list.concat(data.records))
setShowLoadMore(index + pageSize < data.total)
setIndex(index + pageSize)
setLoading(false)
}).catch(function (error) {
console.log(error)
})
};
const loadMore =
!initLoading && showLoadMore ? (
<div
style={{
textAlign: 'center',
marginTop: 12,
height: 32,
lineHeight: '32px',
}}
>
<Button onClick={onLoadMore} loading={loading}></Button>
</div>
) : null;
const readMessage = (item: Notice) => {
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);
});
}
const onMessageClick = (item: Notice, e: any) => {
if (e.target.innerText === '标为已读')
return
console.log('onMessageClick')
switch (item.data.noticeType) {
case 0:
const reimbursementId = item.data.reimbursement?.reimbursementId
if (reimbursementId) {
}
break
case 1:
let invoice: any = item.data.invoice
if (invoice) {
invoice.invoiceAmount /= 100.
invoice.invoiceState = -1
setInvoiceDetail(invoice)
setInvoiceModalOpen(true)
}
break
}
setPopoverOpen(false)
readMessage(item)
}
return ( return (
<Popover placement="bottomLeft" trigger="click" content={ <>
<Popover placement="bottomLeft" trigger="click"
open={popoverOpen}
onOpenChange={(newOpen: boolean) => {
setPopoverOpen(newOpen)
if (newOpen)
onMessageListOpen()
}}
content={
<List <List
style={{width: 400}} className={'MessageList'}
style={{width: 400, overflowY: 'auto', maxHeight: 600}}
itemLayout="horizontal" itemLayout="horizontal"
loading={loading} loading={initLoading}
loadMore={loadMore}
dataSource={list} dataSource={list}
renderItem={(item) => ( renderItem={(item) => (
<div style={{ <div style={{
@ -183,6 +211,7 @@ function MessageList(props: { count: number, onCountChange: (count: number) => v
}}> }}>
<Button <Button
type='text' type='text'
onClick={(e) => onMessageClick(item, e)}
style={{ style={{
width: '100%', width: '100%',
height: "unset", height: "unset",
@ -196,21 +225,10 @@ function MessageList(props: { count: number, onCountChange: (count: number) => v
{ {
key: '1', key: '1',
label: (<span></span>), label: (<span></span>),
}], }
],
onClick: ({key}) => { onClick: ({key}) => {
axiosInstance({ readMessage(item)
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>
@ -225,8 +243,7 @@ function MessageList(props: { count: number, onCountChange: (count: number) => v
<Divider type="vertical"/> <Divider type="vertical"/>
<Text>{ <Text>{
// @ts-ignore // @ts-ignore
dayjs(item.noticeTime).fromNow() dayjs(item.noticeTime).fromNow()}</Text>
}</Text>
</div> </div>
</Dropdown> </Dropdown>
{getContent(item)} {getContent(item)}
@ -240,26 +257,29 @@ function MessageList(props: { count: number, onCountChange: (count: number) => v
url: 'common/notice/' + item.noticeId, url: 'common/notice/' + item.noticeId,
method: 'delete', method: 'delete',
}).then(response => { }).then(response => {
setList(list.filter(value => value !== item)) setList(list.filter(value => value !== item));
if(!item.alreadyRead) props.onCountChange(props.count-1) setIndex(index - 1);
if (!item.alreadyRead)
props.onCountChange(props.count - 1);
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error);
}) });
}}> }}>
<CloseOutlined/> <CloseOutlined/>
</Button> </Button>
</div> </div>
)} )}/>} title="我的消息" arrowPointAtCenter>
/>
} title="我的消息" arrowPointAtCenter onOpenChange={onMessageListOpen}>
<Badge count={props.count} size="small" offset={[-10, 10]}> <Badge count={props.count} size="small" offset={[-10, 10]}>
<Button type="text" shape="circle" size="large"> <Button type="text" shape="circle" size="large">
<BellOutlined/> <BellOutlined/>
</Button> </Button>
</Badge> </Badge>
</Popover> </Popover>
<InvoiceDetailModal open={invoiceModalOpen} invoiceDetail={invoiceDetail}
onClose={() => setInvoiceModalOpen(false)} isBase64={true}/>
</>
) )
} }

View File

@ -180,7 +180,7 @@ function StatView() {
for (const departmentStat of temporalDepartmentStat.departmentStats) { for (const departmentStat of temporalDepartmentStat.departmentStats) {
let valueMap = departmentAmountMap.get(departmentStat.departmentId) let valueMap = departmentAmountMap.get(departmentStat.departmentId)
if (valueMap) { if (valueMap) {
valueMap.set(temporalDepartmentStat.value, departmentStat.reimbursementAmount) valueMap.set(temporalDepartmentStat.value, departmentStat.reimbursementAmount/100.)
departmentAmountMap.set(departmentStat.departmentId, valueMap) departmentAmountMap.set(departmentStat.departmentId, valueMap)
} }
} }
@ -590,7 +590,7 @@ function StatView() {
}, },
emphasis: { emphasis: {
label: { label: {
show: true, show: false,
fontSize: 40, fontSize: 40,
fontWeight: 'bold' fontWeight: 'bold'
} }
@ -637,7 +637,7 @@ function StatView() {
}, },
emphasis: { emphasis: {
label: { label: {
show: true, show: false,
fontSize: 40, fontSize: 40,
fontWeight: 'bold' fontWeight: 'bold'
} }