第二次更新

This commit is contained in:
2026-09-10 20:56:46 +08:00
parent 4d6a7e7aac
commit caeb4d6760
310 changed files with 2427 additions and 382 deletions
+30 -1
View File
@@ -39,6 +39,24 @@
v-decorator="['callback', {rules: [{required: true, min: 2, message: '请输入至少2个字符'}]}]"
/>
</a-form-item>
<a-form-item label="企油通企业" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select
placeholder="不选则使用默认企业"
allowClear
v-decorator="['qyt_enterprise_id']"
>
<a-select-option
v-for="item in enterpriseList"
:key="item.enterprise_id"
:value="item.enterprise_id"
>
{{ item.company_name }}({{ item.passageway === 'pingan' ? '平安银行' : '支付宝企业码' }}){{ item.is_default ? ' · 默认' : '' }}
</a-select-option>
</a-select>
<div class="form-item-help">
<p class="extra">该合作商下单时使用哪个企业的对接参数与支付通道。留空则走默认企业。</p>
</div>
</a-form-item>
</div>
</div>
@@ -52,6 +70,7 @@
<script>
import * as PartnersApi from '@/api/partners'
import * as EnterpriseApi from '@/api/qyt/enterprise'
import PartnersModel from '@/common/model/partners/Index'
export default {
@@ -69,11 +88,14 @@ export default {
// 当前表单元素
form: this.$form.createForm(this),
// 表单数据
formData: PartnersModel.formData
formData: PartnersModel.formData,
// 企油通企业下拉
enterpriseList: []
}
},
// 初始化数据
created () {
this.getEnterpriseList()
this.isLoading = true
// 获取form所需的数据
PartnersModel.getFromData()
@@ -83,6 +105,13 @@ export default {
},
methods: {
// 获取企油通企业下拉(合作商下单时用哪个企业的参数)
getEnterpriseList () {
EnterpriseApi.options().then(result => {
this.enterpriseList = result.data.list
})
},
// 手动强制更新页面
onForceUpdate (bool = false) {
this.$forceUpdate()
+30 -1
View File
@@ -39,6 +39,24 @@
v-decorator="['callback', {rules: [{required: true, min: 2, message: '请输入至少2个字符'}]}]"
/>
</a-form-item>
<a-form-item label="企油通企业" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select
placeholder="不选则使用默认企业"
allowClear
v-decorator="['qyt_enterprise_id']"
>
<a-select-option
v-for="item in enterpriseList"
:key="item.enterprise_id"
:value="item.enterprise_id"
>
{{ item.company_name }}({{ item.passageway === 'pingan' ? '平安银行' : '支付宝企业码' }}){{ item.is_default ? ' · 默认' : '' }}
</a-select-option>
</a-select>
<div class="form-item-help">
<p class="extra">该合作商下单时使用哪个企业的对接参数与支付通道。留空则走默认企业。</p>
</div>
</a-form-item>
</div>
</div>
@@ -52,6 +70,7 @@
<script>
import * as PartnersApi from '@/api/partners'
import * as EnterpriseApi from '@/api/qyt/enterprise'
import { InputNumberGroup } from '@/components'
import PartnersModel from '@/common/model/partners/Index'
import { isEmptyObject } from '@/utils/util'
@@ -76,10 +95,13 @@ export default {
// 合作商ID
partnersId: null,
// 表单数据
formData: PartnersModel.formData
formData: PartnersModel.formData,
// 企油通企业下拉
enterpriseList: []
}
},
created () {
this.getEnterpriseList()
// 初始化数据
this.initData()
},
@@ -89,6 +111,13 @@ export default {
},
methods: {
// 获取企油通企业下拉(合作商下单时用哪个企业的参数)
getEnterpriseList () {
EnterpriseApi.options().then(result => {
this.enterpriseList = result.data.list
})
},
// 初始化数据
initData () {
// 记录合作商ID
+3 -2
View File
@@ -28,6 +28,7 @@
<a-select v-decorator="['oil_status_zsy', { initialValue: -1 }]">
<a-select-option :value="-1">全部</a-select-option>
<a-select-option :value="0">未提交</a-select-option>
<a-select-option :value="1">发放中</a-select-option>
<a-select-option :value="2">发放成功</a-select-option>
<a-select-option :value="3">发放失败</a-select-option>
</a-select>
@@ -275,11 +276,11 @@ export default {
// 企油通发放态文案
qytText (v) {
return { 0: '未提交', 1: '处理中', 2: '发放成功', 3: '发放失败' }[v] || '未知'
return { 0: '未提交', 1: '发放中', 2: '发放成功', 3: '发放失败' }[v] || '未知'
},
// 企油通发放态颜色
qytColor (v) {
return { 2: 'green', 3: 'red' }[v] || ''
return { 1: 'blue', 2: 'green', 3: 'red' }[v] || ''
},
// 重新发放
@@ -0,0 +1,354 @@
<template>
<a-card :bordered="false">
<div class="card-title">{{ $route.meta.title }}</div>
<a-alert type="info" show-icon class="tips-alert">
<div slot="message">
一个接入方 appId 下可挂多个企业,业务接口靠<b>统一社会信用代码</b>区分。
下游下单时按「合作商绑定的企业 → 默认企业」的顺序决定走哪个企业的参数;
企业与通道在下单那一刻固化到订单上,之后改绑定不影响在途订单。
<b>appId 与 RSA 私钥属于接入方级别</b>,在「设置 → 企油通对接」和服务器 .env 里维护,不在本页。
</div>
</a-alert>
<div class="table-operator">
<a-button type="primary" icon="plus" @click="handleCreate">新增企业</a-button>
<a-button icon="cloud-download" :loading="isSyncing" @click="handleSyncList">从企油通拉取企业</a-button>
<a-form layout="inline" :form="searchForm" @submit="handleSearch" class="search-form">
<a-form-item label="企业">
<a-input v-decorator="['name']" placeholder="企业名称 / 信用代码" allowClear />
</a-form-item>
<a-form-item label="状态">
<a-select v-decorator="['status', { initialValue: 'all' }]" style="width: 130px">
<a-select-option value="all">全部</a-select-option>
<a-select-option v-for="(v, k) in StatusText" :key="k" :value="k">{{ v }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="通道">
<a-select v-decorator="['passageway', { initialValue: 'all' }]" style="width: 140px">
<a-select-option value="all">全部</a-select-option>
<a-select-option value="alipay">支付宝企业码</a-select-option>
<a-select-option value="pingan">平安银行</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit">搜索</a-button>
</a-form-item>
</a-form>
</div>
<s-table ref="table" rowKey="enterprise_id" :columns="columns" :data="loadData">
<template slot="company" slot-scope="item">
<div>
<b>{{ item.company_name }}</b>
<a-tag v-if="item.is_default" color="green" class="ml-6">默认</a-tag>
<a-tag v-if="!item.is_enable" class="ml-6">已停用</a-tag>
</div>
<div class="sub">{{ item.business_license }}</div>
<div v-if="item.enterprise_number" class="sub">企业编号:{{ item.enterprise_number }}</div>
</template>
<template slot="passageway" slot-scope="item">
<a-tag :color="item.passageway === 'pingan' ? 'blue' : 'orange'">
{{ item.passageway === 'pingan' ? '平安银行' : '支付宝企业码' }}
</a-tag>
<div v-if="item.passageway === 'pingan'" class="sub">要求实名,下单须传 idCard</div>
<div v-else class="sub">
机构号:<span :class="{ 'text-red': !item.institution_id }">{{ item.institution_id || '未填,无法下发' }}</span>
</div>
<div v-if="item.bound_passageway" class="sub">已开通通道:{{ item.bound_passageway }}</div>
</template>
<template slot="status" slot-scope="item">
<a-badge :status="statusBadge(item.status)" :text="StatusText[item.status] || item.status" />
<div v-if="item.account" class="sub">{{ item.account_bank }} {{ item.account }}</div>
<div v-if="item.sync_text" class="sub">{{ item.sync_text }}</div>
</template>
<template slot="action" slot-scope="item">
<a @click="handleUpdate(item)">编辑</a>
<a-divider type="vertical" />
<a @click="handleApply(item)">提交开户</a>
<a-divider type="vertical" />
<a @click="handleSyncAccount(item)">同步账户</a>
<a-divider type="vertical" />
<a v-if="!item.is_default" @click="handleSetDefault(item)">设为默认</a>
<span v-else class="text-grey">设为默认</span>
<a-divider type="vertical" />
<a class="text-red" @click="handleDelete(item)">删除</a>
</template>
</s-table>
<a-modal
:title="record.enterprise_id ? '编辑企业' : '新增企业'"
:visible="visible"
:confirmLoading="isSaving"
:width="640"
@ok="handleSubmit"
@cancel="visible = false"
>
<a-form :form="form">
<a-form-item label="企业名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input v-decorator="['company_name', { rules: [{ required: true, message: '请填写企业名称' }] }]" />
</a-form-item>
<a-form-item label="统一社会信用代码" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input
placeholder="18 位,企油通的 business_license"
v-decorator="['business_license', { rules: [{ required: true, message: '请填写统一社会信用代码' }] }]"
/>
<div class="form-item-help"><p class="extra">几乎所有企油通接口都会校验它是否归属当前 appId,不属于会直接 403。</p></div>
</a-form-item>
<a-form-item label="营业执照照片地址" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="https://xxx.png" v-decorator="['business_license_url']" />
<div class="form-item-help"><p class="extra">7.2 提交开户申请时必填。</p></div>
</a-form-item>
<a-form-item label="联系人" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input-group compact>
<a-input style="width: 40%" placeholder="姓名" v-decorator="['contact_name']" />
<a-input style="width: 60%" placeholder="手机号" v-decorator="['contact_mobile']" />
</a-input-group>
</a-form-item>
<a-form-item label="支付通道" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-radio-group v-decorator="['passageway', { initialValue: 'alipay' }]">
<a-radio-button value="alipay">支付宝企业码</a-radio-button>
<a-radio-button value="pingan">平安银行</a-radio-button>
</a-radio-group>
<div class="form-item-help">
<p class="extra">平安银行通道要求实名,下单接口的 idCard(司机身份证号)变为必填。</p>
</div>
</a-form-item>
<a-form-item label="机构号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="institution_id" v-decorator="['institution_id']" />
<div class="form-item-help"><p class="extra">支付宝企业码通道必填,不填每一单都会下发失败并原路退回余额。</p></div>
</a-form-item>
<a-form-item label="已开通通道" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="如 1,2" v-decorator="['bound_passageway']" />
<div class="form-item-help"><p class="extra">企油通侧已绑定的通道编码,逗号分隔(1 支付宝企业码 / 2 平安银行)。提交开户申请时按此申请。</p></div>
</a-form-item>
<a-form-item label="编号前缀" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input style="width: 160px" placeholder="留空用全局默认" v-decorator="['no_prefix']" />
<div class="form-item-help">
<p class="extra">batch_no / waybill_no 是<b>全平台唯一</b>而非接入方内唯一,各企业建议用不同前缀,避免撞号。</p>
</div>
</a-form-item>
<a-form-item label="凭证名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input style="width: 160px" placeholder="留空用全局默认" v-decorator="['voucher_name']" />
</a-form-item>
<a-form-item label="appId 覆盖" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="留空用全局 appId" v-decorator="['app_id']" />
<div class="form-item-help">
<p class="extra">仅当该企业使用独立的接入方账号时才填。注意私钥仍在 .env,两者必须配套。</p>
</div>
</a-form-item>
<a-form-item label="设为默认" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-switch v-decorator="['is_default', { valuePropName: 'checked' }]" />
<span class="ml-6 text-grey">合作商未绑定企业时使用该企业</span>
</a-form-item>
<a-form-item label="启用" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-switch v-decorator="['is_enable', { valuePropName: 'checked', initialValue: true }]" />
</a-form-item>
<a-form-item label="备注" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input v-decorator="['remark']" />
</a-form-item>
</a-form>
</a-modal>
</a-card>
</template>
<script>
import * as Api from '@/api/qyt/enterprise'
import { STable } from '@/components/Table'
const StatusText = {
pending: '待完善',
submitted: '待审核',
approved: '审核通过',
rejected: '审核拒绝',
frozen: '已冻结'
}
export default {
name: 'QytEnterpriseIndex',
components: { STable },
data () {
return {
StatusText,
searchForm: this.$form.createForm(this),
form: this.$form.createForm(this),
labelCol: { span: 6 },
wrapperCol: { span: 16 },
queryParam: {},
visible: false,
isSaving: false,
isSyncing: false,
record: {},
columns: [
{ title: 'ID', dataIndex: 'enterprise_id', width: 70 },
{ title: '企业', scopedSlots: { customRender: 'company' } },
{ title: '通道与机构号', scopedSlots: { customRender: 'passageway' } },
{ title: '企油通状态', scopedSlots: { customRender: 'status' } },
{ title: '操作', width: 260, scopedSlots: { customRender: 'action' } }
],
loadData: param => {
return Api.list({ ...param, ...this.queryParam }).then(result => result.data.list)
}
}
},
methods: {
statusBadge (status) {
if (status === 'approved') return 'success'
if (status === 'rejected' || status === 'frozen') return 'error'
if (status === 'submitted') return 'processing'
return 'default'
},
handleSearch (e) {
e.preventDefault()
this.searchForm.validateFields((errors, values) => {
if (errors) return
this.queryParam = { ...this.queryParam, ...values }
this.refresh()
})
},
handleCreate () {
this.record = {}
this.visible = true
this.$nextTick(() => {
this.form.resetFields()
})
},
handleUpdate (item) {
this.record = item
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue({
company_name: item.company_name,
business_license: item.business_license,
business_license_url: item.business_license_url,
contact_name: item.contact_name,
contact_mobile: item.contact_mobile,
passageway: item.passageway,
institution_id: item.institution_id,
bound_passageway: item.bound_passageway,
no_prefix: item.no_prefix,
voucher_name: item.voucher_name,
app_id: item.app_id,
is_default: !!item.is_default,
is_enable: !!item.is_enable,
remark: item.remark
})
})
},
handleSubmit () {
this.form.validateFields((errors, values) => {
if (errors) return
const form = {
...values,
is_default: values.is_default ? 1 : 0,
is_enable: values.is_enable ? 1 : 0
}
this.isSaving = true
const request = this.record.enterprise_id
? Api.edit({ enterpriseId: this.record.enterprise_id, form })
: Api.add({ form })
request
.then(result => {
this.$message.success(result.message, 1.5)
this.visible = false
this.refresh()
})
.finally(() => {
this.isSaving = false
})
})
},
handleSetDefault (item) {
Api.setDefault({ enterpriseId: item.enterprise_id }).then(result => {
this.$message.success(result.message, 1.5)
this.refresh()
})
},
handleDelete (item) {
this.$confirm({
title: '确认删除企业「' + item.company_name + '」?',
content: '已产生订单或已被合作商绑定的企业无法删除,可改为停用。',
onOk: () => {
return Api.deleted({ enterpriseId: item.enterprise_id }).then(result => {
this.$message.success(result.message, 1.5)
this.refresh()
})
}
})
},
handleApply (item) {
this.$confirm({
title: '提交开户申请到企油通?',
content: '将调用 7.2 企业申请创建,提交后由企油通审核。',
onOk: () => {
return Api.apply({ enterpriseId: item.enterprise_id }).then(result => {
this.$message.success(result.message, 2)
this.refresh()
})
}
})
},
handleSyncAccount (item) {
Api.syncAccount({ enterpriseId: item.enterprise_id }).then(result => {
this.$message.success(result.message, 1.5)
this.refresh()
})
},
handleSyncList () {
this.isSyncing = true
Api.syncList({})
.then(result => {
this.$message.success(result.message, 2)
this.refresh()
})
.finally(() => {
this.isSyncing = false
})
},
refresh () {
this.$refs.table && this.$refs.table.refresh()
}
}
}
</script>
<style lang="less" scoped>
.tips-alert {
margin-bottom: 20px;
}
.search-form {
display: inline-block;
margin-left: 16px;
}
.sub {
color: #999;
font-size: 12px;
line-height: 1.7;
}
.ml-6 {
margin-left: 6px;
}
.text-red {
color: #ff4d4f;
}
.text-grey {
color: #bbb;
}
</style>
+180
View File
@@ -0,0 +1,180 @@
<template>
<a-card :bordered="false">
<div class="card-title">{{ $route.meta.title }}</div>
<a-spin :spinning="isLoading">
<a-alert type="warning" show-icon class="tips-alert">
<div slot="message">
出于安全考虑,<b>RSA 私钥与企油通平台公钥不在此处配置</b>,仍只从服务器的
<a-tag>.env</a-tag>文件读取。数据库备份、后台账号一旦外泄,也拿不到签名私钥。
</div>
</a-alert>
<a-alert type="info" show-icon class="tips-alert">
<div slot="message">
本页只放<b>接入方级</b>参数。企业的统一社会信用代码、<b>支付通道开关</b>、机构号属于<b>企业级</b>参数,
请到 <router-link to="/qyt/enterprise/index">设置 → 企业管理</router-link> 里按企业维护。
</div>
</a-alert>
<a-form :form="form" @submit="handleSubmit">
<a-divider orientation="left">接口环境</a-divider>
<a-form-item label="接口域名" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="https://xxx.worknoya.vip" v-decorator="['base_url']" />
<div class="form-item-help">
<p class="extra">不要以斜杠结尾。测试与正式环境地址不同。</p>
</div>
</a-form-item>
<a-form-item label="业务 appId" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input v-decorator="['app_id']" />
</a-form-item>
<a-divider orientation="left">业务参数默认值</a-divider>
<a-form-item label="编号前缀" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input class="input-short" placeholder="HM" v-decorator="['no_prefix']" />
<div class="form-item-help">
<p class="extra">批次号/运单号的全平台唯一前缀,防止与其他接入方撞号。正式建议 HM,测试建议 HMTEST。</p>
<p class="extra">各企业可在企业管理里单独覆盖;已生成编号的订单沿用原编号,改前缀不影响在途订单。</p>
</div>
</a-form-item>
<a-form-item label="凭证名称" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input class="input-short" placeholder="运单卡" v-decorator="['voucher_name']" />
<div class="form-item-help"><p class="extra">各企业可在企业管理里单独覆盖。</p></div>
</a-form-item>
<a-divider orientation="left">H5 与回调</a-divider>
<a-form-item label="H5 落地页地址" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="https://paytaskpage.worknoya.vip/#/" v-decorator="['h5_base_url']" />
<div class="form-item-help">
<p class="extra">司机免登加油页面,后台「获取加油地址」生成的链接以此为前缀。</p>
</div>
</a-form-item>
<a-form-item label="H5 免登 appId" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input v-decorator="['h5_app_id']" />
<div class="form-item-help">
<p class="extra">可能与业务 appId 不同,需线下向对接人确认。</p>
</div>
</a-form-item>
<a-form-item label="消费回调地址" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input placeholder="https://你的域名/qytconsume.php" v-decorator="['consume_notify_url']" />
<div class="form-item-help">
<p class="extra">此处仅作记录留档,实际生效需在企油通后台配置同一地址。</p>
</div>
</a-form-item>
<a-divider orientation="left">其他</a-divider>
<a-form-item label="详细日志" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-switch checked-children="开" un-checked-children="关" v-decorator="['log', { valuePropName: 'checked' }]" />
<div class="form-item-help">
<p class="extra">记录请求与响应报文到 runtime/storelog,手机号与身份证号已脱敏。联调期建议开启。</p>
</div>
</a-form-item>
<a-form-item :wrapperCol="{ span: 10, offset: 5 }">
<a-button type="primary" html-type="submit" :loading="isLoading">提交</a-button>
</a-form-item>
</a-form>
</a-spin>
</a-card>
</template>
<script>
import * as Api from '@/api/setting/store'
import { isEmpty } from '@/utils/util'
export default {
data () {
return {
// 当前设置项的key
key: 'qyt',
// 标签布局属性
labelCol: { span: 5 },
// 输入框布局属性
wrapperCol: { span: 10 },
// loading状态
isLoading: false,
// 当前表单元素
form: this.$form.createForm(this),
// 当前记录详情
record: {}
}
},
// 初始化数据
created () {
this.getDetail()
},
methods: {
// 获取当前详情记录
getDetail () {
this.isLoading = true
Api.detail(this.key)
.then(result => {
this.record = result.data.values
this.setFieldsValue()
})
.finally(() => {
this.isLoading = false
})
},
// 设置默认值
setFieldsValue () {
const { record, $nextTick, form } = this
!isEmpty(form.getFieldsValue()) && $nextTick(() => {
form.setFieldsValue({
'base_url': record.base_url,
'app_id': record.app_id,
'no_prefix': record.no_prefix,
'voucher_name': record.voucher_name,
'h5_base_url': record.h5_base_url,
'h5_app_id': record.h5_app_id,
'consume_notify_url': record.consume_notify_url,
'log': !!Number(record.log)
})
})
},
// 确认按钮
handleSubmit (e) {
e.preventDefault()
const { form: { validateFields } } = this
validateFields((errors, values) => {
!errors && this.onFormSubmit(values)
})
},
// 提交到后端api
onFormSubmit (values) {
this.isLoading = true
const form = { ...values, log: values.log ? 1 : 0 }
Api.update(this.key, { form })
.then(result => {
this.$message.success(result.message, 1.5)
})
.finally(() => {
this.isLoading = false
})
}
}
}
</script>
<style lang="less" scoped>
/deep/.ant-form-item-control {
padding-left: 10px;
.ant-form-item-control {
padding-left: 0;
}
}
.tips-alert {
margin-bottom: 28px;
}
.input-short {
width: 160px;
}
.input-code {
width: 30%;
}
.input-inst {
width: 70%;
}
</style>