This commit is contained in:
2021-07-08 16:58:33 +08:00
17 changed files with 390 additions and 96 deletions

View File

@@ -7189,6 +7189,13 @@
检验验证码并且绑定 检验验证码并且绑定
</summary> </summary>
</member> </member>
<member name="M:Ewide.Core.Service.SysUserService.GetPwdRule">
<summary>
获取密码强度配置
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="F:Ewide.Core.Util.CodeHelper.code_Countdown"> <member name="F:Ewide.Core.Util.CodeHelper.code_Countdown">
<summary> <summary>
发送验证码间隔时间(秒) 发送验证码间隔时间(秒)
@@ -7271,6 +7278,13 @@
<param name="length"></param> <param name="length"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Ewide.Core.Util.CodeHelper.CheckRepeat(System.String)">
<summary>
检测是否重复
</summary>
<param name="Target"></param>
<returns></returns>
</member>
<member name="M:Ewide.Core.Util.MailHelper.#ctor(System.String,System.String)"> <member name="M:Ewide.Core.Util.MailHelper.#ctor(System.String,System.String)">
<summary> <summary>
邮箱类 邮箱类

View File

@@ -34,6 +34,7 @@ namespace Ewide.Core.Extension.DataFilter.Entity
/// <param name="filterFields"></param> /// <param name="filterFields"></param>
public void SetSearchInfo(SearchInfo[] searchInfo, IEnumerable<string> filterFields) public void SetSearchInfo(SearchInfo[] searchInfo, IEnumerable<string> filterFields)
{ {
if (searchInfo == null) return;
try try
{ {
foreach (var elem in searchInfo) foreach (var elem in searchInfo)

View File

@@ -232,7 +232,7 @@ WHERE SNU.UserId = @UserId AND SN.Status = @Status";
}, },
new[] new[]
{ {
"Type","Title" "Type","Title","ReadStatus"
} }
); );
@@ -254,8 +254,10 @@ WHERE SNU.UserId = @UserId AND SN.Status = @Status";
[HttpGet("/sysNotice/unread")] [HttpGet("/sysNotice/unread")]
public async Task<int> GetUnreadCount() public async Task<int> GetUnreadCount()
{ {
var noticeList = await _sysNoticeRep.Where(u => u.Status != (int)NoticeStatus.DELETED).Select(s => s.Id).ToListAsync(); //删除 或者 草稿都不让显示
return await _sysNoticeUserRep.Where(u => u.UserId == _userManager.UserId && noticeList.Contains(u.NoticeId) && u.ReadStatus == (int)NoticeUserStatus.UNREAD).CountAsync(); var noticeList = await _sysNoticeRep.Where(u => u.Status != (int)NoticeStatus.DELETED&& u.Status != (int)NoticeStatus.DRAFT).Select(s => s.Id).ToListAsync();
var noticeUserList = await _sysNoticeUserRep.Where(u => u.UserId == _userManager.UserId && noticeList.Contains(u.NoticeId) && u.ReadStatus == (int)NoticeUserStatus.UNREAD).ToListAsync();
return noticeUserList.Count();
} }
/// <summary> /// <summary>

View File

@@ -128,7 +128,6 @@ namespace Ewide.Core.Service
/// 新密码 /// 新密码
/// </summary> /// </summary>
[Required(ErrorMessage = "新密码不能为空")] [Required(ErrorMessage = "新密码不能为空")]
[StringLength(32, MinimumLength = 5, ErrorMessage = "密码需要大于5个字符")]
public string NewPassword { get; set; } public string NewPassword { get; set; }
/// <summary> /// <summary>

View File

@@ -29,7 +29,7 @@ namespace Ewide.Core.Service
Task<dynamic> SendCode(Usermailphone input); Task<dynamic> SendCode(Usermailphone input);
Task<dynamic> CheckBindcode(Usermailphone input); Task<dynamic> CheckBindcode(Usermailphone input);
Task<dynamic> GetPwdRule();
Task<dynamic> GetOrgUserTree(OrgUserInput input); Task<dynamic> GetOrgUserTree(OrgUserInput input);
} }
} }

View File

@@ -1,6 +1,7 @@
using Ewide.Core.Service.Role; using Ewide.Core.Service.Role;
using Ewide.Core.Service.User.Dto; using Ewide.Core.Service.User.Dto;
using Ewide.Core.Util; using Ewide.Core.Util;
using Furion;
using Furion.DatabaseAccessor; using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions; using Furion.DatabaseAccessor.Extensions;
using Furion.DataEncryption; using Furion.DataEncryption;
@@ -312,11 +313,26 @@ namespace Ewide.Core.Service
public async Task UpdateUserPwd(ChangePasswordUserInput input) public async Task UpdateUserPwd(ChangePasswordUserInput input)
{ {
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId); var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId);
if (MD5Encryption.Encrypt(input.Password) != user.Password) var Password = RSAHandler.RSADecrypt(input.Password);
throw Oops.Oh(ErrorCode.D1004); Password = MD5Encryption.Encrypt(Password);
if (MD5Encryption.Encrypt(input.NewPassword).Equals(user.Password)) if (Password != user.Password)
{
throw Oops.Oh("旧密码不正确");
}
var newPassword = RSAHandler.RSADecrypt(input.NewPassword);
// 验证新密码强度
var pattern = App.Configuration.GetSection("SimplePassword:Pattern").Value;
if (!Regex.Match(newPassword, pattern).Success)
{
throw Oops.Oh("新密码强度不符合规则");
}
newPassword = MD5Encryption.Encrypt(newPassword);
if (newPassword.Equals(user.Password))
throw Oops.Oh(ErrorCode.D10041); throw Oops.Oh(ErrorCode.D10041);
user.Password = MD5Encryption.Encrypt(input.NewPassword);
user.Password = newPassword;
} }
/// <summary> /// <summary>
@@ -467,7 +483,7 @@ namespace Ewide.Core.Service
var Mailcode_Key = "ewide_mailcode"; var Mailcode_Key = "ewide_mailcode";
var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$"; var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$";
var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$";
CodeHelper ch = new CodeHelper(_iMemoryCache); CodeHelper ch = new CodeHelper(_iMemoryCache, _sysUserRep, _userManager);
//Type为1时给原手机号发送验证码 //Type为1时给原手机号发送验证码
if (input.Type == 1) if (input.Type == 1)
{ {
@@ -484,7 +500,7 @@ namespace Ewide.Core.Service
//Type为2时给原邮箱发送验证码 //Type为2时给原邮箱发送验证码
else if (input.Type == 2) else if (input.Type == 2)
{ {
if (new Regex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$").IsMatch(_userManager.User.Email)) if (new Regex(Regex_Email).IsMatch(_userManager.User.Email))
{ {
try try
{ {
@@ -500,6 +516,7 @@ namespace Ewide.Core.Service
//Type为null时则发验证码 //Type为null时则发验证码
else else
{ {
await ch.CheckRepeat(input.Target);
//通过正则判断绑定类型 //通过正则判断绑定类型
if (new Regex(Regex_phone).IsMatch(input.Target)) if (new Regex(Regex_phone).IsMatch(input.Target))
{ {
@@ -542,7 +559,7 @@ namespace Ewide.Core.Service
var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$"; var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$";
var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$";
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId); var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId);
CodeHelper ch = new CodeHelper(_iMemoryCache); CodeHelper ch = new CodeHelper(_iMemoryCache, _sysUserRep,_userManager);
if (input.Type == 1) if (input.Type == 1)
{ {
if (ch.Checkcode(_userManager.User.Phone, input.Orgcode, Orgcode_Key)) if (ch.Checkcode(_userManager.User.Phone, input.Orgcode, Orgcode_Key))
@@ -655,6 +672,23 @@ namespace Ewide.Core.Service
} }
} }
/// <summary>
/// 获取密码强度配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysUser/getPwdRule")]
public async Task<dynamic> GetPwdRule()
{
return new LoginOutput
{
Pattern = App.Configuration.GetSection("SimplePassword:Pattern").Value,
Descriptions = App.Configuration.GetSection("SimplePassword:Descriptions").Value
};
}
[HttpPost("/sysUser/GetOrgUserTree")] [HttpPost("/sysUser/GetOrgUserTree")]
public async Task<dynamic> GetOrgUserTree(OrgUserInput input) public async Task<dynamic> GetOrgUserTree(OrgUserInput input)
{ {

View File

@@ -2,7 +2,9 @@
using Aliyun.Acs.Core.Exceptions; using Aliyun.Acs.Core.Exceptions;
using Aliyun.Acs.Core.Profile; using Aliyun.Acs.Core.Profile;
using Furion; using Furion;
using Furion.DatabaseAccessor;
using Furion.FriendlyException; using Furion.FriendlyException;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -19,9 +21,17 @@ namespace Ewide.Core.Util
public class CodeHelper public class CodeHelper
{ {
private readonly IMemoryCache _IMemoryCache; private readonly IMemoryCache _IMemoryCache;
public CodeHelper(IMemoryCache IMemoryCache) private readonly IRepository<SysUser> _sysUserRep; // 用户表仓储
{ private readonly IUserManager _userManager;
public CodeHelper(
IMemoryCache IMemoryCache,
IRepository<SysUser> sysUserRep,
IUserManager userManager
){
_IMemoryCache = IMemoryCache; _IMemoryCache = IMemoryCache;
_sysUserRep = sysUserRep;
_userManager = userManager;
} }
public static string Aliyun_AccessKey = App.Configuration["SmsHelper:Aliyun_AccessKey"]; public static string Aliyun_AccessKey = App.Configuration["SmsHelper:Aliyun_AccessKey"];
public static string Aliyun_AccessSecret = App.Configuration["SmsHelper:Aliyun_AccessSecret"]; public static string Aliyun_AccessSecret = App.Configuration["SmsHelper:Aliyun_AccessSecret"];
@@ -247,6 +257,24 @@ namespace Ewide.Core.Util
} }
} }
/// <summary>
/// 检测是否重复
/// </summary>
/// <param name="Target"></param>
/// <returns></returns>
public async Task<bool> CheckRepeat(string Target)
{
var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$";
var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$";
if(new Regex(Regex_phone).IsMatch(Target)){
var isExist = await _sysUserRep.DetachedEntities.AnyAsync(u => (u.Id != _userManager.UserId ) && (u.Account == Target || u.Phone == Target));
if (isExist) throw Oops.Oh("手机号与他人账号或者手机号重复");
}else if(new Regex(Regex_Email).IsMatch(Target))
{
var isExist3 = await _sysUserRep.DetachedEntities.AnyAsync(u => (u.Id != _userManager.UserId) && (u.Account == Target || u.Email == Target));
if (isExist3) throw Oops.Oh("邮箱与他人账号或者邮箱重复");
}
return true;
}
} }
} }

View File

@@ -85,6 +85,11 @@ const urls = {
*/ */
sysUserCheckBindcode: ['/sysUser/checkBindcode', 'post'], sysUserCheckBindcode: ['/sysUser/checkBindcode', 'post'],
/**
* 获取密码验证配置
*/
getPwdRule: ['/sysUser/getPwdRule', 'post'],
} }
export default urls export default urls

View File

@@ -78,7 +78,15 @@ export default class base extends Component {
<Descriptions.Item label="用户名">{user.name}</Descriptions.Item> <Descriptions.Item label="用户名">{user.name}</Descriptions.Item>
<Descriptions.Item label="昵称">{user.nickName}</Descriptions.Item> <Descriptions.Item label="昵称">{user.nickName}</Descriptions.Item>
<Descriptions.Item label="帐号">{user.account}</Descriptions.Item> <Descriptions.Item label="帐号">{user.account}</Descriptions.Item>
<Descriptions.Item label="性别">{user.sex}</Descriptions.Item> <Descriptions.Item label="性别">
{user.sex === 0
? '保密'
: user.sex === 1
? '男性'
: user.sex === 2
? '女性'
: '保密'}
</Descriptions.Item>
<Descriptions.Item label="生日"> <Descriptions.Item label="生日">
{user.birthday && {user.birthday &&
(typeof user.birthday === 'string' (typeof user.birthday === 'string'

View File

@@ -115,9 +115,9 @@ export default class form extends Component {
</List.Item> </List.Item>
)} )}
/> />
<ModalForm title={`更新密码`} action={apiAction.updatePwd} ref={this.updatePwdForm}> {/* <ModalForm title={`更新密码`} action={apiAction.updatePwd} ref={this.updatePwdForm}> */}
<PasswordForm loadData={loadData} /> <PasswordForm ref={this.updatePwdForm} loadData={loadData} />
</ModalForm> {/* </ModalForm> */}
<Phone ref={this.mhoneForm} loadData={loadData} /> <Phone ref={this.mhoneForm} loadData={loadData} />
<Mail ref={this.mailForm} loadData={loadData} /> <Mail ref={this.mailForm} loadData={loadData} />
</> </>

View File

@@ -107,7 +107,7 @@ export default class form extends Component {
const data = form.getFieldsValue() const data = form.getFieldsValue()
try { try {
await api.sysUserSendCode(data) await api.sysUserSendCode(data)
const typeName = data.type ? [, '手机', '邮箱'][data.type] : '手机' const typeName = data.type ? [, '手机', '邮箱'][data.type] : '邮箱'
Message.success(`已发送验证码到${typeName},请注意查收`) Message.success(`已发送验证码到${typeName},请注意查收`)
this.addTime() this.addTime()
this.showCountDown() this.showCountDown()

View File

@@ -1,24 +1,68 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Form, Input } from 'antd' import { Form, Input, message as Message, Modal } from 'antd'
import { api } from 'common/api'
import { RSA_PUBLIC_KEY } from 'util/global'
import { cloneDeep } from 'lodash'
import { Button } from 'antd/lib/radio'
import { encryptByRSA } from 'util/rsa'
const initData = {
exist: false,
pattern: '',
descriptions: '',
visible: false,
}
export default class form extends Component { export default class form extends Component {
state = { state = cloneDeep(initData)
// 加载状态
exist: false,
}
// 表单实例 // 表单实例
form = React.createRef() form = React.createRef()
// 初始化数据 // 初始化数据
record = {} record = {}
open = () => {
this.setState({ visible: true })
}
/** /**
* mount后回调 * mount后回调
*/ */
componentDidMount() { componentDidMount() {
this.props.created && this.props.created(this) this.props.created && this.props.created(this)
api.getPwdRule({}).then(({ success, data, message }) => {
if (success) {
const { pattern, descriptions } = data
this.setState({
pattern,
descriptions,
})
} else {
Message.Error(message)
}
})
} }
updatePwd(values) {
let { password, newPassword } = values.current.getFieldsValue()
password = encryptByRSA(password, RSA_PUBLIC_KEY)
newPassword = encryptByRSA(newPassword, RSA_PUBLIC_KEY)
const confirm = newPassword
api.sysUserUpdatePwd({ password, newPassword, confirm }).then(
({ success, data, message }) => {
if (success) {
Message.success('密码修改完成')
this.close()
} else {
Message.warn(message)
}
}
)
}
close() {
this.setState(cloneDeep(initData))
}
/** /**
* 填充数据 * 填充数据
* 可以在设置this.record之后对其作出数据结构调整 * 可以在设置this.record之后对其作出数据结构调整
@@ -46,7 +90,16 @@ export default class form extends Component {
} }
render() { render() {
const { pattern, descriptions, visible } = this.state
return ( return (
<Modal
destroyOnClose
onCancel={() => this.close()}
onOk={() => this.updatePwd(this.form)}
visible={visible}
className="yo-modal-form"
title="更新密码"
>
<Form className="yo-form" ref={this.form}> <Form className="yo-form" ref={this.form}>
<div className="yo-form-group"> <div className="yo-form-group">
<Form.Item <Form.Item
@@ -58,8 +111,12 @@ export default class form extends Component {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="新密码" label="新密码"
rules={[{ required: true, message: '请输入新密码' }]} rules={[
{ required: true, message: '请输入新密码' },
{ pattern, message: '密码格式错误' },
]}
name="newPassword" name="newPassword"
tooltip={descriptions}
> >
<Input.Password autoComplete="off" placeholder="请输入新密码" /> <Input.Password autoComplete="off" placeholder="请输入新密码" />
</Form.Item> </Form.Item>
@@ -82,6 +139,7 @@ export default class form extends Component {
</Form.Item> </Form.Item>
</div> </div>
</Form> </Form>
</Modal>
) )
} }
} }

View File

@@ -139,7 +139,6 @@ export default class form extends Component {
*/ */
async getData() { async getData() {
const form = this.form.current const form = this.form.current
console.log(this.record)
const valid = await form.validateFields() const valid = await form.validateFields()
if (valid) { if (valid) {
const postData = form.getFieldsValue() const postData = form.getFieldsValue()

View File

@@ -56,28 +56,38 @@ export default class index extends Component {
{ {
title: '标题', title: '标题',
dataIndex: 'title', dataIndex: 'title',
width: 300,
sorter: true,
}, },
{ {
title: '发布人', title: '发布人',
dataIndex: 'publicUserName', dataIndex: 'publicUserName',
width: 120,
sorter: true,
}, },
{ {
title: '发布时间', title: '发布时间',
dataIndex: 'createdTime', dataIndex: 'createdTime',
width: 150,
sorter: true,
}, },
{ {
title: '发布单位', title: '发布单位',
dataIndex: 'publicOrgName', dataIndex: 'publicOrgName',
width: 200, width: 150,
sorter: true,
}, },
{ {
title: '类型', title: '类型',
dataIndex: 'type', dataIndex: 'type',
width: 120,
sorter: true,
render: text => this.bindCodeValue(text, 'notice_type'), render: text => this.bindCodeValue(text, 'notice_type'),
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',
width: 120,
render: text => this.bindCodeValue(text, 'notice_status'), render: text => this.bindCodeValue(text, 'notice_status'),
}, },
] ]
@@ -94,7 +104,7 @@ export default class index extends Component {
if (flag) { if (flag) {
this.columns.push({ this.columns.push({
title: '操作', title: '操作',
width: 150, width: 200,
dataIndex: 'actions', dataIndex: 'actions',
render: (text, record) => ( render: (text, record) => (
<QueryTableActions> <QueryTableActions>
@@ -178,6 +188,7 @@ export default class index extends Component {
}) })
}) })
} }
subUniqueKey(text, index) { subUniqueKey(text, index) {
return text.substr(index, 5) return text.substr(index, 5)
} }
@@ -252,26 +263,23 @@ export default class index extends Component {
* @param {*} id * @param {*} id
*/ */
onDelete(id) { onDelete(id) {
this.onAction(apiAction.Status({ id, Status: 3 }), '删除成功') this.onAction(apiAction.Status({ id, status: 3 }), '删除成功')
} }
/** /**
* 发布 * 发布
* @param {*} id * @param {*} id
*/ */
onPublish(id) { onPublish(id) {
this.onAction(apiAction.Status({ id, Status: 1 }), '发布成功') this.onAction(apiAction.Status({ id, status: 1 }), '发布成功')
} }
/** /**
* 撤回 * 撤回
* @param {*} id * @param {*} id
*/ */
onGoBack(id) { onGoBack(id) {
this.onAction(apiAction.Status({ id, Status: 2 }), '撤回成功') this.onAction(apiAction.Status({ id, status: 2 }), '撤回成功')
} // } //
//#region 自定义方法
//#endregion
render() { render() {
const { codes } = this.state const { codes } = this.state
return ( return (
@@ -280,7 +288,6 @@ export default class index extends Component {
<Card bordered={false}> <Card bordered={false}>
<QueryTable <QueryTable
ref={this.table} ref={this.table}
rowkey={record => record.id}
autoLoad={false} autoLoad={false}
loadData={this.loadData} loadData={this.loadData}
columns={this.columns} columns={this.columns}

View File

@@ -1,15 +1,18 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Spin, Divider, Modal, Row } from 'antd' import { Spin, Divider, Modal, Row, Form, Upload } from 'antd'
import { api } from 'common/api' import { api } from 'common/api'
import { AntIcon } from 'components' import { AntIcon } from 'components'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
export default class form extends Component { export default class form extends Component {
state = { state = {
detailVisible: false, detailVisible: false,
detailLoading: false, detailLoading: false,
detailData: {}, detailData: {},
fileValue: false,
} }
filedu = React.createRef()
/** /**
* mount后回调 * mount后回调
*/ */
@@ -20,12 +23,57 @@ export default class form extends Component {
async onOpenDetail(id) { async onOpenDetail(id) {
this.setState({ detailLoading: true, detailVisible: true }) this.setState({ detailLoading: true, detailVisible: true })
const { data } = await api.sysNoticeDetail({ id }) const { data } = await api.sysNoticeDetail({ id })
this.setState({ this.setState({
detailLoading: false, detailLoading: false,
detailData: data, 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()
}
render() { render() {
const { detailLoading, detailVisible, detailData } = this.state const { detailLoading, detailVisible, detailData } = this.state
return ( return (
@@ -43,6 +91,21 @@ export default class form extends Component {
dangerouslySetInnerHTML={{ __html: detailData.content }} dangerouslySetInnerHTML={{ __html: detailData.content }}
></div> ></div>
<Divider /> <Divider />
{this.state.fileValue && (
<Row>
<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"> <Row justify="space-between" className="text-gray">
<span>发布人{detailData.publicUserName}</span> <span>发布人{detailData.publicUserName}</span>
<span>发布时间{detailData.publicTime} </span> <span>发布时间{detailData.publicTime} </span>

View File

@@ -20,6 +20,7 @@ export default class index extends Component {
codes: { codes: {
noticeStatus: [], noticeStatus: [],
noticeType: [], noticeType: [],
readStatus: [],
}, },
} }
@@ -33,25 +34,39 @@ export default class index extends Component {
{ {
title: '标题', title: '标题',
dataIndex: 'title', dataIndex: 'title',
width: 300,
sorter: true,
}, },
{ {
title: '发布人', title: '发布人',
dataIndex: 'publicUserName', dataIndex: 'publicUserName',
width: 150,
sorter: true,
}, },
{ {
title: '发布时间', title: '发布时间',
dataIndex: 'createdTime', dataIndex: 'createdTime',
width: 200,
sorter: true,
}, },
{ {
title: '发布单位', title: '发布单位',
dataIndex: 'publicOrgName', dataIndex: 'publicOrgName',
width: 200, width: 200,
sorter: true,
}, },
{ {
title: '类型', title: '类型',
dataIndex: 'type', dataIndex: 'type',
width: 120,
render: text => this.bindCodeValue(text, 'notice_type'), render: text => this.bindCodeValue(text, 'notice_type'),
}, },
{
title: '已读未读',
dataIndex: 'readStatus',
width: 120,
render: text => this.bindCodeValue(text, 'read_status'),
},
] ]
/** /**
@@ -94,33 +109,13 @@ export default class index extends Component {
componentDidMount() { componentDidMount() {
const { onLoading, onLoadData } = this.table.current const { onLoading, onLoadData } = this.table.current
onLoading() onLoading()
getDictData('notice_status', 'notice_type').then(codes => { getDictData('notice_status', 'notice_type', 'read_status').then(codes => {
this.setState({ codes }, () => { this.setState({ codes }, () => {
onLoadData() onLoadData()
}) })
}) })
} }
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
/** /**
* 调用加载数据接口,可在调用前对query进行处理 * 调用加载数据接口,可在调用前对query进行处理
* [异步,必要] * [异步,必要]
@@ -134,6 +129,7 @@ export default class index extends Component {
queryType: { queryType: {
type: QueryType.Equal, type: QueryType.Equal,
title: QueryType.Like, title: QueryType.Like,
readStatus: QueryType.Equal,
}, },
}) })
@@ -200,6 +196,19 @@ export default class index extends Component {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item label="已读未读" name="readStatus">
<Select
placeholder="请选择是否已读"
className="w-200"
allowClear
>
{codes.readStatus.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
</Auth> </Auth>
} }
/> />

View File

@@ -1,8 +1,9 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { Badge, Button, Divider, List, Menu, Modal, Popover, Row, Spin } from 'antd' import { Badge, Button, Divider, List, Menu, Modal, Popover, Row, Spin, Upload, Tag } from 'antd'
import { AntIcon, Image } from 'components' import { AntIcon, Image } from 'components'
import { api } from 'common/api' import { api } from 'common/api'
import InfiniteScroll from 'react-infinite-scroller' import InfiniteScroll from 'react-infinite-scroller'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
import moment from 'moment' import moment from 'moment'
export default class notice extends Component { export default class notice extends Component {
@@ -16,6 +17,7 @@ export default class notice extends Component {
detailVisible: false, detailVisible: false,
detailLoading: false, detailLoading: false,
detailData: {}, detailData: {},
fileValue: false,
} }
async componentDidMount() { async componentDidMount() {
@@ -42,7 +44,6 @@ export default class notice extends Component {
sortField: 'createdTime', sortField: 'createdTime',
sortOrder: 'descend', sortOrder: 'descend',
}) })
if (!items.length) { if (!items.length) {
return this.finish() return this.finish()
} }
@@ -59,7 +60,51 @@ export default class notice extends Component {
this.setState({ this.setState({
detailLoading: false, detailLoading: false,
detailData: data, 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() { renderList() {
@@ -88,6 +133,13 @@ export default class notice extends Component {
</small> </small>
</> </>
} }
description={
item.readStatus == 0 ? (
<Tag color="#f50">未读</Tag>
) : (
<Tag color="#2db7f5">已读</Tag>
)
}
/> />
<div className="ellipsis-3 text-gray">{item.content}</div> <div className="ellipsis-3 text-gray">{item.content}</div>
</List.Item> </List.Item>
@@ -140,6 +192,21 @@ export default class notice extends Component {
dangerouslySetInnerHTML={{ __html: detailData.content }} dangerouslySetInnerHTML={{ __html: detailData.content }}
></div> ></div>
<Divider /> <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"> <Row justify="space-between" className="text-gray">
<span>发布人{detailData.publicUserName}</span> <span>发布人{detailData.publicUserName}</span>
<span>发布时间{detailData.publicTime} </span> <span>发布时间{detailData.publicTime} </span>