完成个人中心红包模块功能
This commit is contained in:
parent
f70843f3dd
commit
62cc81168f
@ -17,4 +17,8 @@ export const apiArr = {
|
||||
couponList: '/api/v1/wechat/discuss/list', // 获取优惠券列表
|
||||
getUserCommunity:"/api/v1/wechat/community/owner/mylist",//获取用户社区信息
|
||||
createQrcode: '/api/v1/wechat/water/device/qr-code', //获取小程序码
|
||||
|
||||
redPackageCenter: '/api/v2/wechat/red-package/center', //红包中心分页
|
||||
redPackageClaim: '/api/v2/wechat/red-package/claim', //领取红包
|
||||
redPackageMyred: '/api/v2/wechat/red-package/my-red', //我的红包分页
|
||||
};
|
||||
@ -10,14 +10,17 @@
|
||||
<!-- 红包列表 -->
|
||||
<view class="content">
|
||||
<view v-for="(item, index) in redPacketList" :key="index">
|
||||
<view class="item">
|
||||
<view class="item_left">¥{{ item.yuan }}</view>
|
||||
<view class="item" v-if="item.red_package_config">
|
||||
<view class="item_left">¥{{ item.red_package_config.money }}</view>
|
||||
<view class="item_center">
|
||||
<view class="title">{{ item.name }}</view>
|
||||
<view class="time">{{ item.time }}</view>
|
||||
<view class="desc">{{ item.shiyong }}</view>
|
||||
<view class="title">{{ item.red_package_config.red_package_name }}</view>
|
||||
<view class="time">{{ item.red_package_config.start_time }} -
|
||||
{{ item.red_package_config.end_time }}</view>
|
||||
<view class="desc">{{ item.red_package_config.belong_role === 1 ? '限指定店铺使用' :
|
||||
(item.red_package_config.belong_role === 2 ? '限指定地区使用' :
|
||||
'全平台使用') }}</view>
|
||||
</view>
|
||||
<view class="status2" :data-status="item.status"></view>
|
||||
<view class="status2" :data-status="currentTab == 1 ? '已过期' : '已使用'"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -25,49 +28,37 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { menuButtonInfo, NavgateTo, picUrl, request } from '../../../utils/index';
|
||||
import { apiArr } from '../../../api/user';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentTab: 0, // 当前选中的标签索引
|
||||
changeIndex: 1,
|
||||
tabList: ['已使用', '已过期'],
|
||||
redPacketList: [
|
||||
{
|
||||
name: '新用户注册红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '全平台使用',
|
||||
status: '去使用',
|
||||
yuan: 5.32
|
||||
},
|
||||
{
|
||||
name: '满减红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '全平台使用',
|
||||
status: '去使用',
|
||||
yuan: 5,
|
||||
manjian: '满0元可用'
|
||||
},
|
||||
{
|
||||
name: '固定红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '限改店铺指定商品使用',
|
||||
status: '去使用',
|
||||
yuan: 5
|
||||
},
|
||||
{
|
||||
name: '固定红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '限广州地区使用',
|
||||
status: '去使用',
|
||||
yuan: 5
|
||||
}
|
||||
]
|
||||
redPacketList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getRedPacketHistory(changeIndex) {
|
||||
console.log("🚀 ~ changeIndex:", changeIndex)
|
||||
const params = {
|
||||
user_id: uni.getStorageSync('userId'),
|
||||
history: changeIndex,
|
||||
}
|
||||
const res = await request(apiArr.redPackageMyred, 'POST', params);
|
||||
this.redPacketList = res.rows;
|
||||
},
|
||||
switchTab(index) {
|
||||
this.currentTab = index;
|
||||
this.changeIndex = index === 0 ? 1 : 3;
|
||||
this.getRedPacketHistory(this.changeIndex);
|
||||
console.log('选中的标签:', this.tabList[index]);
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.getRedPacketHistory(1);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
page {
|
||||
background-color: #f6f7fb;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
@ -60,11 +61,25 @@ page {
|
||||
/* 红包列表 */
|
||||
.content {
|
||||
padding: 0 20rpx;
|
||||
height: 75vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ad_name{
|
||||
margin: 15rpx 0 30rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ad_name image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 20rpx;
|
||||
height: 130rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 170rpx;
|
||||
margin-bottom: 30rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
|
||||
@ -4,41 +4,108 @@
|
||||
<view class="stats">
|
||||
<view class="stat-item">
|
||||
<view class="stat-label">红包数(个)</view>
|
||||
<view class="stat-value">1000,000</view>
|
||||
<view class="stat-value">{{ redPacketNum }}</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-label">红包金额(元)</view>
|
||||
<view class="stat-value">1000,000</view>
|
||||
<view class="stat-value">{{ redPacketMoney }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 分类标签 -->
|
||||
<view class="tabs">
|
||||
<view
|
||||
v-for="(tab, index) in tabList"
|
||||
:key="index"
|
||||
:class="['tab', { active: currentTab === index }]"
|
||||
@click="switchTab(index)"
|
||||
>
|
||||
<view v-for="(tab, index) in tabList" :key="index" :class="['tab', { active: currentTab === index }]"
|
||||
@click="switchTab(index)">
|
||||
{{ tab }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 红包列表 -->
|
||||
<view class="content">
|
||||
<view v-for="(item, index) in redPacketList" :key="index">
|
||||
<view class="item">
|
||||
<view class="item_left">
|
||||
<view class="price">¥{{ item.yuan }}</view>
|
||||
<view v-if="item.manjian" class="manjian">{{ item.manjian }}</view>
|
||||
<!-- 地区分组显示 -->
|
||||
<view v-if="currentTab === 2">
|
||||
<view v-for="(group, groupIndex) in redPacketList" :key="groupIndex">
|
||||
<view class="ad_name">
|
||||
<image src="https://static.hshuishang.com/Index_add.png" mode="widthFix" />
|
||||
限{{ group.ad_name }}地区使用
|
||||
</view>
|
||||
<view class="item_center">
|
||||
<view class="title">{{ item.name }}</view>
|
||||
<view class="time">{{ item.time }}</view>
|
||||
<view class="desc">{{ item.shiyong }}</view>
|
||||
<view v-for="(item, itemIndex) in group.packets" :key="itemIndex">
|
||||
<view v-if="item.red_package_config">
|
||||
<view class="item">
|
||||
<view class="item_left">
|
||||
<view class="price">¥{{ item.red_package_config.money }}</view>
|
||||
<view v-if="item.red_package_config.manjian" class="manjian">
|
||||
{{ item.red_package_config.manjian }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item_center">
|
||||
<view class="title">{{ item.red_package_config.red_package_name }}</view>
|
||||
<view class="time">{{ item.red_package_config.end_time }}到期</view>
|
||||
<view class="desc">{{ item.red_package_config.belong_role === 1 ? '限指定店铺使用' :
|
||||
(item.red_package_config.belong_role === 2 ? '限指定地区使用' :
|
||||
'全平台使用') }}</view>
|
||||
</view>
|
||||
<view class="item_right" v-if="item.status === 2">
|
||||
<view class="status" @click="useRedPacket(item)">去使用</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item_right">
|
||||
<view class="status" @click="useRedPacket(item)">{{ item.status }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 店铺分组显示 -->
|
||||
<view v-else-if="currentTab === 3">
|
||||
<view v-for="(group, groupIndex) in redPacketList" :key="groupIndex">
|
||||
<view class="ad_name">
|
||||
<image src="https://static.hshuishang.com/Index_add.png" mode="widthFix" />
|
||||
{{ group.merchant_name }}
|
||||
</view>
|
||||
<view v-for="(item, itemIndex) in group.packets" :key="itemIndex">
|
||||
<view v-if="item.red_package_config">
|
||||
<view class="item">
|
||||
<view class="item_left">
|
||||
<view class="price">¥{{ item.red_package_config.money }}</view>
|
||||
<view v-if="item.red_package_config.manjian" class="manjian">
|
||||
{{ item.red_package_config.manjian }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item_center">
|
||||
<view class="title">{{ item.red_package_config.red_package_name }}</view>
|
||||
<view class="time">{{ item.red_package_config.end_time }}到期</view>
|
||||
<view class="desc">{{ item.red_package_config.belong_role === 1 ? '限指定店铺使用' :
|
||||
(item.red_package_config.belong_role === 2 ? '限指定地区使用' :
|
||||
'全平台使用') }}</view>
|
||||
</view>
|
||||
<view class="item_right" v-if="item.status === 2">
|
||||
<view class="status" @click="useRedPacket(item)">去使用</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 普通显示 -->
|
||||
<view v-else>
|
||||
<view v-for="(item, index) in redPacketList" :key="index">
|
||||
<view v-if="item.red_package_config">
|
||||
<view class="item">
|
||||
<view class="item_left">
|
||||
<view class="price">¥{{ item.red_package_config.money }}</view>
|
||||
<view v-if="item.red_package_config.manjian" class="manjian">
|
||||
{{ item.red_package_config.manjian }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item_center">
|
||||
<view class="title">{{ item.red_package_config.red_package_name }}</view>
|
||||
<view class="time">{{ item.red_package_config.end_time }}到期</view>
|
||||
<view class="desc">{{ item.red_package_config.belong_role === 1 ? '限指定店铺使用' :
|
||||
(item.red_package_config.belong_role === 2 ? '限指定地区使用' :
|
||||
'全平台使用') }}</view>
|
||||
</view>
|
||||
<view class="item_right" v-if="item.status === 2">
|
||||
<view class="status" @click="useRedPacket(item)">去使用</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -48,55 +115,108 @@
|
||||
|
||||
<script>
|
||||
import { menuButtonInfo, NavgateTo, picUrl, request } from '../../../utils/index';
|
||||
import { apiArr } from '../../../api/user';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentTab: 0, // 当前选中的标签索引
|
||||
tabList: ['全部', '平台', '地区', '店铺', '历史记录'], // 标签列表
|
||||
redPacketList: [
|
||||
{
|
||||
name: '新用户注册红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '全平台使用',
|
||||
status: '去使用',
|
||||
yuan: 5.32
|
||||
},
|
||||
{
|
||||
name: '满减红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '全平台使用',
|
||||
status: '去使用',
|
||||
yuan: 5,
|
||||
manjian: '满0元可用'
|
||||
},
|
||||
{
|
||||
name: '固定红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '限改店铺指定商品使用',
|
||||
status: '去使用',
|
||||
yuan: 5
|
||||
},
|
||||
{
|
||||
name: '固定红包',
|
||||
time: '2085.07.77 00:27到期',
|
||||
shiyong: '限广州地区使用',
|
||||
status: '去使用',
|
||||
yuan: 5
|
||||
}
|
||||
]
|
||||
redPacketList: [],
|
||||
changeIndex: 0,
|
||||
redPacketNum: 0,
|
||||
redPacketMoney: 0.00,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getMyRedPacket(changeIndex) {
|
||||
const params = {
|
||||
user_id: uni.getStorageSync('userId'),
|
||||
belong_role: changeIndex == 0 ? '' : changeIndex,
|
||||
}
|
||||
const res = await request(apiArr.redPackageMyred, 'POST', params);
|
||||
let processedList = res.rows.map(item => {
|
||||
const newItem = { ...item };
|
||||
|
||||
if (newItem?.agent_info?.ad?.ad_name) {
|
||||
newItem.agent_info.ad.ad_name = newItem.agent_info.ad.ad_name.split(',').join('');
|
||||
}
|
||||
|
||||
return newItem;
|
||||
});
|
||||
|
||||
if (this.currentTab == 2) {
|
||||
// 地区标签:按 ad_name 分组
|
||||
const grouped = {};
|
||||
processedList.forEach(item => {
|
||||
if (item?.agent_info?.ad?.ad_name) {
|
||||
const adName = item.agent_info.ad.ad_name;
|
||||
if (!grouped[adName]) {
|
||||
grouped[adName] = {
|
||||
ad_name: adName,
|
||||
packets: []
|
||||
};
|
||||
}
|
||||
grouped[adName].packets.push(item);
|
||||
}
|
||||
});
|
||||
this.redPacketList = Object.values(grouped);
|
||||
} else if (this.currentTab == 3) {
|
||||
// 店铺标签:按 merchant_name 分组
|
||||
const grouped = {};
|
||||
processedList.forEach(item => {
|
||||
if (item?.merchant_info?.merchant_name) {
|
||||
const merchantName = item.merchant_info.merchant_name;
|
||||
if (!grouped[merchantName]) {
|
||||
grouped[merchantName] = {
|
||||
merchant_name: merchantName,
|
||||
packets: []
|
||||
};
|
||||
}
|
||||
grouped[merchantName].packets.push(item);
|
||||
}
|
||||
});
|
||||
this.redPacketList = Object.values(grouped);
|
||||
} else {
|
||||
this.redPacketList = processedList;
|
||||
}
|
||||
this.redPacketNum = res.rows.filter(item => item.red_package_config).length;
|
||||
this.redPacketMoney = res.rows.reduce((total, item) =>
|
||||
item.red_package_config ? total + item.red_package_config.money : total, 0.00
|
||||
).toFixed(2);
|
||||
},
|
||||
useRedPacket(item) {
|
||||
console.log('使用红包:', item);
|
||||
if (item.belong_role == 1) {
|
||||
uni.setStorageSync('merchantInfo', item.merchant_info);
|
||||
NavgateTo('/packages/localLife/detail/index')
|
||||
} else if (item.belong_role == 2) {
|
||||
NavgateTo('/pages/index/index');
|
||||
} else if (item.belong_role == 3) {
|
||||
NavgateTo('/packages/shop/index/index');
|
||||
}
|
||||
},
|
||||
switchTab(index) {
|
||||
this.currentTab = index;
|
||||
console.log('选中的标签:', this.tabList[index]);
|
||||
if(this.tabList[index] == '历史记录'){
|
||||
if (index == 0) {
|
||||
this.changeIndex = 0;
|
||||
} else if (index == 1) {
|
||||
this.changeIndex = 3;
|
||||
} else if (index == 2) {
|
||||
this.changeIndex = 2;
|
||||
} else if (index == 3) {
|
||||
this.changeIndex = 1;
|
||||
}
|
||||
this.getMyRedPacket(this.changeIndex);
|
||||
if (this.tabList[index] == '历史记录') {
|
||||
NavgateTo('/packages/user/history/index');
|
||||
}
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.switchTab(0);
|
||||
this.getMyRedPacket(0);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 20rpx;
|
||||
height: 130rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 170rpx;
|
||||
margin-bottom: 30rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
@ -17,7 +17,7 @@
|
||||
.item_left {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
width: 170rpx;
|
||||
width: 150rpx;
|
||||
height: 170rpx;
|
||||
line-height: 170rpx;
|
||||
padding-left: 30rpx;
|
||||
@ -52,6 +52,8 @@
|
||||
|
||||
.item_right {
|
||||
margin-right: 20rpx;
|
||||
position: relative; /* 添加相对定位 */
|
||||
z-index: 10; /* 添加更高的z-index值 */
|
||||
}
|
||||
|
||||
.status {
|
||||
@ -63,6 +65,8 @@
|
||||
color: #ff7557;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
position: relative; /* 添加相对定位 */
|
||||
z-index: 10; /* 添加更高的z-index值 */
|
||||
}
|
||||
|
||||
.status2 {
|
||||
@ -73,6 +77,7 @@
|
||||
height: 120rpx;
|
||||
overflow: hidden;
|
||||
transform: rotate(0deg);
|
||||
z-index: 1; /* 添加较低的z-index值 */
|
||||
}
|
||||
|
||||
.status2::after {
|
||||
@ -179,4 +184,13 @@
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(100vh - 100rpx);
|
||||
}
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
@ -1,25 +1,27 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="content">
|
||||
<scroll-view class="content" scroll-y @scrolltolower="loadMore">
|
||||
<view v-for="(item, index) in redPacketList" :key="index">
|
||||
<view class="item">
|
||||
<view class="item_left">¥{{ item.yuan }}</view>
|
||||
<view class="item_left">¥{{ item.money }}</view>
|
||||
<view class="item_center">
|
||||
<view class="title">{{ item.name }}</view>
|
||||
<view class="time">{{ item.time }}</view>
|
||||
<view class="desc">{{ item.shiyong }}</view>
|
||||
<view class="title">{{ item.red_package_name }}</view>
|
||||
<view class="time">{{ item.end_time }}到期</view>
|
||||
<view class="desc">{{ item.scope }}</view>
|
||||
</view>
|
||||
<view class="item_right">
|
||||
<view class="status" @click="showPopup(item)">{{ item.status }}</view>
|
||||
<view class="status" @click="showPopup(item)">{{ item.is_received ? '已领取' : '领取' }}</view>
|
||||
</view>
|
||||
<view class="status2" :data-status="item.status"></view>
|
||||
<view class="status2" :data-status="item.is_received ? '' : '待领取'" v-if="!item.is_received"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="loading" class="loading">加载中...</view>
|
||||
</scroll-view>
|
||||
<view class="bottom">
|
||||
<view @click="goRedPacket()">我的权益</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<view v-if="popupVisible" class="popup-overlay" @click="hidePopup">
|
||||
<view class="popup-content" @click.stop>
|
||||
@ -27,7 +29,7 @@
|
||||
<view class="checkmark"></view>
|
||||
</view>
|
||||
<view class="popup-title">领取成功!</view>
|
||||
<view class="popup-desc">已存入您的账户</view>
|
||||
<view class="popup-desc">{{ currentItem.red_package_name }}{{ currentItem.money }}元已存入您的账户</view>
|
||||
<button class="popup-btn" @click="hidePopup">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
@ -35,40 +37,50 @@
|
||||
</template>
|
||||
<script>
|
||||
import { menuButtonInfo, NavgateTo, picUrl, request } from '../../../utils/index';
|
||||
import { apiArr } from '../../../api/user';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
redPacketList: [
|
||||
{
|
||||
name: '100元红包',
|
||||
time: '2023-01-01 10:00:00',
|
||||
shiyong: '用于购买商品',
|
||||
status: '待领取',
|
||||
yuan: 100
|
||||
},
|
||||
{
|
||||
name: '50元红包',
|
||||
time: '2023-01-02 12:00:00',
|
||||
shiyong: '用于购买商品',
|
||||
status: '待领取',
|
||||
yuan: 50
|
||||
},
|
||||
{
|
||||
name: '20元红包',
|
||||
time: '2023-01-03 14:00:00',
|
||||
shiyong: '用于购买商品',
|
||||
status: '已使用',
|
||||
yuan: 20
|
||||
}
|
||||
],
|
||||
redPacketList: [],
|
||||
popupVisible: false,
|
||||
currentItem: null
|
||||
currentItem: null,
|
||||
page_num: 1,
|
||||
page_size: 10,
|
||||
loading: false,
|
||||
noMore: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getRedPacketCenter() {
|
||||
const params = {
|
||||
pageNum: this.page_num,
|
||||
pageSize: this.page_size,
|
||||
}
|
||||
const res = await request(apiArr.redPackageCenter, 'POST', params);
|
||||
// 触底加载时合并数据,而不是替换
|
||||
if (this.page_num === 1) {
|
||||
this.redPacketList = res.rows;
|
||||
} else {
|
||||
this.redPacketList = [...this.redPacketList, ...res.rows];
|
||||
}
|
||||
// 判断是否还有更多数据
|
||||
this.noMore = res.rows.length < this.page_size;
|
||||
this.loading = false;
|
||||
},
|
||||
showPopup(item) {
|
||||
this.currentItem = item;
|
||||
this.popupVisible = true;
|
||||
if (item.is_received) {
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
user_id: uni.getStorageSync('userId'),
|
||||
red_package_config_id: item.id,
|
||||
}
|
||||
request(apiArr.redPackageClaim, 'POST', params).then(res => {
|
||||
this.currentItem = item;
|
||||
this.popupVisible = true;
|
||||
this.getRedPacketCenter()
|
||||
})
|
||||
},
|
||||
hidePopup() {
|
||||
this.popupVisible = false;
|
||||
@ -76,7 +88,17 @@ export default {
|
||||
},
|
||||
goRedPacket() {
|
||||
NavgateTo('/packages/user/myRedPacket/index');
|
||||
},
|
||||
// 触底加载更多
|
||||
loadMore() {
|
||||
if (this.loading || this.noMore) return;
|
||||
this.loading = true;
|
||||
this.page_num++;
|
||||
this.getRedPacketCenter();
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.getRedPacketCenter();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user