update 通知公告强化

This commit is contained in:
2021-07-08 18:39:16 +08:00
parent 9c076b1aba
commit 3fdef68de8
10 changed files with 198 additions and 210 deletions

View File

@@ -7,8 +7,9 @@ export { default as ComponentDynamic } from './component-dynamic'
export { default as Container } from './container'
export { default as IconSelector } from './icon-selector'
export { default as Image } from './image'
export { default as ModalForm } from './modal-form'
export { default as InputNumberRange } from './form/input-number-range'
export { default as ModalForm } from './modal-form'
export { default as NoticeDetail } from './notice-detail'
export { default as PhotoPreview } from './photo-preview'
export { default as QueryList } from './query-list'
export { default as QueryTable } from './query-table'

View File

@@ -1,45 +1,34 @@
import React, { Component } from 'react'
import { Spin, Divider, Modal, Row, Form, Upload } from 'antd'
import { api } from 'common/api'
import { Col, Divider, Modal, Row, Spin, Upload } from 'antd'
import { AntIcon } from 'components'
import { api } from 'common/api'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
import store from 'store'
export default class form extends Component {
const { dispatch } = store
export default class index extends Component {
state = {
detailVisible: false,
detailLoading: false,
detailData: {},
fileValue: false,
fileList: [],
}
filedu = React.createRef()
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
async onOpenDetail(id) {
onOpenDetail = async id => {
this.setState({ detailLoading: true, detailVisible: true })
const { data } = await api.sysNoticeDetail({ id })
this.setState({
detailLoading: false,
detailData: data,
fileValue: false,
})
let fileList = []
if (data) {
const { attachments } = data
if (attachments) {
const fileValue = []
const fileList = attachments.split(',')
for (const fileId of fileList) {
const fileIds = attachments.split(',')
for (const fileId of fileIds) {
try {
const file = await PreviewFile(fileId)
const base64 = await BlobToBase64(file)
fileValue.push({
fileList.push({
uid: fileId,
response: fileId,
name: file.name,
@@ -48,7 +37,7 @@ export default class form extends Component {
})
} catch {
const { data: file } = await api.sysFileInfoDetail({ id: fileId })
fileValue.push({
fileList.push({
uid: fileId,
response: '文件已丢失',
name: file.fileOriginName,
@@ -56,11 +45,23 @@ export default class form extends Component {
})
}
}
this.setState({
fileValue,
})
}
}
dispatch({
type: 'READ_NOTICE',
id,
})
this.setState({
detailLoading: false,
detailData: data,
fileList,
})
}
onCloseDetail() {
this.setState({ detailVisible: false, detailData: {}, fileList: [] })
}
async onFileDownload(file) {
@@ -74,14 +75,16 @@ export default class form extends Component {
window.URL.revokeObjectURL(url)
a.remove()
}
render() {
const { detailLoading, detailVisible, detailData } = this.state
const { detailLoading, detailVisible, detailData, fileList } = this.state
return (
<Modal
width={1000}
footer={false}
visible={detailVisible}
onCancel={() => this.setState({ detailVisible: false, detailData: {} })}
onCancel={() => this.onCloseDetail()}
>
<Spin spinning={detailLoading} indicator={<AntIcon type="loading" />}>
<div className="h3 mt-lg">{detailData.title}</div>
@@ -91,21 +94,23 @@ export default class form extends Component {
dangerouslySetInnerHTML={{ __html: detailData.content }}
></div>
<Divider />
{this.state.fileValue && (
<Row>
<span>
查看附件
<Upload
fileList={this.state.fileValue}
showUploadList={{
showDownloadIcon: true,
}}
onDownload={file => this.onFileDownload(file)}
></Upload>
</span>
</Row>
{!!fileList.length && (
<>
<Row align="top">
<span className="text-gray mt-sm mr-xs">附件</span>
<Col flex="1">
<Upload
fileList={fileList}
showUploadList={{
showDownloadIcon: true,
}}
onDownload={file => this.onFileDownload(file)}
/>
</Col>
</Row>
<Divider />
</>
)}
<Divider />
<Row justify="space-between" className="text-gray">
<span>发布人{detailData.publicUserName}</span>
<span>发布时间{detailData.publicTime} </span>

View File

@@ -78,7 +78,7 @@ export default class form extends Component {
})
}
}
this.record.Attachments = fileValue
this.record.attachments = fileValue
}
}
//#endregion
@@ -94,6 +94,34 @@ export default class form extends Component {
//加载 BraftEditor 富文本插件
this.state.funloader()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
postData.status = 0
} else {
postData.status = 1
}
//#region 从前段转换后端所需格式
postData.attachments = postData.attachments.map(item =>
item.uid.startsWith('rc-upload') ? item.response : item.uid
)
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 接受子组件传过来的方法
* 等页面加载完毕后调用
@@ -131,34 +159,6 @@ export default class form extends Component {
window.URL.revokeObjectURL(url)
a.remove()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
postData.status = 0
} else {
postData.status = 1
}
//#region 从前段转换后端所需格式
const { Attachments } = postData
for (const key in Attachments) {
Attachments[key] = Attachments[key].response
}
//#endregion
return postData
}
}
//#region 自定义方法
//#endregion
render() {
@@ -207,7 +207,7 @@ export default class form extends Component {
</Form.Item>
<Form.Item
label="上传附件"
name="Attachments"
name="attachments"
valuePropName="fileList"
getValueFromEvent={e => {
if (Array.isArray(e)) {

View File

@@ -20,8 +20,7 @@ const apiAction = {
add: api.sysNoticeAdd,
edit: api.sysNoticeEdit,
delete: api.sysNoticeDelete,
Detail: api.sysNoticeDetail,
Status: api.sysNoticeChangeStatus,
status: api.sysNoticeChangeStatus,
}
/**
@@ -34,7 +33,7 @@ const name = '通知公告'
* 统一配置权限标识
* [必要]
*/
const authName = '/**/'
const authName = 'sysNotice'
export default class index extends Component {
state = {
@@ -99,7 +98,7 @@ export default class index extends Component {
constructor(props) {
super(props)
const flag = auth({ [authName]: [['edit'], ['goBack'], ['publish'], ['delete']] })
const flag = auth({ [authName]: [['edit'], ['changeStatus']] })
if (flag) {
this.columns.push({
@@ -111,7 +110,7 @@ export default class index extends Component {
{record.status === 1 ? (
<Auth
key={this.subUniqueKey(record.id, 1)}
auth={{ [authName]: 'goBack' }}
auth={{ [authName]: 'changeStatus' }}
>
<Popconfirm
placement="topRight"
@@ -133,7 +132,7 @@ export default class index extends Component {
</Auth>,
<Auth
key={this.subUniqueKey(record.id, 3)}
auth={{ [authName]: 'publish' }}
auth={{ [authName]: 'changeStatus' }}
>
<Popconfirm
placement="topRight"
@@ -145,7 +144,7 @@ export default class index extends Component {
</Auth>,
<Auth
key={this.subUniqueKey(record.id, 4)}
auth={{ [authName]: 'delete' }}
auth={{ [authName]: 'changeStatus' }}
>
<Popconfirm
placement="topRight"
@@ -263,21 +262,21 @@ export default class index extends Component {
* @param {*} id
*/
onDelete(id) {
this.onAction(apiAction.Status({ id, status: 3 }), '删除成功')
this.onAction(apiAction.status({ id, status: 3 }), '删除成功')
}
/**
* 发布
* @param {*} id
*/
onPublish(id) {
this.onAction(apiAction.Status({ id, status: 1 }), '发布成功')
this.onAction(apiAction.status({ id, status: 1 }), '发布成功')
}
/**
* 撤回
* @param {*} id
*/
onGoBack(id) {
this.onAction(apiAction.Status({ id, status: 2 }), '撤回成功')
this.onAction(apiAction.status({ id, status: 2 }), '撤回成功')
} //
render() {

View File

@@ -1,19 +1,18 @@
import React, { Component } from 'react'
import { Card, Form, message as Message, Input, Select } from 'antd'
import { Auth, Container, QueryTable } from 'components'
import { Card, Form, Input, Select } from 'antd'
import { Auth, Container, NoticeDetail, QueryTable } from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import FormBody from './form'
import { getSearchInfo, QueryType } from 'util/query'
/**
* 统一配置权限标识
* [必要]
*/
const authName = '/**/'
const authName = 'sysNotice'
export default class index extends Component {
state = {
@@ -27,8 +26,7 @@ export default class index extends Component {
// 表格实例
table = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
detail = React.createRef()
columns = [
{
@@ -75,15 +73,15 @@ export default class index extends Component {
*/
constructor(props) {
super(props)
const flag = auth({ [authName]: [['show']] })
const flag = auth({ [authName]: 'received' })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<Auth auth={{ [authName]: 'show' }}>
<a onClick={() => this.onOpen(this.editForm, record.id)}>查看</a>
<Auth auth={{ [authName]: 'received' }}>
<a onClick={() => this.detail.current.onOpenDetail(record.id)}>查看</a>
</Auth>
),
})
@@ -179,7 +177,7 @@ export default class index extends Component {
loadData={this.loadData}
columns={this.columns}
query={
<Auth auth={{ [authName]: 'page' }}>
<Auth auth={{ [authName]: 'received' }}>
<Form.Item label="关键字" name="title">
<Input
autoComplete="off"
@@ -213,7 +211,7 @@ export default class index extends Component {
}
/>
</Card>
<FormBody ref={this.editForm} />
<NoticeDetail ref={this.detail} />
</Container>
)
}

View File

@@ -3,6 +3,7 @@ import user from './user'
import layout from './layout'
import nav from './nav'
import dictData from './dict-data'
import notice from './notice'
import business from './business'
const combine = combineReducers({
@@ -10,6 +11,7 @@ const combine = combineReducers({
layout,
nav,
dictData,
notice,
business
})

View File

@@ -0,0 +1,40 @@
const defaultState = {
count: 0,
list: []
}
const layout = (state = defaultState, action) => {
switch (action.type) {
case 'SET_NOTICE_COUNT':
{
const _state = {
...state,
count: action.count
}
return _state
}
case 'SET_NOTICE_LIST':
{
const _state = {
...state,
list: action.list
}
return _state
}
case 'READ_NOTICE':
{
const notice = state.list.find(p => p.id === action.id)
if (notice && !notice.readStatus) {
notice.readStatus = 1
state.count -= 1
}
return {
...state
}
}
default:
return state
}
}
export default layout

View File

@@ -1,28 +1,41 @@
import React, { Component } from 'react'
import { Badge, Button, Divider, List, Menu, Modal, Popover, Row, Spin, Upload, Tag } from 'antd'
import { AntIcon, Image } from 'components'
import { Badge, Button, List, Popover, Spin, Tag } from 'antd'
import { AntIcon, Image, NoticeDetail } from 'components'
import { api } from 'common/api'
import InfiniteScroll from 'react-infinite-scroller'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
import moment from 'moment'
import store from 'store'
const { getState, dispatch, subscribe } = store
export default class notice extends Component {
state = {
count: 0,
list: [],
...getState('notice'),
loading: false,
hasMore: true,
}
detailVisible: false,
detailLoading: false,
detailData: {},
fileValue: false,
detail = React.createRef()
constructor(props) {
super(props)
this.unsubscribe = subscribe('notice', notice => {
this.setState({ ...notice })
})
}
async componentDidMount() {
const { data: count } = await api.sysNoticeUnread()
this.setState({ count })
dispatch({
type: 'SET_NOTICE_COUNT',
count,
})
}
componentWillUnmount() {
this.unsubscribe()
}
finish() {
@@ -48,65 +61,16 @@ export default class notice extends Component {
return this.finish()
}
this.setState({
dispatch({
type: 'SET_NOTICE_LIST',
list: [...list, ...items],
})
this.setState({
loading: false,
})
}
async onOpenDetail(id) {
this.setState({ detailLoading: true, detailVisible: true })
const { data } = await api.sysNoticeDetail({ id })
this.setState({
detailLoading: false,
detailData: data,
fileValue: false,
})
if (data) {
const { attachments } = data
if (attachments) {
const fileValue = []
const fileList = attachments.split(',')
for (const fileId of fileList) {
try {
const file = await PreviewFile(fileId)
const base64 = await BlobToBase64(file)
fileValue.push({
uid: fileId,
response: fileId,
name: file.name,
url: base64,
status: 'done',
})
} catch {
const { data: file } = await api.sysFileInfoDetail({ id: fileId })
fileValue.push({
uid: fileId,
response: '文件已丢失',
name: file.fileOriginName,
status: 'error',
})
}
}
this.setState({
fileValue,
})
}
}
}
async onFileDownload(file) {
const { data, headers } = await api.sysFileInfoDownload({ id: file.response })
const url = window.URL.createObjectURL(data)
const fileName = GetFileName(headers['content-disposition'])
const a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
window.URL.revokeObjectURL(url)
a.remove()
}
renderList() {
const { list, loading, hasMore } = this.state
return (
@@ -125,20 +89,24 @@ export default class notice extends Component {
avatar={<Image id={item.avatar} type="avatar" />}
title={
<>
<a onClick={() => this.onOpenDetail(item.id)}>
{item.readStatus ? (
<Tag color="#2db7f5">已读</Tag>
) : (
<Tag color="#f50">未读</Tag>
)}
<a
onClick={() =>
this.detail.current.onOpenDetail(item.id)
}
>
{item.title}
</a>
<small className="text-normal ml-xs">
{moment(item.createdTime || item.publicTime).fromNow()}
</small>
</>
}
description={
item.readStatus == 0 ? (
<Tag color="#f50">未读</Tag>
) : (
<Tag color="#2db7f5">已读</Tag>
)
<span className="text-normal ml-xs">
{moment(item.createdTime || item.publicTime).fromNow()}
</span>
}
/>
<div className="ellipsis-3 text-gray">{item.content}</div>
@@ -152,7 +120,13 @@ export default class notice extends Component {
)}
</List>
{!hasMore && (
<Button type="text" block>
<Button
type="text"
block
onClick={() =>
window.openContentWindowByMenuName('sys_notice_mgr_received')
}
>
查看全部
</Button>
)}
@@ -161,7 +135,7 @@ export default class notice extends Component {
}
render() {
const { count, detailLoading, detailVisible, detailData } = this.state
const { count } = this.state
return (
<Popover
@@ -177,42 +151,7 @@ export default class notice extends Component {
<AntIcon type="message" />
</Badge>
</span>
<Modal
width={1000}
footer={false}
visible={detailVisible}
onCancel={() => this.setState({ detailVisible: false, detailData: {} })}
>
<Spin spinning={detailLoading} indicator={<AntIcon type="loading" />}>
<div className="h3 mt-lg">{detailData.title}</div>
<Divider />
<div
className="pt-lg pb-lg"
dangerouslySetInnerHTML={{ __html: detailData.content }}
></div>
<Divider />
{this.state.fileValue && (
<Row className="text-gray">
<span>
查看附件
<Upload
fileList={this.state.fileValue}
showUploadList={{
showDownloadIcon: true,
}}
onDownload={file => this.onFileDownload(file)}
></Upload>
</span>
</Row>
)}
<Divider />
<Row justify="space-between" className="text-gray">
<span>发布人{detailData.publicUserName}</span>
<span>发布时间{detailData.publicTime} </span>
</Row>
</Spin>
</Modal>
<NoticeDetail ref={this.detail} />
</Popover>
)
}