```
feat(shopro_huifu): 添加汇付进件管理功能 - 新增汇付进件管理模块,支持新建、编辑进件流程 - 实现用户角色绑定验证功能,确保进件角色的有效性 - 集成Vue.js前端界面,提供完整的表单操作体验 - 支持个人和企业两种账户类型的进件资料录入 - 添加银行支行搜索功能,方便结算账户信息填写 - 完善进件状态管理和审核流程控制 - 增加图片上传组件,支持身份证、营业执照等证件上传 ```
这个提交包含在:
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace app\admin\controller\shopro_huifu;
|
||||
|
||||
use addons\shopro_huifu\library\onboard\HuifuOnboardAdminService;
|
||||
use addons\shopro_huifu\library\onboard\HuifuOnboardAdminView;
|
||||
use addons\shopro_huifu\library\onboard\HuifuOnboardBankBranch;
|
||||
use addons\shopro_huifu\library\onboard\HuifuOnboardError;
|
||||
use addons\shopro_huifu\library\onboard\HuifuOnboardQuery;
|
||||
use addons\shopro_huifu\library\onboard\HuifuOnboardService;
|
||||
use addons\shopro_huifu\model\Onboard as OnboardModel;
|
||||
@@ -10,6 +13,7 @@ use app\admin\controller\shopro\Base;
|
||||
|
||||
class Onboard extends Base
|
||||
{
|
||||
protected $noNeedRight = ['userRoles', 'searchBranch'];
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
@@ -77,6 +81,78 @@ class Onboard extends Base
|
||||
}
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
return $this->persistDraft();
|
||||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
public function edit($ids = null)
|
||||
{
|
||||
$row = OnboardModel::get($ids);
|
||||
if (!$row) {
|
||||
$this->error('记录不存在');
|
||||
}
|
||||
if ($this->request->isPost()) {
|
||||
return $this->persistDraft($row);
|
||||
}
|
||||
$this->assignconfig('onboardForm', HuifuOnboardAdminView::formDataFromRow($row));
|
||||
return $this->view->fetch('shopro_huifu/onboard/add');
|
||||
}
|
||||
|
||||
public function userRoles()
|
||||
{
|
||||
$userId = intval($this->request->get('user_id', 0));
|
||||
if ($userId <= 0) {
|
||||
$this->error('请选择用户');
|
||||
}
|
||||
$user = HuifuOnboardAdminView::enrichListRow(['user_id' => $userId, 'payload' => '']);
|
||||
$this->success('ok', null, [
|
||||
'user' => [
|
||||
'id' => $userId,
|
||||
'nickname' => $user['user_nickname'] ?? '',
|
||||
'mobile' => $user['user_mobile'] ?? '',
|
||||
],
|
||||
'roles' => HuifuOnboardAdminView::listRolesForUser($userId),
|
||||
]);
|
||||
}
|
||||
|
||||
public function searchBranch()
|
||||
{
|
||||
$keyword = trim((string) $this->request->get('keyword', ''));
|
||||
$areaId = trim((string) $this->request->get('area_id', ''));
|
||||
$this->success('ok', null, HuifuOnboardBankBranch::search($keyword, $areaId, 20));
|
||||
}
|
||||
|
||||
protected function persistDraft($row = null)
|
||||
{
|
||||
$userId = intval($this->request->post('user_id', 0));
|
||||
$roleType = trim((string) $this->request->post('role_type', ''));
|
||||
$roleRefId = intval($this->request->post('role_ref_id', 0));
|
||||
$form = $this->request->post('form/a', []);
|
||||
if ($row) {
|
||||
$userId = intval($row['user_id']);
|
||||
$roleType = $row['role_type'];
|
||||
$roleRefId = intval($row['role_ref_id']);
|
||||
}
|
||||
if (!$form || !is_array($form)) {
|
||||
$this->error('请填写进件资料');
|
||||
}
|
||||
try {
|
||||
$service = new HuifuOnboardAdminService();
|
||||
$saved = $service->saveDraft($userId, $roleType, $roleRefId, $form);
|
||||
$this->success('进件资料已保存,状态为待后台审核', null, [
|
||||
'id' => $saved ? $saved['id'] : 0,
|
||||
]);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->error($e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
$this->error(HuifuOnboardError::translate($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public function retry($ids = null)
|
||||
{
|
||||
$row = OnboardModel::get($ids);
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
<link rel="stylesheet" href="__CDN__/assets/addons/shopro/libs/element/element.css">
|
||||
<style>
|
||||
[v-cloak] { display: none; }
|
||||
#huifuOnboardAdd { padding: 16px 20px 24px; color: #444; max-height: 78vh; overflow-y: auto; }
|
||||
#huifuOnboardAdd .form-input { width: 100%; }
|
||||
#huifuOnboardAdd .section-title { font-size: 14px; font-weight: 600; margin: 18px 0 12px; padding-left: 8px; border-left: 3px solid #409EFF; }
|
||||
#huifuOnboardAdd .field-tip { color: #909399; font-size: 12px; line-height: 1.5; margin-top: 4px; }
|
||||
#huifuOnboardAdd .upload-box { width: 140px; height: 90px; border: 1px dashed #dcdfe6; border-radius: 4px; display: flex; align-items: center; justify-content: center; overflow: hidden; background: #fafafa; }
|
||||
#huifuOnboardAdd .upload-box img { width: 100%; height: 100%; object-fit: cover; }
|
||||
#huifuOnboardAdd .branch-item { padding: 8px 0; border-bottom: 1px solid #f0f0f0; cursor: pointer; }
|
||||
#huifuOnboardAdd .branch-item:hover { background: #f5f7fa; }
|
||||
#huifuOnboardAdd .branch-panel { max-height: 180px; overflow-y: auto; border: 1px solid #ebeef5; border-radius: 4px; padding: 0 10px; margin-top: 6px; }
|
||||
</style>
|
||||
<script src="__CDN__/assets/addons/shopro/libs/vue.js"></script>
|
||||
<script src="__CDN__/assets/addons/shopro/libs/element/element.js"></script>
|
||||
<div id="huifuOnboardAdd" v-cloak>
|
||||
<el-form label-width="120px" size="small">
|
||||
<div class="section-title">用户与角色</div>
|
||||
<el-form-item label="选择用户" v-if="!isEdit">
|
||||
<el-select v-model="userId" filterable remote reserve-keyword placeholder="输入昵称/手机号/ID搜索" :remote-method="searchUser" :loading="userLoading" class="form-input" @change="onUserChange">
|
||||
<el-option v-for="item in userOptions" :key="item.id" :label="item.nickname + ' (' + (item.mobile || item.id) + ')'" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户" v-else>
|
||||
<span>{{ userLabel }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="进件角色" v-if="!isEdit">
|
||||
<el-select v-model="roleKey" placeholder="请先选择用户" class="form-input" :disabled="!roleOptions.length" @change="onRoleChange">
|
||||
<el-option v-for="item in roleOptions" :key="item.role_type + '-' + item.role_ref_id" :label="item.role_label + (item.onboard_status ? ' [' + item.onboard_status + ']' : '')" :value="item.role_type + ':' + item.role_ref_id" :disabled="!item.can_save">
|
||||
<span>{{ item.role_label }}</span>
|
||||
<span style="color:#909399;font-size:12px;margin-left:8px;">{{ item.disabled_reason || (item.onboard_status ? '已有记录:' + item.onboard_status : '新建') }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="进件角色" v-else>
|
||||
<span>{{ roleLabel }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<div class="section-title">账户类型</div>
|
||||
<el-form-item label="结算账户类型">
|
||||
<el-radio-group v-model="form.account_type">
|
||||
<el-radio label="indv">个人</el-radio>
|
||||
<el-radio label="ent">个体户/企业</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行卡类型" v-if="form.account_type === 'ent'">
|
||||
<el-radio-group v-model="form.bank_account_type">
|
||||
<el-radio label="0">对私</el-radio>
|
||||
<el-radio label="1">对公</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="form.account_type === 'indv'">
|
||||
<div class="section-title">身份实名信息</div>
|
||||
<el-form-item label="真实姓名" required>
|
||||
<el-input v-model="form.name" class="form-input" placeholder="与身份证一致"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" required>
|
||||
<el-input v-model="form.cert_no" maxlength="18" class="form-input"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="证件有效期">
|
||||
<el-radio-group v-model="form.cert_validity_type">
|
||||
<el-radio label="1">长期有效</el-radio>
|
||||
<el-radio label="0">有截止日期</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="证件开始日期" required>
|
||||
<el-date-picker v-model="form.cert_begin_date" type="date" value-format="yyyyMMdd" format="yyyy-MM-dd" placeholder="选择日期" class="form-input"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="证件截止日期" v-if="form.cert_validity_type === '0'" required>
|
||||
<el-date-picker v-model="form.cert_end_date" type="date" value-format="yyyyMMdd" format="yyyy-MM-dd" placeholder="长期可填20991231" class="form-input"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" required>
|
||||
<el-input v-model="form.mobile_no" maxlength="11" class="form-input"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="所在区域" required>
|
||||
<el-cascader v-model="regionPath" :options="areaOptions" :props="areaProps" filterable clearable class="form-input" @change="onRegionChange"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细地址" required>
|
||||
<el-input v-model="form.reg_detail" class="form-input" placeholder="详细居住地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证人像面" required>
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('id_card_front_url', res)">
|
||||
<div class="upload-box">
|
||||
<img v-if="form.id_card_front_url" :src="form.id_card_front_url">
|
||||
<span v-else style="color:#909399;font-size:12px;">点击上传</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证国徽面" required>
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('id_card_back_url', res)">
|
||||
<div class="upload-box">
|
||||
<img v-if="form.id_card_back_url" :src="form.id_card_back_url">
|
||||
<span v-else style="color:#909399;font-size:12px;">点击上传</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="section-title">商户信息</div>
|
||||
<el-form-item label="商户名称" required><el-input v-model="form.reg_name" class="form-input"></el-input></el-form-item>
|
||||
<el-form-item label="统一社会信用代码" required><el-input v-model="form.license_code" maxlength="18" class="form-input"></el-input></el-form-item>
|
||||
<el-form-item label="执照有效期">
|
||||
<el-radio-group v-model="form.license_validity_type"><el-radio label="1">长期</el-radio><el-radio label="0">有截止</el-radio></el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="执照开始日期" required>
|
||||
<el-date-picker v-model="form.license_begin_date" type="date" value-format="yyyyMMdd" format="yyyy-MM-dd" class="form-input"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="执照截止日期" v-if="form.license_validity_type === '0'" required>
|
||||
<el-date-picker v-model="form.license_end_date" type="date" value-format="yyyyMMdd" format="yyyy-MM-dd" class="form-input"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="所在区域" required>
|
||||
<el-cascader v-model="regionPath" :options="areaOptions" :props="areaProps" filterable clearable class="form-input" @change="onRegionChange"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细地址" required><el-input v-model="form.reg_detail" class="form-input"></el-input></el-form-item>
|
||||
<el-form-item label="法人姓名" required><el-input v-model="form.legal_name" class="form-input"></el-input></el-form-item>
|
||||
<el-form-item label="法人手机号" required><el-input v-model="form.legal_mobile" maxlength="11" class="form-input"></el-input></el-form-item>
|
||||
<el-form-item label="法人身份证号" required><el-input v-model="form.legal_cert_no" maxlength="18" class="form-input"></el-input></el-form-item>
|
||||
<el-form-item label="法人证件有效期">
|
||||
<el-radio-group v-model="form.legal_cert_validity_type"><el-radio label="1">长期</el-radio><el-radio label="0">有截止</el-radio></el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="法人证件开始" required>
|
||||
<el-date-picker v-model="form.legal_cert_begin_date" type="date" value-format="yyyyMMdd" format="yyyy-MM-dd" class="form-input"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="法人证件截止" v-if="form.legal_cert_validity_type === '0'" required>
|
||||
<el-date-picker v-model="form.legal_cert_end_date" type="date" value-format="yyyyMMdd" format="yyyy-MM-dd" class="form-input"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照" required>
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('license_url', res)">
|
||||
<div class="upload-box"><img v-if="form.license_url" :src="form.license_url"><span v-else style="color:#909399;font-size:12px;">点击上传</span></div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="法人身份证人像面" required>
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('legal_id_front_url', res)">
|
||||
<div class="upload-box"><img v-if="form.legal_id_front_url" :src="form.legal_id_front_url"><span v-else style="color:#909399;font-size:12px;">点击上传</span></div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="法人身份证国徽面" required>
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('legal_id_back_url', res)">
|
||||
<div class="upload-box"><img v-if="form.legal_id_back_url" :src="form.legal_id_back_url"><span v-else style="color:#909399;font-size:12px;">点击上传</span></div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<div class="section-title">结算账户信息</div>
|
||||
<el-form-item label="银行卡号" required>
|
||||
<el-input v-model="form.card_no" class="form-input" placeholder="一类借记卡"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="开户银行支行" required>
|
||||
<el-input v-model="branchKeyword" class="form-input" placeholder="搜索支行名称" @input="onBranchInput"></el-input>
|
||||
<div class="field-tip" v-if="form.branch_name">已选:{{ form.branch_name }}({{ form.branch_code }})</div>
|
||||
<div class="branch-panel" v-if="branchResults.length">
|
||||
<div class="branch-item" v-for="(item, idx) in branchResults" :key="idx" @click="selectBranch(item)">
|
||||
<div>{{ item.branch_name }}</div>
|
||||
<div style="color:#909399;font-size:12px;">{{ item.branch_code }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行预留手机号" required>
|
||||
<el-input v-model="bankMobile" maxlength="11" class="form-input"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行卡正面照">
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('bank_card_url', res)">
|
||||
<div class="upload-box"><img v-if="form.bank_card_url" :src="form.bank_card_url"><span v-else style="color:#909399;font-size:12px;">点击上传</span></div>
|
||||
</el-upload>
|
||||
<div class="field-tip">建议上传,可加快审核</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="开户许可证" v-if="form.account_type === 'ent' && form.bank_account_type === '1'" required>
|
||||
<el-upload :action="uploadUrl" :show-file-list="false" accept="image/*" :on-success="(res) => onUploadSuccess('account_permit_url', res)">
|
||||
<div class="upload-box"><img v-if="form.account_permit_url" :src="form.account_permit_url"><span v-else style="color:#909399;font-size:12px;">点击上传</span></div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="submitForm">{{ isEdit ? '保存修改' : '保存进件资料' }}</el-button>
|
||||
<el-button @click="closeForm">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -33,6 +33,7 @@
|
||||
<el-option label="已驳回" value="rejected"></el-option>
|
||||
<el-option label="失败" value="failed"></el-option>
|
||||
</el-select>
|
||||
<el-button size="small" type="success" @click="openCreate" style="margin-left:8px;">新建进件</el-button>
|
||||
<el-button size="small" type="primary" @click="getList" style="margin-left:8px;">刷新列表</el-button>
|
||||
</div>
|
||||
<el-table :data="listData" v-loading="loading" border empty-text="暂无进件记录">
|
||||
@@ -55,9 +56,10 @@
|
||||
<el-table-column prop="updatetime" label="更新时间" width="160">
|
||||
<template slot-scope="scope">{{ formatTime(scope.row.updatetime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="240" fixed="right">
|
||||
<el-table-column label="操作" width="300" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="openDetail(scope.row.id)">查看资料</el-button>
|
||||
<el-button size="mini" type="text" @click="openEdit(scope.row.id)" v-if="canEditRow(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="primary" @click="submitRow(scope.row.id)" v-if="scope.row.onboard_status === 'draft'">提交汇付</el-button>
|
||||
<el-button size="mini" @click="pollRow(scope.row.id)" v-if="scope.row.onboard_status !== 'draft' && scope.row.onboard_status !== 'approved'">刷新</el-button>
|
||||
<el-button size="mini" type="warning" @click="retryRow(scope.row.id)" v-if="scope.row.onboard_status === 'rejected' || scope.row.onboard_status === 'failed'">重提</el-button>
|
||||
@@ -133,6 +135,7 @@
|
||||
</div>
|
||||
<span slot="footer">
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
<el-button v-if="detailData && canEditRow(detailData.record)" @click="editFromDetail">编辑资料</el-button>
|
||||
<el-button type="primary" v-if="detailData && detailData.can_submit_huifu" @click="submitFromDetail">提交汇付</el-button>
|
||||
<el-button v-if="detailData && detailData.can_poll" @click="pollFromDetail">刷新状态</el-button>
|
||||
<el-button type="warning" v-if="detailData && detailData.can_retry_huifu" @click="retryFromDetail">重新提交</el-button>
|
||||
|
||||
在新工单中引用
屏蔽一个用户