-
Notifications
You must be signed in to change notification settings - Fork 22
jimmyarea 留言(前端)
Jimmy edited this page Jul 8, 2021
·
1 revision
import React, { useEffect, useState, useRef } from 'react';
import {
Card,
Form,
Input,
Button,
message,
List,
Avatar,
Row,
Col,
Modal,
Comment,
Tooltip,
} from 'antd';
import { postMessage, getMessageList, deleteMessageItem } from '@/services/dashboard/index';
import { deviceEnquire, DEVICE_TYPE } from '@/utils/device';
import { getStore } from '@/utils/storage';
import { logout } from '@/utils/utils';
import Captcha from './components/Captcha';
import {
CloudUploadOutlined,
CommentOutlined,
DeleteOutlined,
MessageOutlined,
} from '@ant-design/icons';
const moment = require('moment');
const Message: React.FC = () => {
const [userInfo, setUserInfo] = useState({ username: '' });
const [loading, setLoading] = useState(false);
const [innerLoading, setInnerLoading] = useState(false);
const [form] = Form.useForm();
const [replyForm] = Form.useForm();
const [isMobile, setIsMobile] = useState(false);
const [hasToken, setHasToken] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const [list, setList] = useState([]);
const [count, setCount] = useState(0);
const [loadingMsg, setLoadingMsg] = useState(false);
const [activePage, setActivePage] = useState(1); // 当前页码
const [pageSize] = useState(10); // 每页的条数
const [type, setType] = useState(1); // 是留言还是回复(1是留言,2是回复)
const [replyPlaceholder, setReplyPlaceholder] = useState(''); // 回复文本的placeholder
const [replyObj, setReplyObj] = useState({ _id: '', pid: '-1' }); // 回复的文本的对象信息
const replyArea = useRef(null);
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 12 },
};
const tailLayout = {
wrapperCol: isMobile ? { span: 24 } : { offset: 6, span: 12 },
};
const replyMsg = (item: any) => {
replyForm.resetFields();
if (!hasToken) {
message.warning('请先登陆~');
return;
}
setReplyObj(item);
setReplyPlaceholder(`${userInfo.username}@${item.userId?.username} ${item?.content}`);
if (replyArea) {
setTimeout(() => {
replyArea?.current?.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center',
});
}, 100);
}
};
const cancelReply = () => {
setReplyObj({ _id: '', pid: '-1' });
};
const onFinish = () => {
setIsModalVisible(true);
};
const onFinishReply = () => {
setType(2);
setIsModalVisible(true);
};
const onFinishFailed = (errorInfo: any) => {
if (errorInfo) {
message.warning('请完成必填字段信息');
}
};
const judgeDevice = () => {
deviceEnquire((deviceType: any) => {
switch (deviceType) {
case DEVICE_TYPE.MOBILE:
setIsMobile(true);
break;
default:
setIsMobile(false);
break;
}
});
};
const judgeHasToken = () => {
const token = getStore('token', false) || undefined;
if (token) {
setHasToken(true);
}
};
const getLoginUserInfo = () => {
const loginUserInfo = getStore('userInfo', true) || {};
setUserInfo(loginUserInfo);
};
const fetchMessageList = () => {
setLoadingMsg(true);
getMessageList(
{
current: activePage,
pageSize,
},
{},
)
.then((res: any) => {
if (res.code === 0) {
setList(res.data?.results || []);
setCount(res.data?.count || 0);
}
})
.finally(() => {
setLoadingMsg(false);
});
};
const passCaptcha = () => {
setIsModalVisible(false);
let subject = '';
let content = '';
if (type === 1) {
subject = form.getFieldValue('subject');
content = form.getFieldValue('content');
setLoading(true);
}
if (type === 2) {
subject = replyPlaceholder;
content = replyForm.getFieldValue('reply');
setInnerLoading(true);
}
postMessage(
{},
{
subject: subject?.trim(),
content: content?.trim(),
type,
pid: replyObj.pid === '-1' ? replyObj._id : replyObj.pid,
replyTargetId: replyObj._id || '-1',
},
)
.then((res) => {
if (res.code === 0) {
setTimeout(() => {
message.success(res.msg || '提交成功!');
if (type === 1) {
setLoading(false);
form.resetFields();
}
if (type === 2) {
setInnerLoading(false);
setReplyObj({ _id: '', pid: '-1' });
replyForm.resetFields();
}
fetchMessageList();
}, 2000);
} else {
message.error(res.msg || '留言失败,请稍后再试!');
setLoading(false);
setInnerLoading(false);
c({ _id: '', pid: '-1' });
if (res.code === 10004) {
let timer: any = null;
let time = 3;
message.info(`${time} 后跳转到登录页面!`);
timer = setInterval(() => {
time -= 1;
if (time <= 0) {
clearInterval(timer);
logout();
}
}, 1000);
}
}
})
.catch(() => {
setLoading(false);
setInnerLoading(false);
})
.finally(() => {
setType(1);
});
};
const handleCancel = () => {
setIsModalVisible(false);
};
const changePage = (page: any) => {
setActivePage(page);
};
const removeMsg = (item: any) => {
Modal.confirm({
title: '温馨提示',
content:
item?.children?.length > 0 ? '你确定删除此条留言及其子留言?' : '你确定删除此条留言?',
onOk: async () => {
const result = await deleteMessageItem(item._id);
if (result.code === 0) {
message.success(result.msg || '删除成功!');
fetchMessageList();
} else {
message.error(result.msg || '删除失败!');
}
},
});
};
useEffect(() => {
judgeDevice();
judgeHasToken();
getLoginUserInfo();
fetchMessageList();
}, [activePage]);
return (
<>
<Card style={{ width: '100%', marginTop: '36px' }}>
<div
style={{
textAlign: 'center',
fontSize: '16px',
fontWeight: 'bolder',
marginBottom: '24px',
position: 'relative',
}}
>
留言
<span
style={{
display: 'inline-block',
fontSize: '12px',
position: 'absolute',
left: '50%',
marginLeft: '36px',
}}
>
{hasToken ? null : <a onClick={logout}>登录后可留言</a>}
</span>
</div>
<Form
{...layout}
form={form}
name="basic"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="主题"
name="subject"
rules={[
{ required: true, message: '请输入你的主题' },
{ whitespace: true, message: '输入不能为空' },
{ min: 6, message: '主题不能小于6个字符' },
{ max: 30, message: '主题不能大于30个字符' },
]}
>
<Input maxLength={30} placeholder="请输入你的主题(最少6字符,最多30字符)" />
</Form.Item>
<Form.Item
label="内容"
name="content"
rules={[
{ required: true, message: '请输入你的内容' },
{ whitespace: true, message: '输入不能为空' },
{ min: 30, message: '内容不能小于30个字符' },
]}
>
<Input.TextArea
placeholder="请输入你的内容(最少30字符)"
autoSize={{
minRows: 6,
maxRows: 12,
}}
showCount
maxLength={300}
/>
</Form.Item>
<Form.Item {...tailLayout}>
<Button
type="primary"
htmlType="submit"
style={{ width: '100%' }}
loading={loading}
disabled={loading}
>
<CloudUploadOutlined />
Submit
</Button>
</Form.Item>
</Form>
<Row style={{ marginTop: '36px' }}>
<Col {...tailLayout.wrapperCol}>
<b style={{ marginBottom: '24px' }}>
留言展示
<CommentOutlined />
</b>
<List
loading={loadingMsg}
itemLayout="horizontal"
pagination={{
size: 'small',
total: count,
showTotal: () => `共 ${count} 条`,
pageSize,
current: activePage,
onChange: changePage,
}}
dataSource={list}
renderItem={(item: any, index: any) => (
<List.Item actions={[]} key={index}>
<List.Item.Meta
avatar={
<Avatar style={{ backgroundColor: '#1890ff' }}>
{item.userId?.username?.slice(0, 1)?.toUpperCase()}
</Avatar>
}
title={<b>{item.subject}</b>}
description={
<>
{item.content}
{/* 子留言 */}
<div
style={{
fontSize: '12px',
marginTop: '8px',
marginBottom: '16px',
alignItems: 'center',
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-between',
}}
>
<span>
用户 {item.userId?.username} 发表于
{moment(item.meta?.createAt).format('YYYY-MM-DD HH:mm:ss')}
</span>
<span>
{item.canDel ? (
<a
style={{ color: 'red', fontSize: '12px', marginRight: '12px' }}
onClick={() => removeMsg(item)}
>
<DeleteOutlined />
Delete
</a>
) : null}
<a
style={{ fontSize: '12px', marginRight: '12px' }}
onClick={() => replyMsg(item)}
>
<MessageOutlined />
Reply
</a>
</span>
</div>
{/* 回复的内容 */}
{item.children && item.children.length ? (
<>
{item.children.map((innerItem: any, innerIndex: any) => (
<Comment
key={innerIndex}
author={<span>{innerItem.subject}</span>}
avatar={
<Avatar style={{ backgroundColor: '#1890ff' }}>
{innerItem.userId?.username?.slice(0, 1)?.toUpperCase()}
</Avatar>
}
content={<p>{innerItem.content}</p>}
datetime={
<Tooltip
title={moment(innerItem.meta?.createAt).format(
'YYYY-MM-DD HH:mm:ss',
)}
>
<span>{moment(innerItem.meta?.createAt).fromNow()}</span>
</Tooltip>
}
actions={[
<>
{innerItem.canDel ? (
<a
style={{
color: 'red',
fontSize: '12px',
marginRight: '12px',
}}
onClick={() => removeMsg(innerItem)}
>
<DeleteOutlined />
Delete
</a>
) : null}
</>,
<a
style={{ fontSize: '12px', marginRight: '12px' }}
onClick={() => replyMsg(innerItem)}
>
<MessageOutlined />
Reply
</a>,
]}
/>
))}
</>
) : null}
{/* 回复的表单 */}
{replyObj._id === item._id || replyObj.pid === item._id ? (
<div style={{ marginTop: '12px' }} ref={replyArea}>
<Form
form={replyForm}
name="reply"
onFinish={onFinishReply}
onFinishFailed={onFinishFailed}
>
<Form.Item
name="reply"
rules={[
{ required: true, message: '请输入你的内容' },
{ whitespace: true, message: '输入不能为空' },
{ min: 2, message: '内容不能小于2个字符' },
]}
>
<Input.TextArea
placeholder={replyPlaceholder}
autoSize={{
minRows: 6,
maxRows: 12,
}}
showCount
maxLength={300}
/>
</Form.Item>
<Form.Item>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
style={{ marginRight: '12px' }}
onClick={() => cancelReply()}
>
Dismiss
</Button>
<Button
type="primary"
htmlType="submit"
loading={innerLoading}
disabled={innerLoading}
>
Submit
</Button>
</div>
</Form.Item>
</Form>
</div>
) : null}
</>
}
/>
</List.Item>
)}
/>
</Col>
</Row>
</Card>
<Modal
title="验证"
destroyOnClose={true}
visible={isModalVisible}
footer={null}
maskClosable={false}
onCancel={handleCancel}
>
{isModalVisible ? <Captcha onSuccess={passCaptcha} /> : null}
</Modal>
</>
);
};
export default Message;