代码生成模板映射枚举字段.
This commit is contained in:
@@ -49,11 +49,13 @@ public class CodeGenerator {
|
||||
.cache("user:preference:{}:{}", "用户偏好 ${type} ${userId}")
|
||||
.formatKeys("type", "userId")
|
||||
.vue("user", "preference")
|
||||
.enableDrawerForm()
|
||||
.enableRowSelection()
|
||||
.enums("type")
|
||||
.names("APP", "HOST")
|
||||
.values("label", "应用", "主机")
|
||||
.values("value", 1)
|
||||
.color(null, "green")
|
||||
.values("value", 1, 2)
|
||||
.color("blue", "green")
|
||||
.build(),
|
||||
};
|
||||
// jdbc 配置 - 使用配置文件
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.orion.ops.launch.generator.engine;
|
||||
|
||||
import com.orion.lang.define.collect.MultiLinkedHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* vue 枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/26 16:50
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EnumMeta {
|
||||
|
||||
/**
|
||||
* 类名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* 配置
|
||||
*/
|
||||
private MultiLinkedHashMap<String, String, Object> info;
|
||||
|
||||
}
|
||||
@@ -31,8 +31,8 @@ import com.orion.lang.utils.reflect.BeanMap;
|
||||
import com.orion.lang.utils.reflect.Fields;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.constant.OrionOpsProConst;
|
||||
import com.orion.ops.launch.generator.template.EnumMeta;
|
||||
import com.orion.ops.launch.generator.template.Table;
|
||||
import com.orion.ops.launch.generator.template.VueEnum;
|
||||
import org.apache.velocity.Template;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.Velocity;
|
||||
@@ -325,7 +325,7 @@ public class VelocityTemplateEngine extends AbstractTemplateEngine {
|
||||
// 功能名称常量
|
||||
beanMap.put("featureConst", VariableStyles.SPINE.toSerpentine(table.getFeature()).toUpperCase());
|
||||
// 枚举
|
||||
this.setEnumMap(beanMap, tableInfo, table);
|
||||
beanMap.put("enums", this.getEnumMap(beanMap, tableInfo, table));
|
||||
objectMap.put("vue", beanMap);
|
||||
|
||||
// 生成文件
|
||||
@@ -391,34 +391,31 @@ public class VelocityTemplateEngine extends AbstractTemplateEngine {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置枚举
|
||||
* 获取枚举
|
||||
*
|
||||
* @param beanMap beanMap
|
||||
* @param tableInfo tableInfo
|
||||
* @param table table
|
||||
* @return enumMap
|
||||
*/
|
||||
private void setEnumMap(BeanMap beanMap, TableInfo tableInfo, Table table) {
|
||||
// 枚举注解
|
||||
Map<String, String> enumComment = new HashMap<>();
|
||||
private Map<String, EnumMeta> getEnumMap(BeanMap beanMap, TableInfo tableInfo, Table table) {
|
||||
// 枚举值
|
||||
Map<String, MultiLinkedHashMap<String, String, Object>> enumMap = new LinkedHashMap<>();
|
||||
for (EnumMeta meta : table.getEnums()) {
|
||||
meta = meta.clone();
|
||||
Map<String, EnumMeta> enumMap = new LinkedHashMap<>();
|
||||
for (VueEnum meta : table.getEnums()) {
|
||||
// 检查字段是否存在
|
||||
String originVariable = meta.getVariable();
|
||||
String variable = meta.getVariable();
|
||||
TableField tableField = tableInfo.getFields()
|
||||
.stream()
|
||||
.filter(s -> originVariable.equals(s.getName()) || originVariable.equals(s.getPropertyName()))
|
||||
.filter(s -> variable.equals(s.getName()) || variable.equals(s.getPropertyName()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("未查询到枚举映射字段 " + originVariable));
|
||||
// 转为大驼峰
|
||||
String enumName = Strings.firstUpper(tableField.getPropertyName()) + "Enum";
|
||||
.orElseThrow(() -> new RuntimeException("未查询到枚举映射字段 " + variable));
|
||||
// 设置枚举名称
|
||||
if (meta.getClassName() == null) {
|
||||
meta.setClassName(Strings.firstUpper(tableField.getPropertyName()) + "Enum");
|
||||
}
|
||||
// 设置枚举注释
|
||||
if (meta.getComment() == null) {
|
||||
enumComment.put(enumName, Strings.def(tableField.getComment(), enumName));
|
||||
} else {
|
||||
enumComment.put(enumName, meta.getComment());
|
||||
|
||||
meta.setComment(Strings.def(tableField.getComment(), meta.getClassName()));
|
||||
}
|
||||
// 设置枚举
|
||||
MultiLinkedHashMap<String, String, Object> enumInfo = new MultiLinkedHashMap<>();
|
||||
@@ -432,11 +429,9 @@ public class VelocityTemplateEngine extends AbstractTemplateEngine {
|
||||
enumInfo.put(name, field, value);
|
||||
}
|
||||
}
|
||||
enumMap.put(enumName, enumInfo);
|
||||
enumMap.put(tableField.getPropertyName(), new EnumMeta(meta.getClassName(), meta.getComment(), enumInfo));
|
||||
}
|
||||
// 设置到上下文
|
||||
beanMap.put("enums", enumMap);
|
||||
beanMap.put("enumComment", enumComment);
|
||||
return enumMap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,18 +16,22 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class EnumsTemplate extends VueTemplate {
|
||||
|
||||
private final EnumMeta meta;
|
||||
private final VueEnum vueEnum;
|
||||
|
||||
public EnumsTemplate(Table table, String variable) {
|
||||
this(table, variable, (String) null);
|
||||
}
|
||||
|
||||
public EnumsTemplate(Table table, String variable, String className) {
|
||||
super(table);
|
||||
this.meta = new EnumMeta(variable);
|
||||
table.enums.add(meta);
|
||||
this.vueEnum = new VueEnum(variable, className);
|
||||
table.enums.add(vueEnum);
|
||||
}
|
||||
|
||||
public EnumsTemplate(Table table, String variable, Class<? extends Enum<?>> enumClass) {
|
||||
super(table);
|
||||
this.meta = new EnumMeta(variable);
|
||||
table.enums.add(meta);
|
||||
this.vueEnum = new VueEnum(variable);
|
||||
table.enums.add(vueEnum);
|
||||
this.parseEnumMeta(enumClass);
|
||||
}
|
||||
|
||||
@@ -51,9 +55,21 @@ public class EnumsTemplate extends VueTemplate {
|
||||
.map(enumItem -> Fields.getFieldValue(enumItem, field))
|
||||
.collect(Collectors.toList()))
|
||||
.collect(Collectors.toList());
|
||||
meta.names.addAll(names);
|
||||
meta.fields.addAll(fields);
|
||||
meta.values.addAll(values);
|
||||
vueEnum.className = enumClass.getSimpleName();
|
||||
vueEnum.names.addAll(names);
|
||||
vueEnum.fields.addAll(fields);
|
||||
vueEnum.values.addAll(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置类名
|
||||
*
|
||||
* @param className className
|
||||
* @return this
|
||||
*/
|
||||
public EnumsTemplate className(String className) {
|
||||
vueEnum.className = className;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +79,7 @@ public class EnumsTemplate extends VueTemplate {
|
||||
* @return this
|
||||
*/
|
||||
public EnumsTemplate comment(String comment) {
|
||||
meta.comment = comment;
|
||||
vueEnum.comment = comment;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -74,7 +90,7 @@ public class EnumsTemplate extends VueTemplate {
|
||||
* @return this
|
||||
*/
|
||||
public EnumsTemplate names(String... names) {
|
||||
meta.names.addAll(Lists.of(names));
|
||||
vueEnum.names.addAll(Lists.of(names));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -85,8 +101,8 @@ public class EnumsTemplate extends VueTemplate {
|
||||
* @return this
|
||||
*/
|
||||
public EnumsTemplate values(String field, Object... values) {
|
||||
meta.fields.add(field);
|
||||
meta.values.add(Lists.of(values));
|
||||
vueEnum.fields.add(field);
|
||||
vueEnum.values.add(Lists.of(values));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public class Table {
|
||||
/**
|
||||
* 生成的枚举文件
|
||||
*/
|
||||
protected List<EnumMeta> enums;
|
||||
protected List<VueEnum> enums;
|
||||
|
||||
protected Table() {
|
||||
this.cacheFormatKeys = new ArrayList<>();
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
* @since 2023/9/26 13:53
|
||||
*/
|
||||
@Data
|
||||
public class EnumMeta {
|
||||
public class VueEnum {
|
||||
|
||||
/**
|
||||
* 替换的枚举字段 数据库/小驼峰
|
||||
@@ -21,7 +21,12 @@ public class EnumMeta {
|
||||
protected String variable;
|
||||
|
||||
/**
|
||||
* 枚举注释 没有的话使用 variable.comment || variable
|
||||
* 枚举类名 如果为空使用 field.propertyName + Enum
|
||||
*/
|
||||
protected String className;
|
||||
|
||||
/**
|
||||
* 枚举注释 如果为空使用 field.comment || className
|
||||
*/
|
||||
protected String comment;
|
||||
|
||||
@@ -40,24 +45,25 @@ public class EnumMeta {
|
||||
*/
|
||||
protected List<List<Object>> values;
|
||||
|
||||
public EnumMeta(String variable) {
|
||||
public VueEnum(String variable) {
|
||||
this(variable, null);
|
||||
}
|
||||
|
||||
public VueEnum(String variable, String className) {
|
||||
this.className = className;
|
||||
this.variable = variable;
|
||||
this.names = new ArrayList<>();
|
||||
this.fields = new ArrayList<>();
|
||||
this.values = new ArrayList<>();
|
||||
}
|
||||
|
||||
public EnumMeta(String variable, String comment, List<String> names, List<String> fields, List<List<Object>> values) {
|
||||
public VueEnum(String variable, String className, String comment, List<String> names, List<String> fields, List<List<Object>> values) {
|
||||
this.variable = variable;
|
||||
this.className = className;
|
||||
this.comment = comment;
|
||||
this.names = names;
|
||||
this.fields = fields;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumMeta clone() {
|
||||
return new EnumMeta(variable, comment, names, fields, values);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,6 +73,17 @@ public class VueTemplate extends Template {
|
||||
return new EnumsTemplate(table, variable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置枚举
|
||||
*
|
||||
* @param variable 枚举字段 数据库/小驼峰
|
||||
* @param className className
|
||||
* @return enums
|
||||
*/
|
||||
public EnumsTemplate enums(String variable, String className) {
|
||||
return new EnumsTemplate(table, variable, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置枚举
|
||||
*
|
||||
|
||||
@@ -20,18 +20,24 @@
|
||||
#if("$field.propertyName" != "id")
|
||||
<!-- $field.comment -->
|
||||
<a-form-item field="${field.propertyName}" label="${field.comment}">
|
||||
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
|
||||
#if(${vue.enums.containsKey(${field.propertyName})})
|
||||
<a-select v-model="formModel.${field.propertyName}"
|
||||
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
|
||||
placeholder="请选择${field.comment}"/>
|
||||
#else
|
||||
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
|
||||
<a-input-number v-model="formModel.${field.propertyName}"
|
||||
placeholder="请输入${field.comment}"
|
||||
hide-button />
|
||||
#elseif("$field.propertyType" == "Date")
|
||||
#elseif("$field.propertyType" == "Date")
|
||||
<a-date-picker v-model="formModel.${field.propertyName}"
|
||||
style="width: 100%"
|
||||
placeholder="请选择${field.comment}"
|
||||
show-time />
|
||||
#else
|
||||
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" />
|
||||
#end
|
||||
#else
|
||||
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear/>
|
||||
#end
|
||||
#end
|
||||
</a-form-item>
|
||||
#end
|
||||
#end
|
||||
|
||||
@@ -24,18 +24,24 @@
|
||||
#if("$field.propertyName" != "id")
|
||||
<!-- $field.comment -->
|
||||
<a-form-item field="${field.propertyName}" label="${field.comment}">
|
||||
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
|
||||
#if(${vue.enums.containsKey(${field.propertyName})})
|
||||
<a-select v-model="formModel.${field.propertyName}"
|
||||
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
|
||||
placeholder="请选择${field.comment}" />
|
||||
#else
|
||||
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
|
||||
<a-input-number v-model="formModel.${field.propertyName}"
|
||||
placeholder="请输入${field.comment}"
|
||||
hide-button />
|
||||
#elseif("$field.propertyType" == "Date")
|
||||
#elseif("$field.propertyType" == "Date")
|
||||
<a-date-picker v-model="formModel.${field.propertyName}"
|
||||
style="width: 100%"
|
||||
placeholder="请选择${field.comment}"
|
||||
show-time />
|
||||
#else
|
||||
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" />
|
||||
#end
|
||||
#else
|
||||
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear />
|
||||
#end
|
||||
#end
|
||||
</a-form-item>
|
||||
#end
|
||||
#end
|
||||
|
||||
@@ -8,20 +8,27 @@
|
||||
#foreach($field in ${table.fields})
|
||||
<!-- $field.comment -->
|
||||
<a-form-item field="${field.propertyName}" label="${field.comment}" label-col-flex="50px">
|
||||
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
|
||||
#if(${vue.enums.containsKey(${field.propertyName})})
|
||||
<a-select v-model="formModel.${field.propertyName}"
|
||||
:options="toOptions(${vue.enums.get(${field.propertyName}).className})"
|
||||
placeholder="请选择${field.comment}"
|
||||
allow-clear />
|
||||
#else
|
||||
#if("$field.propertyType" == "Integer" || "$field.propertyType" == "Long")
|
||||
<a-input-number v-model="formModel.${field.propertyName}"
|
||||
placeholder="请输入${field.comment}"
|
||||
allow-clear
|
||||
hide-button />
|
||||
#elseif("$field.propertyType" == "Date")
|
||||
#elseif("$field.propertyType" == "Date")
|
||||
<a-date-picker v-model="formModel.${field.propertyName}"
|
||||
style="width: 100%"
|
||||
placeholder="请选择${field.comment}"
|
||||
show-time
|
||||
allow-clear />
|
||||
#else
|
||||
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear/>
|
||||
#end
|
||||
#else
|
||||
<a-input v-model="formModel.${field.propertyName}" placeholder="请输入${field.comment}" allow-clear />
|
||||
#end
|
||||
#end
|
||||
</a-form-item>
|
||||
#end
|
||||
</a-query-header>
|
||||
@@ -45,6 +52,7 @@
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
#if($vue.enableRowSelection)
|
||||
<!-- 删除 -->
|
||||
<a-popconfirm position="br"
|
||||
type="warning"
|
||||
@@ -60,6 +68,7 @@
|
||||
</template>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
#end
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
@@ -70,13 +79,23 @@
|
||||
label-align="left"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
#if($vue.enableRowSelection)
|
||||
:selected-keys="selectedKeys"
|
||||
:row-selection="rowSelection"
|
||||
#end
|
||||
:data="tableRenderData"
|
||||
:pagination="pagination"
|
||||
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||
@page-size-change="(size) => fetchTableData(pagination.current, size)"
|
||||
:bordered="false">
|
||||
#foreach($field in ${table.fields})
|
||||
#if(${vue.enums.containsKey(${field.propertyName})})
|
||||
<!-- $field.comment -->
|
||||
<template #${field.propertyName}="{ record }">
|
||||
{{ getEnumValue(record.${field.propertyName}, ${vue.enums.get(${field.propertyName}).className}) }}
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
<!-- 操作 -->
|
||||
<template #handle="{ record }">
|
||||
<div class="table-handle-wrapper">
|
||||
@@ -117,18 +136,28 @@
|
||||
import { Message, PaginationProps } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import columns from '../types/table.columns';
|
||||
#if($vue.enableRowSelection)
|
||||
import { defaultPagination, defaultRowSelection } from '@/types/table';
|
||||
import {} from '../types/enum.types';
|
||||
#else
|
||||
import { defaultPagination } from '@/types/table';
|
||||
#end
|
||||
import {} from '../types/const';
|
||||
import { toOptions } from '@/utils/enum';
|
||||
#if($vue.enums.isEmpty())
|
||||
import {} from '../types/enum.types';
|
||||
#else
|
||||
import { #foreach($entry in ${vue.enums.entrySet()})${entry.value.className}#if($foreach.hasNext), #end#end } from '../types/enum.types';
|
||||
#end
|
||||
import { toOptions, getEnumValue } from '@/utils/enum';
|
||||
|
||||
const tableRenderData = ref<${vue.featureEntity}QueryResponse[]>([]);
|
||||
const { loading, setLoading } = useLoading();
|
||||
const emits = defineEmits(['openAdd', 'openUpdate']);
|
||||
|
||||
const pagination = reactive(defaultPagination()) as PaginationProps;
|
||||
#if($vue.enableRowSelection)
|
||||
const selectedKeys = ref<number[]>([]);
|
||||
const rowSelection = reactive(defaultRowSelection());
|
||||
#end
|
||||
|
||||
const formModel = ref<${vue.featureEntity}QueryRequest>({
|
||||
#foreach($field in ${table.fields})
|
||||
@@ -136,6 +165,7 @@
|
||||
#end
|
||||
});
|
||||
|
||||
#if($vue.enableRowSelection)
|
||||
// 删除选中行
|
||||
const deleteSelectRows = async () => {
|
||||
try {
|
||||
@@ -152,6 +182,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
#end
|
||||
// 删除当前行
|
||||
const deleteRow = async ({ id }: {
|
||||
id: number
|
||||
@@ -192,6 +223,9 @@
|
||||
pagination.total = data.total;
|
||||
pagination.current = request.page;
|
||||
pagination.pageSize = request.limit;
|
||||
#if($vue.enableRowSelection)
|
||||
selectedKeys.value = [];
|
||||
#end
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
<template>
|
||||
<div class="layout-container">
|
||||
#if($vue.drawerForm)
|
||||
<!-- 表格 -->
|
||||
<${vue.feature}-table ref="table"
|
||||
@openAdd="() => drawer.openAdd()"
|
||||
@openAdd="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
|
||||
@openUpdate="(e) => drawer.openUpdate(e)" />
|
||||
#if($vue.enableDrawerForm)
|
||||
<!-- 添加修改模态框 -->
|
||||
<${vue.feature}-form-drawer ref="drawer"
|
||||
@added="() => table.addedCallback()"
|
||||
@updated="() => table.updatedCallback()" />
|
||||
#else
|
||||
<!-- 表格 -->
|
||||
<${vue.feature}-table ref="table"
|
||||
@openAdd="() => modal.openAdd()"
|
||||
@openUpdate="(e) => modal.openUpdate(e)" />
|
||||
<!-- 添加修改模态框 -->
|
||||
<${vue.feature}-form-modal ref="modal"
|
||||
@added="() => table.addedCallback()"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#foreach($enumEntity in ${vue.enums.entrySet()})
|
||||
/**
|
||||
* $!{vue.enumComment.get($enumEntity.key)}
|
||||
* $!{enumEntity.value.comment}
|
||||
*/
|
||||
export const $enumEntity.key = {
|
||||
#foreach($enumEntityItem in $enumEntity.value.entrySet())
|
||||
export const $enumEntity.value.className = {
|
||||
#foreach($enumEntityItem in $enumEntity.value.info.entrySet())
|
||||
$enumEntityItem.key: {
|
||||
#foreach($enumEntityItemFields in $enumEntityItem.value.entrySet())
|
||||
$enumEntityItemFields.key: '$!enumEntityItemFields.value',
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
<!-- 菜单状态 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item field="status" label="菜单状态" label-col-flex="60px">
|
||||
<a-select
|
||||
v-model="formModel.status"
|
||||
:options="toOptions(MenuStatusEnum)"
|
||||
placeholder="请选择菜单状态"
|
||||
allow-clear
|
||||
/>
|
||||
<a-select v-model="formModel.status"
|
||||
:options="toOptions(MenuStatusEnum)"
|
||||
placeholder="请选择菜单状态"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
@@ -22,12 +22,10 @@
|
||||
</a-form-item>
|
||||
<!-- 用户状态 -->
|
||||
<a-form-item field="status" label="用户状态" label-col-flex="50px">
|
||||
<a-select
|
||||
v-model="formModel.status"
|
||||
:options="toOptions(UserStatusEnum)"
|
||||
placeholder="请选择用户状态"
|
||||
allow-clear
|
||||
/>
|
||||
<a-select v-model="formModel.status"
|
||||
:options="toOptions(UserStatusEnum)"
|
||||
placeholder="请选择用户状态"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
<!-- 手机号 -->
|
||||
<a-form-item field="mobile" label="手机号" label-col-flex="50px">
|
||||
|
||||
Reference in New Issue
Block a user