update 实现部分表单使用detail获取详细数据
This commit is contained in:
@@ -20,7 +20,7 @@ export default class data extends Component {
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
id = ''
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
@@ -29,11 +29,11 @@ export default class data extends Component {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
this.id = params.id
|
||||
//#region 从后端转换成前段所需格式
|
||||
const orgData = await this.loadOrgData()
|
||||
const areaData = await this.loadAreaData()
|
||||
const orgCheckedKeys = await this.loadMemberOwn(this.record.id)
|
||||
const orgCheckedKeys = await this.loadMemberOwn(this.id)
|
||||
this.setState({
|
||||
options: {
|
||||
orgData,
|
||||
@@ -42,7 +42,7 @@ export default class data extends Component {
|
||||
},
|
||||
})
|
||||
this.form.current.setFieldsValue({
|
||||
id: this.record.id,
|
||||
id: this.id,
|
||||
grantOrgIdList: orgCheckedKeys,
|
||||
grantAreaCodeList: [],
|
||||
})
|
||||
@@ -63,8 +63,8 @@ export default class data extends Component {
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
if (this.id) {
|
||||
postData.id = this.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Row, Col, Form, Input, DatePicker, Radio, Table, Select, Spin, TreeSelect } from 'antd'
|
||||
import {
|
||||
Button,
|
||||
Row,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
DatePicker,
|
||||
Radio,
|
||||
Table,
|
||||
Select,
|
||||
Spin,
|
||||
TreeSelect,
|
||||
} from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
@@ -9,25 +21,24 @@ import moment from 'moment'
|
||||
|
||||
const initialValues = {
|
||||
sex: 0,
|
||||
sysEmpParam: {}
|
||||
sysEmpParam: {},
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
codes: {
|
||||
orgType: []
|
||||
orgType: [],
|
||||
},
|
||||
|
||||
options: {
|
||||
orgData: [],
|
||||
posData: []
|
||||
posData: [],
|
||||
},
|
||||
sysEmpParam: {
|
||||
extIds: []
|
||||
}
|
||||
extIds: [],
|
||||
},
|
||||
}
|
||||
extColumns = [
|
||||
{
|
||||
@@ -45,7 +56,7 @@ export default class form extends Component {
|
||||
placeholder="请选择附加组织机构"
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '附属岗位',
|
||||
@@ -56,33 +67,28 @@ export default class form extends Component {
|
||||
<Select
|
||||
defaultValue={text}
|
||||
className="w-100-p"
|
||||
placeholder="请选择附加职位信息">
|
||||
{
|
||||
this.state.options.posData.map(item => {
|
||||
return <Select.Option
|
||||
key={item.id}
|
||||
value={item.id}
|
||||
>
|
||||
{item.name}</Select.Option>
|
||||
})
|
||||
}
|
||||
placeholder="请选择附加职位信息"
|
||||
>
|
||||
{this.state.options.posData.map(item => {
|
||||
return (
|
||||
<Select.Option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: '70px',
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={() => this.onRemoveExtData(record)}
|
||||
size="small"
|
||||
danger
|
||||
>
|
||||
<Button onClick={() => this.onRemoveExtData(record)} size="small" danger>
|
||||
删除
|
||||
</Button>
|
||||
)
|
||||
),
|
||||
},
|
||||
]
|
||||
// 表单实例
|
||||
@@ -102,11 +108,13 @@ export default class form extends Component {
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record || {})
|
||||
//#region 从后端转换成前段所需格式
|
||||
if (params.id) {
|
||||
this.record = (await api.sysUserDetail({ id: params.id })).data
|
||||
}
|
||||
const orgData = await this.loadOrgData()
|
||||
const posData = await this.loadPosData()
|
||||
const codes = await getDictData('org_type')
|
||||
@@ -118,17 +126,17 @@ export default class form extends Component {
|
||||
|
||||
// 提交的时候是"param",而获取下来却是"info",在这里转换一下
|
||||
if (this.record.sysEmpInfo) {
|
||||
this.record.sysEmpParam = this.record.sysEmpInfo;
|
||||
delete this.record.sysEmpInfo;
|
||||
this.record.sysEmpParam = this.record.sysEmpInfo
|
||||
delete this.record.sysEmpInfo
|
||||
} else if (!this.record.sysEmpParam) {
|
||||
this.record.sysEmpParam = {
|
||||
extIds: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 转换职位信息列表
|
||||
if (this.record.sysEmpParam.positions) {
|
||||
this.record.sysEmpParam.posIdList = this.record.sysEmpParam.positions.map((p) => p.posId);
|
||||
this.record.sysEmpParam.posIdList = this.record.sysEmpParam.positions.map(p => p.posId)
|
||||
}
|
||||
|
||||
// 附加信息
|
||||
@@ -138,12 +146,12 @@ export default class form extends Component {
|
||||
key: i,
|
||||
orgId: p.orgId,
|
||||
posId: p.posId,
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (params.orgId) {
|
||||
this.record.sysEmpParam.orgId = params.orgId;
|
||||
this.record.sysEmpParam.orgId = params.orgId
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -154,18 +162,18 @@ export default class form extends Component {
|
||||
posData,
|
||||
},
|
||||
sysEmpParam: {
|
||||
...this.record.sysEmpParam
|
||||
}
|
||||
...this.record.sysEmpParam,
|
||||
},
|
||||
})
|
||||
|
||||
this.record = {
|
||||
...this.record
|
||||
...this.record,
|
||||
}
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -173,7 +181,7 @@ export default class form extends Component {
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
@@ -206,24 +214,26 @@ export default class form extends Component {
|
||||
const record = {
|
||||
key: extIds.length > 0 ? extIds[extIds.length - 1].key + 1 : 0,
|
||||
orgId: undefined,
|
||||
posId: undefined
|
||||
posId: undefined,
|
||||
}
|
||||
|
||||
this.setState({
|
||||
sysEmpParam: {
|
||||
extIds: [...extIds, record]
|
||||
this.setState(
|
||||
{
|
||||
sysEmpParam: {
|
||||
extIds: [...extIds, record],
|
||||
},
|
||||
},
|
||||
() => {
|
||||
console.log(this.form.current.getFieldsValue())
|
||||
}
|
||||
}, () => {
|
||||
console.log(this.form.current.getFieldsValue())
|
||||
})
|
||||
|
||||
)
|
||||
}
|
||||
onRemoveExtData(record) {
|
||||
const ext = this.state.sysEmpParam.extIds,
|
||||
remove = ext.find((p) => p.key === record.key),
|
||||
index = ext.indexOf(remove);
|
||||
remove = ext.find(p => p.key === record.key),
|
||||
index = ext.indexOf(remove)
|
||||
|
||||
ext.splice(index, 1);
|
||||
ext.splice(index, 1)
|
||||
console.log(ext)
|
||||
|
||||
// this.form.current.setFieldsValue({
|
||||
@@ -232,14 +242,16 @@ export default class form extends Component {
|
||||
// }
|
||||
// })
|
||||
|
||||
this.setState({
|
||||
sysEmpParam: {
|
||||
extIds: ext
|
||||
this.setState(
|
||||
{
|
||||
sysEmpParam: {
|
||||
extIds: ext,
|
||||
},
|
||||
},
|
||||
() => {
|
||||
//console.log(this.form.current.getFieldsValue())
|
||||
}
|
||||
}, () => {
|
||||
//console.log(this.form.current.getFieldsValue())
|
||||
})
|
||||
|
||||
)
|
||||
}
|
||||
//#endregion
|
||||
renderExtInfoTable() {
|
||||
@@ -251,79 +263,100 @@ export default class form extends Component {
|
||||
pagination={false}
|
||||
size="small"
|
||||
bordered
|
||||
rowKey={(record) => record.key}
|
||||
footer={
|
||||
() =>
|
||||
<Button
|
||||
block
|
||||
icon={
|
||||
<AntIcon type="plus" />
|
||||
}
|
||||
type="dashed"
|
||||
onClick={() => this.onAddExtData()}>
|
||||
新增一项</Button>
|
||||
}
|
||||
>
|
||||
</Table>
|
||||
rowKey={record => record.key}
|
||||
footer={() => (
|
||||
<Button
|
||||
block
|
||||
icon={<AntIcon type="plus" />}
|
||||
type="dashed"
|
||||
onClick={() => this.onAddExtData()}
|
||||
>
|
||||
新增一项
|
||||
</Button>
|
||||
)}
|
||||
></Table>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
className="yo-form"
|
||||
>
|
||||
<Spin
|
||||
spinning={this.state.loading}
|
||||
indicator={<AntIcon type="loading" />}>
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<h3 className="h3">基本信息</h3>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item label="账号" name="account" rules={[{ required: true, message: '请输入账号', trigger: 'blur' }]}>
|
||||
<Form.Item
|
||||
label="账号"
|
||||
name="account"
|
||||
rules={[{ required: true, message: '请输入账号', trigger: 'blur' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入账号" />
|
||||
</Form.Item>
|
||||
<Form.Item label="姓名" name="name" rules={[{ required: true, message: '请输入姓名', trigger: 'blur' }]}>
|
||||
<Form.Item
|
||||
label="姓名"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入姓名', trigger: 'blur' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入姓名" />
|
||||
</Form.Item>
|
||||
{this.props.mode == 'add' && <>
|
||||
<Form.Item label="密码" name="password" rules={[{ required: true, message: '请输入密码', trigger: 'blur' }]}>
|
||||
<Input.Password autoComplete="off" placeholder="请输入密码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="确认密码" name="confirm" rules={[{ required: true, message: '请确认密码', trigger: 'blur' }]}>
|
||||
<Input.Password autoComplete="off" placeholder="请确认密码" />
|
||||
</Form.Item>
|
||||
</>
|
||||
}
|
||||
<Form.Item label="昵称" name="nickName" >
|
||||
{this.props.mode == 'add' && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="密码"
|
||||
name="password"
|
||||
rules={[
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
]}
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请输入密码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="确认密码"
|
||||
name="confirm"
|
||||
rules={[
|
||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||
]}
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请确认密码" />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item label="昵称" name="nickName">
|
||||
<Input autoComplete="off" placeholder="请输入昵称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="生日" name="birthday" >
|
||||
<Form.Item label="生日" name="birthday">
|
||||
<DatePicker className="w-100-p" />
|
||||
</Form.Item>
|
||||
<Form.Item label="性别" name="sex" >
|
||||
<Radio.Group >
|
||||
<Form.Item label="性别" name="sex">
|
||||
<Radio.Group>
|
||||
<Radio.Button value={0}>
|
||||
<AntIcon className="mr-xxs" type="stop" />
|
||||
<span>保密</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={1}>
|
||||
<AntIcon style={{ color: '#1890ff' }} className="mr-xxs" type="man" />
|
||||
<AntIcon
|
||||
style={{ color: '#1890ff' }}
|
||||
className="mr-xxs"
|
||||
type="man"
|
||||
/>
|
||||
<span>男</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={2}>
|
||||
<AntIcon style={{ color: '#eb2f96' }} className="mr-xxs" type="woman" />
|
||||
<AntIcon
|
||||
style={{ color: '#eb2f96' }}
|
||||
className="mr-xxs"
|
||||
type="woman"
|
||||
/>
|
||||
<span>女</span>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="邮箱" name="email" >
|
||||
<Form.Item label="邮箱" name="email">
|
||||
<Input autoComplete="off" placeholder="请输入邮箱" />
|
||||
</Form.Item>
|
||||
<Form.Item label="手机号" name="phone" >
|
||||
<Form.Item label="手机号" name="phone">
|
||||
<Input autoComplete="off" placeholder="请输入手机号" />
|
||||
</Form.Item>
|
||||
<Form.Item label="电话" name="tel" >
|
||||
<Form.Item label="电话" name="tel">
|
||||
<Input autoComplete="off" placeholder="请输入电话" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
@@ -332,7 +365,8 @@ export default class form extends Component {
|
||||
<Form.Item
|
||||
label="所属组织机构"
|
||||
name={['sysEmpParam', 'orgId']}
|
||||
rules={[{ required: true, message: '所属组织机构' }]}>
|
||||
rules={[{ required: true, message: '所属组织机构' }]}
|
||||
>
|
||||
<TreeSelect
|
||||
treeData={this.state.options.orgData}
|
||||
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
|
||||
@@ -340,26 +374,18 @@ export default class form extends Component {
|
||||
placeholder="请选择所属组织机构"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="工号"
|
||||
name={['sysEmpParam', 'jobNum']} >
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入工号" />
|
||||
<Form.Item label="工号" name={['sysEmpParam', 'jobNum']}>
|
||||
<Input autoComplete="off" placeholder="请输入工号" />
|
||||
</Form.Item>
|
||||
<Form.Item label="职位信息" name={['sysEmpParam', 'posIdList']}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="请选择职位信息">
|
||||
{
|
||||
this.state.options.posData.map(item => {
|
||||
return <Select.Option
|
||||
key={item.id}
|
||||
value={item.id}
|
||||
>
|
||||
{item.name}</Select.Option>
|
||||
})
|
||||
}
|
||||
<Select mode="multiple" placeholder="请选择职位信息">
|
||||
{this.state.options.posData.map(item => {
|
||||
return (
|
||||
<Select.Option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
@@ -155,12 +155,12 @@ export default class index extends Component {
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} record
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(modal, record) {
|
||||
onOpen(modal, id) {
|
||||
modal.current.open({
|
||||
orgId: this.selectId,
|
||||
record,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -191,24 +191,25 @@ export default class index extends Component {
|
||||
|
||||
//#region 自定义方法
|
||||
renderItem(record) {
|
||||
const { id, account, name, nickName, avatar, sex, phone, email, status } = record
|
||||
return (
|
||||
<List.Item
|
||||
key={record.id}
|
||||
key={id}
|
||||
actions={[
|
||||
<Auth auth="sysUser:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, record)}>编辑</a>
|
||||
<a onClick={() => this.onOpen(this.editForm, id)}>编辑</a>
|
||||
</Auth>,
|
||||
<Auth auth="sysOrg:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
onConfirm={() => this.onDelete(id)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>,
|
||||
<Auth aut="sysUser:resetPwd">
|
||||
<a onClick={() => this.onResetPassword(record)}>重置密码</a>
|
||||
<a onClick={() => this.onResetPassword(id)}>重置密码</a>
|
||||
</Auth>,
|
||||
<Auth auth={{ sysUser: [['grantRole'], ['grantData']] }}>
|
||||
<Dropdown
|
||||
@@ -217,14 +218,14 @@ export default class index extends Component {
|
||||
<Menu>
|
||||
{auth('sysUser:grantRole') && (
|
||||
<Menu.Item key="1">
|
||||
<a onClick={() => this.onOpen(this.roleForm, record)}>
|
||||
<a onClick={() => this.onOpen(this.roleForm, id)}>
|
||||
授权角色
|
||||
</a>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{auth('sysUser:grantData') && (
|
||||
<Menu.Item key="2">
|
||||
<a onClick={() => this.onOpen(this.dataForm, record)}>
|
||||
<a onClick={() => this.onOpen(this.dataForm, id)}>
|
||||
授权额外数据
|
||||
</a>
|
||||
</Menu.Item>
|
||||
@@ -246,29 +247,28 @@ export default class index extends Component {
|
||||
type="avatar"
|
||||
shape="square"
|
||||
size={48}
|
||||
id={record.avatar}
|
||||
id={avatar}
|
||||
icon={<AntIcon type="user" />}
|
||||
/>
|
||||
}
|
||||
title={record.nickName || record.name}
|
||||
description={record.account}
|
||||
title={nickName || name}
|
||||
description={account}
|
||||
/>
|
||||
<Descriptions className="flex-1" column={2}>
|
||||
<Descriptions.Item label="性别">
|
||||
{this.bindCodeValue(record.sex, 'sex')}
|
||||
{this.bindCodeValue(sex, 'sex')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="手机">{record.phone || '未设置'}</Descriptions.Item>
|
||||
<Descriptions.Item label="邮箱">{record.email || '未设置'}</Descriptions.Item>
|
||||
<Descriptions.Item label="手机">{phone || '未设置'}</Descriptions.Item>
|
||||
<Descriptions.Item label="邮箱">{email || '未设置'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div className="yo-list-content--h">
|
||||
<Auth auth="sysUser:changeStatus">
|
||||
<div className="yo-list-content--h--item text-center">
|
||||
<Switch
|
||||
checked={!record.status}
|
||||
loading={record.statusChanging}
|
||||
checked={!status}
|
||||
checkedChildren={this.bindCodeValue(0, 'common_status')}
|
||||
unCheckedChildren={this.bindCodeValue(1, 'common_status')}
|
||||
onChange={checked => this.onSetUserStatus(record, checked)}
|
||||
onChange={checked => this.onSetUserStatus(id, checked)}
|
||||
/>
|
||||
</div>
|
||||
</Auth>
|
||||
@@ -277,12 +277,12 @@ export default class index extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
onSetUserStatus(record, checked) {
|
||||
this.onAction(apiAction.changeStatus({ id: record.id, status: +!checked }), '设置成功')
|
||||
onSetUserStatus(id, checked) {
|
||||
this.onAction(apiAction.changeStatus({ id, status: +!checked }), '设置成功')
|
||||
}
|
||||
|
||||
onResetPassword(record) {
|
||||
this.onAction(apiAction.resetPwd(record), '重置成功')
|
||||
onResetPassword(id) {
|
||||
this.onAction(apiAction.resetPwd({ id }), '重置成功')
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export default class role extends Component {
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
id = ''
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
@@ -28,10 +28,10 @@ export default class role extends Component {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
this.id = params.id
|
||||
//#region 从后端转换成前段所需格式
|
||||
const roleData = await this.loadRoleData()
|
||||
const roles = await this.loadRole(this.record.id)
|
||||
const roles = await this.loadRole(this.id)
|
||||
this.setState({
|
||||
options: {
|
||||
roleData,
|
||||
@@ -39,7 +39,7 @@ export default class role extends Component {
|
||||
roles,
|
||||
})
|
||||
this.form.current.setFieldsValue({
|
||||
id: this.record.id,
|
||||
id: this.id,
|
||||
grantRoleIdList: roles,
|
||||
})
|
||||
|
||||
@@ -59,8 +59,8 @@ export default class role extends Component {
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
if (this.id) {
|
||||
postData.id = this.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
|
||||
Reference in New Issue
Block a user