```后台: 新增用户搜索功能并优化进件页面UI

- 在Onboard控制器中添加searchUser方法,支持按昵称、手机号、ID搜索用户
- 修改noNeedRight权限配置,添加searchUser访问权限
- 重构用户选择逻辑,使用新的fetchUserPublic和formatUserOption方法
- 实现用户搜索功能,支持关键词模糊匹配并返回格式化的用户选项
- 优化前端页面样式,采用卡片式布局提升用户体验
- 添加用户头像显示和个人信息展示功能
- 调整页面结构,增加标题栏、底部操作栏等组件
```
这个提交包含在:
可乐加冰
2026-09-29 15:47:22 +08:00
父节点 2e7cbefdcd
当前提交 375bc7af20
共有 7 个文件被更改,包括 949 次插入 和 359 次删除

查看文件

@@ -13,7 +13,7 @@ use app\admin\controller\shopro\Base;
class Onboard extends Base
{
protected $noNeedRight = ['userRoles', 'searchBranch'];
protected $noNeedRight = ['userRoles', 'searchUser', 'searchBranch'];
public function index()
{
@@ -106,17 +106,22 @@ class Onboard extends Base
if ($userId <= 0) {
$this->error('请选择用户');
}
$user = HuifuOnboardAdminView::enrichListRow(['user_id' => $userId, 'payload' => '']);
$userRow = HuifuOnboardAdminView::fetchUserPublic($userId);
$this->success('ok', null, [
'user' => [
'id' => $userId,
'nickname' => $user['user_nickname'] ?? '',
'mobile' => $user['user_mobile'] ?? '',
],
'user' => HuifuOnboardAdminView::formatUserOption($userRow),
'roles' => HuifuOnboardAdminView::listRolesForUser($userId),
]);
}
public function searchUser()
{
$keyword = trim((string) $this->request->get('keyword', ''));
if ($keyword === '') {
$this->success('ok', null, []);
}
$this->success('ok', null, HuifuOnboardAdminView::searchUsers($keyword, intval($this->request->get('limit', 20))));
}
public function searchBranch()
{
$keyword = trim((string) $this->request->get('keyword', ''));

查看文件

@@ -1,180 +1,391 @@
<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 {
padding: 0;
color: #303133;
max-height: 82vh;
overflow-y: auto;
background: #f5f7fa;
}
#huifuOnboardAdd .page-header {
padding: 16px 20px 12px;
background: #fff;
border-bottom: 1px solid #ebeef5;
}
#huifuOnboardAdd .page-title { font-size: 16px; font-weight: 600; color: #303133; margin-bottom: 4px; }
#huifuOnboardAdd .page-desc { font-size: 12px; color: #909399; line-height: 1.6; }
#huifuOnboardAdd .form-body { padding: 12px 16px 0; }
#huifuOnboardAdd .section-card {
background: #fff;
border-radius: 8px;
padding: 14px 16px 4px;
margin-bottom: 12px;
border: 1px solid #ebeef5;
}
#huifuOnboardAdd .section-title {
font-size: 14px;
font-weight: 600;
margin: 0 0 14px;
padding-left: 10px;
border-left: 3px solid #7536D0;
line-height: 1.2;
}
#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 {
width: 148px;
height: 96px;
border: 1px dashed #dcdfe6;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: #fafafa;
cursor: pointer;
transition: border-color .2s;
}
#huifuOnboardAdd .upload-box:hover { border-color: #7536D0; }
#huifuOnboardAdd .upload-box img { width: 100%; height: 100%; object-fit: cover; }
#huifuOnboardAdd .upload-grid { display: flex; flex-wrap: wrap; gap: 16px; }
#huifuOnboardAdd .upload-item-label { font-size: 12px; color: #606266; margin-bottom: 6px; }
#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; }
#huifuOnboardAdd .branch-panel {
max-height: 180px;
overflow-y: auto;
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 0 10px;
margin-top: 6px;
background: #fff;
}
#huifuOnboardAdd .user-selected-card {
display: flex;
align-items: center;
margin-top: 8px;
padding: 8px 12px;
background: #f8f5ff;
border: 1px solid #e8dff5;
border-radius: 6px;
}
#huifuOnboardAdd .user-selected-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
background: #7536D0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-shrink: 0;
}
#huifuOnboardAdd .user-selected-name { font-size: 14px; font-weight: 600; color: #303133; }
#huifuOnboardAdd .user-selected-meta { font-size: 12px; color: #909399; margin-top: 2px; }
#huifuOnboardAdd .form-footer {
position: sticky;
bottom: 0;
z-index: 2;
padding: 12px 20px;
background: #fff;
border-top: 1px solid #ebeef5;
text-align: right;
}
.user-select-dropdown .el-select-dropdown__item { height: auto; line-height: 1.4; padding: 8px 12px; }
.user-option { display: flex; align-items: center; min-width: 320px; }
.user-option-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
flex-shrink: 0;
background: #7536D0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
}
.user-option-body { flex: 1; min-width: 0; }
.user-option-name { font-size: 13px; color: #303133; font-weight: 600; }
.user-option-nick { margin-right: 4px; }
.user-option-real { font-weight: 400; color: #7536D0; font-size: 12px; }
.user-option-meta { font-size: 12px; color: #909399; margin-top: 2px; }
</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 class="page-header">
<div class="page-title">{{ isEdit ? '编辑汇付进件资料' : '新建汇付进件' }}</div>
<div class="page-desc">请按用户真实信息填写;个人进件持卡人姓名需与身份证姓名一致。保存后为草稿,可在列表中提交汇付审核。</div>
</div>
<div class="form-body">
<el-form label-width="110px" size="small">
<div class="section-card">
<div class="section-title">用户与角色</div>
<el-form-item label="选择用户" v-if="!isEdit">
<el-select
v-model="userId"
filterable
remote
reserve-keyword
clearable
popper-class="user-select-dropdown"
placeholder="输入昵称/手机号/ID搜索"
:remote-method="searchUser"
:loading="userLoading"
class="form-input"
@change="onUserChange">
<el-option v-for="item in userOptions" :key="item.id" :label="item.option_label" :value="item.id">
<div class="user-option">
<img v-if="item.avatar" :src="item.avatar" class="user-option-avatar">
<div v-else class="user-option-avatar">{{ userAvatarText(item) }}</div>
<div class="user-option-body">
<div class="user-option-name">
<span class="user-option-nick">{{ item.display_name }}</span>
<span v-if="item.real_name && item.real_name !== item.display_name" class="user-option-real">(实名:{{ item.real_name }})</span>
</div>
<div class="user-option-meta">
ID {{ item.id }}
<template v-if="item.mobile"> · 手机 {{ item.mobile }}</template>
<template v-if="item.nickname && item.nickname !== item.display_name"> · 昵称 {{ item.nickname }}</template>
</div>
</div>
</div>
</el-option>
</el-select>
<div class="user-selected-card" v-if="selectedUser && selectedUser.id">
<img v-if="selectedUser.avatar" :src="selectedUser.avatar" class="user-selected-avatar" style="border-radius:50%;">
<div v-else class="user-selected-avatar">{{ userAvatarText(selectedUser) }}</div>
<div>
<div class="user-selected-name">{{ selectedUser.option_label }}</div>
<div class="user-selected-meta" v-if="selectedUser.real_name">实名:{{ selectedUser.real_name }}</div>
</div>
</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>
</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>
</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 class="section-card">
<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>
</div>
<template v-if="form.account_type === 'indv'">
<div class="section-card">
<div class="section-title">身份实名信息</div>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="真实姓名" required>
<el-input v-model="form.name" class="form-input" placeholder="与身份证一致"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身份证号" required>
<el-input v-model="form.cert_no" maxlength="18" class="form-input"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号" required>
<el-input v-model="form.mobile_no" maxlength="11" class="form-input"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<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-col>
<el-col :span="12">
<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-col>
<el-col :span="12" v-if="form.cert_validity_type === '0'">
<el-form-item label="证件截止日期" 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-col>
<el-col :span="24">
<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-col>
<el-col :span="24">
<el-form-item label="详细地址" required>
<el-input v-model="form.reg_detail" class="form-input" placeholder="详细居住地址"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="证件照片" required>
<div class="upload-grid">
<div>
<div class="upload-item-label">身份证人像面</div>
<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>
</div>
<div>
<div class="upload-item-label">身份证国徽面</div>
<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>
</div>
</div>
</el-form-item>
</div>
</template>
<template v-else>
<div class="section-card">
<div class="section-title">商户信息</div>
<el-row :gutter="16">
<el-col :span="12"><el-form-item label="商户名称" required><el-input v-model="form.reg_name" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="统一社会信用代码" required><el-input v-model="form.license_code" maxlength="18" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12">
<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-col>
<el-col :span="12">
<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-col>
<el-col :span="12" v-if="form.license_validity_type === '0'">
<el-form-item label="执照截止日期" 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-col>
<el-col :span="24">
<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-col>
<el-col :span="24"><el-form-item label="详细地址" required><el-input v-model="form.reg_detail" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="法人姓名" required><el-input v-model="form.legal_name" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="法人手机号" required><el-input v-model="form.legal_mobile" maxlength="11" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="法人身份证号" required><el-input v-model="form.legal_cert_no" maxlength="18" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12">
<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-col>
<el-col :span="12">
<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-col>
<el-col :span="12" v-if="form.legal_cert_validity_type === '0'">
<el-form-item label="法人证件截止" 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-col>
</el-row>
<el-form-item label="证照上传" required>
<div class="upload-grid">
<div>
<div class="upload-item-label">营业执照</div>
<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>
</div>
<div>
<div class="upload-item-label">法人身份证人像面</div>
<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>
</div>
<div>
<div class="upload-item-label">法人身份证国徽面</div>
<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>
</div>
</div>
</el-form-item>
</div>
</template>
<div class="section-card">
<div class="section-title">结算账户信息</div>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="银行卡号" required>
<el-input v-model="form.card_no" class="form-input" placeholder="一类借记卡"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="银行预留手机号" required>
<el-input v-model="bankMobile" maxlength="11" class="form-input"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<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-col>
</el-row>
<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>
</div>
</el-form>
</div>
<div class="form-footer">
<el-button @click="closeForm">取消</el-button>
<el-button type="primary" :loading="saving" @click="submitForm">{{ isEdit ? '保存修改' : '保存进件资料' }}</el-button>
</div>
</div>

查看文件

@@ -94,6 +94,11 @@ class HuifuOnboardAdminView
];
}
public static function fetchUserPublic($userId)
{
return self::fetchUser($userId);
}
protected static function fetchUser($userId)
{
$userId = intval($userId);
@@ -101,12 +106,113 @@ class HuifuOnboardAdminView
return [];
}
try {
return Db::name('user')->where('id', $userId)->field('id,nickname,mobile,avatar')->find() ?: [];
return Db::name('user')->where('id', $userId)->field('id,nickname,username,mobile,avatar')->find() ?: [];
} catch (\Throwable $e) {
return [];
}
}
/**
* 后台进件:搜索可选用户(下拉展示昵称/实名/手机/ID)
*/
public static function searchUsers($keyword, $limit = 20)
{
$keyword = trim((string) $keyword);
if ($keyword === '') {
return [];
}
$limit = max(1, min(50, intval($limit)));
$idKeyword = is_numeric($keyword) ? intval($keyword) : 0;
$query = Db::name('user')->where(function ($q) use ($keyword, $idKeyword) {
if ($idKeyword > 0) {
$q->whereOr('id', '=', $idKeyword);
}
$q->whereOr('nickname', 'like', "%{$keyword}%")
->whereOr('mobile', 'like', "%{$keyword}%")
->whereOr('username', 'like', "%{$keyword}%");
});
$list = $query->field('id,nickname,username,mobile,avatar')->order('id desc')->limit($limit)->select();
$rows = [];
foreach ($list as $user) {
$rows[] = self::formatUserOption($user);
}
return $rows;
}
public static function formatUserOption($user)
{
$user = is_array($user) ? $user : (method_exists($user, 'toArray') ? $user->toArray() : []);
$id = intval($user['id'] ?? 0);
if ($id <= 0) {
return [];
}
$nickname = trim((string) ($user['nickname'] ?? ''));
$username = trim((string) ($user['username'] ?? ''));
$mobile = trim((string) ($user['mobile'] ?? ''));
$avatar = self::fullUrl($user['avatar'] ?? '');
$realName = self::fetchUserRealNameHint($id);
$displayName = self::resolveUserDisplayName($id, $nickname, $username, $realName);
return [
'id' => $id,
'nickname' => $nickname,
'username' => $username,
'mobile' => $mobile,
'avatar' => $avatar,
'display_name' => $displayName,
'real_name' => $realName,
'option_label' => self::buildUserOptionLabel($displayName, $realName, $mobile, $id),
];
}
protected static function resolveUserDisplayName($id, $nickname, $username, $realName = '')
{
if ($nickname !== '' && $nickname !== (string) $id) {
return $nickname;
}
if ($username !== '' && $username !== (string) $id) {
return $username;
}
if ($realName !== '') {
return $realName;
}
if ($nickname !== '') {
return $nickname;
}
return '用户' . $id;
}
protected static function fetchUserRealNameHint($userId)
{
$userId = intval($userId);
if ($userId <= 0) {
return '';
}
try {
$row = Db::name('shopro_huifu_onboard')->where('user_id', $userId)->order('id desc')->find();
if (!$row) {
return '';
}
$payload = self::parsePayload($row['payload'] ?? '');
return trim((string) ($payload['name'] ?? $payload['legal_name'] ?? $payload['reg_name'] ?? ''));
} catch (\Throwable $e) {
return '';
}
}
protected static function buildUserOptionLabel($displayName, $realName, $mobile, $id)
{
$parts = [$displayName];
if ($realName !== '' && $realName !== $displayName) {
$parts[] = '实名:' . $realName;
}
if ($mobile !== '') {
$parts[] = $mobile;
}
$parts[] = 'ID:' . $id;
return implode(' · ', $parts);
}
protected static function businessStatus(array $data)
{
$roleType = $data['role_type'] ?? '';

查看文件

@@ -82,6 +82,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
roleLabel: '',
userOptions: [],
userLoading: false,
selectedUser: null,
roleOptions: [],
form: defaultForm(),
regionPath: [],
@@ -160,16 +161,34 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
this.form.area_id = this.form.shopro_city_id;
this.form.district_id = this.form.shopro_district_id;
},
userAvatarText: function (item) {
if (!item) return '?';
var name = item.display_name || item.nickname || '';
return name ? name.slice(0, 1) : '?';
},
applySelectedUser: function (user) {
if (!user || !user.id) {
this.selectedUser = null;
this.userLabel = '';
return;
}
this.selectedUser = user;
this.userLabel = user.option_label || (user.display_name + ' (' + (user.mobile || user.id) + ')');
},
searchUser: function (keyword) {
if (!keyword) return;
keyword = (keyword || '').trim();
if (!keyword) {
this.userOptions = [];
return;
}
var vm = this;
vm.userLoading = true;
Fast.api.ajax({
url: 'shopro/user/user/index',
url: 'shopro_huifu/onboard/searchUser',
type: 'GET',
data: { offset: 0, limit: 20, searchWhere: keyword }
data: { keyword: keyword, limit: 20 }
}, function (ret, res) {
vm.userOptions = (res.data && res.data.rows) || [];
vm.userOptions = res.data || [];
vm.userLoading = false;
return false;
}, function () {
@@ -182,7 +201,14 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
vm.roleType = '';
vm.roleRefId = 0;
vm.roleOptions = [];
if (!vm.userId) return;
if (!vm.userId) {
vm.applySelectedUser(null);
return;
}
var cached = vm.userOptions.find(function (item) { return item.id === vm.userId; });
if (cached) {
vm.applySelectedUser(cached);
}
Fast.api.ajax({
url: 'shopro_huifu/onboard/userRoles',
type: 'GET',
@@ -191,7 +217,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
var data = res.data || {};
vm.roleOptions = data.roles || [];
if (data.user) {
vm.userLabel = (data.user.nickname || '') + ' (' + (data.user.mobile || data.user.id) + ')';
vm.applySelectedUser(data.user);
}
return false;
});
@@ -311,7 +337,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
openCreate: function () {
var vm = this;
Backend.api.open('shopro_huifu/onboard/add', '新建汇付进件', {
area: ['900px', '90%'],
area: ['960px', '92%'],
callback: function () {
vm.getList();
}
@@ -320,7 +346,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
openEdit: function (id) {
var vm = this;
Backend.api.open('shopro_huifu/onboard/edit/ids/' + id, '编辑汇付进件', {
area: ['900px', '90%'],
area: ['960px', '92%'],
callback: function () {
vm.getList();
if (vm.detailVisible && vm.detailId === id) {

查看文件

@@ -13,7 +13,7 @@ use app\admin\controller\shopro\Base;
class Onboard extends Base
{
protected $noNeedRight = ['userRoles', 'searchBranch'];
protected $noNeedRight = ['userRoles', 'searchUser', 'searchBranch'];
public function index()
{
if ($this->request->isAjax()) {
@@ -108,17 +108,22 @@ class Onboard extends Base
if ($userId <= 0) {
$this->error('请选择用户');
}
$user = HuifuOnboardAdminView::enrichListRow(['user_id' => $userId, 'payload' => '']);
$userRow = HuifuOnboardAdminView::fetchUserPublic($userId);
$this->success('ok', null, [
'user' => [
'id' => $userId,
'nickname' => $user['user_nickname'] ?? '',
'mobile' => $user['user_mobile'] ?? '',
],
'user' => HuifuOnboardAdminView::formatUserOption($userRow),
'roles' => HuifuOnboardAdminView::listRolesForUser($userId),
]);
}
public function searchUser()
{
$keyword = trim((string) $this->request->get('keyword', ''));
if ($keyword === '') {
$this->success('ok', null, []);
}
$this->success('ok', null, HuifuOnboardAdminView::searchUsers($keyword, intval($this->request->get('limit', 20))));
}
public function searchBranch()
{
$keyword = trim((string) $this->request->get('keyword', ''));

查看文件

@@ -1,180 +1,391 @@
<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 {
padding: 0;
color: #303133;
max-height: 82vh;
overflow-y: auto;
background: #f5f7fa;
}
#huifuOnboardAdd .page-header {
padding: 16px 20px 12px;
background: #fff;
border-bottom: 1px solid #ebeef5;
}
#huifuOnboardAdd .page-title { font-size: 16px; font-weight: 600; color: #303133; margin-bottom: 4px; }
#huifuOnboardAdd .page-desc { font-size: 12px; color: #909399; line-height: 1.6; }
#huifuOnboardAdd .form-body { padding: 12px 16px 0; }
#huifuOnboardAdd .section-card {
background: #fff;
border-radius: 8px;
padding: 14px 16px 4px;
margin-bottom: 12px;
border: 1px solid #ebeef5;
}
#huifuOnboardAdd .section-title {
font-size: 14px;
font-weight: 600;
margin: 0 0 14px;
padding-left: 10px;
border-left: 3px solid #7536D0;
line-height: 1.2;
}
#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 {
width: 148px;
height: 96px;
border: 1px dashed #dcdfe6;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: #fafafa;
cursor: pointer;
transition: border-color .2s;
}
#huifuOnboardAdd .upload-box:hover { border-color: #7536D0; }
#huifuOnboardAdd .upload-box img { width: 100%; height: 100%; object-fit: cover; }
#huifuOnboardAdd .upload-grid { display: flex; flex-wrap: wrap; gap: 16px; }
#huifuOnboardAdd .upload-item-label { font-size: 12px; color: #606266; margin-bottom: 6px; }
#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; }
#huifuOnboardAdd .branch-panel {
max-height: 180px;
overflow-y: auto;
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 0 10px;
margin-top: 6px;
background: #fff;
}
#huifuOnboardAdd .user-selected-card {
display: flex;
align-items: center;
margin-top: 8px;
padding: 8px 12px;
background: #f8f5ff;
border: 1px solid #e8dff5;
border-radius: 6px;
}
#huifuOnboardAdd .user-selected-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
background: #7536D0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-shrink: 0;
}
#huifuOnboardAdd .user-selected-name { font-size: 14px; font-weight: 600; color: #303133; }
#huifuOnboardAdd .user-selected-meta { font-size: 12px; color: #909399; margin-top: 2px; }
#huifuOnboardAdd .form-footer {
position: sticky;
bottom: 0;
z-index: 2;
padding: 12px 20px;
background: #fff;
border-top: 1px solid #ebeef5;
text-align: right;
}
.user-select-dropdown .el-select-dropdown__item { height: auto; line-height: 1.4; padding: 8px 12px; }
.user-option { display: flex; align-items: center; min-width: 320px; }
.user-option-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
flex-shrink: 0;
background: #7536D0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
}
.user-option-body { flex: 1; min-width: 0; }
.user-option-name { font-size: 13px; color: #303133; font-weight: 600; }
.user-option-nick { margin-right: 4px; }
.user-option-real { font-weight: 400; color: #7536D0; font-size: 12px; }
.user-option-meta { font-size: 12px; color: #909399; margin-top: 2px; }
</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 class="page-header">
<div class="page-title">{{ isEdit ? '编辑汇付进件资料' : '新建汇付进件' }}</div>
<div class="page-desc">请按用户真实信息填写;个人进件持卡人姓名需与身份证姓名一致。保存后为草稿,可在列表中提交汇付审核。</div>
</div>
<div class="form-body">
<el-form label-width="110px" size="small">
<div class="section-card">
<div class="section-title">用户与角色</div>
<el-form-item label="选择用户" v-if="!isEdit">
<el-select
v-model="userId"
filterable
remote
reserve-keyword
clearable
popper-class="user-select-dropdown"
placeholder="输入昵称/手机号/ID搜索"
:remote-method="searchUser"
:loading="userLoading"
class="form-input"
@change="onUserChange">
<el-option v-for="item in userOptions" :key="item.id" :label="item.option_label" :value="item.id">
<div class="user-option">
<img v-if="item.avatar" :src="item.avatar" class="user-option-avatar">
<div v-else class="user-option-avatar">{{ userAvatarText(item) }}</div>
<div class="user-option-body">
<div class="user-option-name">
<span class="user-option-nick">{{ item.display_name }}</span>
<span v-if="item.real_name && item.real_name !== item.display_name" class="user-option-real">(实名:{{ item.real_name }})</span>
</div>
<div class="user-option-meta">
ID {{ item.id }}
<template v-if="item.mobile"> · 手机 {{ item.mobile }}</template>
<template v-if="item.nickname && item.nickname !== item.display_name"> · 昵称 {{ item.nickname }}</template>
</div>
</div>
</div>
</el-option>
</el-select>
<div class="user-selected-card" v-if="selectedUser && selectedUser.id">
<img v-if="selectedUser.avatar" :src="selectedUser.avatar" class="user-selected-avatar" style="border-radius:50%;">
<div v-else class="user-selected-avatar">{{ userAvatarText(selectedUser) }}</div>
<div>
<div class="user-selected-name">{{ selectedUser.option_label }}</div>
<div class="user-selected-meta" v-if="selectedUser.real_name">实名:{{ selectedUser.real_name }}</div>
</div>
</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>
</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>
</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 class="section-card">
<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>
</div>
<template v-if="form.account_type === 'indv'">
<div class="section-card">
<div class="section-title">身份实名信息</div>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="真实姓名" required>
<el-input v-model="form.name" class="form-input" placeholder="与身份证一致"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身份证号" required>
<el-input v-model="form.cert_no" maxlength="18" class="form-input"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号" required>
<el-input v-model="form.mobile_no" maxlength="11" class="form-input"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<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-col>
<el-col :span="12">
<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-col>
<el-col :span="12" v-if="form.cert_validity_type === '0'">
<el-form-item label="证件截止日期" 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-col>
<el-col :span="24">
<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-col>
<el-col :span="24">
<el-form-item label="详细地址" required>
<el-input v-model="form.reg_detail" class="form-input" placeholder="详细居住地址"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="证件照片" required>
<div class="upload-grid">
<div>
<div class="upload-item-label">身份证人像面</div>
<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>
</div>
<div>
<div class="upload-item-label">身份证国徽面</div>
<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>
</div>
</div>
</el-form-item>
</div>
</template>
<template v-else>
<div class="section-card">
<div class="section-title">商户信息</div>
<el-row :gutter="16">
<el-col :span="12"><el-form-item label="商户名称" required><el-input v-model="form.reg_name" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="统一社会信用代码" required><el-input v-model="form.license_code" maxlength="18" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12">
<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-col>
<el-col :span="12">
<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-col>
<el-col :span="12" v-if="form.license_validity_type === '0'">
<el-form-item label="执照截止日期" 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-col>
<el-col :span="24">
<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-col>
<el-col :span="24"><el-form-item label="详细地址" required><el-input v-model="form.reg_detail" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="法人姓名" required><el-input v-model="form.legal_name" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="法人手机号" required><el-input v-model="form.legal_mobile" maxlength="11" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12"><el-form-item label="法人身份证号" required><el-input v-model="form.legal_cert_no" maxlength="18" class="form-input"></el-input></el-form-item></el-col>
<el-col :span="12">
<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-col>
<el-col :span="12">
<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-col>
<el-col :span="12" v-if="form.legal_cert_validity_type === '0'">
<el-form-item label="法人证件截止" 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-col>
</el-row>
<el-form-item label="证照上传" required>
<div class="upload-grid">
<div>
<div class="upload-item-label">营业执照</div>
<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>
</div>
<div>
<div class="upload-item-label">法人身份证人像面</div>
<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>
</div>
<div>
<div class="upload-item-label">法人身份证国徽面</div>
<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>
</div>
</div>
</el-form-item>
</div>
</template>
<div class="section-card">
<div class="section-title">结算账户信息</div>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="银行卡号" required>
<el-input v-model="form.card_no" class="form-input" placeholder="一类借记卡"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="银行预留手机号" required>
<el-input v-model="bankMobile" maxlength="11" class="form-input"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<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-col>
</el-row>
<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>
</div>
</el-form>
</div>
<div class="form-footer">
<el-button @click="closeForm">取消</el-button>
<el-button type="primary" :loading="saving" @click="submitForm">{{ isEdit ? '保存修改' : '保存进件资料' }}</el-button>
</div>
</div>

查看文件

@@ -82,6 +82,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
roleLabel: '',
userOptions: [],
userLoading: false,
selectedUser: null,
roleOptions: [],
form: defaultForm(),
regionPath: [],
@@ -160,16 +161,34 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
this.form.area_id = this.form.shopro_city_id;
this.form.district_id = this.form.shopro_district_id;
},
userAvatarText: function (item) {
if (!item) return '?';
var name = item.display_name || item.nickname || '';
return name ? name.slice(0, 1) : '?';
},
applySelectedUser: function (user) {
if (!user || !user.id) {
this.selectedUser = null;
this.userLabel = '';
return;
}
this.selectedUser = user;
this.userLabel = user.option_label || (user.display_name + ' (' + (user.mobile || user.id) + ')');
},
searchUser: function (keyword) {
if (!keyword) return;
keyword = (keyword || '').trim();
if (!keyword) {
this.userOptions = [];
return;
}
var vm = this;
vm.userLoading = true;
Fast.api.ajax({
url: 'shopro/user/user/index',
url: 'shopro_huifu/onboard/searchUser',
type: 'GET',
data: { offset: 0, limit: 20, searchWhere: keyword }
data: { keyword: keyword, limit: 20 }
}, function (ret, res) {
vm.userOptions = (res.data && res.data.rows) || [];
vm.userOptions = res.data || [];
vm.userLoading = false;
return false;
}, function () {
@@ -182,7 +201,14 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
vm.roleType = '';
vm.roleRefId = 0;
vm.roleOptions = [];
if (!vm.userId) return;
if (!vm.userId) {
vm.applySelectedUser(null);
return;
}
var cached = vm.userOptions.find(function (item) { return item.id === vm.userId; });
if (cached) {
vm.applySelectedUser(cached);
}
Fast.api.ajax({
url: 'shopro_huifu/onboard/userRoles',
type: 'GET',
@@ -191,7 +217,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
var data = res.data || {};
vm.roleOptions = data.roles || [];
if (data.user) {
vm.userLabel = (data.user.nickname || '') + ' (' + (data.user.mobile || data.user.id) + ')';
vm.applySelectedUser(data.user);
}
return false;
});
@@ -311,7 +337,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
openCreate: function () {
var vm = this;
Backend.api.open('shopro_huifu/onboard/add', '新建汇付进件', {
area: ['900px', '90%'],
area: ['960px', '92%'],
callback: function () {
vm.getList();
}
@@ -320,7 +346,7 @@ define(['jquery', 'bootstrap', 'backend'], function ($, undefined, Backend) {
openEdit: function (id) {
var vm = this;
Backend.api.open('shopro_huifu/onboard/edit/ids/' + id, '编辑汇付进件', {
area: ['900px', '90%'],
area: ['960px', '92%'],
callback: function () {
vm.getList();
if (vm.detailVisible && vm.detailId === id) {