mirror of
https://gitee.com/bootx/dax-pay-h5.git
synced 2025-10-15 14:40:27 +00:00
PC端收银台添加聚合支付 以及 功能优化
This commit is contained in:
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -37,9 +37,9 @@ export function auth(param: GatewayAuthCodeParam) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付成功获取信息
|
* 支付成功获取状态
|
||||||
*/
|
*/
|
||||||
export function getSuccessOrder(orderNo) {
|
export function getSuccessOrderStatus(orderNo) {
|
||||||
return http.request<Result<paySuccess>>({
|
return http.request<Result<paySuccess>>({
|
||||||
url: '/unipay/gateway/findStatusByOrderNo',
|
url: '/unipay/gateway/findStatusByOrderNo',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -47,6 +47,17 @@ export function getSuccessOrder(orderNo) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付成功获取信息
|
||||||
|
*/
|
||||||
|
export function getSuccessOrderMessage(orderNo) {
|
||||||
|
return http.request<Result<paySuccess>>({
|
||||||
|
url: '/unipay/gateway/findOrderByOrderNo',
|
||||||
|
method: 'get',
|
||||||
|
params: { orderNo },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发起支付
|
* 发起支付
|
||||||
*/
|
*/
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { getSuccessOrder } from '@/views/daxpay/h5/aggregate/Aggregate.api'
|
import { getSuccessOrderMessage } from '@/views/daxpay/h5/aggregate/Aggregate.api'
|
||||||
import type { paySuccess } from '@/views/daxpay/h5/aggregate/Aggregate.api'
|
import type { paySuccess } from '@/views/daxpay/h5/aggregate/Aggregate.api'
|
||||||
import { getBrowserUA } from '@/utils/uaUtil'
|
import { getBrowserUA } from '@/utils/uaUtil'
|
||||||
// 获取路由参数
|
// 获取路由参数
|
||||||
@@ -70,7 +70,7 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
getSuccessOrder(orderNo).then(({ data }) => {
|
getSuccessOrderMessage(orderNo).then(({ data }) => {
|
||||||
orderAndConfig.value = data
|
orderAndConfig.value = data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,14 @@ export function orderStatus(orderNo) {
|
|||||||
params: { orderNo },
|
params: { orderNo },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 查询订单信息
|
||||||
|
export function orderMessage(orderNo) {
|
||||||
|
return http.request<Result<boolean>>({
|
||||||
|
url: '/unipay/gateway/findOrderByOrderNo',
|
||||||
|
method: 'GET',
|
||||||
|
params: { orderNo },
|
||||||
|
})
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 收银台配置
|
* 收银台配置
|
||||||
*/
|
*/
|
||||||
@@ -41,6 +48,8 @@ export interface OrderAndConfig {
|
|||||||
config: GatewayPayConfig
|
config: GatewayPayConfig
|
||||||
/** 收银台分类配置信息 */
|
/** 收银台分类配置信息 */
|
||||||
groupConfigs: CashierGroupConfig[]
|
groupConfigs: CashierGroupConfig[]
|
||||||
|
/** 聚合二维码 */
|
||||||
|
aggregateUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,5 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="pcPayTai">
|
<div class="pcPayTai">
|
||||||
|
<van-overlay :show="loading" class-name="loadingOverlay">
|
||||||
|
<div class="loading-wrapper" @click.stop>
|
||||||
|
<van-loading size="24px">
|
||||||
|
请稍后...
|
||||||
|
</van-loading>
|
||||||
|
</div>
|
||||||
|
</van-overlay>
|
||||||
|
|
||||||
<div class="pcPayBox">
|
<div class="pcPayBox">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>支付收银台</h1>
|
<h1>支付收银台</h1>
|
||||||
@@ -39,15 +47,19 @@
|
|||||||
:class="{ methodItemClick: payMethObj.payClickItemId === item.id }"
|
:class="{ methodItemClick: payMethObj.payClickItemId === item.id }"
|
||||||
@click="payMethObj.payClick(item)"
|
@click="payMethObj.payClick(item)"
|
||||||
>
|
>
|
||||||
<!-- <img :src="item.icon" alt=""> -->
|
<img v-if="item.icon === 'aggregate-pay'" :src="getImageUrl(item.icon)" alt="">
|
||||||
<img src="@/assets/images/alipay.png" alt="">
|
<img v-else src="@/assets/images/alipay.png" alt="">
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<div v-if="item.recommend" class="recommon">
|
<div v-if="item.recommend" class="recommon">
|
||||||
推荐
|
推荐
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payMethodChildBox">
|
<!-- 聚合支付 -->
|
||||||
|
<div v-if="isAggregateShow" class="payMethodCode">
|
||||||
|
<vue-qr :text="orderObj?.aggregateUrl" :size="200" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="payMethodChildBox">
|
||||||
<div
|
<div
|
||||||
v-for="item in childRenList"
|
v-for="item in childRenList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@@ -60,7 +72,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<van-popup v-model:show="codeshow" round closeable :close-on-click-overlay="false">
|
<van-popup
|
||||||
|
v-model:show="codeshow"
|
||||||
|
round
|
||||||
|
closeable
|
||||||
|
:close-on-click-overlay="false"
|
||||||
|
@click-close-icon="closeCode"
|
||||||
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="codeBox">
|
<div class="codeBox">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -80,7 +98,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue'
|
import { onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { showDialog, showFailToast } from 'vant'
|
import { showConfirmDialog, showDialog, showFailToast } from 'vant'
|
||||||
import { result } from 'lodash-es'
|
import { result } from 'lodash-es'
|
||||||
import vueQr from 'vue-qr/src/packages/vue-qr.vue'
|
import vueQr from 'vue-qr/src/packages/vue-qr.vue'
|
||||||
import { getOrderAndConfig, orderStatus, payOrder } from '@/views/daxpay/pc/cashier/Cashier.api'
|
import { getOrderAndConfig, orderStatus, payOrder } from '@/views/daxpay/pc/cashier/Cashier.api'
|
||||||
@@ -91,6 +109,7 @@ import { GatewayCallTypeEnum } from '@/enums/daxpay/DaxPayEnum'
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { orderNo } = route.params
|
const { orderNo } = route.params
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
// 页面信息对象
|
// 页面信息对象
|
||||||
const orderObj = ref<OrderAndConfig>()
|
const orderObj = ref<OrderAndConfig>()
|
||||||
// 分组下的支付列表
|
// 分组下的支付列表
|
||||||
@@ -104,6 +123,25 @@ function getImageUrl(icon) {
|
|||||||
const codeshow = ref<boolean>(false)
|
const codeshow = ref<boolean>(false)
|
||||||
// 二维码链接
|
// 二维码链接
|
||||||
const codeLink = ref<string>('')
|
const codeLink = ref<string>('')
|
||||||
|
// 判断是否含有聚合支付
|
||||||
|
const isAggregateShow = ref<boolean>(false)
|
||||||
|
|
||||||
|
// 关闭菜单触发
|
||||||
|
function closeCode() {
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '是否关闭支付弹窗?',
|
||||||
|
message: '关闭弹窗后该订单会被取消',
|
||||||
|
className: 'confirmDialogPC',
|
||||||
|
overlayClass: 'confirmOverlayPC',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
codeshow.value = false
|
||||||
|
router.replace({ path: `/pc/payFail`, query: { msg: '订单已取消,请重新发起支付!' } })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
codeshow.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 倒计时对象
|
// 倒计时对象
|
||||||
const orderTime = reactive({
|
const orderTime = reactive({
|
||||||
@@ -133,17 +171,21 @@ const orderTime = reactive({
|
|||||||
return time.toString().padStart(2, '0')
|
return time.toString().padStart(2, '0')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// 定时器
|
// 倒计时定时器
|
||||||
const { pause, resume } = useIntervalFn(() => {
|
const { pause: timePause, resume: timeResume } = useIntervalFn(() => {
|
||||||
orderTime.getMinter() // 每秒获取分秒方法
|
orderTime.getMinter() // 每秒获取分秒方法
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
|
// 轮询监听定时器
|
||||||
|
const { pause: lunPause, resume: lunResume } = useIntervalFn(() => {
|
||||||
|
queryOrderStatus() // 执行订单状态查询
|
||||||
|
}, 3000)
|
||||||
// 监听倒计时,到时间跳转超时页面
|
// 监听倒计时,到时间跳转超时页面
|
||||||
watch(
|
watch(
|
||||||
() => orderTime.totalTme,
|
() => orderTime.totalTme,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
if (newValue === 0) {
|
if (newValue === 0) {
|
||||||
router.replace({ path: `/pc/payFail`, replace: true })
|
router.replace({ path: `/pc/payFail`, query: { msg: '支付超时,请重新发起支付!' } })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -158,11 +200,13 @@ const payMethObj = reactive({
|
|||||||
},
|
},
|
||||||
// 点击支付
|
// 点击支付
|
||||||
toPayTypeClick: (item) => {
|
toPayTypeClick: (item) => {
|
||||||
|
loading.value = true
|
||||||
payOrder({ orderNo: orderNo as string, itemId: item.id }).then(({ code, data, msg }) => {
|
payOrder({ orderNo: orderNo as string, itemId: item.id }).then(({ code, data, msg }) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
router.replace({ path: `/pc/payFail`, query: { msg } })
|
router.replace({ path: `/pc/payFail`, query: { msg } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
loading.value = false
|
||||||
// 如果是二维码方式 打开支付弹窗
|
// 如果是二维码方式 打开支付弹窗
|
||||||
if (item.callType === GatewayCallTypeEnum.qr_code) {
|
if (item.callType === GatewayCallTypeEnum.qr_code) {
|
||||||
codeshow.value = true
|
codeshow.value = true
|
||||||
@@ -181,21 +225,24 @@ watch(
|
|||||||
() => payMethObj.payClickItemId,
|
() => payMethObj.payClickItemId,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
|
// 判断是否为聚合支付
|
||||||
|
if (newValue === '111111111111111111') {
|
||||||
|
isAggregateShow.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isAggregateShow.value = false
|
||||||
// 查找点击的分组下面的子项
|
// 查找点击的分组下面的子项
|
||||||
childRenList.value = orderObj.value?.groupConfigs.find(item => item.id === newValue)?.items
|
childRenList.value = orderObj.value?.groupConfigs.find(item => item.id === newValue)?.items
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// 轮询定时器
|
|
||||||
const timer = ref()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init() // 初始化数据
|
init() // 初始化数据
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
pause()
|
timePause() // 关闭倒计时
|
||||||
timer.value = null
|
lunPause() // 关闭轮询
|
||||||
clearInterval(timer.value)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
@@ -206,12 +253,21 @@ function init() {
|
|||||||
router.replace({ path: `/pc/payFail`, query: { msg } })
|
router.replace({ path: `/pc/payFail`, query: { msg } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
queryOrderStatus() // 查询订单状态开启
|
lunResume() // 开始轮询查询状态
|
||||||
|
// 判断是否存在聚合支付
|
||||||
|
if (data.config.aggregateShow) {
|
||||||
|
data.groupConfigs.unshift({
|
||||||
|
id: '111111111111111111',
|
||||||
|
name: '聚合支付',
|
||||||
|
recommend: false,
|
||||||
|
icon: 'aggregate-pay',
|
||||||
|
})
|
||||||
|
}
|
||||||
orderObj.value = data
|
orderObj.value = data
|
||||||
payMethObj.payClickItemId = orderObj.value.groupConfigs[0].id || '' // 赋值第一个
|
payMethObj.payClickItemId = orderObj.value.groupConfigs[0].id || '' // 赋值第一个
|
||||||
orderTime.getDownTotalTime(data.order.expiredTime) // 计算倒计时
|
orderTime.getDownTotalTime(data.order.expiredTime) // 计算倒计时
|
||||||
orderTime.getMinter() // 先执行一下 解决进入页面一秒后才显示倒计时
|
orderTime.getMinter() // 先执行一下 解决进入页面一秒后才显示倒计时
|
||||||
resume() // 开启倒计时
|
timeResume() // 开启倒计时
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@@ -220,19 +276,31 @@ function init() {
|
|||||||
|
|
||||||
// 查询订单状态
|
// 查询订单状态
|
||||||
function queryOrderStatus() {
|
function queryOrderStatus() {
|
||||||
timer.value = setInterval(() => {
|
orderStatus(orderNo).then((res) => {
|
||||||
orderStatus(orderNo).then((res) => {
|
// 判断订单是否支付成功
|
||||||
// 判断订单是否支付成功
|
if (res.data) {
|
||||||
if (res.data) {
|
codeshow.value = false
|
||||||
codeshow.value = false
|
router.replace({ path: `/pc/paySuccess/${orderNo}`, replace: true })
|
||||||
router.replace({ path: `/pc/paySuccess/${orderNo}`, replace: true })
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
}, 3000) as unknown as number
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
.loadingOverlay {
|
||||||
|
.van-loading__circular {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.van-loading__text {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.confirmOverlayPC {
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.pcPayTai {
|
.pcPayTai {
|
||||||
.van-overlay {
|
.van-overlay {
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -406,7 +474,12 @@ function queryOrderStatus() {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.payMethodCode {
|
||||||
|
padding: 2.0833vw 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.payMethodChildBox {
|
.payMethodChildBox {
|
||||||
padding: 2.0833vw 0 2.0833vw 15.625vw;
|
padding: 2.0833vw 0 2.0833vw 15.625vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@@ -2,7 +2,9 @@
|
|||||||
<div class="pcPayTai">
|
<div class="pcPayTai">
|
||||||
<div class="pcPayBox">
|
<div class="pcPayBox">
|
||||||
<img src="@/assets/images/fail1.png" alt="">
|
<img src="@/assets/images/fail1.png" alt="">
|
||||||
<p class="errMessage">{{ msg ? msg : "支付失败,请重新尝试!" }}</p>
|
<p class="errMessage">
|
||||||
|
{{ msg ? msg : "支付失败,请重新尝试!" }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -3,12 +3,63 @@
|
|||||||
<div class="pcPayBox">
|
<div class="pcPayBox">
|
||||||
<img src="@/assets/images/success1.png" alt="">
|
<img src="@/assets/images/success1.png" alt="">
|
||||||
<p>支付成功</p>
|
<p>支付成功</p>
|
||||||
|
|
||||||
|
<div class="payPrice">
|
||||||
|
<span class="unit">¥</span>
|
||||||
|
<div class="price">
|
||||||
|
{{ orderAndConfig?.amount }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="payMessBox">
|
||||||
|
<div class="payMessItem">
|
||||||
|
<div class="itemTitle">
|
||||||
|
支付标题:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{ orderAndConfig?.title }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="payMessItem">
|
||||||
|
<div class="itemTitle">
|
||||||
|
订单编号:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{ orderAndConfig?.orderNo }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="payBtnBox" @click="closeSuccessClick">
|
||||||
|
完成
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { orderMessage } from '@/views/daxpay/pc/cashier/Cashier.api'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const { orderNo } = route.params
|
||||||
|
const orderAndConfig = ref()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
console.log(route)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化函数
|
||||||
|
function init() {
|
||||||
|
orderMessage(orderNo).then(({ data }) => {
|
||||||
|
orderAndConfig.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击关闭
|
||||||
|
function closeSuccessClick() {
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@@ -35,7 +86,7 @@
|
|||||||
overflow: hidden; // 防止内容溢出
|
overflow: hidden; // 防止内容溢出
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
padding: 7.2083vw 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1.5625vw;
|
gap: 1.5625vw;
|
||||||
p {
|
p {
|
||||||
@@ -43,5 +94,59 @@
|
|||||||
color: #1e90ff;
|
color: #1e90ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.payPrice {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.2604vw;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: 0.625vw;
|
||||||
|
transform: translateY(0.4167vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price {
|
||||||
|
font-size: 1.6667vw;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.payMessBox {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0px 1.0417vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5208vw;
|
||||||
|
|
||||||
|
.payMessItem {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.itemTitle {
|
||||||
|
font-size: 0.8333vw;
|
||||||
|
color: #797d81;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemContent {
|
||||||
|
font-size: 0.8333vw;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: '微软雅黑';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.payBtnBox {
|
||||||
|
width: 20%;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #0d6eff;
|
||||||
|
color: #fff;
|
||||||
|
height: 2.7083vw;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 7.6042vw;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 2.7083vw;
|
||||||
|
border-radius: 0.5208vw;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Reference in New Issue
Block a user