update 通知公告强化
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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>
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
|
||||
40
web-react/src/store/reducer/notice.js
Normal file
40
web-react/src/store/reducer/notice.js
Normal 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
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user