风控提交

This commit is contained in:
磷叶 2024-11-26 17:30:09 +08:00
parent 126921cbc4
commit 7b73d0adeb
10 changed files with 324 additions and 35 deletions

View File

@ -42,3 +42,12 @@ export function delRiskInfo(infoId) {
method: 'delete'
})
}
// 审核风控材料
export function verifyRiskInfo(data) {
return request({
url: '/ss/riskInfo/verify',
method: 'put',
data
})
}

View File

@ -0,0 +1,43 @@
<template>
<div>
<video :src="src" :width="width" :height="height" @click="handlePreview" style="cursor: pointer"/>
<el-dialog :visible.sync="visible" width="800px" :title="title">
<video :src="src" controls style="width: 720px; height: 405px;background-color:#000;"/>
</el-dialog>
</div>
</template>
<script>
export default {
name: "VideoPreview",
props: {
src: {
type: String,
default: null
},
width: {
type: String,
default: "50px"
},
height: {
type: String,
default: "50px"
},
title: {
type: String,
default: '视频预览'
}
},
data() {
return {
visible: false,
}
},
methods: {
handlePreview() {
this.visible = true;
}
}
}
</script>

View File

@ -249,3 +249,23 @@ export const RiskSubmitType = {
BUSINESS_LICENCE: "5", // 营业执照
DUTY_VIDEO: "6", // 责任视频
}
/**
* 风控材料状态
* @type {{}}
*/
export const RiskInfoStatus = {
WAIT_SUBMIT: "1", // 待提交
WAIT_VERIFY: "2", // 待审核
PASS: "3", // 已通过
REJECT: "4", // 未通过
// 允许审核的状态
canVerify() {
return [this.WAIT_VERIFY];
},
// 默认查询的列表
defaultList() {
return [this.WAIT_VERIFY, this.PASS, this.REJECT]
}
}

View File

@ -0,0 +1,164 @@
<template>
<el-drawer :visible.sync="visible" size="1300px" :with-header="false" @open="onOpen">
<el-form label-position="top" :model="detail" ref="form" :rules="rules" class="app-container" v-loading="loading">
<el-row :gutter="16">
<el-col :span="16">
<el-scrollbar style="height: calc(100vh - 40px);">
<el-row style="padding: 1em;overflow: hidden">
<form-col :span="span * 2" label="风控信息">
<el-descriptions border :column="2">
<el-descriptions-item label="风控用户">
{{detail.userName | defaultValue}}
</el-descriptions-item>
<el-descriptions-item label="风控类型">
<dict-tag :value="detail.riskType" :options="dict.type.risk_type"/>
</el-descriptions-item>
<el-descriptions-item label="风控原因">
{{detail.riskReason | defaultValue}}
</el-descriptions-item>
<el-descriptions-item label="所需材料">
<dict-tag :value="detail.riskSubmitType" :options="dict.type.risk_submit_type"/>
</el-descriptions-item>
</el-descriptions>
</form-col>
<form-col :span="span * 2" label="实名认证" v-if="detail.riskSubmitType.includes(RiskSubmitType.REAL_NAME)">
<el-descriptions border>
<el-descriptions-item label="姓名">{{detail.realName | defaultValue}}</el-descriptions-item>
<el-descriptions-item label="身份证">{{detail.realIdCard | defaultValue}}</el-descriptions-item>
<el-descriptions-item label="人脸图片">
<image-preview :src="detail.realFace" :width="50" :height="50"/>
</el-descriptions-item>
</el-descriptions>
</form-col>
<form-col :span="span" label="身份证正面" v-if="detail.riskSubmitType.includes(RiskSubmitType.ID_CARD)">
<image-preview :src="detail.idCardFront" width="90%" :height="200"/>
</form-col>
<form-col :span="span" label="身份证反面" v-if="detail.riskSubmitType.includes(RiskSubmitType.ID_CARD)">
<image-preview :src="detail.idCardBack" width="90%" :height="200"/>
</form-col>
<form-col :span="span" label="手持身份证" v-if="detail.riskSubmitType.includes(RiskSubmitType.ID_CARD_HAND)">
<image-preview :src="detail.idCardHand" width="90%" :height="200"/>
</form-col>
<form-col :span="span" label="营业执照" v-if="detail.riskSubmitType.includes(RiskSubmitType.BUSINESS_LICENCE)">
<image-preview :src="detail.businessLicence" width="90%" :height="200"/>
</form-col>
<form-col :span="span * 2" label="使用场景视频" v-if="detail.riskSubmitType.includes(RiskSubmitType.VIDEO)">
<video :src="detail.video" width="100%" height="300px" style="background-color:#000000;" controls/>
</form-col>
<form-col :span="span * 2" label="责任视频" v-if="detail.riskSubmitType.includes(RiskSubmitType.DUTY_VIDEO)">
<video :src="detail.dutyVideo" width="100%" height="300px" style="background-color:#000000;" controls/>
视频提示{{detail.riskVideoWords | defaultValue}}
</form-col>
</el-row>
</el-scrollbar>
</el-col>
<el-col :span="8">
<el-card>
<el-row>
<form-col label="审核意见" prop="verifyRemark">
<el-input
v-model="detail.verifyRemark"
placeholder="请输入审核意见"
type="textarea"
show-word-limit
maxlength="200"
:autosize="{minRows: 3, maxRows: 10}"
/>
</form-col>
</el-row>
<el-row type="flex" justify="center">
<el-button type="success" plain icon="el-icon-check" @click="onVerify(true)">通过</el-button>
<el-button type="danger" plain icon="el-icon-close" @click="onVerify(false)">驳回</el-button>
</el-row>
</el-card>
</el-col>
</el-row>
</el-form>
</el-drawer>
</template>
<script>
import { getRiskInfo, verifyRiskInfo } from '@/api/ss/riskInfo'
import VideoPreview from '@/components/VideoPreview/index.vue'
import { RiskSubmitType } from '@/utils/constants'
import UserLink from '@/components/Business/SmUser/UserLink.vue'
export default {
name: "RiskInfoVerifyDialog",
dicts: ['risk_submit_type', 'risk_type'],
components: { UserLink, VideoPreview },
props: {
show: {
type: Boolean,
default: false,
},
infoId: {
type: String,
default: null
}
},
data() {
return {
detail: {
riskSubmitType: []
},
span: 12,
rules: {
verifyRemark: [
{ required: true, message: '请输入审核意见', trigger: 'blur' }
]
},
loading: false,
}
},
computed: {
RiskSubmitType() {
return RiskSubmitType
},
visible: {
set(val) {
this.$emit('update:show', val);
},
get() {
return this.show;
}
}
},
methods: {
onOpen() {
this.getDetail();
},
getDetail() {
this.loading = true;
getRiskInfo(this.infoId).then(res => {
this.detail = res.data;
}).finally(() => {
this.loading = false;
})
},
onVerify(pass) {
this.$confirm(`确定要${pass ? '通过' : '驳回'}吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(c => {
this.$refs.form.validate(valid => {
if (valid) {
this.loading = true;
verifyRiskInfo({infoId: this.detail.infoId, pass: pass, verifyRemark: this.detail.verifyRemark}).then(res => {
if (res.code === 200) {
this.$modal.msgSuccess("操作成功");
this.visible = false;
this.$emit('success');
}
}).finally(() => {
this.loading = false;
})
}
})
})
}
}
}
</script>

View File

@ -0,0 +1,8 @@
<template>
<div class="app-container">
<el-card header="基本信息">
</el-card>
</div>
</template>

View File

@ -1,15 +1,15 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable @change="handleQuery">
<el-option
<el-form-item label="状态" prop="statusList">
<el-checkbox-group v-model="queryParams.statusList" @change="handleQuery">
<el-checkbox-button
v-for="dict in dict.type.risk_info_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
:label="dict.value"
>{{dict.label}}
</el-checkbox-button>
</el-checkbox-group>
</el-form-item>
<el-form-item label="审核意见" prop="verifyRemark">
<el-input
@ -19,6 +19,14 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="用户" prop="userName">
<el-input
v-model="queryParams.userName"
placeholder="请输入用户名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -92,6 +100,15 @@
<template v-else-if="column.key === 'status'">
<dict-tag :options="dict.type.risk_info_status" :value="d.row[column.key]"/>
</template>
<template v-else-if="column.key === 'userName'">
<user-link :id="d.row.userId" :name="d.row.userName"/>
</template>
<template v-else-if="['idCardFront', 'idCardBack', 'idCardHand', 'businessLicence'].includes(column.key)">
<image-preview :src="d.row[column.key]" :width="50" :height="50"/>
</template>
<template v-else-if="[ 'video', 'dutyVideo'].includes(column.key)">
<video-preview :src="d.row[column.key]" :title="column.label"/>
</template>
<template v-else>
{{d.row[column.key]}}
</template>
@ -103,9 +120,10 @@
<el-button
size="mini"
type="text"
icon="el-icon-check"
icon="el-icon-s-check"
@click="handleVerify(scope.row)"
v-hasPermi="['ss:riskInfo:verify']"
v-show="RiskInfoStatus.canVerify().includes(scope.row.status)"
>审核</el-button>
<el-button
size="mini"
@ -126,6 +144,8 @@
@pagination="getList"
/>
<risk-info-verify-dialog :show.sync="openVerify" :info-id="row.infoId" @success="getList"/>
<!-- 添加或修改风控材料对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
@ -199,6 +219,10 @@
<script>
import { listRiskInfo, getRiskInfo, delRiskInfo, addRiskInfo, updateRiskInfo } from "@/api/ss/riskInfo";
import { $showColumns } from '@/utils/mixins';
import VideoPreview from '@/components/VideoPreview/index.vue'
import RiskInfoVerifyDialog from '@/views/ss/riskInfo/components/RiskInfoVerifyDialog.vue'
import { RiskInfoStatus } from '@/utils/constants'
import UserLink from '@/components/Business/SmUser/UserLink.vue'
//
const defaultSort = {
@ -208,6 +232,12 @@ const defaultSort = {
export default {
name: "RiskInfo",
computed: {
RiskInfoStatus() {
return RiskInfoStatus
}
},
components: { UserLink, RiskInfoVerifyDialog, VideoPreview },
mixins: [$showColumns],
dicts: ['risk_info_status'],
data() {
@ -216,7 +246,8 @@ export default {
columns: [
{key: 'infoId', visible: false, label: 'ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'riskId', visible: false, label: '风控ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'submitTime', visible: true, label: '提交时间', minWidth: null, sortable: false, overflow: false, align: 'center', width: null},
{key: 'userName', visible: true, label: '用户', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
{key: 'submitTime', visible: true, label: '提交时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
{key: 'status', visible: true, label: '状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'realNameId', visible: false, label: '实名认证ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'idCardFront', visible: true, label: '身份证正面', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
@ -227,7 +258,8 @@ export default {
{key: 'businessLicence', visible: true, label: '营业执照', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'verifyBy', visible: true, label: '审核人', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'verifyRemark', visible: true, label: '审核意见', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'verifyTime', visible: true, label: '审核时间', minWidth: null, sortable: false, overflow: false, align: 'center', width: null},
{key: 'verifyTime', visible: true, label: '审核时间', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100"},
{key: 'createTime', visible: true, label: '创建时间', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100"},
],
//
orderSorts: ['ascending', 'descending', null],
@ -263,6 +295,7 @@ export default {
verifyId: null,
verifyBy: null,
verifyRemark: null,
statusList: RiskInfoStatus.defaultList()
},
//
form: {},
@ -274,7 +307,9 @@ export default {
status: [
{ required: true, message: "状态不能为空", trigger: "change" }
],
}
},
openVerify:false,
row: {},
};
},
created() {
@ -362,13 +397,8 @@ export default {
},
/** 修改按钮操作 */
handleVerify(row) {
this.reset();
const infoId = row.infoId || this.ids
getRiskInfo(infoId).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改风控材料";
});
this.row = row;
this.openVerify = true;
},
/** 提交按钮 */
submitForm() {

View File

@ -147,13 +147,16 @@
<recharge-link :bill-id="d.row.billId" :text="d.row.billNo"/>
</template>
<template v-else-if="column.key === 'userName'">
<user-link :id="d.row.userId" :name="d.row.userName"/>
<user-link :id="d.row.userId" :name="d.row.userName"/><br/>
<user-link :id="d.row.userId" :name="d.row.userMobile"/>
</template>
<template v-else-if="column.key === 'deviceName'">
<device-link :id="d.row.deviceId" :text="`${d.row.deviceName ? d.row.deviceName : '--'} (${d.row.deviceNo})`"/>
<device-link :id="d.row.deviceId" :text="d.row.deviceName"/><br/>
<device-link :id="d.row.deviceId" :text="d.row.deviceNo"/>
</template>
<template v-else-if="column.key === 'mchName'">
<user-link :id="d.row.mchId" :name="d.row.mchName"/>
<user-link :id="d.row.mchId" :name="d.row.mchName"/><br/>
<user-link :id="d.row.mchId" :name="d.row.mchMobile"/>
</template>
<template v-else-if="column.key === 'storeId'">
<store-link :id="d.row.storeId" :name="d.row.storeName"/>
@ -209,7 +212,7 @@
</template>
</el-table-column>
</template>
<el-table-column label="操作" align="center" fixed="right" width="200">
<el-table-column label="操作" align="center" width="160">
<template slot-scope="d">
<slot :row="d.row" name="row-operator"/>
</template>
@ -280,21 +283,21 @@ export default {
orderSorts: ['ascending', 'descending', null],
//
columns: [
{key: 'billId', visible: false, label: '订单ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80", fixed: 'left'},
{key: 'createTime', visible: true, label: '时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: 'left'},
{key: 'billNo', visible: true, label: '订单编号', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: 'left'},
{key: 'billId', visible: false, label: '订单ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80", fixed: null},
{key: 'createTime', visible: true, label: '时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'billNo', visible: true, label: '订单编号', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'deviceName', visible: true, label: '设备/SN', minWidth: null, sortable: false, overflow: false, align: 'center', width: "120", fixed: null},
{key: 'userName', visible: true, label: '用户', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'mchName', visible: true, label: '商户', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'storeId', visible: false, label: '店铺', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'money', visible: true, label: '订单金额', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'refundAmount', visible: true, label: '退款金额', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'money', visible: true, label: '金额', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'refundAmount', visible: true, label: '退款', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'channelCost', visible: true, label: '渠道成本', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'channelId', visible: true, label: '支付方式', minWidth: null, sortable: false, overflow: false, align: 'center', width: "120", fixed: null},
{key: 'suitFeeMode', visible: true, label: '收费模式', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'channelId', visible: true, label: '支付方式', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100", fixed: null},
{key: 'suitFeeMode', visible: true, label: '收费模式', minWidth: null, sortable: false, overflow: false, align: 'center', width: "80", fixed: null},
{key: 'suitFeeType', visible: true, label: '收费方式', minWidth: null, sortable: false, overflow: false, align: 'center', width: "120", fixed: null},
{key: 'status', visible: true, label: '交易状态', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'isUsing', visible: true, label: '使用状态', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'status', visible: true, label: '交易状态', minWidth: null, sortable: false, overflow: false, align: 'center', width: "80", fixed: null},
{key: 'isUsing', visible: true, label: '使用状态', minWidth: null, sortable: false, overflow: false, align: 'center', width: "80", fixed: null},
{key: 'deviceRechargeStatus', visible: true, label: '设备状态', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},
{key: 'suitDeviceInfo', visible: false, label: '设备套餐详情', minWidth: null, sortable: false, overflow: false, align: 'center', width: "250", fixed: null},
{key: 'version', visible: false, label: '订单版本', minWidth: null, sortable: false, overflow: false, align: 'center', width: null, fixed: null},

View File

@ -22,7 +22,7 @@
icon="el-icon-s-promotion"
@click="rechargeDevice(d.row.billId)"
v-if="canRechargeDevice(d.row)"
>手动设备充值</el-button>
>设备充值</el-button>
<el-button
size="small"
type="text"
@ -30,7 +30,7 @@
@click="handleRefund(d.row)"
v-has-permi="['system:bill:refund']"
v-show="canRefund(d.row)"
>订单退款</el-button>
>退款</el-button>
<el-button
size="small"
type="text"

View File

@ -129,6 +129,9 @@
<el-tab-pane label="风控记录" lazy>
<risk :query="{userId: detail.userId}" :view="views.user"/>
</el-tab-pane>
<el-tab-pane label="风控审核" lazy>
<risk-info :query="{userId: detail.userId}" :view="views.user"/>
</el-tab-pane>
<el-tab-pane label="实名认证记录" lazy>
<real-name :query="{userId: detail.userId}" :view="views.user"/>
</el-tab-pane>
@ -172,11 +175,12 @@ import ReceiveBill from '@/views/ss/receiveBill/index.vue'
import Risk from '@/views/ss/risk/index.vue'
import UserDailyRechargeReport from '@/views/system/smUser/components/UserDailyRechargeReport.vue'
import Bonus from '@/views/ss/bonus/index.vue'
import RiskInfo from '@/views/ss/riskInfo/index.vue'
export default {
name: 'User/:userId',
mixins: [$view, $serviceType],
components: { Bonus, UserDailyRechargeReport, Risk, ReceiveBill, UserConfigDialog, RealName, Withdraw, BooleanTag, Recharge, Device, Suit, Account, RecordBalance, Store, Access, UserRechargeReport, UserAccount, UserDevice, LineChart},
components: { RiskInfo, Bonus, UserDailyRechargeReport, Risk, ReceiveBill, UserConfigDialog, RealName, Withdraw, BooleanTag, Recharge, Device, Suit, Account, RecordBalance, Store, Access, UserRechargeReport, UserAccount, UserDevice, LineChart},
dicts: ['sm_user_type', 'service_type', 'withdraw_service_type'],
computed: {
BonusArrivalType() {

View File

@ -60,6 +60,13 @@
/>
</el-select>
</el-form-item>
<el-form-item label="是否管理" prop="deviceAdmin">
<el-radio-group v-model="queryParams.deviceAdmin" @change="handleQuery">
<el-radio :label="null">全部</el-radio>
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -342,7 +349,7 @@ export default {
{key: 'withdrawServiceRate', visible: true, label: '提现服务费', align: 'center', minWidth: null, sortable: false, width: "120"},
{key: 'storeCount', visible: true, label: '店铺数量', align: 'center', minWidth: null, sortable: false, width: null},
{key: 'deviceCount', visible: true, label: '设备数量', align: 'center', minWidth: null, sortable: false, width: null},
{key: 'deviceAdmin', visible: false, label: '设备管理员', align: 'center', minWidth: null, sortable: false, width: null},
{key: 'deviceAdmin', visible: true, label: '是否管理', align: 'center', minWidth: null, sortable: false, width: null},
{key: 'riskCount', visible: false, label: '风险次数', align: 'center', minWidth: null, sortable: true, width: "120"},
],
openServiceRate: false,
@ -375,6 +382,7 @@ export default {
serviceType: null,
status: null,
realOrUserName: null,
deviceAdmin: null
},
//
form: {},