合并公告的管理菜单

This commit is contained in:
何童崇 2022-03-11 11:42:43 +08:00
parent 094e7d1d5a
commit c0fe0772e4
20 changed files with 2492 additions and 1181 deletions

2298
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -116,7 +116,7 @@
"styled-components": "^4.4.1",
"sw-precache-webpack-plugin": "0.11.4",
"url-loader": "0.6.2",
"wangeditor-for-react": "^1.4.0",
"wangeditor-for-react": "^1.5.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"webpack-manifest-plugin": "^2.2.0",

View File

@ -0,0 +1,22 @@
// 公告开始
export const noticeStatus = [
{ code: 0, name: "关闭" },
{ code: 1, name: "正常" },
{ code: 2, name: "草稿" },
];
export const noticeType = [
{ code: 4, name: "招标" },
{ code: 1, name: "更正" },
{ code: 2, name: "中标" },
{ code: 3, name: "废标" },
{ code: 5, name: "技术资产" },
{ code: 6, name: "成交" },
];
export const noticeChecked = [
{ code: 0, name: "未通过" },
{ code: 1, name: "已发布" },
{ code: 2, name: "待审核" },
];
//公告结束

View File

@ -0,0 +1,88 @@
/**
* 生成唯一字符串
*/
export function uuid() {
const s = [];
const hexDigits = '0123456789abcdef';
for (let i = 0; i < 36; i += 1) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = '4';
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = '-';
s[13] = '-';
s[18] = '-';
s[23] = '-';
s[0] = 'abcdefghigklmnopqrst'.substr(Math.floor(Math.random() * 0x10), 1);
return s.join('');
}
// 获取当前日期前N天数组
// format='MM-dd'
export function beforeDayArr(length = 31, myDate = new Date(), flag = 1) {
myDate.setDate(myDate.getDate());
let dateArray = [];
let dateTemp;
for (let i = length; i > 0; i--) {
dateTemp = (myDate.getMonth() + 1) + "-" + myDate.getDate();
dateArray.unshift(dateTemp);
myDate.setDate(myDate.getDate() - flag);
}
return dateArray;
}
/**
* param拼接到url地址上,get请求
* @param {*} url
* @param {*} params
* @param {*} prefix
*/
export const paramToUrl = (url, params) => {
if (url.indexOf('?') == -1) {
// url += '?r=' + Math.random();
url += '?';
}
let time = 0;
for (let attr in params) {
if (!time) {
url += attr + '=' + params[attr] || '';
time++;
} else {
url += '&' + attr + '=' + params[attr] || '';
}
}
return url;
}
const isDev = window.location.port == 8000;
// const isdev2 = window.location.hostname === 'www.educoder.net'
export const TEST_HOST = "https://testforgeplus.trustie.net/"
export function getImageUrl(path) {
const local = 'https://testforgeplus.trustie.net';
if (isDev) {
return `${local}/${path}`
}
return `https://forgeplus.trustie.net${path}`;
}
export const uploadNameSizeSeperator = '  '
export function appendFileSizeToUploadFileAll(fileList) {
return fileList && fileList.map(item => {
if (item.name&&item.name.indexOf(uploadNameSizeSeperator) == -1) {
return Object.assign({}, item, { name: `${item.name}${uploadNameSizeSeperator}${bytesToSize(item.size)}` })
}
return item;
})
}
export function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return '0 Byte';
if (!bytes) return '';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return parseFloat(bytes / Math.pow(1024, i), 2).toFixed(1) + ' ' + sizes[i];
}

View File

@ -0,0 +1,20 @@
import { Modal } from 'antd';
export default (
handleOk,
title,
content,
handleCancel) => {
return Modal.confirm({
title: title || "警告",
content: content || "确认删除?",
okText: '确定',
cancelText: '取消',
onOk() {
handleOk && handleOk();
},
onCancel() {
handleCancel && handleCancel();
}
});
}

View File

@ -0,0 +1,8 @@
import { notification } from 'antd';
export default (message)=>{
notification.open({
message: "提示",
description: message,
});
}

View File

@ -0,0 +1,77 @@
import React, { useEffect, useState } from "react";
import { Upload, Button } from 'antd';
import { appendFileSizeToUploadFileAll } from '../common/utils';
import ShowNotification from './ShowNotification';
function Uploads({ className, size, actionUrl, fileList, load, number }) {
const [files, setFiles] = useState(undefined);
useEffect(() => {
if (fileList) {
init();
}
}, [fileList]);
function init() {
let f = appendFileSizeToUploadFileAll(fileList);
setFiles(f);
}
function onAttachmentRemove(file) {
if (!file.percent || file.percent === 100) {
deleteAttachment(file);
return false;
}
}
function deleteAttachment(file) {
let id = (file.response && file.response.data && file.response.data.id) || file.id;
//
let nf = files.filter(item => {
let itemId = (item.response && item.response.data && item.response.data.id) || item.id;
return itemId !== id;
});
setFiles(nf);
load && load(nf);
}
function handleChange(info) {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
if (number) {
fileList = fileList.slice(-number);
}
if (info.file.status === 'done' && info.file.response.code === "0") {
fileList.pop();
ShowNotification(info.file.response.message);
}
setFiles(appendFileSizeToUploadFileAll(fileList));
if (info.file.status === 'done' || info.file.status === 'removed') load && load(fileList);
}
}
function beforeUpload(file) {
const isLt100M = file.size / 1024 / 1024 < size;
if (!isLt100M) {
ShowNotification(`文件大小必须小于${size}MB!`);
}
return isLt100M;
}
const upload = {
name: 'file',
fileList: files,
action: actionUrl + `/busiAttachments/upload`,
onChange: handleChange,
onRemove: onAttachmentRemove,
beforeUpload: beforeUpload,
};
return (
<Upload {...upload} className={className}>
<Button type="primary">点击上传</Button>
<span className="ml10 color-grey-9">(你可以上传小于<span className="color-red">{size}MB</span>的文件)</span>
</Upload>
)
}
export default Uploads;

View File

@ -219,28 +219,30 @@ export default [
target: "_self",
urltype: "self",
children: [
// {
// key: "competitionList",
// title: "竞赛列表",
// location: "/managements/competition/list",
// menustatus: "Y",
// parentKey: "task",
// icon: "",
// target: "",
// urltype: "self",
// children: [],
// },
// {
// key: "competitionRegion",
// title: "赛区配置",
// location: "/admin/competitions/region_zone_edit",
// menustatus: "Y",
// parentKey: "task",
// icon: "",
// target: "_blank",
// urltype: "main_web_site_url",
// children: [],
// },
{
key: "competitionList",
title: "竞赛列表",
location: "/admin/competitions/list",
menustatus: "Y",
parentKey: "task",
icon: "",
target: "",
urltype: "main_web_site_url",
params:'layout=none',
children: [],
},
{
key: "competitionRegion",
title: "赛区配置",
location: "/admin/competitions/region_zone_edit",
menustatus: "Y",
parentKey: "task",
icon: "",
target: "_blank",
urltype: "main_web_site_url",
params:'layout=none',
children: [],
},
{
key: "competitionExpert",
title: "竞赛评审列表",
@ -286,4 +288,69 @@ export default [
},
],
},
{
key: "notice",
title: "公告管理",
location: "",
icon: "icon-yonghuguanli",
target: "_self",
urltype: "self",
children: [
{
key: "noticeList1",
title: "已发布",
location: "/managements/notice/list/1",
menustatus: "Y",
parentKey: "notice",
icon: "",
target: "",
urltype: "self",
children: [],
},
{
key: "noticeList2",
title: "待审核",
location: "/managements/notice/list/2",
menustatus: "Y",
parentKey: "notice",
icon: "",
target: "",
urltype: "self",
children: [],
},
{
key: "noticeList0",
title: "未通过",
location: "/managements/notice/list/0",
menustatus: "Y",
parentKey: "notice",
icon: "",
target: "",
urltype: "self",
children: [],
},
{
key: "noticeDraft",
title: "草稿箱",
location: "/managements/notice/list/draft",
menustatus: "Y",
parentKey: "notice",
icon: "",
target: "",
urltype: "self",
children: [],
},
{
key: "noticeEdit",
title: "发布公告",
location: "/managements/notice/edit",
menustatus: "Y",
parentKey: "notice",
icon: "",
target: "",
urltype: "self",
children: [],
},
],
},
];

View File

@ -74,7 +74,9 @@ export default (props) => {
function iframeLoad(){
try{
document.getElementById("iframe").height=document.getElementById("iframe").contentWindow.top.document.documentElement.scrollHeight;
}catch(err){}
}catch(err){
console.error(err);
}
}
return (
@ -93,7 +95,7 @@ export default (props) => {
<div className="managements">
<div className="head-title">{head(keyPath)}</div>
{acitve.urltype == "self" ? children :
<iframe id="iframe" className="iframe-item" src={`${setting[acitve.urltype] + acitve.location}`} onLoad={iframeLoad}></iframe>
<iframe id="iframe" className="iframe-item" src={`${setting[acitve.urltype] + acitve.location}?${acitve.params}`} onLoad={iframeLoad}></iframe>
}
</div>
</Fragment>

View File

@ -15,10 +15,12 @@
.left-icon {
font-size: 1.25rem;
margin-right: 0.625rem;
margin-left: -.3125rem;
}
.ant-menu-submenu:hover, .ant-menu-submenu-selected, .ant-menu:hover, .ant-menu-submenu-selected .ant-menu{
background-color:#fbfbff;
}
.ant-menu-submenu-selected{
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected{
background-image:linear-gradient(90deg,#e7efff 0%,#f0f5ff 65.69%,#ffffff 100%);

View File

@ -90,7 +90,28 @@ const ReviewResult = Loadable({
const CompetitionList = Loadable({
loader: () => import("../military/expert/competionList"),
loading: Loading,
})
});
// 公告管理
const NoticeList = Loadable({
loader: () => import("./notice/noticeList"),
loading: Loading,
});
// 公告管理详情
const NoticeDetail = Loadable({
loader: () => import("./notice/noticeDetail"),
loading: Loading,
});
// 公告管理编辑
const NoticeEdit = Loadable({
loader: () => import("./notice/noticeEdit"),
loading: Loading,
});
// 公告reader
const NoticeReader = Loadable({
loader: () => import("./notice/noticeReader"),
loading: Loading,
});
const Managements = (propsF) => {
@ -214,6 +235,42 @@ const Managements = (propsF) => {
<CompetitionList {...propsF} {...props} />
)}
></Route>
{/* 公告列表 */}
<Route
path="/managements/notice/list/:isChecked"
render={(props) => (
<NoticeList {...propsF} {...props} />
)}
></Route>
{/* 公告详情 */}
<Route
path="/managements/notice/detail/:noticeId"
render={(props) => (
<NoticeDetail {...propsF} {...props} />
)}
></Route>
{/* 公告编辑 */}
<Route
path="/managements/notice/edit/:noticeId"
render={(props) => (
<NoticeEdit {...propsF} {...props} />
)}
></Route>
{/* 公告新增 */}
<Route
path="/managements/notice/edit"
render={(props) => (
<NoticeEdit {...propsF} {...props} />
)}
></Route>
{/* 公告预览 */}
<Route
path="/managements/notice/reader/:noticeId"
render={(props) => (
<NoticeReader {...propsF} {...props} />
)}
></Route>
</Switch>
</Layouts>
</div>

View File

@ -0,0 +1,80 @@
import fetch from 'military/notice/fetch';
import showNotification from '../components/ShowNotification';
// 公告列表查询
export async function getNoticeList(params) {
let res = await fetch({
url: '/api/announcements/',
method: 'get',
params,
});
if (res.message === 'success') {
return res.data;
} else {
showNotification(res.message || '请求错误');
}
}
//新增公告
export function addNotice(data) {
return fetch({
url: '/api/announcements/add',
method: 'post',
data: data
});
}
//删除公告
export function deleteNotice(id) {
return fetch({
url: '/api/announcements/' + id,
method: 'DELETE',
});
}
//更新公告
export function editNotice(data) {
return fetch({
url: '/api/announcements/update',
method: 'put',
data: data
});
}
//审核公告
export function checkNotice(data) {
return fetch({
url: '/api/announcements/check',
method: 'put',
data: data
});
}
// 公告详情查询
export async function getNoticeDetail(id) {
let res = await fetch({
url: '/api/announcements/' + id,
method: 'get',
});
if (res.data) {
return res.data;
} else {
showNotification(res.message || '请求错误');
}
}
// 公告密文查看人详情
export async function getNoticeReader(params) {
let res = await fetch({
url: '/api/request_contact_reader_info/',
method: 'get',
params,
});
if (res.data) {
return res.data;
} else {
showNotification(res.message || '请求错误');
}
}

View File

@ -0,0 +1,29 @@
.notice-content {
min-height: 50vh;
margin: 1em;
background: #fff;
border-radius: 1em;
box-shadow: 0 1px 2px #d9d9d9;
overflow-y: scroll;
}
.notice-title {
padding: 0.75em;
border-bottom: 1px solid #e5e5e5;
}
.notice-form .edit-input{
max-width:450px;
}
.my-search-button{
margin-top:4px;
}
.encrypt-item{
margin-top: -24px;
}
.encrypt-item .ant-form-item-label{
visibility: hidden;
}

View File

@ -0,0 +1,84 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Descriptions, Icon } from 'antd';
import ShowNotification from '../../components/ShowNotification';
import { noticeType, noticeChecked } from '../../common/static';
import { getNoticeDetail, checkNotice } from '../api';
import '../index.css';
import './index.scss';
const noticeTypeArr = [];
for (const item of noticeType) {
noticeTypeArr[item.code] = (item.name);
}
const noticeCheckedArr = [];
for (const item of noticeChecked) {
noticeCheckedArr[item.code] = (item.name);
}
const IndexPage = ({ match, history, current_user: { admin } }) => {
const [noticeData, setNoticeData] = useState({});
const id = match.params.noticeId;
useEffect(() => {
id && getNoticeDetail(id).then(data => {
setNoticeData(data || {});
})
}, [id]);
//
function checkItem(isChecked) {
checkNotice({
id: noticeData.id,
isChecked,
}).then(res => {
if (res && res.code == '1') {
ShowNotification("操作成功");
history.go(-1);
} else {
ShowNotification(res.message || "操作失败");
history.go(-1);
}
})
}
function download(url) {
if (location.href.indexOf('localhost') > -1) {
url = 'http://106.75.31.211:58088' + url;
}
window.open(url);
}
return (
<div className="notice-content">
<h4 className="notice-title"><span className='backList' onClick={() => { history.go(-1) }}><Icon type="left" />返回</span>公告详情</h4>
<Descriptions className='itemContent' column={1}>
<Descriptions.Item label="公告状态"><span className='statusColor'>{noticeData.status == 1 ? noticeCheckedArr[noticeData.isChecked] : '草稿'}</span></Descriptions.Item>
<Descriptions.Item label="公告标题">{noticeData.title}</Descriptions.Item>
<Descriptions.Item label="公告类型">{noticeTypeArr[noticeData.type]}</Descriptions.Item>
<Descriptions.Item label="发布单位">{noticeData.publisher}</Descriptions.Item>
<Descriptions.Item label="公告描述" className='alignTop'><div className="editor-w-text" dangerouslySetInnerHTML={{ __html: noticeData.text }}></div></Descriptions.Item>
{[4, 5, 6].includes(noticeData.type) && <Descriptions.Item label="联系方式" className='alignTop'><div className="editor-w-text" dangerouslySetInnerHTML={{ __html: noticeData.contactInfo && noticeData.contactInfo.replace(/\n/g, '</br>') }}></div></Descriptions.Item>}
<Descriptions.Item label="公告附件">
<div style={{ color: '#1890ff' }} onClick={() => { download(noticeData.fileDownloadPath) }}>{noticeData.fileName}</div>
</Descriptions.Item>
<Descriptions.Item label="创建时间">{noticeData.createdAt}</Descriptions.Item>
{noticeData.publishDate && <Descriptions.Item label="发布时间">{noticeData.publishDate}</Descriptions.Item>}
<Descriptions.Item label="截止时间">{noticeData.closingDate}</Descriptions.Item>
<Descriptions.Item label="">
{admin && noticeData.status == 1 && noticeData.isChecked == 2 &&
<React.Fragment>
<Button className="mr20" type="primary" onClick={() => { checkItem(1) }}>通过</Button>
<Button className="mr20" type="primary" onClick={() => { checkItem(0) }}>拒绝</Button>
<Button className="mr20" onClick={() => { history.go(-1) }}>取消</Button>
</React.Fragment>
}
</Descriptions.Item>
</Descriptions>
</div>
)
}
export default IndexPage;

View File

@ -0,0 +1,22 @@
.itemContent {
padding: 10px 10px 0 30px;
}
.statusColor{
color: #1890ff;
}
.alignTop{
display: flex;
align-items: flex-start;
}
.backList{
margin-right:1em;
&:hover{
color: #1890ff;
}
}
:global{
.ant-descriptions-item-label{
min-width: 5em;
}
}

View File

@ -0,0 +1,322 @@
import React, { forwardRef, Fragment, useCallback, useEffect, useState } from 'react';
import ReactWEditor from 'wangeditor-for-react';
import { Form, Input, Button, Radio, Checkbox, DatePicker } from 'antd';
import moment from 'moment';
import { noticeType } from '../../common/static';
import Upload from '../../components/Upload';
import ShowNotification from '../../components/ShowNotification';
import { getNoticeDetail, addNotice, editNotice } from '../api';
import { httpUrl } from 'military/notice/fetch';
import '../index.css';
const format = "YYYY-MM-DD HH:mm:ss";
const { TextArea } = Input;
let actionUrl = httpUrl;
// if (window.location.href.indexOf('localhost') > -1) {
// actionUrl = httpUrl;
// } else {
// actionUrl = "https://info.osredm.com";
// }
const NoticeEdit = Form.create()(forwardRef(({ form, match, history }, ref) => {
const [fileList, setFileList] = useState(null);
const [noticeHtml, setNoticeHtml] = useState('');
const id = match.params.noticeId;
const { getFieldDecorator, validateFields, setFieldsValue } = form;
const [typeValue, setTypeValue] = useState(4);
const [isSecret, setIsSecret] = useState();
useEffect(() => {
id && getNoticeDetail(id).then(data => {
const formValue = {
type: data.type,
title: data.title,
publisher: data.publisher,
text: data.text,
closingDate: data.closingDate && moment(data.closingDate),
contactInfo: data.contactInfo,
};
setFieldsValue(formValue);
setNoticeHtml(data.text || '');
setIsSecret(Boolean(data.isSecret));
if (data.fileName) {
setFileList([{
name: data.fileName,
fileName: data.fileName,
fileDownloadPath: data.fileDownloadPath,
uid: "rc-upload" + data.id
}])
}
})
}, [id]);
//
function UploadFunc(fileList) {
setFileList(fileList);
}
const helper = useCallback(
(label, name, rules, widget, initialValue) => (
<Form.Item label={label}>
{getFieldDecorator(name, { rules, initialValue, validateFirst: true })(widget)}
</Form.Item>
),
[]
);
const editor = useCallback(() => (
<Form.Item label={"公告描述:"} required={true}>
{(!id || (id && noticeHtml)) &&
<ReactWEditor
defaultValue={noticeHtml}
config={
{
placeholder: "请输入公告描述",
excludeMenus: [
'list',
'todo',
'emoticon',
'video'
],
uploadImgServer: actionUrl + '/busiAttachments/upload',
uploadFileName: 'file',
uploadImgHeaders: {
'X-Requested-With': 'XMLHttpRequest'
},
uploadImgHooks: {
//
customInsert: function (insertImgFn, result) {
// result
// insertImgFn src
if (result && result.data && result.data.id) {
insertImgFn(`${actionUrl}/busiAttachments/view/${result.data.id}`);
}
}
}
}
}
onChange={(html) => {
changeHtml(html);
}}
/>}
{getFieldDecorator('text', {
rules: [{ required: true, message: "请输入公告描述" }],
validateFirst: true
})(<Input style={{ display: 'none' }} />)}
</Form.Item>
), [noticeHtml])
const formItemLayout = {
labelCol: {
xs: { span: 10 },
sm: { span: 8 },
lg: { span: 3 },
},
wrapperCol: {
xs: { span: 14 },
sm: { span: 16 },
lg: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 20,
offset: 4,
},
sm: {
span: 20,
offset: 4,
},
},
};
//
function saveItem(status) {
validateFields((error, values) => {
if (!error) {
let params = {
...values,
status,
fileDownloadPath: '',
fileName: '',
templateUrl: '',
};
params.closingDate = params.closingDate.format(format);
params.isSecret = Number(params.isSecret);
if (fileList && fileList.length) {
params.fileName = fileList[0].fileName || fileList[0].response.data.fileName;
params.fileDownloadPath = fileList[0].fileDownloadPath || `${actionUrl}/busiAttachments/download/${fileList[0].response.data.id}`;
}
if (id) {
//
params.id = id;
editNotice(params).then(res => {
if (res.message === 'success') {
ShowNotification("操作成功!");
if (status == 1) {
history.push('/managements/notice/list/2');
} else {
history.push('/managements/notice/list/draft');
}
} else {
ShowNotification(res.message);
}
});
} else {
//
addNotice(params).then(res => {
if (res.message === 'success') {
ShowNotification("公告新增成功!");
if (status == 1) {
history.push('/managements/notice/list/2');
} else {
history.push('/managements/notice/list/draft');
}
} else {
ShowNotification(res.message);
}
});
}
}
})
}
function changeClosingDate(val) {
if (val) {
let nextTime = moment(new Date().setDate(new Date().getDate() + val)).format('YYYY-MM-DD');
nextTime += ' 23:59:59';
setFieldsValue({
closingDate: moment(nextTime)
});
}
}
function changeHtml(html) {
setFieldsValue({
text: html
});
// setNoticeHtml(html);
}
return (
<div className="notice-content">
<h4 className="notice-title">{id ? '修改' : '发布'}公告</h4>
<Form className="notice-form" {...formItemLayout}>
{helper(
"公告类型:",
"type",
[{ required: true, message: "请选择公告类型" }],
<Radio.Group
onChange={(e) => { setTypeValue(e.target.value) }}
>
{
noticeType.map(item => {
return <Radio key={item.code} value={item.code}>{item.name}</Radio>
})
}
</Radio.Group>,
4
)}
{helper(
"公告标题:",
"title",
[{ required: true, message: "请输入公告标题" },
{ max: 50, message: '长度不能超过50个字符' }],
<Input
className="edit-input"
placeholder="请输入公告标题"
/>
)}
{helper(
"发布单位:",
"publisher",
[{ required: true, message: "请输入发布单位" },
{ max: 50, message: '长度不能超过50个字符' }],
<Input
className="edit-input"
placeholder="请输入发布单位"
/>
)}
{editor()}
{[4, 5, 6].includes(typeValue) && <Fragment>
{helper(
"联系方式:",
"contactInfo",
[{ max: 5000, message: '长度不能超过5000个字符' }],
<TextArea
rows={4}
// className="edit-input"
placeholder="请输入联系方式"
/>
)}
<Form.Item className="encrypt-item" label="加密与否">
{/* <Checkbox checked={isSecret} onChange={e => { setIsSecret(e.target.checked) }}>设置联系方式为加密内容</Checkbox> */}
{getFieldDecorator('isSecret',
{
validateFirst: true
})(<Checkbox checked={isSecret} onChange={e => { setIsSecret(e.target.checked) }}>设置联系方式为加密内容</Checkbox>)}
</Form.Item>
</Fragment>}
<Form.Item label={"上传文件:"}>
<Upload
className="commentStyle"
load={UploadFunc}
size={100}
ShowNotification={ShowNotification}
actionUrl={actionUrl}
fileList={fileList}
number={1}
/>
{/* 用一个隐藏的input实现上传文件的必填校验 */}
{/* {getFieldDecorator('file_name', {
rules: [{ required: true, message: "请上传公告文件" }],
validateFirst: true
})(<Input style={{ display: 'none' }} />)} */}
</Form.Item>
<Form.Item label="截止时间:">
<Radio.Group onChange={(e) => { changeClosingDate(e.target.value) }}>
<Radio value={6}>一周内</Radio>
<Radio value={29}>一个月内</Radio>
<Radio value={90}>三个月内</Radio>
<Radio value={182}>半年内</Radio>
<Radio value={0}>自定义</Radio>
</Radio.Group>
{getFieldDecorator('closingDate',
{
rules: [{ required: true, message: "请选择截止时间" }],
validateFirst: true
})(<DatePicker
showTime
format={format}
placeholder="请选择截止时间"
/>)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button className="mr20" type={"primary"} onClick={() => { saveItem(1) }}>发布</Button>
<Button className="mr20" type={"primary"} onClick={() => { saveItem(2) }}>保存</Button>
<Button onClick={() => { history.go(-1) }}>取消</Button>
</Form.Item>
</Form>
</div>
)
})
)
export default NoticeEdit;

View File

@ -0,0 +1,301 @@
import React, { forwardRef, useEffect, useState, useCallback } from 'react';
import { Table, Pagination, Button, Form, Modal, Input, Select, Radio, } from 'antd';
import PaginationTable from "../../../components/pagination-table";
import { noticeType } from '../../common/static';
import DelModal from '../../components/DelModal';
import ShowNotification from '../../components/ShowNotification';
import { getNoticeList, checkNotice, deleteNotice } from '../api';
import '../index.css';
import './index.scss';
const Option = Select.Option;
const noticeTypeArr = [];
for (const item of noticeType) {
noticeTypeArr[item.code] = (item.name);
}
const NoticeList = Form.create()(forwardRef(({ form, match, history, current_user: { admin } }, ref) => {
let status = 1;
let isChecked = match.params.isChecked;
if (history.location.pathname.indexOf('draft') > -1) {
status = 2;
isChecked ='';
}
const { getFieldDecorator, validateFields, setFieldsValue, } = form;
const [type, setType] = useState(undefined);
const [title, setTitle] = useState(undefined);
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [orderBy, setOrderBy] = useState('');
const [dataList, setDataList] = useState([]);
const [loading, setLoading] = useState(false);
const [visible, setVisible] = useState(false);
const [currentId, setCurrentId] = useState('');
const [currentIsChecked, setCurrentIsChecked] = useState(1);
const [reload, setReload] = useState(0);
useEffect(() => {
//
console.log('useEffect --- ');
//
history.listen(historyLocation => {
//
// console.log('route history , ', history);
// console.log('route history location , ', historyLocation);
setCurPage(1);
setType(undefined);
setTitle(undefined);
setFieldsValue({
type: undefined,
title: undefined,
});
})
}, [history]);
useEffect(() => {
setLoading(true);
getNoticeList({
orderBy,
curPage,
isChecked,
pageSize: 10,
status,
type,
title,
flag: 2, //21
}).then(data => {
setLoading(false);
if (data) {
setDataList(data.rows);
setTotal(data.total);
} else {
setTotal(0);
setDataList([]);
}
});
}, [curPage, type, title, isChecked, reload]);
const columns = [
{
title: '公告标题',
dataIndex: 'title',
key: 'title',
width: '30%'
},
{
title: '公告类型',
key: 'type',
dataIndex: 'type',
render: (text, record) => {
return noticeTypeArr[text];
}
},
{
title: '创建时间',
key: 'createdAt',
dataIndex: 'createdAt',
},
{
title: '截止时间',
key: 'closingDate',
dataIndex: 'closingDate',
},
{
title: '操作',
key: 'action',
render: (text, record) => (
<span>
{record.status == 2 && <Button className="mr5 font-12" type="primary" size="small" onClick={() => { editItem(record.id) }}>修改</Button>}
<Button className="mr5 font-12" type="info" size="small" onClick={() => { detailItem(record.id) }}>详情</Button>
{admin && record.status == 1 && record.isChecked == 2 && <Button className="mr5 font-12" type="primary" size="small" onClick={() => { setCurrentId(record.id); setVisible(true); }}>审核</Button>}
{(admin || (record.isChecked != 1)) && < Button className="mr5 font-12" type="danger" size="small" onClick={() => { deletItem(record.id) }}>删除</Button>}
</span >
),
},
];
//
if (isChecked == 1) {
columns.splice(3, 0, {
title: '发布时间',
key: 'publishDate',
dataIndex: 'publishDate',
});
columns.splice(4, 0, {
title: '密文浏览数',
key: 'requestViewCount',
dataIndex: 'requestViewCount',
render: (text, record) => {
return <span className={`${text > 0 ? 'link' : ''}`} onClick={() => { text > 0 && history.push(`/managements/notice/reader/${record.id}`) }}>{text}</span>;
}
});
}
function detailItem(id) {
history.push(`/managements/notice/detail/${id}`);
}
//
function checkItem() {
checkNotice({
id: currentId,
isChecked: currentIsChecked,
}).then(res => {
if (res && res.code == '1') {
ShowNotification("操作成功");
setVisible(false);
setReload(reload + 1);
} else {
ShowNotification(res.message || "操作失败");
}
})
}
function editItem(id) {
history.push(`/managements/notice/edit/${id}`);
}
function deletItem(id) {
DelModal(() => {
deleteNotice(id).then(res => {
if (res.message === 'success') {
ShowNotification("删除成功");
if (dataList.length == 1 && curPage > 1) {
setCurPage(curPage - 1);
} else {
setReload(reload + 1);
}
} else {
ShowNotification("删除失败");
}
})
}, '此操作将删除该记录, 是否继续?');
}
function handleSearch() {
validateFields((error, values) => {
console.log(values);
if (!error) {
setType(values.type || "");
setTitle(values.title);
}
});
}
const helper = useCallback(
(label, name, rules, widget) => (
<Form.Item label={label} className='searchForm'>
{getFieldDecorator(name, { rules, validateFirst: true, })(widget)}
</Form.Item>
), []);
const formItemLayout = {
labelCol: {
xs: { span: 12 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 12 },
sm: { span: 18 },
},
};
function onShowSizeChange(current, pageSize) {
setCurPage(current);
setPageSize(pageSize);
}
return (
<div className="content">
<Form className="clearfix">
{helper(
"",
"type",
[],
<Select
placeholder="公告类型"
allowClear
showArrow
>
{
noticeType.map(item => {
return <Option key={item.code}>{item.name}</Option>
})
}
</Select>
)}
{helper(
"",
"title",
[{ max: 20, message: '关键字长度不能超过20个字符' }],
<Input
placeholder="输入标题关键字进行搜索"
/>
)}
<Button className="my-search-button" type="primary" onClick={handleSearch}>查询</Button>
</Form>
<div className="table-detail">
<PaginationTable
loading={loading}
dataSource={dataList}
columns={columns}
total={total}
setCurPage={setCurPage}
current={curPage}
onShowSizeChange={onShowSizeChange}
showSizeChanger
/>
{/* <Table
loading={loading}
rowKey={(row) => row.id}
dataSource={dataList}
columns={columns}
pagination={false}
/>
{dataList.length > 0 &&
<Pagination
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
/>} */}
</div>
<Modal
title="公告审核"
visible={visible}
onOk={checkItem}
onCancel={() => { setVisible(false) }}
className="form-edit-modal"
>
<Form {...formItemLayout}>
<Form.Item label={"审核意见:"} >
<Radio.Group defaultValue={currentIsChecked} onChange={(e) => { setCurrentIsChecked(e.target.value) }} >
<Radio className="columsRadio" value={1}>
通过
</Radio>
<Radio className="columsRadio" value={0}>
拒绝
</Radio>
</Radio.Group>
</Form.Item>
</Form>
</Modal>
</div>
);
})
)
export default NoticeList;

View File

@ -0,0 +1,7 @@
.searchForm{
float: left;
min-width: 260px;
margin-right:2em !important;
}

View File

@ -0,0 +1,110 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Descriptions,Pagination, Icon, Table } from 'antd';
import { noticeType, noticeChecked } from '../../common/static';
import { getNoticeDetail, getNoticeReader } from '../api';
import '../index.css';
import './index.scss';
const noticeTypeArr = [];
for (const item of noticeType) {
noticeTypeArr[item.code]=(item.name);
}
const noticeCheckedArr = [];
for (const item of noticeChecked) {
noticeCheckedArr[item.code]=(item.name);
}
const NoticeReader = ({ match, history }) => {
const id = match.params.noticeId;
const [noticeData, setNoticeData] = useState({});
const [loading, setLoading] = useState(false);
const [dataList, setDataList] = useState([]);
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
useEffect(() => {
id && getNoticeDetail(id).then(data => {
setNoticeData(data || {});
})
}, [id]);
useEffect(() => {
setLoading(true);
id && getNoticeReader({
currentPage:curPage,
annId:id,
pageSize:10
}).then(data => {
setLoading(false);
if (data) {
setDataList(data.rows);
setTotal(data.total);
} else {
setTotal(0);
setDataList([]);
}
})
}, [id,curPage]);
function download(url) {
if (location.href.indexOf('localhost') > -1) {
url = 'http://106.75.31.211:58088' + url;
}
window.open(url);
}
const columns = [
{
title: '用户姓名',
key: 'readerName',
dataIndex: 'readerName',
width:'20%',
},
{
title: '公司名称',
key: 'companyName',
dataIndex: 'companyName',
width:'40%',
},
{
title: '联系方式',
key: 'contactInfo',
dataIndex: 'contactInfo',
width:'40%',
},
];
return (
<div className="notice-content">
<h4 className="notice-title"><span className='backList' onClick={() => { history.go(-1) }}><Icon type="left" />返回</span>浏览过该公告加密内容的用户详情</h4>
<Descriptions className='itemContent' column={1}>
<Descriptions.Item label="公告标题">{noticeData.title}</Descriptions.Item>
<Descriptions.Item label="加密信息被浏览数">{total}</Descriptions.Item>
<Descriptions.Item label="浏览过该公告加密内容的用户信息"></Descriptions.Item>
</Descriptions>
<div className="table-detail">
<Table
loading={loading}
rowKey={(row) => row.id}
dataSource={dataList}
columns={columns}
pagination={false}
/>
{dataList.length > 0 &&
<Pagination
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
/>}
</div>
</div>
)
}
export default NoticeReader;

View File

@ -0,0 +1,25 @@
.itemContent {
padding: 10px 10px 0 30px;
}
.statusColor{
color: #1890ff;
}
.alignTop{
display: flex;
align-items: flex-start;
}
.backList{
margin-right:1em;
&:hover{
color: #1890ff;
}
}
:global{
.ant-descriptions-item-label{
min-width: 5em;
}
.ant-table-row-cell-break-word{
word-break: break-all;
}
}