Commit 156ef0c5 by li

merchant

parents
> 1%
last 2 versions
not ie <= 10
NODE_ENV=production
VUE_APP_PREVIEW=false
VUE_APP_BASE_URL=/
VUE_APP_API_BASE_URL=
\ No newline at end of file
NODE_ENV=development
VUE_APP_PREVIEW=true
VUE_APP_BASE_URL=/
VUE_APP_API_BASE_URL=http://38.47.232.137:9218
\ No newline at end of file
/**
默认eslint规则:
代码末尾不能加分号 ;(强迫症的我受不了 哭)
代码中不能存在多行空行;(这个我更也忍不了大哭)
tab键不能使用,必须换成两个空格;(超级不习惯)
代码中不能存在声明了但未使用的变量;(这个我觉得可以有)
* **/
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/strongly-recommended',
'@vue/standard'
],
rules: {
'no-console': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'generator-star-spacing': 'off',
'no-mixed-operators': 0,
'vue/max-attributes-per-line': [
2,
{
'singleline': 5, //单行的情况下可以接受的属性数量
'multiline': {
'max': 1, //多行的情况下每行最多属性数量
'allowFirstLine': false //不允许在多行的情况下第一行有属性
}
}
],
'vue/attribute-hyphenation': 0,
'vue/html-self-closing': 0,
'vue/component-name-in-template-casing': 0,
'vue/html-closing-bracket-spacing': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/no-unused-components': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/no-use-v-if-with-v-for': 0,
'vue/html-closing-bracket-newline': 0,
'vue/no-parsing-error': 0,
'no-tabs': 0,
'quotes': [
2,
'single',
{
'avoidEscape': true,
'allowTemplateLiterals': true
}
],
/**
* 分号配置项:
* 第一个参数:
" off"或0 - 关闭规则
" warn"或1 - 将该规则作为警告打开(不影响退出代码)
" error"或2 - 将规则作为错误打开(退出代码将为1)
*
*第二个参数
always(默认):在语句末尾需要分号
never:不允许加分号
*
* 第三个参数:
"beforeStatementContinuationChars": "any"(默认)如果下一行语句以 [,(,/,+,或 - 开头,忽略语句末尾的分号(或缺失分号),
"beforeStatementContinuationChars": "always" 如果下一行语句以 [,(,/,+,或 - 开头,在语句末尾需要添加分号。
"beforeStatementContinuationChars": "never" 如果该语句不会因为ASI而带来风险,那么即使它的下一行语句以 [,(,/,+,或 - 开头,也不允许在语句末尾添加分号。
* **/
'semi': [
2,
'never',
{
'beforeStatementContinuationChars': 'never'
}
],
'no-delete-var': 2,
'prefer-const': [
2,
{
'ignoreReadBeforeAssign': false
}
],
'template-curly-spacing': 'off',
'indent': 'off'
},
parserOptions: {
parser: 'babel-eslint'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
}
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
const plugins = []
if (IS_PROD) {
plugins.push('transform-remove-console')
}
// lazy load ant-design-vue
// if your use import on Demand, Use this code
plugins.push(['import', {
'libraryName': 'ant-design-vue',
'libraryDirectory': 'es',
'style': true // `style: true` 会加载 less 文件
}])
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': 3
}
]
],
plugins
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "jeepay-ui-merchant",
"version": "1.10.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"build:preview": "vue-cli-service build --mode preview",
"lint:nofix": "vue-cli-service lint --no-fix"
},
"dependencies": {
"@ant-design-vue/pro-layout": "^1.0.7",
"@antv/data-set": "^0.10.2",
"@antv/g2plot": "^2.3.21",
"@antv/util": "^2.0.13",
"ant-design-vue": "^1.7.2",
"axios": "^0.19.0",
"core-js": "^3.1.2",
"enquire.js": "^2.1.6",
"js-base64": "^2.5.2",
"lodash.clonedeep": "^4.5.0",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.2.1",
"mockjs2": "1.0.8",
"moment": "^2.24.0",
"nprogress": "^0.2.0",
"reconnectingwebsocket": "^1.0.0",
"store": "^2.0.12",
"viser-vue": "^2.4.6",
"vue": "^2.6.10",
"vue-clipboard2": "^0.2.1",
"vue-cropper": "0.4.9",
"vue-i18n": "^8.17.4",
"vue-qr": "^2.5.0",
"vue-quill-editor": "^3.0.6",
"vue-router": "^3.1.2",
"vue-svg-component-runtime": "^1.0.1",
"vuex": "^3.1.1",
"wangeditor": "^3.1.1"
},
"devDependencies": {
"@ant-design/colors": "^3.2.1",
"@vue/cli-plugin-babel": "^4.0.4",
"@vue/cli-plugin-eslint": "^4.0.4",
"@vue/cli-plugin-router": "^4.0.4",
"@vue/cli-plugin-unit-jest": "^4.0.4",
"@vue/cli-plugin-vuex": "^4.0.4",
"@vue/cli-service": "^4.0.4",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-eslint": "^10.0.1",
"babel-plugin-import": "^1.12.2",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^5.16.0",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-vue": "^5.2.3",
"git-revision-webpack-plugin": "^3.0.6",
"image-webpack-loader": "^7.0.1",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"opencollective": "^1.0.3",
"opencollective-postinstall": "^2.0.2",
"style-resources-loader": "^1.4.1",
"url-loader": "^4.1.1",
"vue-cli-plugin-style-resources-loader": "^0.1.5",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "^2.6.10",
"webpack-theme-color-replacer": "^1.3.12"
}
}
module.exports = {
plugins: {
autoprefixer: {}
}
}
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="90.000000pt" height="32.000000pt" viewBox="0 0 90.000000 32.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,32.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
</g>
</svg>
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>imgs/favicon.ico">
<title>商户系统 - Jeepay聚合支付</title>
<style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
<!-- require cdn assets css -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
<% } %>
</head>
<body>
<noscript>
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<div class="first-loading-wrp">
<% { %>
<img type="text/javascript" src="<%= BASE_URL %>imgs/logo.svg">
<% } %>
<div class="loading-wrp">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
<div style="display: flex; justify-content: center; align-items: center;">聚合支付</div>
</div>
</div>
<!-- require cdn assets js -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<a-config-provider :locale="locale">
<div id="app">
<router-view/>
<loading v-show="globalLoading"></loading>
</div>
</a-config-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import Loading from './components/GlobalLoad/GlobalLoad' // 全局Loading组件
import { mapState } from 'vuex' // 引入vuex状态管理,mapState管理中存在全局loading
export default {
data () {
return {
locale: zhCN
}
},
components: {
Loading // 注册全局loading 组件
},
computed: {
// 全局 loading
...mapState([
'globalLoading'
])
}
}
</script>
import request from '@/http/request'
import { Base64 } from 'js-base64'
// 登录认证接口
export function login ({ username, password, vercode, vercodeToken }) {
const data = {
ia: Base64.encode(username), // 账号
ip: Base64.encode(password), // 密码
vc: Base64.encode(vercode), // 验证码值
vt: Base64.encode(vercodeToken) // 验证码token
}
return request.request({
url: '/api/anon/auth/validate',
method: 'post',
data: data
}, true, true, false)
}
// 获取图形验证码信息接口
export function vercode () {
return request.request({ url: '/api/anon/auth/vercode', method: 'get' }, true, true, true)
}
// 获取当前用户信息
export function getInfo () {
return request.request({
url: '/api/current/user',
method: 'get'
})
}
// 退出接口
export function logout () {
return new Promise(resolve => { resolve() })
}
/*
* 全系列 restful api格式, 定义通用req对象
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/5/8 07:18
*/
import request from '@/http/request'
export const req = {
// 通用列表查询接口
list: (url, params) => {
return request.request({ url: url, method: 'GET', params: params }, true, true, false)
},
// 通用新增接口
add: (url, data) => {
return request.request({ url: url, method: 'POST', data: data }, true, true, false)
},
// 通用查询单条数据接口
getById: (url, bizId) => {
return request.request({ url: url + '/' + bizId, method: 'GET' }, true, true, false)
},
// 通用修改接口
updateById: (url, bizId, data) => {
return request.request({ url: url + '/' + bizId, method: 'PUT', data: data }, true, true, false)
},
// 通用删除接口
delById: (url, bizId) => {
return request.request({ url: url + '/' + bizId, method: 'DELETE' }, true, true, false)
}
}
// 全系列 restful api格式 (全局loading方式)
export const reqLoad = {
// 通用列表查询接口
list: (url, params) => {
return request.request({ url: url, method: 'GET', params: params }, true, true, true)
},
// 通用新增接口
add: (url, data) => {
return request.request({ url: url, method: 'POST', data: data }, true, true, true)
},
// 通用查询单条数据接口
getById: (url, bizId) => {
return request.request({ url: url + '/' + bizId, method: 'GET' }, true, true, true)
},
// 通用修改接口
updateById: (url, bizId, data) => {
return request.request({ url: url + '/' + bizId, method: 'PUT', data: data }, true, true, true)
},
// 通用删除接口
delById: (url, bizId) => {
return request.request({ url: url + '/' + bizId, method: 'DELETE' }, true, true, true)
}
}
/** 角色管理页面 **/
export const API_URL_ENT_LIST = '/api/sysEnts'
export const API_URL_ROLE_LIST = '/api/sysRoles'
export const API_URL_ROLE_ENT_RELA_LIST = '/api/sysRoleEntRelas'
export const API_URL_SYS_USER_LIST = '/api/sysUsers'
export const API_URL_USER_ROLE_RELA_LIST = '/api/sysUserRoleRelas'
/** 首页统计 **/
export const API_URL_MAIN_STATISTIC = 'api/mainChart'
/** 商户App管理 **/
export const API_URL_MCH_APP = '/api/mchApps'
/** 支付订单管理 **/
export const API_URL_PAY_ORDER_LIST = '/api/payOrder'
/** 退款订单管理 **/
export const API_URL_REFUND_ORDER_LIST = '/api/refundOrder'
/** 支付方式列表 **/
export const API_URL_PAYWAYS_LIST = '/api/payWays'
/** 商户支付参数配置 **/
export const API_URL_MCH_PAYCONFIGS_LIST = '/api/mch/payConfigs'
/** 商户支付通道配置 **/
export const API_URL_MCH_PAYPASSAGE_LIST = '/api/mch/payPassages'
/** 转账订单管理 **/
export const API_URL_TRANSFER_ORDER_LIST = '/api/transferOrders'
/** 分账组管理 **/
export const API_URL_DIVISION_RECEIVER_GROUP = '/api/divisionReceiverGroups'
/** 分账账号管理 **/
export const API_URL_DIVISION_RECEIVER = '/api/divisionReceivers'
/** 分账记录管理 **/
export const API_URL_PAY_ORDER_DIVISION_RECORD_LIST = '/api/division/records'
/** 上传图片/文件地址 **/
export const upload = {
avatar: request.baseUrl + '/api/ossFiles/avatar',
cert: request.baseUrl + '/api/ossFiles/cert'
}
const api = {
user: '/user',
role_list: '/role',
service: '/service',
permission: '/permission',
permissionNoPager: '/permission/no-pager',
orgTree: '/org/tree'
}
export default api
/** 获取权限树状结构图 **/
export function getEntTree () {
return request.request({ url: '/api/sysEnts/showTree', method: 'GET' })
}
/** 更新用户角色信息 */
export function uSysUserRoleRela (sysUserId, roleIdList) {
return request.request({
url: 'api/sysUserRoleRelas/relas/' + sysUserId,
method: 'POST',
data: { roleIdListStr: JSON.stringify(roleIdList) }
})
}
export function getRoleList (parameter) {
return request({
url: '/api/sysRoles',
method: 'get',
params: parameter
})
}
/** 根据支付接口查询支付参数配置 **/
export function getMchPayConfigUnique (infoId, ifCode) {
return request.request({
url: '/api/mch/payConfigs/' + infoId + '/' + ifCode,
method: 'get'
})
}
/** 支付体验配置 **/
export function payTest (appId) {
return request.request({
url: 'api/paytest/payways/' + appId,
method: 'GET'
})
}
/** 支付体验下单配置 **/
export function payTestOrder (parameter) {
return request.request({
url: '/api/paytest/payOrders',
method: 'POST',
data: parameter
})
}
/** 根据支付方式查询可用支付接口 **/
export function getAvailablePayInterfaceList (appId, wayCode) {
return request.request({
url: '/api/mch/payPassages/availablePayInterface/' + appId + '/' + wayCode,
method: 'GET'
})
}
export function getPayAmountWeek () {
return request.request({
url: API_URL_MAIN_STATISTIC + '/payAmountWeek',
method: 'GET'
})
}
export function getNumCount () {
return request.request({
url: API_URL_MAIN_STATISTIC + '/numCount',
method: 'GET'
})
}
export function getPayCount (parameter) {
return request.request({
url: API_URL_MAIN_STATISTIC + '/payCount',
method: 'GET',
params: parameter
})
}
export function getPayType (parameter) {
return request.request({
url: API_URL_MAIN_STATISTIC + '/payTypeCount',
method: 'GET',
params: parameter
})
}
export function getMainUserInfo () {
return request.request({
url: API_URL_MAIN_STATISTIC,
method: 'GET'
})
}
export function updateUserPass (parameter) {
return request.request({
url: '/api/current/modifyPwd',
method: 'put',
data: parameter
})
}
export function updateUserInfo (parameter) {
return request.request({
url: '/api/current/user',
method: 'put',
data: parameter
})
}
export function getUserInfo () {
return request.request({
url: '/api/current/user',
method: 'get'
})
}
/** 获取到webSocket的前缀 (ws://localhost) **/
export function getWebSocketPrefix () {
// 获取网站域名 + 端口号
let domain = document.location.protocol + '//' + document.location.host
// 判断api_base_url 是否设置
if (process.env.VUE_APP_API_BASE_URL && process.env.VUE_APP_API_BASE_URL !== '/') {
domain = process.env.VUE_APP_API_BASE_URL
}
if (domain.startsWith('https:')) {
return 'wss://' + domain.replace('https://', '')
} else {
return 'ws://' + domain.replace('http://', '')
}
}
/** 查询支付宝授权地址URL **/
export function queryAlipayIsvsubMchAuthUrl (mchAppId) {
return request.request({
url: '/api/mch/payConfigs/alipayIsvsubMchAuthUrls/' + mchAppId,
method: 'GET'
})
}
/** 查询商户转账支出的接口 **/
export function queryMchTransferIfCode (appId) {
return request.request({
url: 'api/mchTransfers/ifCodes/' + appId,
method: 'GET'
})
}
/** 获取渠道用户ID二维码地址 **/
export function getChannelUserQrImgUrl (ifCode, appId, extParam) {
return request.request({
url: '/api/mchTransfers/channelUserId',
method: 'GET',
params: { ifCode, appId, extParam }
})
}
/** 转账 **/
export function doTransfer (parameter) {
return request.request({
url: '/api/mchTransfers/doTransfer',
method: 'POST',
data: parameter
}, true, true, true)
}
/** 查询当前应用支持的支付接口 **/
export function getIfCodeByAppId (appId) {
return request.request({
url: '/api/mch/payConfigs/ifCodes/' + appId,
method: 'GET'
}, true, true, true)
}
/** 退款接口 */
export function payOrderRefund (payOrderId, refundAmount, refundReason) {
return request.request({
url: '/api/payOrder/refunds/' + payOrderId,
method: 'POST',
data: { refundAmount, refundReason }
})
}
/** 分账重试 */
export function resendDivision (recordId) {
return request.request({
url: '/api/division/records/resend/' + recordId,
method: 'POST'
})
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32">
<defs>
<clipPath id="clip-path">
<rect id="矩形_2548" data-name="矩形 2548" width="32" height="32" transform="translate(6848 11598)" fill="#fff" stroke="#707070" stroke-width="1"/>
</clipPath>
<clipPath id="clip-path-2">
<rect id="矩形_2547" data-name="矩形 2547" width="32" height="29.211" transform="translate(0 0)" fill="#1953ff"/>
</clipPath>
</defs>
<g id="logo-jeepay" transform="translate(-6848 -11598)" clip-path="url(#clip-path)">
<g id="组_1000" data-name="组 1000" transform="translate(3462.959 9987.395)">
<g id="组_999" data-name="组 999" transform="translate(3385.041 1612)" clip-path="url(#clip-path-2)">
<path id="路径_4157" data-name="路径 4157" d="M3398.508,1679.5h0l-4.173,3.659a2.08,2.08,0,0,0,1.371,3.644h7.49a.729.729,0,0,0,.553-1.2Z" transform="translate(-3390.837 -1657.592)" fill="#1953ff" opacity="0.5"/>
<path id="路径_4158" data-name="路径 4158" d="M3387.958,1615.772l-2.874,20.448a4.382,4.382,0,0,0,4.339,4.991h1.628a.73.73,0,0,0,.723-.628l.762-5.418a1.459,1.459,0,0,1,1.445-1.256h4.765a1.46,1.46,0,0,0,1.446-1.256l.673-4.789a1.461,1.461,0,0,1,1.446-1.257h4.353a1.461,1.461,0,0,1,1.446,1.664l-.792,5.639-.91,6.473a.729.729,0,0,0,.722.83h2.655a4.382,4.382,0,0,0,4.339-3.772l2.874-20.448a4.381,4.381,0,0,0-4.338-4.991H3392.3A4.382,4.382,0,0,0,3387.958,1615.772Zm19.628,7.182h-4.351a1.462,1.462,0,0,1-1.447-1.665l.615-4.379a1.462,1.462,0,0,1,1.448-1.258h4.351a1.462,1.462,0,0,1,1.448,1.665l-.615,4.379A1.462,1.462,0,0,1,3407.586,1622.954Z" transform="translate(-3385.041 -1612)" fill="#1953ff"/>
</g>
</g>
</g>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="90.000000pt" height="32.000000pt" viewBox="0 0 90.000000 32.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,32.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40">
<defs>
<clipPath id="clip-path">
<path id="路径_4169" data-name="路径 4169" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6271.75 11622.75)" fill="#04c361"/>
</clipPath>
</defs>
<g id="支付宝APP" transform="translate(-6425 -11757)" clip-path="url(#clip-path)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6271.75 11622.75)" fill="#1977fd"/>
<path id="路径_4168" data-name="路径 4168" d="M677.256,334.179a14.853,14.853,0,0,0,1.374-2.946,13.7,13.7,0,0,0,.808-3.024l-5.6-.049v-1.91l6.782-.048V324.85h-6.782v-3.08h-3.322v3.08h-6.328V326.2l6.324-.048v2.052h-5.073v1.076h10.447a10.038,10.038,0,0,1-.514,1.948c-.4,1.084-.812,2.03-.812,2.03s-4.909-1.713-7.5-1.713-5.728,1.039-6.033,4.048,1.464,4.644,3.955,5.248a9.9,9.9,0,0,0,6.793-.987,15.752,15.752,0,0,0,3.966-3.207l16.4,7.306s.652-.983,1.121-1.9a21.179,21.179,0,0,0,.86-1.959Zm-10.719,5.013c-3.665,0-4.432-1.8-4.432-3.091s.763-2.7,3.877-2.9,7.326,2.208,7.326,2.208-3.106,3.788-6.771,3.788Zm0,0" transform="translate(5773 11445.265)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="支付宝条码" transform="translate(-6535 -11679)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6381.75 11544.75)" fill="#1977fd"/>
<path id="路径_4161" data-name="路径 4161" d="M703.52,282.559h1.913v12.715H703.52Zm9.245,0h2.75v12.715h-2.75Zm-5.472,0h.89v12.715h-.89Zm2.722,0h.89v12.715h-.89Zm7.357,0h.89v12.715h-.89Zm0,0" transform="translate(5844.109 11410.043)" fill="#fff"/>
<path id="减去_1" data-name="减去 1" d="M20,20H2a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H20a2,2,0,0,1,2,2V18A2,2,0,0,1,20,20ZM2.5,1.5a1,1,0,0,0-1,1v15a1,1,0,0,0,1,1h17a1,1,0,0,0,1-1V2.5a1,1,0,0,0-1-1Z" transform="translate(6544 11689)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40">
<defs>
<clipPath id="clip-path">
<rect id="矩形_2579" data-name="矩形 2579" width="20.901" height="22" fill="#fff"/>
</clipPath>
</defs>
<g id="支付宝生活号" transform="translate(-6425 -11707)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6271.75 11572.75)" fill="#1977fd"/>
<g id="组_1007" data-name="组 1007" transform="translate(5864.55 11585)">
<g id="组_1006" data-name="组 1006" transform="translate(570 131)">
<g id="组_1005" data-name="组 1005" transform="translate(0 0)" clip-path="url(#clip-path)">
<path id="路径_4167" data-name="路径 4167" d="M589.606,152.943a1.382,1.382,0,0,1-.472-.088l-8.257-3.279a1.252,1.252,0,0,0-.921,0l-8.257,3.279a1,1,0,0,1-.45.088,1.21,1.21,0,0,1-1.162-.8,1.161,1.161,0,0,1-.088-.472V132.23a1.261,1.261,0,0,1,1.261-1.261h18.366a1.261,1.261,0,0,1,1.261,1.261v19.452a1.3,1.3,0,0,1-1.283,1.261ZM571.634,151.1l7.708-3.07a2.864,2.864,0,0,1,1.086-.219,3.077,3.077,0,0,1,1.1.208l7.708,3.059V132.6h-17.6Zm11.711-5.022a.837.837,0,0,1-.362-.088l-2.566-1.3-2.566,1.294a.838.838,0,0,1-.362.088.8.8,0,0,1-.614-.285.806.806,0,0,1-.164-.636l.494-2.741-2.062-1.93a.8.8,0,0,1-.219-.811.812.812,0,0,1,.647-.548l2.862-.395,1.272-2.5a.783.783,0,0,1,.7-.428.8.8,0,0,1,.7.428l1.272,2.5,2.862.395a.809.809,0,0,1,.647.548.781.781,0,0,1-.219.811l-2.061,1.941.493,2.741a.778.778,0,0,1-.164.636.754.754,0,0,1-.592.285Zm-2.906-2.785a1.071,1.071,0,0,1,.362.077l1.853.943-.351-1.985a.8.8,0,0,1,.23-.7L584,140.245l-2.05-.285a.736.736,0,0,1-.581-.417l-.921-1.831-.921,1.831a.782.782,0,0,1-.581.417l-2.039.285,1.436,1.382a.785.785,0,0,1,.23.7l-.362,2.007.175-.088c.6-.307,1.524-.779,1.667-.844l.022-.011a.69.69,0,0,1,.362-.1Zm0,0" transform="translate(-570 -130.97)" fill="#fff"/>
</g>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40">
<defs>
<clipPath id="clip-path">
<rect id="矩形_2580" data-name="矩形 2580" width="8" height="8" fill="#fff"/>
</clipPath>
</defs>
<g id="支付宝WEB" transform="translate(-6425 -11807)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6271.75 11672.75)" fill="#1977fd"/>
<g id="组_1011" data-name="组 1011" transform="translate(5891 11690.5)">
<g id="组_1009" data-name="组 1009" transform="translate(550 130)">
<g id="组_1008" data-name="组 1008" transform="translate(0 0)" clip-path="url(#clip-path)">
<path id="路径_4170" data-name="路径 4170" d="M553.877,130.52a3.993,3.993,0,1,0,4,3.993,4,4,0,0,0-4-3.993Zm.321,7.273a.17.17,0,0,0-.061.009v-1.883c.452-.009.86-.017,1.234-.043a4.974,4.974,0,0,1-1.173,1.918Zm-.643,0a5.015,5.015,0,0,1-1.173-1.918c.365.026.782.043,1.234.043V137.8c-.017,0-.035-.009-.061-.009Zm-2.98-3.281a3.92,3.92,0,0,1,.061-.642c.191-.035.556-.087,1.069-.139a4.6,4.6,0,0,0-.07.79,5.476,5.476,0,0,0,.061.781c-.5-.052-.878-.1-1.069-.139a5.478,5.478,0,0,1-.052-.651Zm1.59,0a4.228,4.228,0,0,1,.087-.833c.4-.026.852-.052,1.364-.052V135.4c-.5-.009-.964-.026-1.373-.061a4.831,4.831,0,0,1-.078-.825Zm2.024-3.281a5.288,5.288,0,0,1,1.173,1.918c-.365-.026-.773-.043-1.225-.043v-1.883c.017,0,.035.009.052.009Zm-.573-.009v1.883c-.452.009-.86.017-1.225.043a5.305,5.305,0,0,1,1.164-1.918c.026,0,.043-.009.061-.009Zm.521,4.175v-1.771c.5.009.965.026,1.364.052a4.245,4.245,0,0,1,.009,1.667c-.4.026-.86.043-1.373.052Zm1.9-1.666c.513.052.878.1,1.069.139a3.16,3.16,0,0,1,.07.642,3.92,3.92,0,0,1-.061.642c-.191.035-.556.087-1.069.139a4.621,4.621,0,0,0,.061-.79,4.48,4.48,0,0,0-.069-.772Zm.912-.417c-.243-.035-.591-.078-1.034-.122a5.7,5.7,0,0,0-.878-1.771,3.31,3.31,0,0,1,1.911,1.892Zm-4.24-1.892a5.374,5.374,0,0,0-.878,1.762c-.434.035-.782.087-1.034.122a3.345,3.345,0,0,1,1.912-1.883ZM550.8,135.71c.243.035.591.078,1.025.122a5.371,5.371,0,0,0,.869,1.762,3.341,3.341,0,0,1-1.894-1.883Zm4.258,1.883a5.318,5.318,0,0,0,.869-1.753c.434-.035.782-.087,1.025-.121a3.351,3.351,0,0,1-1.894,1.875Zm0,0" transform="translate(-549.881 -130.515)" fill="#fff"/>
</g>
</g>
</g>
<path id="联合_46" data-name="联合 46" d="M3.25,20A.249.249,0,0,1,3,19.75v-1a.25.25,0,0,1,.25-.25h6V15H2a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H18a2,2,0,0,1,2,2V13a2,2,0,0,1-2,2H10.75v3.5h6a.25.25,0,0,1,.25.25v1a.25.25,0,0,1-.25.249ZM1.5,2.25v10.5a.751.751,0,0,0,.75.75h15.5a.751.751,0,0,0,.751-.75V2.25a.751.751,0,0,0-.751-.75H2.25A.751.751,0,0,0,1.5,2.25Z" transform="translate(6435 11817)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="支付宝二维码" transform="translate(-6475 -11607)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6321.75 11472.75)" fill="#1977fd"/>
<rect id="矩形_2549" data-name="矩形 2549" width="22" height="1.5" transform="translate(6484 11626.25)" fill="#fff"/>
<path id="联合_40" data-name="联合 40" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6485 11617)" fill="#fff"/>
<path id="联合_41" data-name="联合 41" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6505 11617) rotate(90)" fill="#fff"/>
<g id="组_1001" data-name="组 1001" transform="translate(0 17)">
<path id="联合_43" data-name="联合 43" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6505 11620) rotate(180)" fill="#fff"/>
<path id="联合_42" data-name="联合 42" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6485 11620) rotate(-90)" fill="#fff"/>
</g>
<path id="矩形_2559" data-name="矩形 2559" d="M1,0H11a1,1,0,0,1,1,1V4a0,0,0,0,1,0,0H0A0,0,0,0,1,0,4V1A1,1,0,0,1,1,0Z" transform="translate(6489 11621)" fill="#fff"/>
<path id="矩形_2560" data-name="矩形 2560" d="M0,0H12a0,0,0,0,1,0,0V3a1,1,0,0,1-1,1H1A1,1,0,0,1,0,3V0A0,0,0,0,1,0,0Z" transform="translate(6489 11629)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40">
<defs>
<clipPath id="clip-path">
<rect id="矩形_2580" data-name="矩形 2580" width="8" height="8" fill="#fff"/>
</clipPath>
</defs>
<g id="支付宝WAP" transform="translate(-6425 -11857)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6271.75 11722.75)" fill="#1977fd"/>
<g id="组_1010" data-name="组 1010" transform="translate(5891 11743)">
<g id="组_1009" data-name="组 1009" transform="translate(550 130)">
<g id="组_1008" data-name="组 1008" transform="translate(0 0)" clip-path="url(#clip-path)">
<path id="路径_4170" data-name="路径 4170" d="M553.877,130.52a3.993,3.993,0,1,0,4,3.993,4,4,0,0,0-4-3.993Zm.321,7.273a.17.17,0,0,0-.061.009v-1.883c.452-.009.86-.017,1.234-.043a4.975,4.975,0,0,1-1.173,1.918Zm-.643,0a5.016,5.016,0,0,1-1.173-1.918c.365.026.782.043,1.234.043V137.8c-.017,0-.035-.009-.061-.009Zm-2.98-3.281a3.92,3.92,0,0,1,.061-.642c.191-.035.556-.087,1.069-.139a4.6,4.6,0,0,0-.07.79,5.476,5.476,0,0,0,.061.781c-.5-.052-.878-.1-1.069-.139a5.465,5.465,0,0,1-.052-.651Zm1.59,0a4.227,4.227,0,0,1,.087-.833c.4-.026.852-.052,1.364-.052V135.4c-.5-.009-.964-.026-1.373-.061a4.831,4.831,0,0,1-.078-.825Zm2.024-3.281a5.289,5.289,0,0,1,1.173,1.918c-.365-.026-.773-.043-1.225-.043v-1.883c.017,0,.035.009.052.009Zm-.573-.009v1.883c-.452.009-.86.017-1.225.043a5.306,5.306,0,0,1,1.164-1.918c.026,0,.043-.009.061-.009Zm.521,4.175v-1.771c.5.009.964.026,1.364.052a4.245,4.245,0,0,1,.009,1.666c-.4.026-.86.043-1.373.052Zm1.9-1.666c.513.052.878.1,1.069.139a3.16,3.16,0,0,1,.07.642,3.92,3.92,0,0,1-.061.642c-.191.035-.556.087-1.069.139a4.621,4.621,0,0,0,.061-.79,4.48,4.48,0,0,0-.07-.772Zm.912-.417c-.243-.035-.591-.078-1.034-.122a5.7,5.7,0,0,0-.878-1.771,3.31,3.31,0,0,1,1.912,1.892Zm-4.24-1.892a5.371,5.371,0,0,0-.878,1.762c-.435.035-.782.087-1.034.122a3.344,3.344,0,0,1,1.911-1.883ZM550.8,135.71c.243.035.591.078,1.025.122a5.371,5.371,0,0,0,.869,1.762,3.34,3.34,0,0,1-1.894-1.883Zm4.258,1.883a5.319,5.319,0,0,0,.869-1.753c.434-.035.782-.087,1.025-.121a3.351,3.351,0,0,1-1.894,1.875Zm0,0" transform="translate(-549.881 -130.515)" fill="#fff"/>
</g>
</g>
</g>
<path id="联合_47" data-name="联合 47" d="M2,22a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H14a2,2,0,0,1,2,2V20a2,2,0,0,1-2,2ZM1.5,2.25v17.5a.751.751,0,0,0,.75.751h11.5a.751.751,0,0,0,.75-.751V2.25a.751.751,0,0,0-.75-.75H11V2a1,1,0,0,1-1,1H6A1,1,0,0,1,5,2V1.5H2.25A.751.751,0,0,0,1.5,2.25Z" transform="translate(6437 11866)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="聚合被扫" transform="translate(-6535 -11679)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6381.75 11544.75)" fill="#354268"/>
<path id="路径_4161" data-name="路径 4161" d="M703.52,282.559h1.913v12.715H703.52Zm9.245,0h2.75v12.715h-2.75Zm-5.472,0h.89v12.715h-.89Zm2.722,0h.89v12.715h-.89Zm7.357,0h.89v12.715h-.89Zm0,0" transform="translate(5844.109 11410.043)" fill="#fff"/>
<path id="减去_1" data-name="减去 1" d="M20,20H2a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H20a2,2,0,0,1,2,2V18A2,2,0,0,1,20,20ZM2.5,1.5a1,1,0,0,0-1,1v15a1,1,0,0,0,1,1h17a1,1,0,0,0,1-1V2.5a1,1,0,0,0-1-1Z" transform="translate(6544 11689)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="98.594" height="90" viewBox="0 0 98.594 90"><defs><clipPath id="a"><rect width="98.594" height="90" transform="translate(4766.87 7992)" fill="#fff"/></clipPath></defs><g transform="translate(-4766.87 -7992)" clip-path="url(#a)"><path d="M3044.676,1822.5h0l-12.856,11.273a6.408,6.408,0,0,0,4.225,11.227h23.076a2.246,2.246,0,0,0,1.7-3.71Z" transform="translate(1745.828 6237)" fill="#fff" opacity="0.5"/><path d="M3030.029,1766.621l-8.854,63A13.5,13.5,0,0,0,3034.544,1845h5.016a2.249,2.249,0,0,0,2.227-1.936l2.347-16.694a4.493,4.493,0,0,1,4.45-3.87h14.684a4.5,4.5,0,0,0,4.453-3.871l2.073-14.756a4.5,4.5,0,0,1,4.456-3.873h13.412a4.5,4.5,0,0,1,4.456,5.126l-2.44,17.374-2.8,19.943A2.245,2.245,0,0,0,3089.1,1845h8.183a13.5,13.5,0,0,0,13.368-11.621l8.854-63A13.5,13.5,0,0,0,3106.134,1755H3043.4A13.5,13.5,0,0,0,3030.029,1766.621Zm60.476,22.129H3077.1a4.5,4.5,0,0,1-4.458-5.129l1.9-13.494a4.506,4.506,0,0,1,4.46-3.877h13.4a4.5,4.5,0,0,1,4.461,5.131l-1.9,13.493A4.505,4.505,0,0,1,3090.5,1788.75Z" transform="translate(1745.829 6237)" fill="#fff"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="384.389" height="57" viewBox="0 0 384.389 57"><defs><style>.a{fill:none;}.b{clip-path:url(#a);}.c{fill:#fff;}</style><clipPath id="a"><rect class="a" width="384.389" height="57"/></clipPath></defs><g transform="translate(-2806.535 -455)"><g class="b" transform="translate(2806.535 455)"><g transform="translate(273.491)"><path class="c" d="M3299.561,471.467h-14.567L3287.308,455h-5.066l-2.314,16.467H3265.36l-.712,5.067h14.566l-4.094,29.133h5.067l4.094-29.133h14.566Z" transform="translate(-3248.001 -455)"/><path class="c" d="M3247.124,506.134l2.82-20.067.712-5.067h-11.4l-.712,5.067h6.333l-3.231,22.992-3.283,1.753,1.917,4.388,14.4-7.688-1.917-4.388Z" transform="translate(-3238.363 -464.533)"/><path class="c" d="M3258.2,461.973,3249.353,455l-3.512,4.133,8.852,6.973Z" transform="translate(-3241.105 -455)"/></g><g transform="translate(331.644)"><path class="c" d="M3357.386,503.733h15.2l.712-5.067h-15.2l1.068-7.6h13.3l.712-5.067h-31.667l-.712,5.067h13.3l-1.068,7.6h-15.2l-.712,5.067h15.2l-1.157,8.233H3330.9l-.712,5.067h45.6l.712-5.067h-20.267Z" transform="translate(-3330.184 -466.367)"/><path class="c" d="M3349.253,468.542c4.045-2.72,7.8-5.447,11.294-8.129,2.742,2.682,5.729,5.409,9.009,8.129,3.844,3.188,7.624,5.995,11.257,8.464l3.319-4.153c-3.728-2.549-7.6-5.441-11.537-8.722-3.669-3.058-7-6.127-10.016-9.132l-.005.006V455h-2.533v.006l0-.006c-3.861,3.005-8.055,6.074-12.583,9.132-4.858,3.281-9.545,6.172-13.989,8.722l2.151,4.153C3339.943,474.536,3344.512,471.73,3349.253,468.542Z" transform="translate(-3331.387 -455)"/></g><path class="c" d="M3068.662,470l-.8,5.7-.713,5.067-.089.633-1.958,13.933a8.3,8.3,0,0,1-7.946,6.967h-8.866a3.767,3.767,0,0,1-3.81-4.433l2.314-16.467.8-5.7.8-5.7h-5.7l-3.916,27.867A8.6,8.6,0,0,0,3047.489,508h8.867a14.089,14.089,0,0,0,7.261-2.093l-.294,2.093h5.7l1.78-12.667,2.047-14.567L3074.362,470Z" transform="translate(-2891.654 -460.5)"/><path class="c" d="M3166.378,508l.8-5.7.712-5.067.089-.633,1.958-13.933a8.3,8.3,0,0,1,7.946-6.967h8.867a3.767,3.767,0,0,1,3.81,4.433l-2.314,16.467-.8,5.7-.8,5.7h5.7l3.917-27.867A8.6,8.6,0,0,0,3187.552,470h-8.867a14.09,14.09,0,0,0-7.261,2.093l.295-2.093h-5.7l-1.78,12.667-2.047,14.567L3160.678,508Z" transform="translate(-2936.387 -460.5)"/><path class="c" d="M3134.819,476.894a8.915,8.915,0,0,0-9.065-6.894h-19.528l-.8,5.7h19.129a4.871,4.871,0,0,1,3.856,1.7,3.613,3.613,0,0,1-2.784,5.9H3112.96a12.056,12.056,0,0,0-11.558,10.133l-.623,4.433A8.6,8.6,0,0,0,3109.489,508h8.866a14.088,14.088,0,0,0,7.261-2.093l-.294,2.093h5.7l1.78-12.667,1.691-12.033.445-3.167h0A9.224,9.224,0,0,0,3134.819,476.894ZM3127.992,489l-.356,2.533-.534,3.8a8.3,8.3,0,0,1-7.946,6.967h-8.867a3.767,3.767,0,0,1-3.81-4.433l.623-4.433a5.281,5.281,0,0,1,5.057-4.433h12.667a9.518,9.518,0,0,0,3.249-.582Z" transform="translate(-2914.388 -460.5)"/><path class="c" d="M3002.283,470h-11.4a12.056,12.056,0,0,0-11.558,10.133l-2.492,17.733A8.6,8.6,0,0,0,2985.542,508h8.867a14.089,14.089,0,0,0,7.261-2.093l-1.63,11.593h5.7l3.115-22.167,1.246-8.867.89-6.333A8.6,8.6,0,0,0,3002.283,470Zm.873,25.333a8.3,8.3,0,0,1-7.946,6.967h-8.867a3.767,3.767,0,0,1-3.81-4.433l2.492-17.733a5.281,5.281,0,0,1,5.057-4.433h11.4a3.767,3.767,0,0,1,3.81,4.433l-.891,6.333Z" transform="translate(-2868.941 -460.5)"/><path class="c" d="M2940.229,470h-11.4a12.056,12.056,0,0,0-11.557,10.133l-.845,6.017-.8,5.7-.845,6.017A8.6,8.6,0,0,0,2923.489,508h11.4a12.056,12.056,0,0,0,11.558-10.133l.044-.317h-5.7l-.044.317a5.281,5.281,0,0,1-5.056,4.433h-11.4a3.767,3.767,0,0,1-3.81-4.433l.846-6.017h25.967l.8-5.7.846-6.017A8.6,8.6,0,0,0,2940.229,470Zm-18.1,16.15.845-6.017a5.281,5.281,0,0,1,5.057-4.433h11.4a3.767,3.767,0,0,1,3.81,4.433l-.845,6.017Z" transform="translate(-2846.188 -460.5)"/><path class="c" d="M2878.229,470h-11.4a12.055,12.055,0,0,0-11.557,10.133l-.845,6.017-.8,5.7-.845,6.017A8.6,8.6,0,0,0,2861.489,508h11.4a12.056,12.056,0,0,0,11.558-10.133l.044-.317h-5.7l-.044.317a5.281,5.281,0,0,1-5.056,4.433h-11.4a3.767,3.767,0,0,1-3.81-4.433l.846-6.017h25.967l.8-5.7.846-6.017A8.6,8.6,0,0,0,2878.229,470Zm-18.1,16.15.845-6.017a5.281,5.281,0,0,1,5.057-4.433h11.4a3.767,3.767,0,0,1,3.81,4.433l-.845,6.017Z" transform="translate(-2823.454 -460.5)"/><path class="c" d="M2832.211,455h-12.667l-.89,6.333h6.333l-3.026,21.533L2820.8,491.1a6.036,6.036,0,0,1-5.779,5.067h-7.6l-.89,6.333h7.6a13.563,13.563,0,0,0,13-11.4l4.183-29.767h0Z" transform="translate(-2806.535 -455)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="86.211" height="26" viewBox="0 0 86.211 26"><defs><clipPath id="a"><rect width="86.211" height="26" transform="translate(0 0)" fill="#1953ff"/></clipPath><clipPath id="b"><rect width="28.483" height="26" fill="#1953ff"/></clipPath></defs><g transform="translate(-3021.041 -1755)"><g transform="translate(3021.041 1755)" clip-path="url(#a)"><g transform="translate(35.626 2.889)"><path d="M3186.573,1772.511h-6.645l1.056-7.511h-2.311l-1.056,7.511h-6.645l-.325,2.311h6.645l-1.868,13.289h2.311l1.868-13.289h6.645Z" transform="translate(-3163.055 -1765)" fill="#1953ff"/><path d="M3148.359,1802.465l1.286-9.154.325-2.311h-5.2l-.325,2.311h2.889l-1.474,10.488-1.5.8.874,2,6.566-3.507-.874-2Z" transform="translate(-3144.363 -1783.489)" fill="#1953ff"/><path d="M3157.48,1768.181l-4.038-3.181-1.6,1.885,4.038,3.181Z" transform="translate(-3149.68 -1765)" fill="#1953ff"/><g transform="translate(26.526)"><path d="M3248.592,1804.089h6.933l.325-2.311h-6.934l.487-3.467h6.067l.325-2.311h-14.445l-.325,2.311h6.067l-.487,3.467h-6.933l-.325,2.311h6.933l-.528,3.756h-9.245l-.325,2.311h20.8l.325-2.311h-9.245Z" transform="translate(-3236.184 -1787.044)" fill="#1953ff"/><path d="M3246.667,1771.177c1.845-1.241,3.557-2.484,5.152-3.708,1.251,1.224,2.613,2.467,4.109,3.708,1.753,1.454,3.478,2.734,5.135,3.861l1.514-1.894c-1.7-1.163-3.467-2.482-5.263-3.978-1.673-1.395-3.193-2.795-4.569-4.166l0,0v0h-1.156v0c-1.761,1.371-3.674,2.771-5.74,4.166-2.216,1.5-4.354,2.815-6.381,3.978l.981,1.894C3242.42,1773.911,3244.5,1772.631,3246.667,1771.177Z" transform="translate(-3238.517 -1765)" fill="#1953ff"/></g></g><g transform="translate(0 0)"><g clip-path="url(#b)"><path d="M3033.971,1822.5h0l-3.714,3.257a1.851,1.851,0,0,0,1.22,3.243h6.667a.649.649,0,0,0,.492-1.072Z" transform="translate(-3027.144 -1803)" fill="#1953ff" opacity="0.5"/><path d="M3023.638,1758.357l-2.558,18.2a3.9,3.9,0,0,0,3.862,4.443h1.449a.65.65,0,0,0,.643-.559l.678-4.823A1.3,1.3,0,0,1,3029,1774.5h4.242a1.3,1.3,0,0,0,1.287-1.118l.6-4.263a1.3,1.3,0,0,1,1.287-1.119h3.875a1.3,1.3,0,0,1,1.287,1.481l-.705,5.019-.81,5.761a.648.648,0,0,0,.642.739h2.364a3.9,3.9,0,0,0,3.862-3.357l2.558-18.2a3.9,3.9,0,0,0-3.862-4.442H3027.5A3.9,3.9,0,0,0,3023.638,1758.357Zm17.471,6.393h-3.873a1.3,1.3,0,0,1-1.288-1.482l.548-3.9a1.3,1.3,0,0,1,1.289-1.12h3.872a1.3,1.3,0,0,1,1.289,1.482l-.548,3.9A1.3,1.3,0,0,1,3041.108,1764.75Z" transform="translate(-3021.041 -1755)" fill="#1953ff"/></g></g></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40">
<defs>
<clipPath id="clip-path">
<rect id="矩形_3796" data-name="矩形 3796" width="17" height="20" transform="translate(7694 5293)" fill="#fff" stroke="#707070" stroke-width="1"/>
</clipPath>
</defs>
<g id="PayPal" transform="translate(-7454 -5095)">
<path id="路径_4159" data-name="路径 4159" d="M190.218,137.3l-.02-.02-.02-.02c-1.442-1.442-3.6-2.176-6.511-2.561a91.682,91.682,0,0,0-10.426-.453,91.64,91.64,0,0,0-10.426.453c-2.929.387-5.089,1.126-6.531,2.582s-2.2,3.6-2.581,6.531a91.665,91.665,0,0,0-.453,10.426,91.651,91.651,0,0,0,.453,10.426c.385,2.916,1.12,5.069,2.562,6.511l.02.02.02.02c1.442,1.442,3.6,2.176,6.511,2.561a91.679,91.679,0,0,0,10.426.453,91.64,91.64,0,0,0,10.426-.453c2.929-.387,5.089-1.126,6.531-2.582s2.2-3.6,2.581-6.531a91.69,91.69,0,0,0,.453-10.426,91.64,91.64,0,0,0-.453-10.426c-.385-2.916-1.12-5.069-2.562-6.511Z" transform="translate(7300.76 4960.764)" fill="#c3cbe6"/>
<path id="矩形_2559" data-name="矩形 2559" d="M1,0H11a1,1,0,0,1,1,1V4H0V1A1,1,0,0,1,1,0Z" transform="translate(7468.001 5109.007)" fill="#e60012"/>
<g id="蒙版组_330" data-name="蒙版组 330" transform="translate(-228.5 -188)" clip-path="url(#clip-path)">
<g id="组_1372" data-name="组 1372" transform="translate(7694 5293)">
<path id="路径_11885" data-name="路径 11885" d="M302.719,129.325l.345-2.136H298.75l2.416-15.7c0-.058,0-.058.057-.173a.252.252,0,0,1,.173-.058h5.983c2.013,0,3.337.462,4.084,1.27a4.157,4.157,0,0,1,.69,1.212,8.721,8.721,0,0,1,0,1.732v.519l.345.173a3.1,3.1,0,0,1,.69.519,2.356,2.356,0,0,1,.575,1.27,6.219,6.219,0,0,1-.058,1.789,6.092,6.092,0,0,1-.748,2.02,3.2,3.2,0,0,1-1.208,1.27,7.716,7.716,0,0,1-1.553.75,7.418,7.418,0,0,1-1.9.231h-.69a1.419,1.419,0,0,0-1.381,1.212v.231l-.575,3.694v.173a.056.056,0,0,1-.057.058h-.058a22.977,22.977,0,0,0-2.819-.058Z" transform="translate(-298.051 -110.452)" fill="#253b80"/>
<path id="路径_11886" data-name="路径 11886" d="M420.32,248.293c0,.173-.058.231-.058.4-.863,4.329-3.624,5.714-7.191,5.714H411.23a.865.865,0,0,0-.863.75l-.92,6-.23,1.732c-.058.231.173.462.4.519h3.337a.752.752,0,0,0,.748-.693v-.173l.633-3.925.058-.231a.8.8,0,0,1,.748-.693h.46c3.164,0,5.523-1.328,6.328-5.021.288-1.558.173-2.886-.633-3.752a5.7,5.7,0,0,0-.978-.635Z" transform="translate(-405.066 -243.214)" fill="#179bd7"/>
<path id="路径_11887" data-name="路径 11887" d="M459.074,228.212c-.173,0-.23-.058-.4-.058s-.288-.058-.4-.058a15.249,15.249,0,0,0-1.611-.173h-4.832a.752.752,0,0,0-.748.693l-.978,6.637v.173a.865.865,0,0,1,.863-.75H452.8c3.567,0,6.386-1.443,7.191-5.714,0-.173.057-.231.057-.4-.23-.058-.46-.231-.69-.289l-.288-.058Z" transform="translate(-444.684 -223.479)" fill="#222d65"/>
<path id="路径_11888" data-name="路径 11888" d="M282.712,90.528a.8.8,0,0,1,.748-.693h4.832a5.112,5.112,0,0,1,1.611.173c.173,0,.288.058.4.058.173,0,.23.058.4.058l.173.058a3.516,3.516,0,0,1,.69.289,3.938,3.938,0,0,0-.863-3.578c-.92-1.1-2.646-1.558-4.775-1.558h-6.213a.866.866,0,0,0-.863.75l-2.531,16.507c-.057.289.173.52.46.635H280.7l.978-6.118c.058,0,1.035-6.58,1.035-6.58Z" transform="translate(-276.318 -85.333)" fill="#253b80"/>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="聚合主扫" transform="translate(-6475 -11607)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6321.75 11472.75)" fill="#354268"/>
<rect id="矩形_2549" data-name="矩形 2549" width="22" height="1.5" transform="translate(6484 11626.25)" fill="#fff"/>
<path id="联合_40" data-name="联合 40" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6485 11617)" fill="#fff"/>
<path id="联合_41" data-name="联合 41" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6505 11617) rotate(90)" fill="#fff"/>
<g id="组_1001" data-name="组 1001" transform="translate(0 17)">
<path id="联合_43" data-name="联合 43" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6505 11620) rotate(180)" fill="#fff"/>
<path id="联合_42" data-name="联合 42" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6485 11620) rotate(-90)" fill="#fff"/>
</g>
<path id="矩形_2559" data-name="矩形 2559" d="M1,0H11a1,1,0,0,1,1,1V4a0,0,0,0,1,0,0H0A0,0,0,0,1,0,4V1A1,1,0,0,1,1,0Z" transform="translate(6489 11621)" fill="#fff"/>
<path id="矩形_2560" data-name="矩形 2560" d="M0,0H12a0,0,0,0,1,0,0V3a1,1,0,0,1-1,1H1A1,1,0,0,1,0,3V0A0,0,0,0,1,0,0Z" transform="translate(6489 11629)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="127" viewBox="0 0 100 127">
<defs>
<clipPath id="clip-path">
<rect id="SVGID" width="100" height="127" fill="none"/>
</clipPath>
</defs>
<g id="组_1021" data-name="组 1021" transform="translate(-877.908 -86.051)">
<g id="组_1020" data-name="组 1020" transform="translate(877.908 86.051)" clip-path="url(#clip-path)">
<rect id="矩形_2605" data-name="矩形 2605" width="99.744" height="48.028" fill="#eee"/>
<g id="组_1017" data-name="组 1017" transform="translate(7.74 8.423)">
<rect id="矩形_2606" data-name="矩形 2606" width="4.369" height="31.182" transform="translate(25.558)" fill="#313133"/>
<rect id="矩形_2607" data-name="矩形 2607" width="2.966" height="31.182" transform="translate(38.139)" fill="#313133"/>
<rect id="矩形_2608" data-name="矩形 2608" width="3.962" height="31.182" transform="translate(51.021)" fill="#313133"/>
<rect id="矩形_2609" data-name="矩形 2609" width="3.244" height="31.182" transform="translate(44.812)" fill="#313133"/>
<rect id="矩形_2610" data-name="矩形 2610" width="2.911" height="31.182" transform="translate(33.003)" fill="#313133"/>
<rect id="矩形_2611" data-name="矩形 2611" width="3.274" height="31.182" transform="translate(13.322)" fill="#313133"/>
<rect id="矩形_2612" data-name="矩形 2612" width="3.93" height="31.182" transform="translate(18.647)" fill="#313133"/>
<rect id="矩形_2613" data-name="矩形 2613" width="3.337" height="31.182" transform="translate(7.761)" fill="#313133"/>
<rect id="矩形_2614" data-name="矩形 2614" width="4.795" height="31.182" fill="#313133"/>
<rect id="矩形_2615" data-name="矩形 2615" width="3.683" height="31.182" transform="translate(74.157)" fill="#313133"/>
<rect id="矩形_2616" data-name="矩形 2616" width="4.646" height="31.182" transform="translate(79.618)" fill="#313133"/>
<rect id="矩形_2617" data-name="矩形 2617" width="2.718" height="31.182" transform="translate(67.731)" fill="#313133"/>
<rect id="矩形_2618" data-name="矩形 2618" width="2.966" height="31.182" transform="translate(62.546)" fill="#313133"/>
<rect id="矩形_2619" data-name="矩形 2619" width="3.436" height="31.182" transform="translate(56.762)" fill="#313133"/>
</g>
<g id="组_1019" data-name="组 1019" transform="translate(-0.267 30.926)">
<image id="矩形_2620" data-name="矩形 2620" width="100.303" height="45.176" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAv4AAAFZCAYAAADglCtyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAABQnSURBVHhe7d1llCVJnYdhYnEncHd3d3d3d3d3W1/cZXF3X9zd3d3dIXCH2LnMv2Z6eqqr6t7KzBuZ+Txf5uUcDtDddTN/J4iqTrXWQwAAANP2L/FPAABgwgx/AACYAcMfAABmwPAHAIAZOPg395aUogAAgLHKBx36mw1//y8AAACMWa7/iDrAwUf+Jv8mAABg3Pb9c/xLOmQUAAAwFrn+Peogtv4LvEo6VBQAANC6XP8WdTDb/829JR06CgAAaFWuf43alG/kBQCAsdtm9C9sf+K/UNJhogAAgNbk+peofdrZ8F8o6bBRAABAK3L9c9SWdj78F0o6XBQAALBuuf4palvL3fFf4j8YAADo0ZLbfLkT/w0lHT4KAAAYWq5/jNqxVYf/EaIAAIAh5fqHqKWsNvwXSjpiFAAAMJRcfx+1lNV/jv+K/4UAAMCKdrHBVz/x31DSkaIAAIC+5Pq7qJXsfvgvlHTkKAAAoGu5/jZqZV0N/6NEAQAAXcr1N1G7svod/z119D8GAADoRzcn/htKOmoUAACwW7n+OmrXuh3+CyUdLQoAAFhVrr+K6kT3w3+hpBwFAAAsK9cS1Zlu7vgDAADd6GH0L/Rz4r9Q0tGjAACAncr1l1Gd6m/4L5R0jCgAAGA7uf4iqnP9Dv+Fko4ZBQAA7EuuP4/qRf93/Hv+BQAAwOgNsJn7P/HfUNKxogAAgA25/iyqV0MO/2NHAQAAC7n+NKp3ww3/hZKOEwUAAOT6k6jeDftz/Af8hQEAQNMG3sbDnvhvKOm4UQAAMD+5/jhqMOsZ/gslHS8KAADmI9cfRQ1qncP/+FEAADAPuf4wanDD3vHf0xp/0QAAMDfrO/HfUNIJogAAYLpy/UHUWqx/+C+UdMIoAACYnly/H7U2bQz/hZJOFAUAANOR6/ei1mp9d/z31shvCAAAdKahjdvOif9CSSeOAgCAccv1u1FNaGv4L5R0kigAABivXL8T1YT2hv9CSSeNAgCA8cn121HNaOeO/54a/I0CAIAdaXTLtnniv6Gkk0UBAED7cv1WVHNaH/4njwIAgLbl+s2oJrV51WdD4795AAAwFm2f+G8o6RRRAADQnly/EdWscQz/hZJOGQUAAO3I9etRTRvP8F8o6VRRAACwfrl+Lap5Yxv+p44CAID1yvWrUaPQ9jf37m1kv7kAANCKcZ34byjpNFEAADC8XL8SNRrjHP4LJZ02CgAAhpPrl6NGZVxXffY00t9wAABGbMQbdLwn/htKOl0UAAD0J9cvRY3SFIb/6aMAAKAfuX4xarTGP/wXSjpDFAAAdC/XL0SN1njv+O9pAn8QAAA0aiJbcxon/htKOmMUAADsXq6fjxq9aQ3/hZLOFAUAAKvL9XNRkzDF4X/mKAAAWE2un42ajOkN/4WSzhIFAADLy/UzUZMxjW/u3dsE/6AAABjIRLfkNE/8N5R01igAANherp+OmpxpD/+Fks4WBQAA+5brp6ImaZpXffY08T9AAAA6MIPNOP0T/4WSzh4FAAAHlesnoyZtHsN/oaRzRAEAwIFy/UTUpM1n+C+UdM4oAABYjP6PR03e9O/472lGf7AAAGxjZttwXif+G0o6VxQAAHOU68eiZmOuw//cUQAAzE2uH42alXkO/4WSzhMFAMCc5PqRqFmZ1x3/Pc30DxwAYNZmvAHne+K/oaTzRgEAMGW5fjhqlgz/hZLOFwUAwBTl+qGo2TL8F0o6fxQAAFOT6wejZm2+d/z35IsBAGCqPhD/nD0n/nsq6QJRAABMQa6GfzD891bSBaMAABizXN8fxX4M/82UdKEoAADGKNf3RRHc8d+MLxQAgPGy5TblxH9fSrpwFAAAY5Hre6PYi+G/lZIuEgUAwBjk+p4o9mL4b6eki0YBANCyXN8dxSbc8d+OLyAAgPbZbNty4r9TJV0sCgCAluT6rii2YPjvVEkXjwIAoBW5vjOKbRj+yyjpElEAALQg13dEsQ13/JfhCwsAoB222VKc+K+ipEtGAQCwDrm+PYodMvxXVdKlogAAGFKub4tiCYb/bpR06SgAAIaQ61ujWJI7/gAAjIPRvytO/HerpMtEAQDQp1zfEsUKDP8ulHTZKAAA+pDrm6NYkeHflZIuFwUAQJdyfVMUu+COf1d8QQIAdM/G6owT/y6VdPkoAAB2K9c3RtEBw79rJV0hCgCA3cj1DVF0wPDvQ0lXjAIAYBW5vj6Kjrjj3wdfqAAAq7OleuHEv08lXSkKAICdyPV1UXTM8O9bSVeOAgBgK7m+NooeGP59K+kqUQAA7Euur4miJ+74980XMQAADXDiP5SSrhoFAMCecn11FD0y/IdU0tWiAABYyPX/ouiZ4T+0kq4eBQAwb7m+KooBuOMPAMDwjP7BOfFfh5KuEQUAME+5vjKKgRj+61LSNaMAAOYl11dEMSDDf51KulYUAMA85PryKAbmjv86+cIHAObE9lkrJ/7rVtK1owAApivXl0WxJoZ/C0q6ThQAwDTl+tIo1sTwb0VJ140CAJiWXF8SxRq5498KHwgAYIpsnGY48W9NSdeLAgAYt1xfHEUDDP8WlXT9KACAccr1RVE0wvBvUUk3iAIAGJ9cXxhFQ9zxb5EPCwAAHXPi37KSbhgFADAOub4gisYY/q0r6UZRAABty/X5UTTI8B+Dkm4cBQDQplyfF0Wj3PEHAGB3jP5RcOI/FiXdJAoAoC25PjeKhhn+Y1LSTaMAANqQ63OiaJzhPzYl3SwKAGC9cn12FCPgjv/Y+IABAC2wSUbHif9YlXTzKACAYeX6rChGxPAfq5JuEQUAMJxcnxnFyBj+Y1bSLaMAAIaR6zOiGBl3/MfMBw8AGJLtMWpO/KegpFtFAQD0I9enRzFShv9UlHTrKACAbuX6tChGzPCfipJuEwUA0J1cnxrFyLnjPxU+lAAAbMGJ/9SUdNsoAIDdyfUpUUyA4T9FJd0uCgBgNbk+OYqJMPynqqTbRwEALCfXJ0UxIe74AwBwIKN/spz4T1lJd4gCANiJJ+43/FM0E2P4T11Jd4wCANhark+MYoIM/zko6U5RAACby/UJUUyUO/5z4IMMAGzFVpgFJ/5zUtKdowAA9pfr46OYOMN/Tkq6SxQAwGL0Py6KGTD856aku0YBAHOX62OjmAF3/OfGBxwAWLAJZseJ/1yVdLcoAGBucn1MFDNi+M9ZSXePAgDmItdHRzEzhv+clXSPKABgDnJ9VBQz5I7/nPnwAwDMhhN/Fif/94wCAKYq10dGMVOGP/sr6V5RAMDU5PqIKGbM8OdAJd07CgCYilwfHsXMuePPgTwYAGBavNvZgxN/Dqqk+0QBAGOW68Oi4J8Mfw6upPtGAQBjletDo+CfDH82V9L9ogCAscn1IVFwAHf82ZwHBgCMk3c4++DEn62VdP8oAKB1uT44Cg7G8GdrJT0gCgBoWa4PioJNGf5sr6QHRgEArcr1f6JgU+74sz0PEgBom3c1O+DEn50r6V+jAIBW5PrfUbAlw5/llPRvUQDAuuX6X1GwLcOf5ZX071EAwLrk+p9RsCPu+AMAjI3Rzwqc+LOakv4jCgAYWq7ewyzN8Gd1JTltAICh5erKLSsx/NmdknxTEQAMJVc/ZIOVuePP7ngAAcAwvHPZJSf+7F5Jfn4wAPQpV3+XDrtm+NONkvyNgQDQl1wfGAUrM/zpTkkPigIAupLrA6JgV9zxpzseTADQLe9WOuTEn+6V9OAoAGBVud4/Cjph+NO9kh4SBQCsItf7RUFnDH/6UdJDowCAZeV63yjojDv+9MMDCwBW4x1KT5z406+SHhYFAGwn1/tEQecMf/pX0sOjAIB9yfXeUdALV33onwcZAGzNu5IBOPFnGCU9IgoA2FOu94qCXhn+DKekR0YBABtyvWcU9MrwZ1glPSoKAMj1HlHQO8Of4ZX06CgAmK9c7x4Fg/DNvQzPgw6AufMuZA2c+LMeJT0mCgDmJde7RcGgDH/Wp6THRgHAfOR61ygYlKs+rI8HHwBz493HGjnxZ/1KelwUAExXrneJgrUw/GlDSY+PAoDpyfXOUbA2hj/tKOkJUQAwHbneKQrWyh1/AIC+GP00xIk/bSnpiVEAMH653jEK1s7wpz0l/W8UAIxXrneIgiYY/rSppCdFAcD45Hr7KGiGO/60yQMTgLHyDqNRTvxpV0lPjgKAccj1dlHQHMOftpX0lCgAaF+ut42C5hj+tK+kp0YBQLtyvU0UNMkdf9rnQQpA67yrGAEn/oxHSU+LAoB25HrrKGia4c94lPT0KABoQ663ioLmGf6MS0nPiAKA9cv1llHQPHf8GRcPWABa4Z3EyDjxZ5xKemYUAAwv11tEwWgY/oxXSc+KAoDh5HrzKBgVw59xK+nZUQDQv1xvFgWj444/AMBOGP2MnBN/xq+k50QBQH9yvWkUjJLhzzSU9NwoAOherjeJgtEy/JmOkp4XBQDdyfXGUTBq7vgzHR7MAHTNu4UJceLPtJT0/CgA2J1cbxQFk2D4Mz0lvSAKAFaX6w2jYBIMf6appBdGAcDycr1BFEyGO/5Mkwc2AKvyDmGinPgzbSW9KAoAtpfr9aNgcgx/pq+kF0cBwL7ler0omCTDn+kr6SVRALC5XK8bBZPljj/T52EOAODEnxkp6aVRAHCgXK8TBZNm+DMvJb0sCgAWo//aUTB5hj/zU9LLowCYs1yvFQWz4I4/ADA/Rj8z5MSfeSrpFVEAzFGu14yC2TD8ma+SXhkFwJzkeo0omBXDn3kr6VVRAMxBrlePgtlxx5958wIAmA/PfGbOiT+UtPgQvHr/fwHAJOV6tSiYLcMfFkoy/AGmKterRsGsGf6woaTXRAEwJbleJQpmzR1/2ODFADA9nu1wACf+sLeSXhsFwJjleuUoYD+GP2ympNdFATBGuV4pCgiGP2ympNdHATA2uV4xCtiDO/6wGS8NAGBinPjDVkp6QxQAY5DrFaKAvRj+sJ2S3hgFQMtyvXwUsAnDH3aipDdFAdCiXC8XBeyDO/4AwLgZ/bAjTvxhp0p6cxQALcn1slHAFgx/WEZJb4kCoAW5XiYK2IbhD8sq6a1RAKxTrpeOAnbAHX9YlhcNwPp5FsPSnPjDqkp6WxQAQ8r1UlHAEgx/WFVJb48CYCi5XjIKWJLhD7tR0juiABhCrpeIApbkjj/shhcQwHA8c2FXnPhDF0p6ZxQAfcj14lHAigx/6EpJ74oCoEu5XiwK2AXDH7pS0rujAOhKrheNAnbJHX/oipcTANAwJ/7QtZLeEwXAbuR6kSigA4Y/9KGk90YBsIpcLxwFdMTwh76U9L4oAJaR64WigA654w998eICWJ5nJ/TGiT/0qaT3RwGwnVwvGAX0wPCHvpX0gSgAtpLrBaKAHhj+MISSPhgFwGZyPX8U0BN3/GEIXmgA++YZCYNw4g9DKulDUQAs5Hq+KKBnhj8MqaQPRwGQ63mjgAEY/jC0kj4SBTBvuZ4nChiAO/4wNC86AM9CWAMn/rAuJX00CmBecj13FDAgwx/WqaSPRQHMQ67nigIGZvjDOpX08SiA6cv1nFHAGrjjD+vkJQgADMSJP7SgpE9EAUxTrueIAtbE8IdWlPTJKIBpyfXsUcAaGf7QkpI+FQUwDbmeLQpYM3f8oSVekMCUeKZBU5z4Q2tK+nQUwHjletYooBGGP7SopM9EAYxTrmeJAhph+EOrSvpsFMC45HrmKKAh7vhDq7w4gTHy7IJmOfGH1pX0uSiAtuV6piigQYY/tK6kz0cBtCvXM0YBjTL8YQxK+kIUQJtyPUMU0Ch3/GEMvFCBlnlGwSg48YcxKemLUQBtyPX0UUDjDH8Ym5K+FAWwXrmeLgoYAcMfxqikL0cBrEeup40CRsIdfwBgOUY/jJITfxirkr4SBTCsXE8TBYyI4Q9jVtJXowCGkeupo4CRMfxh7Er6WhRAv3I9VRQwQu74w9h5EQND8KyB0XPiD1NQ0tejALqX6ymjgBEz/GEqSvpGFEC3cj1FFDBihj9MSUnfjALoRq4njwJGzh1/mBIvaKBLnikwKU78YYpK+lYUwGpyPVkUMBGGP0xRSd+OAlherieNAibE8IepKuk7UQDLOPF+wz9FAxPijj9MVa4niQLYOaMfJsuJP0xdSd+NAtharieOAibI8Ic5KOl7UQCby/VEUcBEueoDc+CFDmzFMwJmwYk/zEVJ348COFCuJ4wCJs7whzkp6QdRAPvL9QRRwMQZ/jA3Jf0wCpi7XI8fBcyA4Q9zVNKPooC5yvV4UcBM+OZemCMvfJg3zwCYJSf+MFcl/TgKmJNcjxsFzIzhD3NW0k+igLnI9ThRwMy46gNzZgDAvPjMw6w58QcWJ/8/jQKmKtdjRwEzZfgD+yvpZ1HA1OR6rChgxgx/4EAl/TwKmIpcjxkFzJw7/gAwVUY/sAcn/sBBlfSLKGDscj1GFIDhD2yipF9GAWOV69GjAP7J8Ac2V1KJAsYm1xwFcAB3/IHNGQ4wTj67wD448Qf2raRfRQFjkOvRogAOxvAHtlbSr6OA1uV61CiAgzH8ge2V9JsooFW5HiUKYFPu+APbMyigbT6jwA448Qd2rqTfRgGtyPXIUQBbMvyB5ZT0uyhg3XI9UhTAtgx/YDkl/T4KWKdcjxgFsCPu+APLMTYAYJSc+AOrKekPUcDQcj1CFMCOGf7A6kr6YxQwlFwPHwWwFMMf2J2S/hQF9C3Xw0UBLM0dfwAYA6Mf2CUn/sDulfTnKKAvuR42CmAlhj/QjZL+EgV0LdfDRAGszPAHulPSX6OAruR66CiAXXHHH+iOgQLd8pkCOuTEH+hWSX+LAnYj10NFAXTC8Ae6V9Lfo4BV5XrIKIBOGP5AP0r6RxSwrFxdxQU658EC9MNwgdX47AC9OMQh/h88wtXqDezGnQAAAABJRU5ErkJggg=="/>
<g id="组_1018" data-name="组 1018" transform="translate(32.131 44.584)">
<path id="矩形_2621" data-name="矩形 2621" d="M0,0H36.016a0,0,0,0,1,0,0V29.29A16.014,16.014,0,0,1,20,45.3H16.014A16.014,16.014,0,0,1,0,29.29V0A0,0,0,0,1,0,0Z" transform="translate(0 6.186)" fill="#0063b0"/>
<rect id="矩形_2622" data-name="矩形 2622" width="28.119" height="6.186" transform="translate(3.948)" fill="#313133"/>
<rect id="矩形_2623" data-name="矩形 2623" width="23.12" height="7.511" transform="translate(6.448 12.077)" fill="#fff"/>
<path id="路径_4175" data-name="路径 4175" d="M955.437,273.217a2.206,2.206,0,1,1-2.205-2.209A2.207,2.207,0,0,1,955.437,273.217Z" transform="translate(-942.997 -245.438)" fill="#313133"/>
<path id="路径_4176" data-name="路径 4176" d="M969.786,273.217a2.206,2.206,0,1,1-2.205-2.209A2.208,2.208,0,0,1,969.786,273.217Z" transform="translate(-949.517 -245.438)" fill="#313133"/>
<path id="路径_4177" data-name="路径 4177" d="M984.135,273.217a2.206,2.206,0,1,1-2.205-2.209A2.208,2.208,0,0,1,984.135,273.217Z" transform="translate(-956.038 -245.438)" fill="#313133"/>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1618754752076" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1998" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M316.16 657.493333 512 462.08l195.84 195.413333L768 597.333333l-256-256-256 256L316.16 657.493333z" p-id="1999" fill="#e6e6e6"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40">
<defs>
<clipPath id="clip-path">
<rect id="矩形_2573" data-name="矩形 2573" width="22" height="17.991" transform="translate(0 0)" fill="#fff"/>
</clipPath>
</defs>
<g id="微信APP" transform="translate(-6475 -11757)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6321.75 11622.75)" fill="#04c361"/>
<g id="组_1004" data-name="组 1004" transform="translate(5596.355 11342.004)">
<g id="组_1003" data-name="组 1003" transform="translate(887.645 426)" clip-path="url(#clip-path)">
<g id="组_1002" data-name="组 1002" transform="translate(0 0)">
<path id="路径_4166" data-name="路径 4166" d="M902.538,431.443a7.021,7.021,0,0,1,.75.046c-.674-3.148-4.029-5.489-7.856-5.489-4.28,0-7.787,2.929-7.787,6.649a6.413,6.413,0,0,0,3.115,5.276l-.778,2.352,2.722-1.371a13.241,13.241,0,0,0,2.727.393c.244,0,.486-.013.727-.032a5.84,5.84,0,0,1-.241-1.637,6.388,6.388,0,0,1,6.619-6.187Zm-4.187-2.12a.977.977,0,0,1,0,1.955.993.993,0,1,1,0-1.955Zm-5.449,1.955a.994.994,0,1,1,0-1.955.977.977,0,0,1,0,1.955Zm16.742,6.259c0-3.126-3.115-5.674-6.614-5.674-3.7,0-6.623,2.547-6.623,5.674s2.918,5.671,6.623,5.671a9.732,9.732,0,0,0,2.335-.391l2.136,1.175-.586-1.954a5.926,5.926,0,0,0,2.729-4.5Zm-8.761-.978a.782.782,0,0,1,0-1.564.8.8,0,1,1,0,1.564Zm4.283,0a.782.782,0,0,1,0-1.564.8.8,0,1,1,0,1.564Zm0,0" transform="translate(-887.645 -426)" fill="#fff"/>
</g>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="微信条码" transform="translate(-6535 -11679)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6381.75 11544.75)" fill="#04c361"/>
<path id="路径_4161" data-name="路径 4161" d="M703.52,282.559h1.913v12.715H703.52Zm9.245,0h2.75v12.715h-2.75Zm-5.472,0h.89v12.715h-.89Zm2.722,0h.89v12.715h-.89Zm7.357,0h.89v12.715h-.89Zm0,0" transform="translate(5844.109 11410.043)" fill="#fff"/>
<path id="减去_1" data-name="减去 1" d="M20,20H2a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H20a2,2,0,0,1,2,2V18A2,2,0,0,1,20,20ZM2.5,1.5a1,1,0,0,0-1,1v15a1,1,0,0,0,1,1h17a1,1,0,0,0,1-1V2.5a1,1,0,0,0-1-1Z" transform="translate(6544 11689)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="微信H5" transform="translate(-6475 -11807)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6321.75 11672.75)" fill="#04c361"/>
<path id="减去_3" data-name="减去 3" d="M18,20H2a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H18a2,2,0,0,1,2,2V18A2,2,0,0,1,18,20ZM2.25,1.5a.751.751,0,0,0-.75.75v15.5a.751.751,0,0,0,.75.751h15.5a.751.751,0,0,0,.751-.751V2.25a.751.751,0,0,0-.751-.75Z" transform="translate(6485 11817)" fill="#fff"/>
<path id="联合_44" data-name="联合 44" d="M7,10V5.75H1.5V10H0V0H1.5V4.25H7V0H8.5V10Z" transform="translate(6490.75 11822)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="公众号_小程序" data-name="公众号&amp;小程序" transform="translate(-6475 -11707)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6321.75 11572.75)" fill="#04c361"/>
<path id="路径_4165" data-name="路径 4165" d="M738.834,343.512a3.3,3.3,0,0,0-3.269,3.333v3.333a2.193,2.193,0,1,1-2.193-2.208v-1.125a3.334,3.334,0,1,0,3.269,3.333v-3.333a2.193,2.193,0,1,1,2.193,2.208v1.125a3.334,3.334,0,0,0,0-6.667Zm0,0" transform="translate(5758.897 11378.488)" fill="#fff" stroke="#fff" stroke-width="0.5"/>
<path id="减去_2" data-name="减去 2" d="M11,22A11,11,0,0,1,3.222,3.222,11,11,0,1,1,18.778,18.778,10.928,10.928,0,0,1,11,22ZM11,1.5A9.5,9.5,0,1,0,20.5,11,9.51,9.51,0,0,0,11,1.5Z" transform="translate(6484 11716)" fill="#fff"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g id="微信二维码" transform="translate(-6475 -11607)">
<path id="路径_4159" data-name="路径 4159" d="M190.234,137.3l-.02-.02-.02-.02c-1.443-1.443-3.6-2.177-6.514-2.562a91.718,91.718,0,0,0-10.431-.453,91.681,91.681,0,0,0-10.431.453c-2.93.387-5.091,1.126-6.534,2.583s-2.2,3.6-2.582,6.534a91.7,91.7,0,0,0-.453,10.431,91.682,91.682,0,0,0,.453,10.431c.385,2.917,1.12,5.071,2.563,6.514l.02.02.02.02c1.443,1.443,3.6,2.177,6.514,2.562a91.714,91.714,0,0,0,10.431.453,91.681,91.681,0,0,0,10.431-.453c2.93-.387,5.091-1.126,6.534-2.583s2.2-3.6,2.582-6.534a91.729,91.729,0,0,0,.453-10.431,91.681,91.681,0,0,0-.453-10.431c-.385-2.917-1.12-5.071-2.563-6.514Z" transform="translate(6321.75 11472.75)" fill="#04c361"/>
<rect id="矩形_2549" data-name="矩形 2549" width="22" height="1.5" transform="translate(6484 11626.25)" fill="#fff"/>
<path id="联合_40" data-name="联合 40" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6485 11617)" fill="#fff"/>
<path id="联合_41" data-name="联合 41" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6505 11617) rotate(90)" fill="#fff"/>
<g id="组_1001" data-name="组 1001" transform="translate(0 17)">
<path id="联合_43" data-name="联合 43" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6505 11620) rotate(180)" fill="#fff"/>
<path id="联合_42" data-name="联合 42" d="M0,8V1A1,1,0,0,1,1,0H8V1.5H1.5V8Z" transform="translate(6485 11620) rotate(-90)" fill="#fff"/>
</g>
<path id="矩形_2559" data-name="矩形 2559" d="M1,0H11a1,1,0,0,1,1,1V4a0,0,0,0,1,0,0H0A0,0,0,0,1,0,4V1A1,1,0,0,1,1,0Z" transform="translate(6489 11621)" fill="#fff"/>
<path id="矩形_2560" data-name="矩形 2560" d="M0,0H12a0,0,0,0,1,0,0V3a1,1,0,0,1-1,1H1A1,1,0,0,1,0,3V0A0,0,0,0,1,0,0Z" transform="translate(6489 11629)" fill="#fff"/>
</g>
</svg>
@jee-theme: #1A53FF; //主题色
@jee-back: #F0F2F5; //主要背景色
@jee-card-back: #FFF; //卡片底色
@jee-theme-mask: rgba(26,83,255,0.1); //主体遮罩色(10% 透明度)
@jee-strengthen: #596380; //强化色
@jee-theme-hover: #0033CC; //主题Hover
@jee-warning: #FF4B33; //危险,强化警告色
@jee-inside-link: #1A79FF; //内部链接
@jee-external-link: #AE1B6E; //外部链接
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="308" height="230" viewBox="0 0 308 230"><defs><style>.a{fill:none;}.b{clip-path:url(#a);}.c{opacity:0.05;}.d{opacity:0.1;}.e{fill:#999691;}.f{fill:#ffd200;}.g{fill:#aeb8c2;}.h{fill:#ff4633;}.i{fill:#fff;}</style><clipPath id="a"><rect class="a" width="308" height="230" transform="translate(179 574)"/></clipPath></defs><g transform="translate(-179 -574)"><g class="b"><ellipse class="c" cx="154" cy="35" rx="154" ry="35" transform="translate(179 734)"/><ellipse class="d" cx="34" cy="7.727" rx="34" ry="7.727" transform="translate(354 774.773)"/><path class="e" d="M235.514,736.729H179V716.794l40.835-86.53H244.19l-40.835,86.53h32.159V683.575h21.607v33.219h11.958v19.935H257.121V757.8H235.514Z"/><path class="e" d="M290.391,714.573V673.478c0-27.189,18.694-45.883,45.883-45.883s45.884,18.694,45.884,45.883v41.095c0,27.2-18.694,45.883-45.884,45.883S290.391,741.777,290.391,714.573Zm69.979,1.515v-44.2a24.092,24.092,0,1,0-48.184,0v44.2a24.092,24.092,0,0,0,48.184,0Z"/><path class="e" d="M487,717.948c0,24.175-19.574,42.249-43.489,42.249-19.134,0-36.313-11.871-41.809-30.378l20.642-5.582A21.347,21.347,0,0,0,443.424,740,21.571,21.571,0,0,0,465.3,717.948c0-12.578-9.03-22.242-21.875-22.242a23.294,23.294,0,0,0-11.07,3.015l-9.744-16.833,36.847-31.532H406.664V630.264H484.88v20.193L456.089,678.08C475.663,682.075,487,698.548,487,717.948Z"/><path class="f" d="M487,649.66,179,736.34v-19L487,630.66Z"/><path d="M194.8,732l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M218.8,725.241l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M242.8,718.487l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M266.8,711.733l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M290.8,704.978l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M314.8,698.224l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M338.8,691.47l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M362.8,684.715l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M386.8,677.961l-12,3.126,2.394-19.628,12-3.126Z"/><path d="M410.8,671.207l-12,3.126L401.2,654.7l12-3.126Z"/><path d="M434.8,664.452l-12,3.126L425.2,647.95l12-3.126Z"/><path d="M458.8,657.7l-12,3.126L449.2,641.2l12-3.126Z"/><path d="M482.8,650.944l-12,3.126,2.394-19.628,12-3.126Z"/><path class="g" d="M386,650V782c0,.552.895,1,2,1s2-.448,2-1V650Z"/><rect class="h" width="80" height="80" rx="9.891" transform="translate(348 574)"/><path class="i" d="M388,584a30,30,0,1,0,30,30A30,30,0,0,0,388,584Zm0,55a25,25,0,1,1,25-25A25,25,0,0,1,388,639Z"/><rect class="i" width="25" height="5" transform="translate(375.5 611.5)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="308" height="230" viewBox="0 0 308 230"><defs><style>.a{fill:none;}.b{clip-path:url(#a);}.c{opacity:0.05;}.d{fill:#f5f5f7;}.e{fill:#dce0e6;}.f{fill:#24285b;}.g{fill:#596380;}.h{fill:#ffd200;}.i{fill:#fff;}.j{fill:#97a3ad;}</style><clipPath id="a"><rect class="a" width="308" height="230" transform="translate(1010 582)"/></clipPath></defs><g transform="translate(-1010 -582)"><g class="b"><ellipse class="c" cx="154" cy="35" rx="154" ry="35" transform="translate(1010 742)"/><ellipse class="c" cx="121" cy="2" rx="121" ry="2" transform="translate(1043 752)"/><rect class="d" width="228.672" height="158.82" transform="translate(1049.664 582)"/><rect class="e" width="228.672" height="125.754" transform="translate(1049.664 615.066)"/><ellipse class="f" cx="4.398" cy="4.429" rx="4.398" ry="4.429" transform="translate(1063.711 592.142)"/><ellipse class="g" cx="4.398" cy="4.429" rx="4.398" ry="4.429" transform="translate(1076.794 592.142)"/><ellipse class="h" cx="4.398" cy="4.429" rx="4.398" ry="4.429" transform="translate(1089.123 592.142)"/><path class="i" d="M1111.9,664.221a2.54,2.54,0,0,1,0-3.575l0,0,14.97-15.067a2.5,2.5,0,0,1,3.545.2,2.542,2.542,0,0,1,0,3.369l-14.971,15.077A2.5,2.5,0,0,1,1111.9,664.221Z"/><path class="i" d="M1127.106,664.464l-14.97-15.077a2.541,2.541,0,0,1-.057-3.575,2.5,2.5,0,0,1,3.55-.057c.021.02.041.041.061.062l14.961,15.067a2.542,2.542,0,0,1,.066,3.575,2.5,2.5,0,0,1-3.549.067l-.062-.062Z"/><path class="i" d="M1194.91,664.221a2.54,2.54,0,0,1-.005-3.575l.005,0,14.97-15.067a2.5,2.5,0,0,1,3.544-.2,2.539,2.539,0,0,1,.2,3.569,2.483,2.483,0,0,1-.2.2l-14.96,15.077A2.517,2.517,0,0,1,1194.91,664.221Z"/><path class="i" d="M1210.151,664.464l-14.97-15.077a2.541,2.541,0,0,1-.057-3.575,2.5,2.5,0,0,1,3.549-.057l.062.062,14.96,15.067a2.541,2.541,0,0,1,.067,3.575,2.5,2.5,0,0,1-3.549.067l-.062-.062Z"/><path class="i" d="M1142.82,707.39a2.508,2.508,0,0,1-2.49-2.326c-.7-8.605,2.54-25.543,19.8-28.01,5.392-.759,9.9.485,13.394,3.721,8.2,7.594,7.39,23.561,7.36,24.269a2.514,2.514,0,1,1-5.02-.283h0c0-.142.713-14.258-5.743-20.225-2.37-2.184-5.4-2.973-9.288-2.426-16.948,2.335-15.573,21.72-15.5,22.56a2.523,2.523,0,0,1-2.3,2.72Z"/><ellipse cx="52.8" cy="12" rx="52.8" ry="12" transform="translate(1111.2 765)"/><path class="j" d="M1065.337,756.844H1010V737.324l39.985-84.729h23.855l-39.985,84.729h31.482V704.8H1086.5v32.527H1098.2v19.519H1086.5v20.635h-21.158Z"/><path class="j" d="M1285.134,756.844H1229.8V737.324l39.985-84.729h23.855l-39.985,84.729h31.482V704.8h21.158v32.527H1318v19.519h-11.709v20.635h-21.158Z"/><path class="j" d="M1142.457,787.955a23.67,23.67,0,0,1,42.566.054,115.012,115.012,0,0,0,21.241-3.822c-5.762-17.839-21.556-29.205-42.531-29.205-20.925,0-36.693,11.3-42.5,29.049A111.5,111.5,0,0,0,1142.457,787.955Z"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="308" height="230" viewBox="0 0 308 230"><defs><style>.a{fill:none;}.b{clip-path:url(#a);}.c{fill:#e6e6e6;}.d{fill:#f5f5f7;}.e{fill:#9294ad;}.f{fill:#ffd200;}.g{fill:#aeb8c2;}.h{fill:#dce0e6;}.i{fill:#24285b;}.j{fill:#ff4d4d;}.k{fill:#fff;}</style><clipPath id="a"><rect class="a" width="308" height="230" transform="translate(638 574)"/></clipPath></defs><g transform="translate(-638 -574)"><g class="b"><path class="c" d="M788,799.455H748.216v-3.017h36.743V742.822H788Z"/><path class="d" d="M879.86,659.975c-.944,0-1.868.06-2.781.15v-1.3a39.4,39.4,0,0,0-39.585-39.217h-1.675c-.724-25.9-22.5-46.31-48.642-45.593a47.1,47.1,0,0,0-46.021,48.19,46.481,46.481,0,0,0,1.192,9.239,39.2,39.2,0,0,0,1.492,78.363H879.85a24.909,24.909,0,1,0,0-49.815h0Z"/><path class="e" d="M724.522,702.141c0,23.811-20.829,42.683-44.952,42.683A42.973,42.973,0,0,1,638,713.341l20.651-5.468c2.582,10.23,12.107,16.579,21.72,16.579a22.462,22.462,0,0,0,22.61-22.311c0-12.346-10.326-22.223-22.61-22.223-9.88,0-16.2,4.321-19.672,8.289l-18.338-5.82,3.917-67.2h69.61v19.93h-50.56l-1.959,31.219a39.764,39.764,0,0,1,19.94-5.644C707.342,660.693,724.522,678.33,724.522,702.141Z"/><path class="e" d="M744.521,699.142V658.224c0-27.074,18.782-45.682,46.11-45.682s46.109,18.608,46.109,45.682v40.918c0,27.074-18.782,45.682-46.109,45.682S744.521,726.216,744.521,699.142Zm70.322,1.5V656.636a24.212,24.212,0,0,0-48.424,0v44.006a24.213,24.213,0,0,0,48.424,0Z"/><path class="e" d="M853.89,699.142V658.224c0-27.074,18.782-45.682,46.11-45.682s46.11,18.608,46.11,45.682v40.918c0,27.074-18.782,45.682-46.11,45.682S853.89,726.216,853.89,699.142Zm70.322,1.5V656.636a24.212,24.212,0,0,0-48.424,0v44.006a24.213,24.213,0,0,0,48.424,0Z"/><rect class="f" width="94.385" height="58.936" transform="translate(743.832 700.859)"/><rect class="g" width="94.385" height="58.936" transform="translate(743.832 700.859)"/><rect class="h" width="111.021" height="28.91" transform="translate(735.691 676.123)"/><rect class="i" width="66.239" height="6.727" transform="translate(748.216 686.741)"/><ellipse class="j" cx="5.542" cy="5.49" rx="5.542" ry="5.49" transform="translate(823.063 685.615)"/><rect class="h" width="111.021" height="28.91" transform="translate(735.691 710.975)"/><rect class="i" width="66.239" height="6.727" transform="translate(748.216 721.584)"/><ellipse class="k" cx="5.542" cy="5.49" rx="5.542" ry="5.49" transform="translate(823.063 720.468)"/><rect class="h" width="111.021" height="28.91" transform="translate(735.691 745.828)"/><rect class="i" width="66.239" height="6.727" transform="translate(748.216 756.437)"/><ellipse class="k" cx="5.542" cy="5.49" rx="5.542" ry="5.49" transform="translate(823.063 755.321)"/><rect class="c" width="36.581" height="3.017" transform="translate(786.482 796.438)"/><ellipse class="i" cx="5.988" cy="5.933" rx="5.988" ry="5.933" transform="translate(743.842 792.134)"/><ellipse class="i" cx="5.988" cy="5.933" rx="5.988" ry="5.933" transform="translate(817.074 792.134)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="76" height="76" viewBox="0 0 76 76"><defs><style>.a{fill:#ccced1;}</style></defs><path class="a" d="M-1198-1084a6.007,6.007,0,0,1-6-6v-64a6.007,6.007,0,0,1,6-6h64a6.007,6.007,0,0,1,6,6v64a6.006,6.006,0,0,1-6,6Zm-4-70v64a4,4,0,0,0,4,4h64a4,4,0,0,0,4-4v-64a4,4,0,0,0-4-4h-64A4.005,4.005,0,0,0-1202-1154Zm35,47v-14h-14a1,1,0,0,1-1-1,1,1,0,0,1,1-1h14v-14a1,1,0,0,1,1-1,1,1,0,0,1,1,1v14h14a1,1,0,0,1,1,1,1,1,0,0,1-1,1h-14v14a1,1,0,0,1-1,1A1,1,0,0,1-1167-1107Z" transform="translate(1204 1160)"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="970" viewBox="0 0 1920 970"><defs><style>.a{fill:url(#a);}.b{clip-path:url(#b);}.c{filter:url(#n);}.d{filter:url(#k);}.e{filter:url(#h);}.f{filter:url(#e);}</style><linearGradient id="a" y1="0.383" x2="1" y2="0.589" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f1f8ff"/><stop offset="1" stop-color="#f7fbfe"/></linearGradient><clipPath id="b"><rect class="a" width="1920" height="970"/></clipPath><filter id="e" x="-169" y="375" width="756" height="756" filterUnits="userSpaceOnUse"><feOffset dx="20" dy="-20" input="SourceAlpha"/><feGaussianBlur stdDeviation="30" result="f"/><feFlood flood-color="#211f47" flood-opacity="0.071"/><feComposite operator="in" in2="f"/><feComposite in="SourceGraphic"/></filter><filter id="h" x="1230" y="568" width="433" height="433" filterUnits="userSpaceOnUse"><feOffset dx="2" dy="-10" input="SourceAlpha"/><feGaussianBlur stdDeviation="30" result="i"/><feFlood flood-color="#211f47" flood-opacity="0.071"/><feComposite operator="in" in2="i"/><feComposite in="SourceGraphic"/></filter><filter id="k" x="1440" y="88.44" width="655.121" height="655.121" filterUnits="userSpaceOnUse"><feOffset dx="2" dy="30" input="SourceAlpha"/><feGaussianBlur stdDeviation="30" result="l"/><feFlood flood-color="#211f47" flood-opacity="0.122"/><feComposite operator="in" in2="l"/><feComposite in="SourceGraphic"/></filter><filter id="n" x="422.061" y="2.561" width="350.879" height="350.879" filterUnits="userSpaceOnUse"><feOffset dx="2" dy="10" input="SourceAlpha"/><feGaussianBlur stdDeviation="30" result="o"/><feFlood flood-color="#211f47" flood-opacity="0.122"/><feComposite operator="in" in2="o"/><feComposite in="SourceGraphic"/></filter></defs><g class="b"><rect class="a" width="1920" height="970"/><g class="f" transform="matrix(1, 0, 0, 1, 0, 0)"><circle class="a" cx="288" cy="288" r="288" transform="translate(-99 485)"/></g><g class="e" transform="matrix(1, 0, 0, 1, 0, 0)"><circle class="a" cx="126.5" cy="126.5" r="126.5" transform="translate(1318 668)"/></g><g class="d" transform="matrix(1, 0, 0, 1, 0, 0)"><rect class="a" width="335.961" height="335.961" rx="30" transform="translate(1528 386) rotate(-45)"/></g><g class="c" transform="matrix(1, 0, 0, 1, 0, 0)"><rect class="a" width="170.879" height="170.879" transform="translate(510.06 82.56)"/></g></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"><defs><style>.a{fill:#fff;stroke:#707070;}.b{fill:#596380;}.c{clip-path:url(#a);}.d{clip-path:url(#b);}</style><clipPath id="a"><rect class="a" width="14" height="14" transform="translate(796 520)"/></clipPath><clipPath id="b"><rect class="b" width="12" height="12" transform="translate(0)"/></clipPath></defs><g class="c" transform="translate(-796 -520)"><g transform="translate(-100 289.628)"><g class="d" transform="translate(897 231.372)"><path class="b" d="M1182.9,231.267h-3.343v3.745a2.254,2.254,0,0,1,0,4.508v3.75h3.343a2.291,2.291,0,0,0,2.258-2.308V233.58a2.289,2.289,0,0,0-2.258-2.313Zm0,0" transform="translate(-1173.16 -231.269)"/><path class="b" d="M903.133,235.581a1.657,1.657,0,0,0-.531.086v-4.3h-3.343A2.291,2.291,0,0,0,897,233.68v7.381a2.291,2.291,0,0,0,2.258,2.308H902.6v-4.293a1.713,1.713,0,0,0,.531.086,1.791,1.791,0,0,0,0-3.582Zm0,0" transform="translate(-897 -231.372)"/></g></g></g></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621586617012" class="icon" viewBox="0 0 1146 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6384" xmlns:xlink="http://www.w3.org/1999/xlink" width="17.90625" height="16"><defs><style type="text/css"></style></defs><path d="M0 788.389381a507.469027 108.743363 0 1 0 1014.938053 0 507.469027 108.743363 0 1 0-1014.938053 0Z" fill="#F3F3F8" p-id="6385"></path><path d="M321.880354 246.303717h367.915044l174.714337 220.749026s-174.895575 180.151504-352.328496 179.607788-357.584425-181.238938-357.584425-181.238938z" fill="#FBFBFD" p-id="6386"></path><path d="M797.451327 815.575221H217.486726a63.433628 63.433628 0 0 1-63.433629-63.433628V471.221239h166.558584l14.680354 29.360708A81.738761 81.738761 0 0 0 416.849558 579.964602h199.362831a63.614867 63.614867 0 0 0 63.433629-60.533806L711.72531 471.221239h149.159646v280.920354A63.433628 63.433628 0 0 1 797.451327 815.575221z" fill="#FBFBFD" p-id="6387"></path><path d="M797.451327 806.513274a54.371681 54.371681 0 0 0 54.371682-54.371681v-271.858407h-135.385487L688.707965 522.330619a72.495575 72.495575 0 0 1-72.495576 66.69593H416.849558a90.619469 90.619469 0 0 1-90.61947-86.088496l-11.236814-22.654867H163.115044v271.858407a54.371681 54.371681 0 0 0 54.371682 54.371681h579.964601m0 18.123894H217.486726a72.495575 72.495575 0 0 1-72.495576-72.495575v-289.982301h181.238938l18.123894 36.247788a72.495575 72.495575 0 0 0 72.495576 72.495575h199.362831a54.371681 54.371681 0 0 0 54.371682-54.371682l36.247787-54.371681h163.115045v289.982301a72.495575 72.495575 0 0 1-72.495576 72.495575z" fill="#E2E2EE" p-id="6388"></path><path d="M863.059823 467.958938L684.539469 253.734513H330.398584L151.87823 467.958938l-13.774159-11.599292L322.061593 235.610619h370.814867l183.957522 220.749027z" fill="#E2E2EE" p-id="6389"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="90.000000pt" height="32.000000pt" viewBox="0 0 90.000000 32.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,32.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"><defs><style>.a{fill:#fff;stroke:#707070;}.b{fill:#596380;}.c{clip-path:url(#a);}.d{clip-path:url(#b);}</style><clipPath id="a"><rect class="a" width="14" height="14" transform="translate(786 458)"/></clipPath><clipPath id="b"><rect class="b" width="11" height="14"/></clipPath></defs><g class="c" transform="translate(-786 -458)"><g transform="translate(155.5 328)"><g transform="translate(632 130)"><g class="d" transform="translate(0)"><path class="b" d="M636.689,140.115V141.7a.687.687,0,1,0,1.374,0v-1.584a1.4,1.4,0,0,0,.687-1.21,1.374,1.374,0,1,0-2.748,0,1.4,1.4,0,0,0,.687,1.21Zm-2.061-4.7v-2.1a2.748,2.748,0,1,1,5.5,0v2.1H641.5a1.386,1.386,0,0,1,1.374,1.4V143.1a1.386,1.386,0,0,1-1.374,1.4h-8.243a1.386,1.386,0,0,1-1.374-1.4v-6.288a1.386,1.386,0,0,1,1.374-1.4Zm2.748-3.493a1.386,1.386,0,0,0-1.374,1.4v2.1h2.748v-2.1a1.386,1.386,0,0,0-1.374-1.4Zm0,0" transform="translate(-631.882 -130.511)"/></g></g></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90.667" height="22" viewBox="0 0 90.667 22"><defs><clipPath id="a"><path d="M2404.472,992h-22.8l3.2-22h22.8Z" transform="translate(-2381.676 -970)" fill="#1a53ff"/></clipPath><clipPath id="b"><rect width="62.667" height="16" fill="#262626"/></clipPath></defs><g transform="translate(-30 -21)"><g transform="translate(30 21)"><g clip-path="url(#a)"><path d="M2388.177,1037.5h0l-6.5,5.5h11.4Z" transform="translate(-2381.676 -1021)" fill="#1a53ff" opacity="0.5"/><path d="M2384.881,970l-3.2,22h5.7l.8-5.5h5.7l.8-5.5h5.7l-.8,5.5-.8,5.5h5.7l3.2-22Zm15.9,8.25h-5.7l.8-5.5h5.7Z" transform="translate(-2381.677 -970)" fill="#1a53ff"/></g></g><g transform="translate(-628.729 -2051)"><g transform="translate(686.73 2076)" clip-path="url(#b)"><path d="M930.451,2092.935a2.5,2.5,0,0,0-2.543-1.935h-5.479l-.225,1.6h5.367a1.366,1.366,0,0,1,1.082.476,1.014,1.014,0,0,1-.781,1.657h-3.554a3.383,3.383,0,0,0-3.243,2.844l-.175,1.245a2.414,2.414,0,0,0,2.443,2.845h2.488a3.952,3.952,0,0,0,2.037-.588l-.082.588h1.6l.5-3.556.474-3.378.125-.889h0A2.585,2.585,0,0,0,930.451,2092.935Zm-1.915,3.4-.1.711-.15,1.067a2.329,2.329,0,0,1-2.229,1.956h-2.488a1.057,1.057,0,0,1-1.069-1.244l.175-1.245a1.482,1.482,0,0,1,1.419-1.244h3.554a2.665,2.665,0,0,0,.911-.163Z" transform="translate(-879.269 -2088.333)" fill="#262626"/><path d="M802.041,2091h-3.2a3.383,3.383,0,0,0-3.242,2.844l-.237,1.689-.225,1.6-.237,1.689a2.414,2.414,0,0,0,2.443,2.845h3.2a3.383,3.383,0,0,0,3.242-2.845l.013-.089h-1.6l-.013.089a1.482,1.482,0,0,1-1.418,1.244h-3.2a1.057,1.057,0,0,1-1.069-1.244l.237-1.689h7.285l.225-1.6.237-1.689A2.414,2.414,0,0,0,802.041,2091Zm-5.079,4.533.237-1.689a1.482,1.482,0,0,1,1.419-1.244h3.2a1.057,1.057,0,0,1,1.069,1.244l-.237,1.689Z" transform="translate(-775.657 -2088.333)" fill="#262626"/><path d="M740.041,2091h-3.2a3.383,3.383,0,0,0-3.242,2.844l-.237,1.689-.225,1.6-.237,1.689a2.415,2.415,0,0,0,2.444,2.845h3.2a3.383,3.383,0,0,0,3.242-2.845l.013-.089h-1.6l-.013.089a1.482,1.482,0,0,1-1.418,1.244h-3.2a1.057,1.057,0,0,1-1.069-1.244l.237-1.689h7.285l.225-1.6.237-1.689A2.414,2.414,0,0,0,740.041,2091Zm-5.079,4.533.237-1.689a1.482,1.482,0,0,1,1.418-1.244h3.2a1.057,1.057,0,0,1,1.069,1.244l-.237,1.689Z" transform="translate(-724.674 -2088.333)" fill="#262626"/><path d="M693.933,2076H690.38l-.25,1.778h1.777l-.849,6.044-.325,2.311a1.694,1.694,0,0,1-1.621,1.423H686.98l-.25,1.777h2.132a3.806,3.806,0,0,0,3.648-3.2l1.174-8.355h0Z" transform="translate(-686.73 -2076)" fill="#262626"/><path d="M990.5,2091l-.225,1.6-.325,2.311-.474,3.377-.075.533a1.482,1.482,0,0,1-1.418,1.244h-3.2a1.057,1.057,0,0,1-1.069-1.244l.549-3.911.325-2.311.225-1.6h-1.6l-1.1,7.822a2.414,2.414,0,0,0,2.443,2.845h3.2a3.094,3.094,0,0,0,1.284-.29l-.041.29a1.268,1.268,0,0,1-1.216,1.066H982.1l-.225,1.6h5.686a3.171,3.171,0,0,0,3.04-2.666l.4-2.845.075-.533L992.1,2091Z" transform="translate(-929.43 -2088.333)" fill="#262626"/><path d="M862.644,2091h-3.2a3.094,3.094,0,0,0-1.285.29l.041-.29h-1.6l-.4,2.844-.7,4.978-.774,5.511h1.6l.415-2.956a2.574,2.574,0,0,0,1.2.29h3.2a3.383,3.383,0,0,0,3.242-2.845l.7-4.978A2.414,2.414,0,0,0,862.644,2091Zm.145,7.822a1.482,1.482,0,0,1-1.419,1.244h-3.2a1.057,1.057,0,0,1-1.069-1.244l.7-4.978a1.482,1.482,0,0,1,1.418-1.244h3.2a1.057,1.057,0,0,1,1.069,1.244Z" transform="translate(-824.879 -2088.333)" fill="#262626"/></g></g></g></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621851766037" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2360" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><defs><style type="text/css"></style></defs><path d="M243.2 512c0-49.664-39.936-89.6-89.6-89.6S64 462.336 64 512s39.936 89.6 89.6 89.6 89.6-39.936 89.6-89.6z m179.2 0c0 49.664 39.936 89.6 89.6 89.6s89.6-39.936 89.6-89.6-39.936-89.6-89.6-89.6-89.6 39.936-89.6 89.6z m358.4 0c0 49.664 39.936 89.6 89.6 89.6S960 561.664 960 512s-39.936-89.6-89.6-89.6c-49.152 0-89.6 39.936-89.6 89.6z" p-id="2361" fill="#2c2c2c"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="20.001" height="20" viewBox="0 0 20.001 20"><defs><style>.a{fill:#252626;}</style></defs><path class="a" d="M-3862-1142a3,3,0,0,1-3-3v-14a3,3,0,0,1,3-3h14a3,3,0,0,1,3,3v14a3,3,0,0,1-3,3Zm-1-17v14a1,1,0,0,0,1,1h14a1,1,0,0,0,1-1v-14a1,1,0,0,0-1-1h-14A1,1,0,0,0-3863-1159Zm11,12v-2a1,1,0,0,1,1-1,1,1,0,0,1,1,1v2a1,1,0,0,1-1,1A1,1,0,0,1-3852-1147Zm-4,0v-10a1,1,0,0,1,1-1,1,1,0,0,1,1,1v10a1,1,0,0,1-1,1A1,1,0,0,1-3856-1147Zm-4,0v-7a1,1,0,0,1,1-1,1,1,0,0,1,1,1v7a1,1,0,0,1-1,1A1,1,0,0,1-3860-1147Z" transform="translate(3865 1162)"/></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619595883977" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3229" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M495.055 647.042a26.196 26.196 0 0 1-18.583-7.697L250.689 413.561c-10.263-10.263-10.263-26.903 0-37.166 10.263-10.263 26.903-10.263 37.166 0l207.2 207.2 207.2-207.2c10.263-10.263 26.904-10.263 37.167 0 10.262 10.263 10.262 26.903 0 37.166L513.638 639.345a26.196 26.196 0 0 1-18.583 7.697z" fill="#2c2c2c" p-id="3230"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619597015272" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5736" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M617.6 233.6c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 28.8 3.2 41.6l-3.2 3.2-233.6 233.6 233.6 233.6c12.8 12.8 12.8 28.8 3.2 41.6l-3.2 3.2c-12.8 12.8-28.8 12.8-41.6 3.2l-3.2-3.2-256-256c-12.8-12.8-12.8-28.8-3.2-41.6l3.2-3.2 256-256z" fill="#2c2c2c" p-id="5737"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619597331494" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1762" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M406.4 233.6c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 28.8-3.2 41.6l3.2 3.2 233.6 233.6-233.6 233.6c-12.8 12.8-12.8 28.8-3.2 41.6l3.2 3.2c12.8 12.8 28.8 12.8 41.6 3.2l3.2-3.2 256-256c12.8-12.8 12.8-28.8 3.2-41.6l-3.2-3.2-256-256z" fill="#2c2c2c" p-id="1763"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619595944852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4607" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M495.055 308.698a26.196 26.196 0 0 0-18.583 7.697L250.689 542.179c-10.263 10.263-10.263 26.903 0 37.166s26.903 10.263 37.166 0l207.2-207.2 207.2 207.2c10.263 10.263 26.904 10.263 37.167 0 10.262-10.263 10.262-26.903 0-37.166L513.638 316.395a26.196 26.196 0 0 0-18.583-7.697z" fill="#2c2c2c" p-id="4608"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"><defs><style>.a,.b{fill:#1a53ff;}.a{stroke:#707070;}.c{clip-path:url(#a);}.d{clip-path:url(#b);}</style><clipPath id="a"><rect class="a" width="14" height="14" transform="translate(796 520)"/></clipPath><clipPath id="b"><rect class="b" width="12" height="12" transform="translate(0)"/></clipPath></defs><g class="c" transform="translate(-796 -520)"><g transform="translate(-100 289.628)"><g class="d" transform="translate(897 231.372)"><path class="b" d="M1182.9,231.267h-3.343v3.745a2.254,2.254,0,0,1,0,4.508v3.75h3.343a2.291,2.291,0,0,0,2.258-2.308V233.58a2.289,2.289,0,0,0-2.258-2.313Zm0,0" transform="translate(-1173.16 -231.269)"/><path class="b" d="M903.133,235.581a1.657,1.657,0,0,0-.531.086v-4.3h-3.343A2.291,2.291,0,0,0,897,233.68v7.381a2.291,2.291,0,0,0,2.258,2.308H902.6v-4.293a1.713,1.713,0,0,0,.531.086,1.791,1.791,0,0,0,0-3.582Zm0,0" transform="translate(-897 -231.372)"/></g></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"><defs><style>.a,.b{fill:#1a53ff;}.a{stroke:#707070;}.c{clip-path:url(#a);}.d{clip-path:url(#b);}</style><clipPath id="a"><rect class="a" width="14" height="14" transform="translate(786 458)"/></clipPath><clipPath id="b"><rect class="b" width="11" height="14"/></clipPath></defs><g class="c" transform="translate(-786 -458)"><g transform="translate(155.5 328)"><g transform="translate(632 130)"><g class="d" transform="translate(0)"><path class="b" d="M636.689,140.115V141.7a.687.687,0,1,0,1.374,0v-1.584a1.4,1.4,0,0,0,.687-1.21,1.374,1.374,0,1,0-2.748,0,1.4,1.4,0,0,0,.687,1.21Zm-2.061-4.7v-2.1a2.748,2.748,0,1,1,5.5,0v2.1H641.5a1.386,1.386,0,0,1,1.374,1.4V143.1a1.386,1.386,0,0,1-1.374,1.4h-8.243a1.386,1.386,0,0,1-1.374-1.4v-6.288a1.386,1.386,0,0,1,1.374-1.4Zm2.748-3.493a1.386,1.386,0,0,0-1.374,1.4v2.1h2.748v-2.1a1.386,1.386,0,0,0-1.374-1.4Zm0,0" transform="translate(-631.882 -130.511)"/></g></g></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"><defs><style>.a,.b{fill:#1a53ff;}.a{stroke:#707070;}.c{clip-path:url(#a);}.d{clip-path:url(#b);}</style><clipPath id="a"><rect class="a" width="14" height="14" transform="translate(793 387)"/></clipPath><clipPath id="b"><rect class="b" width="13" height="14" transform="translate(0 0)"/></clipPath></defs><g class="c" transform="translate(-793 -387)"><g transform="translate(216.5 256)"><g class="d" transform="translate(577 131)"><path class="b" d="M740.84,134.733A3.714,3.714,0,1,0,744.554,131a3.724,3.724,0,0,0-3.714,3.733Zm0,0" transform="translate(-738.054 -131)"/><path class="b" d="M581.643,622.52h3.714a4.631,4.631,0,0,1,3.283,1.367,4.679,4.679,0,0,1,1.36,3.3v.933H577v-.933a4.679,4.679,0,0,1,1.36-3.3,4.631,4.631,0,0,1,3.283-1.367Zm0,0" transform="translate(-577 -614.12)"/></g></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"><defs><style>.a,.b{fill:#596380;}.a{stroke:#707070;}.c{clip-path:url(#a);}.d{clip-path:url(#b);}</style><clipPath id="a"><rect class="a" width="14" height="14" transform="translate(793 387)"/></clipPath><clipPath id="b"><rect class="b" width="13" height="14" transform="translate(0 0)"/></clipPath></defs><g class="c" transform="translate(-793 -387)"><g transform="translate(216.5 256)"><g class="d" transform="translate(577 131)"><path class="b" d="M740.84,134.733A3.714,3.714,0,1,0,744.554,131a3.724,3.724,0,0,0-3.714,3.733Zm0,0" transform="translate(-738.054 -131)"/><path class="b" d="M581.643,622.52h3.714a4.631,4.631,0,0,1,3.283,1.367,4.679,4.679,0,0,1,1.36,3.3v.933H577v-.933a4.679,4.679,0,0,1,1.36-3.3,4.631,4.631,0,0,1,3.283-1.367Zm0,0" transform="translate(-577 -614.12)"/></g></g></g></svg>
\ No newline at end of file
<template>
<div>
<a-modal v-model="visible" title="自动获取渠道用户ID" @ok="handleClose" :footer="null" :width="300">
<div style="width:100%;margin-bottom:20px;text-align:center">
<div style="width: 300px" class="qrcode" id="qrCodeUrl"></div>
<vueQr :text="qrImgUrl"/>
<hr/>
<span>{{ payText }}</span>
</div>
</a-modal>
</div>
</template>
<script>
import ReconnectingWebSocket from 'reconnectingwebsocket'
import vueQr from 'vue-qr'
import { getWebSocketPrefix, getChannelUserQrImgUrl } from '@/api/manage'
export default {
components: { vueQr },
data () {
return {
visible: false,
qrImgUrl: '',
payText: '', // 二维码底部描述文字
transferOrderWebSocket: null, // 支付订单webSocket对象
extObject: null // 扩展对象, 将原样返回。
}
},
methods: {
// show
showModal (appId, ifCode, extObject) {
const that = this
that.extObject = extObject
// 关闭上一个webSocket监听
if (this.transferOrderWebSocket) {
this.transferOrderWebSocket.close()
}
// 根据不同的支付方式,展示不同的信息
this.payText = ''
if (ifCode === 'wxpay') {
this.payText = '请使用微信客户端"扫一扫"'
} else if (ifCode === 'alipay') {
this.payText = '请使用支付宝客户端"扫一扫"'
}
// 当前客户端CID
const cid = appId + new Date().getTime()
// 获取二维码地址
getChannelUserQrImgUrl(ifCode, appId, cid).then(res => {
that.qrImgUrl = res
that.visible = true // 打开弹窗
// 监听响应结果
that.transferOrderWebSocket = new ReconnectingWebSocket(getWebSocketPrefix() + '/api/anon/ws/channelUserId/' + appId + '/' + cid)
that.transferOrderWebSocket.onopen = () => {}
that.transferOrderWebSocket.onmessage = (msgObject) => {
that.$emit('changeChannelUserId', { channelUserId: msgObject.data, extObject: that.extObject }) // 上层赋值
that.handleClose()
}
})
},
handleClose (e) {
if (this.transferOrderWebSocket) {
this.transferOrderWebSocket.close()
}
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
.describe {
img {
width: 30px;
height: 25px;
}
}
</style>
<template>
<global-footer class="footer custom-render">
<template v-slot:links>
</template>
<template v-slot:copyright>
Copyright © 2023 <a href="http://www.juhezhifu.com" target="_blank">juhezhifu.com</a>. All rights reserved.
</template>
</global-footer>
</template>
<script>
import { GlobalFooter } from '@ant-design-vue/pro-layout'
export default {
name: 'ProGlobalFooter',
components: {
GlobalFooter
}
}
</script>
<template>
<a-dropdown placement="bottomRight">
<span class="ant-pro-account-avatar">
<a-avatar size="small" :src="greetImg" class="antd-pro-global-header-index-avatar" />
<span>{{ currentUserName }}</span>
</span>
<template v-slot:overlay>
<a-menu class="ant-pro-drop-down menu" :selected-keys="[]">
<a-menu-item v-if="$access('ENT_C_USERINFO')" key="settings" @click="handleToSettings">
<a-icon type="setting" />
账户设置
</a-menu-item>
<a-menu-divider />
<a-menu-item key="logout" @click="handleLogout">
<a-icon type="logout" />
退出登录
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
<script>
export default {
name: 'AvatarDropdown',
props: {
},
data: function () {
return {
// currentUserName: this.$store.state.user.userName
}
},
computed: {
// 返回用户名
currentUserName () {
return this.$store.state.user.userName
},
// 返回头像
greetImg () {
return this.$store.state.user.avatarImgPath
}
},
methods: {
handleToSettings () {
this.$router.push({ name: 'ENT_C_USERINFO' })
},
handleLogout: function (e) {
this.$infoBox.confirmPrimary('确认退出?', '', () => {
this.$store.dispatch('Logout').then(() => {
this.$router.push({ name: 'login' })
})
})
}
}
}
</script>
<style lang="less" scoped>
.ant-pro-drop-down {
/deep/ .action {
margin-right: 8px;
}
/deep/ .ant-dropdown-menu-item {
min-width: 160px;
}
}
</style>
<template>
<div :class="wrpCls">
<avatar-dropdown :menu="showMenu" :current-user="currentUser" :class="prefixCls" />
</div>
</template>
<script>
import AvatarDropdown from './AvatarDropdown'
// import store from '@/store'
export default {
name: 'RightContent',
components: {
AvatarDropdown
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-global-header-index-action'
},
isMobile: {
type: Boolean,
default: () => false
},
topMenu: {
type: Boolean,
required: true
},
theme: {
type: String,
required: true
}
},
data () {
return {
showMenu: true,
currentUser: {}
}
},
computed: {
wrpCls () {
return {
'ant-pro-global-header-index-right': true,
[`ant-pro-global-header-index-${(this.isMobile || !this.topMenu) ? 'light' : this.theme}`]: true
}
}
},
mounted () {
// console.log(store)
this.currentUser = {
name: 'dd'
}
}
}
</script>
<template>
<div class="loading">
<div>
<a-spin size="large" />
</div>
</div>
</template>
<script>
export default {
name: 'GlobalLoad',
data () {
return {}
}
}
</script>
<style scoped>
.loading{
position: fixed;
top:0;
left:0;
z-index:100;
width: 100%;
height: 100%;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255,255,255,0.25);
}
</style>
<template>
<div>
<a-row :gutter="[24,24]" style="width:100%">
<!-- 卡片默认新增框 -->
<a-col
:xxl="24/span.xxl"
:xl="24/span.xl"
:lg="24/span.lg"
:md="24/span.md"
:sm="24/span.sm"
:xs="24/span.xs"
@click="$emit('addJeepayCard')"
v-if="addAuthority"
>
<div class="jeepay-card-add" :style="{'height': height + 'px'}">
<img src="~@/assets/svg/add-icon.svg" alt="add-icon" :style="{'width': height/3 + 'px', 'height': height/3 + 'px'}">
<div class="jeepay-card-add-text">
新建{{ name }}
</div>
</div>
</a-col>
<!-- 数据 -->
<a-col
v-for="(item, key) in cardList"
:key="key"
:xxl="24/span.xxl"
:xl="24/span.xl"
:lg="24/span.lg"
:md="24/span.md"
:sm="24/span.sm"
:xs="24/span.xs"
>
<slot name="cardContentSlot" :record="item"></slot>
<slot name="cardOpSlot" :record="item"></slot>
</a-col>
</a-row>
</div>
</template>
<script>
export default {
name: 'JeepayCard',
props: {
span: { type: Object, default: () => ({ xxl: 6, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 }) },
height: { type: Number, default: 200 },
name: { type: String, default: '' },
addAuthority: { type: Boolean, default: false },
reqCardListFunc: { type: Function, default: () => () => ({}) }
},
data () {
return {
cardList: []
}
},
created () {
this.refCardList()
},
methods: {
refCardList () {
const that = this
this.reqCardListFunc().then(resData => {
that.cardList = resData
})
}
}
}
</script>
<style lang="less" scoped>
.jeepay-card-add {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 2px dashed rgba(0, 0, 0, 0.15);
background: rgba(0, 0, 0, 0.03);
border-radius: 6px;
box-sizing: border-box;
}
.jeepay-card-add-text {
padding-top: 5px;
font-size: 16px;
color: rgba(0, 0, 0, 0.35);
}
</style>
<!--
Jeepay 通用表格, 支持基础分页, 检索
@author terrfly
@site https://www.jeepay.vip
@date 2021/5/8 07:18
-->
<template>
<div>
<a-table
:columns="tableColumns"
:data-source="apiResData.records"
:pagination="pagination"
:loading="showLoading"
@change="handleTableChange"
:row-selection="rowSelection"
:rowKey="rowKey"
:scroll="{ x: scrollX }"
:customRow="(record, index) => {
if(!tableRowCrossColor){
return {};
}
return { style: { 'background-color': index % 2 == 0 ? '#FCFCFC' : '#FFFFFF'} }
}"
>
<!-- 自定义列插槽, 参考:https://github.com/feseed/admin-antd-vue/blob/master/src/components/ShTable.vue -->
<!-- eslint-disable-next-line -->
<template v-for="colCustom in columnsCustomSlots" :slot="colCustom.customRender" slot-scope="record">
<slot :name="colCustom.customRender" :record="record"></slot>
</template>
</a-table>
</div>
</template>
<script>
export default {
name: 'JeepayTable', // 定义组件名称
// 传递数据参数 ( 父-->子 参数 )
props: {
initData: { type: Boolean, default: true }, // 初始化列表数据, 默认true
tableColumns: Array, // 表格数组列
reqTableDataFunc: { type: Function }, // 请求列表数据
currentChange: { type: Function, default: (v1, v2) => {} }, // 更新当前选择行事件, 默认空函数
searchData: Object, // 搜索条件参数
pageSize: { type: Number, default: 10 }, // 默认每页条数
rowSelection: Object, // checkbox选择
rowKey: { type: [String, Function] }, // 定义rowKey 如果不定义将会出现(树状结构出问题, checkbox不消失等)
scrollX: { type: Number, default: 800 }, // 定义表格的最小宽度,在小就会出现横向的滚动条
tableRowCrossColor: { type: Boolean, default: false } // 是隔行换色
},
data () {
return {
apiResData: { total: 0, records: [] }, // 接口返回数据
iPage: { pageNumber: 1, pageSize: this.pageSize }, // 默认table 分页/排序请求后端格式
pagination: { total: 0, current: 1, pageSize: this.pageSize, showSizeChanger: true, showTotal: total => `共${total}条` }, // ATable 分页配置项
showLoading: false
}
},
// 计算属性
computed: {
columnsCustomSlots () { // 自定义列插槽 1. 过滤器仅获取到包含slot属性的元素, 2. 返回slot数组
return this.tableColumns.filter(item => item.scopedSlots).map(item => item.scopedSlots)
}
},
mounted () {
if (this.initData) { // 是否自动加载数据
this.refTable(true)
}
},
methods: {
handleTableChange (pagination, filters, sorter) { // 分页、排序、筛选变化时触发
this.pagination = pagination
this.iPage = {
pageSize: pagination.pageSize, // 每页条数
pageNumber: pagination.current, // 当前页码
sortField: sorter.columnKey, // 排序字段
sortOrder: sorter.order, // 排序顺序
...filters // 过滤数据
}
this.refTable()
},
// 查询数据
refTable (isToFirst = false) {
const that = this
if (isToFirst) {
this.iPage.pageNumber = 1
this.pagination.current = 1
}
// 更新检索数据
this.showLoading = true
this.reqTableDataFunc(Object.assign({}, this.iPage, this.searchData)).then(resData => {
this.pagination.total = resData.total // 更新总数量
this.apiResData = resData // 列表数据更新
this.showLoading = false // 关闭loading
// 数据为0 ,并且为当前页面没有在第一页则需要自动跳转到上一页(解决,删除第二页数据全部删除后无数据的情况 )
if (resData.records.length === 0 && this.iPage.pageNumber > 1) {
that.$nextTick(() => {
// 最大页码
const maxPageNumber = (resData.total / this.iPage.pageSize) + ((resData.total % this.iPage.pageSize) === 0 ? 0 : 1)
if (maxPageNumber === 0) { // 已经无数据
return false
}
// 跳转到的页码
const toPageSize = (this.iPage.pageNumber - 1) > maxPageNumber ? maxPageNumber : (this.iPage.pageNumber - 1)
this.iPage.pageNumber = toPageSize
this.pagination.current = toPageSize
that.refTable(false)
})
}
// 请求成功后,关闭查询按钮的loading
that.$emit('btnLoadClose')
}).catch(res => {
this.showLoading = false
that.$emit('btnLoadClose')
}) // 关闭loading
}
}
}
</script>
<style lang="less">
// 调整antdv table默认padding高度
.ant-table-fixed{
tr{
th{
padding: 8px 8px !important;
}
th:first-child{ // 第一个表格 左填充16, 其他为8
padding-left: 16px !important;
}
td{
padding: 8px 8px !important;
}
td:first-child{
padding-left: 16px !important;
}
}
}
</style>
<!--
Jeepay 通用状态切换按钮, 支持switch和badge两个格式, 根据权限进行判断
@author terrfly
@site https://www.jeepay.vip
@date 2021/5/8 07:18
-->
<template>
<div>
<template v-if="!showSwitchType">
<div v-if="state == 0" ><a-badge status="error" text="停用" /></div>
<div v-else-if="state == 1" ><a-badge status="processing" text="启用" /></div>
<div v-else ><a-badge status="warning" text="未知" /></div>
</template>
<template v-if="showSwitchType">
<a-switch class="els" checked-children="启用" un-checked-children="停用" :checked="switchChecked" @change="onChangeInner" />
</template>
</div>
</template>
<script>
export default {
name: 'JeepayTableColState', // 定义组件名称
// 传递数据参数 ( 父-->子 参数 )
props: {
state: { type: Number, default: -1 }, // 初始化列表数据, 默认true
showSwitchType: { type: Boolean, default: false }, // 默认 badge
onChange: { type: Function, default: (checked) => { return new Promise(resolve => { resolve() }) } } // change回调事件
},
data: function () {
return { switchChecked: false }
},
mounted () {
this.switchChecked = this.state === 1
},
watch: {
state: function (o, n) {
this.switchChecked = this.state === 1
}
},
methods: {
onChangeInner: function (checked) {
const that = this
this.switchChecked = checked
// 回调输出 0 和 1; 成功不需要处理, 失败需要经状态变更为原始状态
this.onChange((checked ? 1 : 0)).then().catch(res => {
that.$nextTick(() => { that.switchChecked = !checked })
})
}
}
}
</script>
<!--
Jeepay 通用列,添加更多菜单项
@author terrfly
@site https://www.jeepay.vip
@date 2021/5/8 07:18
-->
<script>
export default {
name: 'JeepayTableColumns', // 定义组件名称
render (createElement, context) {
const slots = []
this.$slots.default.map(item => {
if (item.tag) {
slots.push(item)
}
return false
})
if (slots.length <= 3) { // 小于等于三个直接渲染
return createElement(
'div',
{ style: 'display:flex; justify-content: space-evenly;' },
slots // 子节点数组
)
} else {
const firstEL = [slots[0], slots[1]]
const menuEL = []
for (let i = 2; i < slots.length; i++) {
menuEL.push(<a-menu-item>{slots[i]}</a-menu-item>)
}
return <div style="display:flex; justify-content: space-evenly;"> {firstEL}
<a-dropdown>
<a-button style="" type="link" class="ant-dropdown-link">更多<a-icon type="down" /></a-button>
<a-menu slot="overlay">
{menuEL}
</a-menu>
</a-dropdown>
</div>
}
}
}
</script>
<style lang="less" scoped>
//当前页面的按钮, 减少padding
button { padding: 8px !important;}
</style>
<template>
<div class="jee-text-up" >
<a-input required="required" :value="msg" @input="$emit('input', $event.target.value)">
</a-input>
<label>{{ placeholder }}</label>
</div>
</template>
<script>
export default {
name: 'JeepayTextUp',
// props: ['msg', 'placeholder']
props: {
msg: { type: String },
placeholder: { type: String }
}
}
</script>
<style scoped lang="less">
// 文字上移 效果
.jee-text-up {
flex-grow:1;
flex-shrink: 1;
position: relative;
min-width: 180px;
max-width: 230px;
margin-bottom:30px;
margin-right: 16px;
input {
outline: 0;
text-indent: 60px;
transition: all .3s ease-in-out;
}
input::-webkit-input-placeholder {
color: #BFBFBF;
text-indent: 0;
}
input + label {
pointer-events: none;
position: absolute;
left: 0;
bottom: 6px;
padding: 2px 11px;
color: #BFBFBF;
font-size: 13px;
text-transform: uppercase;
transition: all .3s ease-in-out;
border-radius: 3px;
background: rgba(122, 184, 147, 0);
height: 20px;
line-height: 20px;
display: flex;
justify-content: center;
align-items: center;
}
// 三角形
input + label:after {
position: absolute;
content: "";
width: 0;
height: 0;
top: 100%;
left: 50%;
margin-left: -3px;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
transition: all .3s ease-in-out;
}
input:focus,
input:active,
input:valid + label {
text-indent: 0;
background: #fff;
}
input:focus + label,
input:active + label,
input:valid + label {
color: #fff;
background: @jee-theme;
transform: translateY(-33px);
}
input:focus + label:after,
input:active + label:after {
border-top: 4px solid @jee-theme;
}
input:valid {
text-indent: 0; //文字不下移
}
input:valid + label{
background: #dadada; // 更换背景色
}
input:valid + label:after{ // 更换背景色
border-top: 4px solid #dadada;
}
}
// 初版文字上移 效果
// .jee-text-up {
// position: relative;
// .jee-text-label {
// position: absolute;
// z-index: 1;
// left: 15px;
// top: 0px;
// transition: all .3s;
// color: #bfbfbf;
// pointer-events: none;
// height: 20px;
// line-height: 20px;
// }
// input:focus + .jee-text-label, input:valid + .jee-text-label {
// top: -35px;
// padding: 1px 10px;
// border-radius: 5px;
// background: @jee-theme;
// color: #fff;
// }
// }
</style>
<template>
<div>
<a-upload
name="file"
:action="action"
:headers="headers"
:showUploadList="false"
:multiple="true"
:before-upload="beforeUpload"
@change="handleChange"
>
<slot name="uploadSlot" :loading="loading"></slot>
<!-- <img v-if="fileUrl && type === 'image'" :src="fileUrl" />
<a-input v-else-if="fileUrl && type === 'file'" :value="fileUrl" />
<a-button style="marginLeft:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button> -->
</a-upload>
</div>
</template>
<script>
import appConfig from '@/config/appConfig'
import storage from '@/utils/jeepayStorageWrapper'
function getHeaders () {
const headers = {}
headers[appConfig.ACCESS_TOKEN_NAME] = storage.getToken()
return headers
}
export default {
name: 'JeepayUpload',
props: {
action: { type: String, default: '' },
size: { type: Number, default: 10 }, // 文件大小限制
num: { type: Number, default: 1 } // 文件数量限制
},
data () {
return {
loading: false, // 上传状态
fileList: [],
headers: getHeaders() // 放入token
}
},
created () {
},
methods: {
// 上传回调
handleChange (info) {
// 限制文件数量
/* let fileList = [...info.fileList]
fileList = fileList.length > this.num ? fileList.splice(0 - this.num) : fileList // 取最新加入的元素
fileList = fileList.map(file => {
if (file.response) {
file.url = file.response.data
}
return file
}) */
if (info.file.status === 'uploading') {
this.loading = true
}
if (info.file.status === 'done') {
this.loading = false
this.$emit('uploadSuccess', info.file.response.data)
} else if (info.file.status === 'error') {
this.$message.error(`上传失败`)
}
},
// 上传图片前的校验
beforeUpload (file) {
const validate = file.size / 1024 / 1024 < this.size
if (!validate) {
this.$message.error('文件应小于' + this.size + 'M!')
}
return validate
}
}
}
</script>
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: @primary-color;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: @primary-color;
border-left-color: @primary-color;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/**
* 全局配置信息, 包含网站标题, 动态组件定义
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/5/8 07:18
*/
/** 应用配置项 **/
export default {
APP_TITLE: 'Jeepay商户系统', // 设置浏览器title
ACCESS_TOKEN_NAME: 'iToken' // 设置请求token的名字, 用于请求header 和 localstorage中存在名称
}
/**
* 与后端开发人员的路由名称及配置项
* 组件名称 :{ 默认跳转路径(如果后端配置则已动态配置为准), 组件渲染 }
* */
export const asyncRouteDefine = {
'CurrentUserInfo': { defaultPath: '/current/userinfo', component: () => import('@/views/current/UserinfoPage') }, // 用户设置
'MainPage': { defaultPath: '/main', component: () => import('@/views/dashboard/Analysis') },
'SysUserPage': { defaultPath: '/users', component: () => import('@/views/sysuser/SysUserPage') },
'RolePage': { defaultPath: '/roles', component: () => import('@/views/role/RolePage') },
'MchAppPage': { defaultPath: '/apps', component: () => import ('@/views/mchApp/List') }, // 商户应用列表
'PayTestPage': { defaultPath: '/paytest', component: () => import ('@/views/payTest/PayTest') }, // 支付测试
'MchTransferPage': { defaultPath: '/doTransfer', component: () => import ('@/views/transfer/MchTransferPage') }, // 转账
'PayOrderListPage': { defaultPath: '/payOrder', component: () => import('@/views/order/pay/PayOrderList') }, // 支付订单列表
'RefundOrderListPage': { defaultPath: '/refundOrder', component: () => import('@/views/order/refund/RefundOrderList') }, // 退款订单列表
'TransferOrderListPage': { defaultPath: '/transferOrder', component: () => import('@/views/order/transfer/TransferOrderList') }, // 转账订单
'DivisionReceiverGroupPage': { defaultPath: '/divisionReceiverGroup', component: () => import('@/views/division/group/DivisionReceiverGroupPage') }, // 分账账号组管理
'DivisionReceiverPage': { defaultPath: '/divisionReceiver', component: () => import('@/views/division/receiver/DivisionReceiverPage') }, // 分账账号管理
'DivisionRecordPage': { defaultPath: '/divisionRecord', component: () => import('@/views/division/record/DivisionRecordPage') } // 分账记录
}
import { printANSI } from '@/utils/screenLog'
export default function Initializer () {
printANSI() // 请自行移除该行. please remove this line
// last step
}
import Vue from 'vue'
// base library
import {
ConfigProvider,
Layout,
Input,
InputNumber,
Button,
Switch,
Radio,
Checkbox,
Select,
Card,
Form,
FormModel,
Row,
Col,
Modal,
Table,
Tabs,
Icon,
Badge,
Popover,
Dropdown,
List,
Avatar,
Breadcrumb,
Steps,
Spin,
Menu,
Drawer,
Tooltip,
Alert,
Tag,
Divider,
DatePicker,
TimePicker,
Upload,
Progress,
Skeleton,
Popconfirm,
PageHeader,
Result,
Statistic,
Descriptions,
Space,
Pagination,
message,
notification,
Tree
} from 'ant-design-vue'
import Viser from 'viser-vue'
// ext library
import VueCropper from 'vue-cropper'
Vue.use(ConfigProvider)
Vue.use(Layout)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Button)
Vue.use(Switch)
Vue.use(Radio)
Vue.use(Checkbox)
Vue.use(Select)
Vue.use(Card)
Vue.use(Form)
Vue.use(FormModel)
Vue.use(Row)
Vue.use(Col)
Vue.use(Modal)
Vue.use(Table)
Vue.use(Tabs)
Vue.use(Icon)
Vue.use(Badge)
Vue.use(Popover)
Vue.use(Dropdown)
Vue.use(List)
Vue.use(Avatar)
Vue.use(Breadcrumb)
Vue.use(Steps)
Vue.use(Spin)
Vue.use(Menu)
Vue.use(Drawer)
Vue.use(Tooltip)
Vue.use(Alert)
Vue.use(Tag)
Vue.use(Divider)
Vue.use(DatePicker)
Vue.use(TimePicker)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Skeleton)
Vue.use(Popconfirm)
Vue.use(PageHeader)
Vue.use(Result)
Vue.use(Statistic)
Vue.use(Descriptions)
Vue.use(Space)
Vue.use(Pagination)
Vue.use(Tree)
Vue.prototype.$confirm = Modal.confirm
Vue.prototype.$message = message
Vue.prototype.$notification = notification
Vue.prototype.$info = Modal.info
Vue.prototype.$success = Modal.success
Vue.prototype.$error = Modal.error
Vue.prototype.$warning = Modal.warning
Vue.use(Viser)
Vue.use(VueCropper)
process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] NOTICE: Antd use lazy-load.')
import Vue from 'vue'
// base library
import Antd from 'ant-design-vue'
import Viser from 'viser-vue'
import VueCropper from 'vue-cropper'
import 'ant-design-vue/dist/antd.less'
// ext library
import VueClipboard from 'vue-clipboard2'
import './directives/action'
VueClipboard.config.autoSetContainer = true
Vue.use(Antd)
Vue.use(Viser)
Vue.use(VueClipboard)
Vue.use(VueCropper)
process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] WARNING: Antd now use fulled imported.')
@import '../node_modules/ant-design-vue/es/style/themes/default.less';
// @import './default.less';
html,
body,
#app, #root {
height: 100%;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Microsoft YaHei,PingFang SC,Hiragino Sans GB,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;
letter-spacing: 0.4px;
}
// jee主题颜色class列表, 用于文字颜色
.jee-theme {
color: @jee-theme; //主题色
}
.jee-back {
color: @jee-back; //主要背景色
}
.jee-card-back {
color: @jee-card-back; //卡片底色
}
.jee-theme-mask {
color: @jee-theme-mask; //主体遮罩色(10% 透明度)
}
.jee-strengthen {
color: @jee-strengthen;//强化色
}
.jee-theme-hover {
color: @jee-theme-hover;//主题Hover
}
.jee-warning {
color: @jee-warning; //危险,强化警告色
}
.jee-inside-link {
color: @jee-inside-link; //内部链接
}
.jee-external-link {
color: @jee-external-link; //外部链接
}
// jee主题颜色class列表, 用于文字颜色
.jee-theme {
color: @jee-theme; //主题色
}
.jee-back {
color: @jee-back; //主要背景色
}
.jee-card-back {
color: @jee-card-back; //卡片底色
}
.jee-theme-mask {
color: @jee-theme-mask; //主体遮罩色(10% 透明度)
}
.jee-strengthen {
color: @jee-strengthen;//强化色
}
.jee-theme-hover {
color: @jee-theme-hover;//主题Hover
}
.jee-warning {
color: @jee-warning; //危险,强化警告色
}
.jee-inside-link {
color: @jee-inside-link; //内部链接
}
.jee-external-link {
color: @jee-external-link; //外部链接
}
// jee主题颜色class列表, 用于背景颜色
.jee-theme-back-color {
background-color: @jee-theme; //主题色
}
.jee-back-color {
background-color: @jee-back; //主要背景色
}
.jee-card-back-color {
background-color: @jee-card-back; //卡片底色
}
.jee-theme-mask-back-color {
background-color: @jee-theme-mask; //主体遮罩色(10% 透明度)
}
.jee-strengthen-back-color {
background-color: @jee-strengthen;//强化色
}
.jee-theme-hover-back-color {
background-color: @jee-theme-hover;//主题Hover
}
.jee-warning-back-color {
background-color: @jee-warning; //危险,强化警告色
}
.jee-inside-link-back-color {
background-color: @jee-inside-link; //内部链接
}
.jee-external-link-back-color {
background-color: @jee-external-link; //外部链接
}
.colorWeak {
filter: invert(80%);
}
.ant-layout{
.layout-basic {
height: 100vh;
min-height: 100vh;
}
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: #f0f2f5;
}
ul,
ol {
list-style: none;
}
// 滚动条高度 和宽度
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
// 滚动条颜色
::-webkit-scrollbar-thumb {
background: #B3B3B3;
}
// 滑块区域底色
::-webkit-scrollbar-track {
background: rgba(0,0,0,0.03);
}
// 滚动条hover色
::-webkit-scrollbar-thumb:hover {
background: #A6A6A6;
}
// 滚动条选中色
::-webkit-scrollbar-thumb:active {
background: #8C8C8C;
}
// 结合 表格组件中的 :scroll ,让表格在收缩时,展示滚动条
.ant-table-body{
overflow-x: auto !important;
}
// /*显示滚动条上方以及下方的渐增按钮*/
// ::-webkit-scrollbar-button:start:decrement,
// ::-webkit-scrollbar-button:end:increment {
// display: block;
// }
// /* 定义垂直滚动条渐增按扭的样式 */
// ::-webkit-scrollbar-button:vertical:end:increment {
// background-image: url(~@/assets/svg/scroll_down.svg);
// background-size: cover;
// background-position: center;
// }
// /* 定义垂直滚动条渐减按扭的样式 */
// ::-webkit-scrollbar-button:vertical:start:decrement {
// background-image: url(~@/assets/svg/scroll_up.svg);
// background-size: cover;
// background-position: center;
// }
// /* 定义水平滚动条渐增按扭的样式 */
// ::-webkit-scrollbar-button:horizontal:end:increment {
// background-image: url(~@/assets/svg/scroll_right.svg);
// background-size: cover;
// background-position: center;
// }
// /* 定义水平滚动条渐减按扭的样式 */
// ::-webkit-scrollbar-button:horizontal:start:decrement {
// background-image: url(~@/assets/svg/scroll_left.svg);
// background-size: cover;
// background-position: center;
// }
// 隐藏面包屑底下的标题
.ant-page-header-heading {
display: none;
}
// 数据列表 样式
.table-alert {
margin-bottom: 16px;
}
// 数据列表 操作
.table-operator {
margin-bottom: 18px;
button {
margin-right: 8px;
}
}
// 数据列表 搜索条件
.table-page-search-wrapper {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
padding: 30px;
padding-bottom: 0;
border-bottom: 1px solid #e8e8e8;
background: #fafafa;
.ant-form-inline {
.ant-form-item {
display: flex;
margin-bottom: 24px;
margin-right: 0;
.ant-form-item-control-wrapper {
flex: 1 1;
display: inline-block;
vertical-align: middle;
}
> .ant-form-item-label {
line-height: 32px;
padding-right: 8px;
width: auto;
}
.ant-form-item-control {
height: 32px;
line-height: 32px;
}
}
}
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
@media (max-width: @screen-xs) {
.ant-table {
width: 100%;
overflow-x: auto;
&-thead > tr,
&-tbody > tr {
> th,
> td {
white-space: pre;
> span {
display: block;
}
}
}
}
}
// 修改侧边栏宽度为230
.ant-pro-sider-menu-sider {
// min-width: 80px !important;
// max-width: 230px !important;
// 去掉侧边栏阴影
.light {
box-shadow: none;
}
}
// 删除表格的内边距
.ant-card-body {
padding: 0 !important;
}
// 增加内容区域的边框圆角
.ant-card {
border-radius: 10px !important;
overflow: hidden;
}
// 登录页输入框fcous hover事件,边框为jee主题蓝
.ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled) {
border-color: @jee-theme
}
.ant-input:focus {
border-color: @jee-theme
}
// 抽屉,按钮板块,居中
.drawer-btn-center {
position: absolute;
right: 0px;
bottom: 0px;
width: 100%;
border-top: 1px solid rgb(233, 233, 233);
padding: 10px 16px;
background: rgb(255, 255, 255);
text-align: center;
z-index: 2;
&:first-child {
margin-right: 80px;
}
button {
margin: 0;
padding: 3px 20px;
}
}
.els {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap
}
// 内容区域去掉最外层的magin 24px ,改为 padding 15px
.ant-pro-basicLayout-content {
margin: 0 ;
padding: 15px;
}
// 表格页面的间距
.ant-table-pagination.ant-pagination {
margin: 20px;
}
// 去掉表格边框线
.ant-card-bordered {
border:none;
}
.ant-table-align-left {
padding-left: 38px;
}
// 向下的30外边距
.mg-b-30 {
margin-bottom: 30px
}
.ant-card-bordered {
border: none !important;
}
// 表格,搜索框板块布局
.table-head-ground {
display: flex;
justify-content: start;
flex-wrap: wrap;
.table-layer {
display: flex;
flex-wrap: wrap;
flex-grow: 1;
flex-shrink: 1;
}
}
.table-head-layout {
min-width: 220px;
max-width: 240px;
flex-grow: 1;
flex-shrink: 1;
margin-bottom:30px !important;
margin-right: 16px !important;
}
// 404 500 403
.result-err {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
width: 100%;
overflow: auto;
div {
margin: 20px 0;
text-align:center;
font-size: 16px;
p.big-text {
font-size: 36px;
font-weight: 700;
}
}
}
\ No newline at end of file
/**
* Http请求包装对象
* 参考: iview https://githubtest.com/icarusion/iview-admin/blob/master/src/libs/axios.js
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/5/8 07:18
*/
import axios from 'axios'
import storage from '@/utils/jeepayStorageWrapper'
import appConfig from '@/config/appConfig'
import Vue from 'vue'
import store from '@/store/index'
class HttpRequest {
constructor (baseUrl = process.env.VUE_APP_API_BASE_URL) {
this.baseUrl = baseUrl
this.queue = {} // 发送队列, 格式为: {请求url: true}, 可以做一些验证之类
}
// 基础配置信息
baseConfig () {
const headers = {}
headers[appConfig.ACCESS_TOKEN_NAME] = storage.getToken()
return {
baseURL: this.baseUrl,
headers: headers
}
}
destroy (url, showLoading) {
delete this.queue[url]
}
interceptors (instance, url, showErrorMsg, showLoading) {
// 请求拦截
instance.interceptors.request.use(config => {
// 添加全局的loading...
if (!Object.keys(this.queue).length && showLoading) {
store.commit('showLoading') // 加载中显示loading组件
}
this.queue[url] = true
return config
}, error => {
store.commit('hideLoading') // 报错关闭loading组件
return Promise.reject(error)
})
// 响应拦截
instance.interceptors.response.use(res => {
this.destroy(url, showLoading)
if (showLoading) {
store.commit('hideLoading') // 报错关闭loading组件
}
const resData = res.data // 接口实际返回数据 格式为:{code: '', msg: '', data: ''}, res.data 是axios封装对象的返回数据;
if (resData.code !== 0) { // 相应结果不为0, 说明异常
if (showErrorMsg) {
Vue.prototype.$message.error(resData.msg) // 显示异常信息
}
return Promise.reject(resData)
} else {
return resData.data
}
}, error => {
this.destroy(url, showLoading)
if (showLoading) {
store.commit('hideLoading') // 报错关闭loading组件
}
let errorInfo = error.response && error.response.data && error.response.data.data
if (!errorInfo) {
errorInfo = error.response.data
}
if (error.response.status === 401) { // 无访问权限,会话超时, 提示用户信息 & 退出系统
const toLoginTimeout = setTimeout(function () {
store.dispatch('Logout').then(() => {
window.location.reload()
})
}, 3000)
Vue.prototype.$infoBox.confirmDanger(
'会话超时,请重新登录', '3s后将自动退出...',
() => {
store.dispatch('Logout').then(() => {
window.location.reload()
})
},
() => {
clearTimeout(toLoginTimeout)
},
{ okText: '重新登录', cancelText: '关闭对话' })
} else {
if (showErrorMsg) {
Vue.prototype.$message.error(JSON.stringify(errorInfo)) // 显示异常信息
}
}
return Promise.reject(errorInfo)
})
}
// interceptorsFlag: 是否进行自定义拦截器处理,默认为: true
// showErrorMsg 发送请求出现异常是否全局提示错误信息
// showLoading 发送请求前后显示全局loading
request (options, interceptorsFlag = true, showErrorMsg = true, showLoading = false) {
const instance = axios.create()
options = Object.assign(this.baseConfig(), options)
if (interceptorsFlag) { // 注入 req, respo 拦截器
this.interceptors(instance, options.url, showErrorMsg, showLoading)
}
return instance(options)
}
}
export default HttpRequest
import HttpRequest from '@/http/HttpRequest'
const request = new HttpRequest()
export default request
@import "~ant-design-vue/es/style/themes/default.less";
// 清除头部栏下方阴影
.ant-layout-header, .ant-pro-basicLayout .ant-layout-header:not(.ant-pro-top-menu) {
background: initial;
}
// 清除头部栏下方阴影 修改背景色
.ant-pro-global-header {
background: initial;
box-shadow: initial;
}
// 面包屑导航部分 修改背景色
.ant-pro-page-header-wrap-page-header-warp {
background: initial;
}
//左上角 logo 图标样式
.ant-pro-sider-menu-logo {
padding-left: 30px;
}
.ant-pro-sider-menu-logo svg {
height: 26px;
width: initial;
}
.ant-pro-global-header-index-right {
margin-right: 8px;
&.ant-pro-global-header-index-dark {
.ant-pro-global-header-index-action {
color: hsla(0, 0%, 100%, .85);
&:hover {
background: #1890ff;
}
}
}
.ant-pro-account-avatar {
.antd-pro-global-header-index-avatar {
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
background: rgba(255, 255, 255, 0.85);
}
}
.menu {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 100px;
}
}
}
<template>
<pro-layout
:menus="menus"
:collapsed="collapsed"
:mediaQuery="query"
:isMobile="isMobile"
:handleMediaQuery="handleMediaQuery"
:handleCollapse="handleCollapse"
:i18nRender="false"
v-bind="settings"
:breadcrumbRender="handleBreadcrumbRender"
:siderWidth="210"
>
<!-- 1.0.0+ 版本 pro-layout 提供 API,
我们推荐使用这种方式进行 LOGO 和 title 自定义
-->
<template v-slot:menuHeaderRender>
<div>
<!-- 当侧边栏卷起来的时候,切换仅有J字母的图标 -->
<img src="@/assets/logo-j.svg" alt="jeequan">
<!-- 在这里可以添加title,我们以图片的方式替代文字 -->
<img v-show="!collapsed" src="@/assets/svg/jeepay.svg" alt="jeepay" style="width:90px;margin: 5px 0 0 5px">
</div>
</template>
<!-- 1.0.0+ 版本 pro-layout 提供 API,
增加 Header 左侧内容区自定义
-->
<template v-slot:headerContentRender>
<div class="ant-pro-global-header-trigger" @click="routeReload()">
<a-tooltip title="刷新页面">
<a-icon type="reload" style="font-size: 18px;cursor: pointer;" />
</a-tooltip>
</div>
</template>
<template v-slot:rightContentRender>
<right-content :top-menu="settings.layout === 'topmenu'" :is-mobile="isMobile" :theme="settings.theme" />
</template>
<!-- custom footer / 自定义Footer -->
<template v-slot:footerRender>
<global-footer />
</template>
<router-view v-if="isRouterAlive"/>
</pro-layout>
</template>
<script>
import { mapState } from 'vuex'
import RightContent from '@/components/GlobalHeader/RightContent'
import GlobalFooter from '@/components/GlobalFooter'
import appConfig from '@/config/appConfig'
export default {
name: 'BasicLayout',
components: {
RightContent,
GlobalFooter
},
data () {
return {
isRouterAlive: true,
// preview.pro.antdv.com only use.
isProPreviewSite: process.env.VUE_APP_PREVIEW === 'true' && process.env.NODE_ENV !== 'development',
// end
// base
menus: [],
// 侧栏收起状态
collapsed: false,
title: appConfig.APP_TITLE,
settings: {
// 布局类型
layout: 'sidemenu', // 'sidemenu', 'topmenu'
contentWidth: 'Fluid', // Fixed , Fluid 内容区布局: 固定 | 流式
theme: 'light', // 主题 'dark' | 'light'
// 主色调
primaryColor: '#1a53ff', // 默认主题色, 如果修改颜色不生效,请清理 localStorage
fixedHeader: false, // 固定 Header
fixSiderbar: true, // 固定左侧菜单栏
colorWeak: false, // 色盲模式将
hideHintAlert: false,
hideCopyButton: false
},
// 媒体查询
query: {},
// 是否手机模式
isMobile: false
}
},
computed: {
...mapState({
// 动态主路由
mainMenu: state => state.asyncRouter.addRouters
})
},
created () {
const routes = this.mainMenu.find(item => item.path === '/')
this.menus = (routes && routes.children) || []
// 处理侧栏收起状态
// this.$watch('collapsed', () => {
// this.$store.commit(SIDEBAR_TYPE, this.collapsed)
// })
// this.$watch('isMobile', () => {
// this.$store.commit(TOGGLE_MOBILE_TYPE, this.isMobile)
// })
},
mounted () {
const userAgent = navigator.userAgent
if (userAgent.indexOf('Edge') > -1) {
this.$nextTick(() => {
this.collapsed = !this.collapsed
setTimeout(() => {
this.collapsed = !this.collapsed
}, 16)
})
}
},
methods: {
handleMediaQuery (val) {
this.query = val
if (this.isMobile && !val['screen-xs']) {
this.isMobile = false
return
}
if (!this.isMobile && val['screen-xs']) {
this.isMobile = true
this.collapsed = false
}
},
handleCollapse (val) {
this.collapsed = val
},
handleBreadcrumbRender (rrr) {
if (rrr.route.path.startsWith('/ENT')) {
return <span>{rrr.route.breadcrumbName}</span>
}
return <router-link to={rrr.route.path || '/'}><span>{rrr.route.breadcrumbName}</span></router-link>
},
routeReload () { // 刷新页面函数
this.isRouterAlive = false
this.$nextTick(() => { this.isRouterAlive = true })
}
}
}
</script>
<style lang="less">
@import "./BasicLayout.less";
</style>
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {
name: 'BlankLayout'
}
</script>
<style scoped>
</style>
<template>
<page-header-wrapper>
<router-view />
</page-header-wrapper>
</template>
<script>
export default {
name: 'PageView'
}
</script>
<script>
export default {
name: 'RouteView',
props: {
keepAlive: {
type: Boolean,
default: true
}
},
data () {
return {}
},
render () {
const { $route: { meta } } = this
const inKeep = (
<keep-alive exclude="BasicLayout">
<router-view />
</keep-alive>
)
const notKeep = (
<router-view />
)
// 这里增加了 multiTab 的判断,当开启了 multiTab 时
// 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态
// 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep
if (!meta.keepAlive) {
return notKeep
}
return this.keepAlive || meta.keepAlive ? inKeep : notKeep
}
}
</script>
<template>
<div id="userLayout" :class="['user-layout-wrapper']">
<div class="container">
<div class="user-layout-lang">
</div>
<div class="user-layout-content">
<div class="top">
<div class="header">
<a href="/">
<img src="~@/assets/logo.svg" class="logo" alt="logo">
</a>
</div>
<div class="desc">
<img src="~@/assets/svg/operate.svg" class="logo" alt="logo">
<span>商户系统</span>
</div>
</div>
<router-view />
<div class="footer">
<!-- <div class="links">-->
<!-- <a href="_self">帮助</a>-->
<!-- <a href="_self">隐私</a>-->
<!-- <a href="_self">条款</a>-->
<!-- </div>-->
<!-- <div class="copyright">
Copyright &copy; 2021 juhezhifu.com
</div> -->
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'UserLayout',
components: {
},
mounted () {
document.body.classList.add('userLayout')
},
beforeDestroy () {
document.body.classList.remove('userLayout')
}
}
</script>
<style lang="less" scoped>
#userLayout.user-layout-wrapper {
height: 100%;
&.mobile {
.container {
.main {
max-width: 368px;
width: 98%;
}
}
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
min-height: 100%;
background: #f0f2f5 url(~@/assets/images/background.png) no-repeat 50%;
background-size: cover;
//padding: 50px 0 84px;
position: relative;
.user-layout-lang {
width: 100%;
// height: 40px;
// line-height: 44px;
height: 0;
text-align: right;
}
.user-layout-content {
.top {
text-align: center;
.header {
height: 44px;
line-height: 44px;
margin-bottom:80px;
.badge {
position: absolute;
display: inline-block;
line-height: 1;
vertical-align: middle;
margin-left: -12px;
margin-top: -10px;
opacity: 0.8;
}
.logo {
height: 44px;
vertical-align: top;
border-style: none;
}
.title {
font-size: 33px;
color: rgba(0, 0, 0, .85);
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
font-weight: 600;
position: relative;
top: 2px;
}
}
.desc {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
margin-bottom: 40px;
font-size: 20px;
font-family: PingFang SC, PingFang SC-Medium;
font-weight: 500;
color: #252626;
letter-spacing: 1px;
display: flex;
justify-content: center;
align-items: center;
height: 28px;
line-height: 28px;
span {
margin-left: 10px;
}
}
}
.main {
min-width: 260px;
width: 368px;
margin: 0 auto;
}
.footer {
// position: absolute;
width: 100%;
bottom: 0;
padding: 0 16px;
margin: 48px 0 24px;
text-align: center;
.links {
margin-bottom: 8px;
font-size: 14px;
a {
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
}
}
}
.copyright {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}
a {
text-decoration: none;
}
}
}
</style>
import UserLayout from './UserLayout'
import BlankLayout from './BlankLayout'
import BasicLayout from './BasicLayout'
import RouteView from './RouteView'
import PageView from './PageView'
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }
// with polyfills
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/'
import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout'
import bootstrap from './core/bootstrap'
import './core/lazy_use' // use lazy load components
import './permission' // permission control 路由守卫
import './utils/filter' // global filter
import './global.less' // global style
import 'ant-design-vue/dist/antd.less'
import infoBox from '@/utils/infoBox'
import VueClipboard from 'vue-clipboard2' // 复制插件
Vue.use(VueClipboard) // 复制插件
Vue.config.productionTip = false
// use pro-layout components
Vue.component('pro-layout', ProLayout)
Vue.component('page-container', PageHeaderWrapper)
Vue.component('page-header-wrapper', PageHeaderWrapper)
/**
* @description 全局注册权限验证
*/
Vue.prototype.$access = (entId) => {
// eslint-disable-next-line eqeqeq
return store.state.user.accessList.some(item => item == entId)
}
Vue.prototype.$infoBox = infoBox
new Vue({
router,
store,
// init localstorage, vuex
created: bootstrap,
render: h => h(App)
}).$mount('#app')
import router from './router'
import store from './store'
import storage from '@/utils/jeepayStorageWrapper'
import NProgress from 'nprogress' // progress bar
import '@/components/NProgress/nprogress.less' // progress bar custom style
import { setDocumentTitle } from '@/utils/domUtil'
import appConfig from '@/config/appConfig'
import { getInfo } from '@/api/login'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const allowList = ['login', 'register', 'registerResult'] // no redirect allowList
const loginRoutePath = '/user/login'
// 路由守卫
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${appConfig.APP_TITLE}`)) // 设置浏览器标题
// 如果在免登录页面则直接放行
if (allowList.includes(to.name)) {
// 在免登录名单,直接进入
next()
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
return false
}
// 不包含Token 则直接跳转到登录页面
if (!storage.getToken()) {
next({ path: loginRoutePath, query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
return false
}
// 以下为包含Token的情况
// 如果用户信息不存在, 则重新获取 [用户登录成功 & 强制刷新浏览器时 会执行该函数]
if (!store.state.user.userId) {
// request login userInfo
getInfo().then(bizData => {
store.commit('SET_USER_INFO', bizData) // 调用vuex设置用户基本信息
// TODO 生成菜单
store.dispatch('GenerateRoutes', {}).then(() => {
// 根据roles权限生成可访问的路由表
// 动态添加可访问路由表
router.addRoutes(store.state.asyncRouter.addRouters)
})
next()
}).catch(() => {
// 失败时,获取用户信息失败时,调用登出,来清空历史保留信息
store.dispatch('Logout').then(() => {
next({ path: loginRoutePath, query: { redirect: to.fullPath } })
})
})
} else {
next()
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
// eslint-disable-next-line
import * as loginService from '@/api/login'
// eslint-disable-next-line
import { BasicLayout, BlankLayout, PageView, RouteView } from '@/layouts'
import store from '@/store'
import { asyncRouteDefine } from '@/config/appConfig'
// 前端路由表 = 基础定义 + 动态组件
const constantRouterComponents = Object.assign({
// 基础页面 layout 必须引入
BasicLayout: { component: BasicLayout },
BlankLayout: { component: BlankLayout },
RouteView: { component: RouteView },
PageView: { component: PageView },
'403': () => import('@/views/exception/403'),
'404': () => import('@/views/exception/404'),
'500': () => import('@/views/exception/500')
}, asyncRouteDefine)
// 前端未找到页面路由(固定不用改)
const notFoundRouter = {
path: '*', component: () => import('@/views/exception/404')
}
// 根级菜单
const rootRouter = {
name: 'index',
path: '/',
component: BasicLayout,
redirect: redirectFunc, // 根页面【/】默认跳转 地址
children: [],
meta: { title: '主页' }
}
// 动态跳转路径 func
function redirectFunc () {
let mainPageUri = ''
store.state.user.allMenuRouteTree.forEach(item => {
if (item.entId === 'ENT_MCH_MAIN') { // 当前用户是否拥有主页权限, 如果有直接跳转到该路径
mainPageUri = item.menuUri
return false
}
})
if (mainPageUri) {
return mainPageUri
}
return getOneUri(store.state.user.allMenuRouteTree)
}
// 获取到第一个uri (递归查找)
function getOneUri (item) {
let result = ''
for (let i = 0; i < item.length; i++) {
if (item[i].menuUri && item[i].entType === 'ML') {
return item[i].menuUri
}
if (item[i].children) {
result = getOneUri(item[i].children)
if (result) {
return result
}
}
}
return result
}
/**
* 动态生成菜单
* @param token
* @returns {Promise<Router>}
*/
export const generatorDynamicRouter = () => {
return new Promise((resolve, reject) => {
// 根据树状结构生成路由格式
rootRouter.children = generator(store.state.user.allMenuRouteTree)
// 构建完整路由
resolve([rootRouter, notFoundRouter])
})
}
/**
* 格式化树形结构数据 生成 vue-router 层级路由表
*
* @param routerMap
* @returns {*}
*/
export const generator = (allMenuRouteTreeArray) => {
const menuResult = []
// 遍历map
allMenuRouteTreeArray.map(item => {
const defComponent = constantRouterComponents[item.componentName || item.entId]
// 找不到组件 || 其他菜单
if (!defComponent) {
return
}
// 跳转uri
let path = item.menuUri || defComponent.defaultPath
// 没有配置path, 如果为目录则允许为空, 否则不在加载此配置
if (!path) {
if (item.children && item.children.length > 0) {
path = `/${item.entId}`
} else {
return // 不再加载此配置项
}
}
const currentRouter = {
// 如果路由设置了 path,则作为默认 path,否则 路由地址 为默认配置
path: path,
// 路由名称,建议唯一
name: item.entId,
// 该路由对应页面的 组件 :方案2 (动态加载)
component: ((defComponent && defComponent.component) || (() => import(`@/views/${item.componentName}`))),
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
meta: {
title: item.entName,
icon: item.menuIcon,
keepAlive: false
},
hidden: item.entType === 'MO' // 当其他菜单时需要隐藏显示
}
// 是否有子菜单,并递归处理
if (item.children && item.children.length > 0) {
// Recursion
currentRouter.children = generator(item.children)
}
menuResult.push(currentRouter)
})
return menuResult
}
import Vue from 'vue'
import Router from 'vue-router'
import { UserLayout } from '@/layouts'
// hack router push callback
// [解决 vue-router跳转相同路径报错 ]
const originalPush = Router.prototype.push
Router.prototype.push = function push (location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch(err => err)
}
Vue.use(Router)
// 纯静态路由配置项
const constantRouterMap = [
{
path: '/user',
component: UserLayout,
children: [
{ path: 'login', name: 'login', component: () => import('@/views/user/Login') }
]
}
]
export default new Router({
mode: 'history',
routes: constantRouterMap
})
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import asyncRouter from './modules/async-router'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user, // 用户相关状态机
asyncRouter // 动态菜单
},
state: {
// 定义全局loading 为false
globalLoading: false
},
mutations: {
// 显示与关闭 全局 loading
showLoading (state) {
state.globalLoading = true
},
hideLoading (state) {
state.globalLoading = false
}
},
actions: {
}
})
/**
* 向后端请求用户的菜单,动态生成路由
*/
import { generatorDynamicRouter } from '@/router/generator-routers'
const asyncRouter = {
state: {
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
}
},
actions: {
GenerateRoutes ({ commit }, data) {
return new Promise(resolve => {
generatorDynamicRouter().then(routers => {
commit('SET_ROUTERS', routers)
resolve()
})
})
}
}
}
export default asyncRouter
import storage from '@/utils/jeepayStorageWrapper'
import { login, logout } from '@/api/login'
import appConfig from '@/config/appConfig'
const user = {
state: {
token: '',
userName: '', // 用户名
userId: '', // 用户ID
avatarImgPath: '', // 头像
allMenuRouteTree: [], // 全部动态 router
accessList: [], // 用户权限集合
isAdmin: '', // 是否是超级管理员
loginUsername: '', // 登录用户名
state: '', // 用户状态
sysType: '', // 所属系统
telphone: '' // 手机号
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
// 设置头像
SET_AVATAR (state, avatarPath) {
state.avatarImgPath = avatarPath
},
// 设置用户信息
SET_USER_INFO: (state, userInfo) => {
state.userId = userInfo.sysUserId // 用户ID
state.userName = userInfo.realname // 昵称
state.avatarImgPath = userInfo.avatarUrl // 头像
state.accessList = userInfo.entIdList // 权限集合
state.allMenuRouteTree = userInfo.allMenuRouteTree // 全部路由集合
state.isAdmin = userInfo.isAdmin // 是否是超级管理员
state.loginUsername = userInfo.loginUsername // 登录用户名
state.state = userInfo.state // 用户状态
state.sysType = userInfo.sysType // 所属系统
state.telphone = userInfo.telphone // 手机号
}
},
actions: {
// 登录
Login ({ commit }, { loginParams, isSaveStorage }) {
return new Promise((resolve, reject) => {
login(loginParams).then(bizData => {
storage.setToken(bizData[appConfig.ACCESS_TOKEN_NAME], isSaveStorage)
commit('SET_TOKEN', bizData[appConfig.ACCESS_TOKEN_NAME])
resolve()
}).catch(error => {
reject(error)
})
})
},
// 登出
Logout ({ commit, state }) {
return new Promise((resolve) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
storage.cleanToken()
location.reload() // 退出时 重置缓存
resolve()
}).catch(() => {
resolve()
}).finally(() => {
})
})
}
}
}
export default user
export const setDocumentTitle = function (title) {
document.title = title
const ua = navigator.userAgent
// eslint-disable-next-line
const regex = /\bMicroMessenger\/([\d\.]+)/
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
const i = document.createElement('iframe')
i.src = '/favicon.ico'
i.style.display = 'none'
i.onload = function () {
setTimeout(function () {
i.remove()
}, 9)
}
document.body.appendChild(i)
}
}
import Vue from 'vue'
import moment from 'moment'
import 'moment/locale/zh-cn'
moment.locale('zh-cn')
Vue.filter('NumberFormat', function (value) {
if (!value) {
return '0'
}
const intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') // 将整数部分逢三一断
return intPartFormat
})
Vue.filter('dayjs', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(dataStr).format(pattern)
})
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(dataStr).format(pattern)
})
/**
* 通用信息弹层
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/5/8 07:18
*/
import { Modal } from 'ant-design-vue'
// 确认提示: 标题, 内容, 点击确定回调函数, 取消回调, 扩展参数
export const confirmResult = {
confirm: function (title, content, okFunc, cancelFunc = (() => {}), extConfig = {}) {
return Modal.confirm(
Object.assign({
okText: '确定',
cancelText: '取消',
title: title || '提示',
content: content,
onOk: okFunc,
onCancel: cancelFunc,
confirmLoading: true
}, extConfig))
},
confirmPrimary: function (title, content, okFunc, cancelFunc = (() => {}), extConfig = {}) {
return this.confirm(title, content, okFunc, cancelFunc, Object.assign({ okType: 'primary' }, extConfig))
},
confirmDanger: function (title, content, okFunc, cancelFunc = (() => {}), extConfig = {}) {
return this.confirm(title, content, okFunc, cancelFunc, Object.assign({ okType: 'danger' }, extConfig))
},
modalError: function (title, content, okFunc = (() => {})) {
return Modal.error({ title: title, content: content, onOk: okFunc })
},
modalSuccess: function (title, content, okFunc = (() => {})) {
return Modal.success({ title: title, content: content, onOk: okFunc })
},
modalWarning: function (title, content, okFunc = (() => {})) {
return Modal.warning({ title: title, content: content, onOk: okFunc })
}
}
export default confirmResult
/**
* storage 存储包装类
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/5/8 07:18
*/
import storage from 'store'
import appConfig from '@/config/appConfig'
var SESSION_TOKEN = ''
const wrapper = {
/* 获取当前Token **/
getToken: () => {
return SESSION_TOKEN || storage.get(appConfig.ACCESS_TOKEN_NAME)
},
/* 清空Token **/
cleanToken: () => {
SESSION_TOKEN = ''
storage.remove(appConfig.ACCESS_TOKEN_NAME)
},
/* 设置token信息 **/
setToken (tokenVal, isSaveStorage) {
SESSION_TOKEN = tokenVal
if (isSaveStorage) {
storage.set(appConfig.ACCESS_TOKEN_NAME, tokenVal, 7 * 24 * 60 * 60 * 1000)
}
}
}
export default wrapper
/* eslint-disable */
export const printANSI = () => {
let text = ``
console.log(`%c${text}`, 'color: #fc4d50')
console.log('%c!', 'color: #fff; font-size: 14px; font-weight: 300; text-shadow:#000 1px 0 0,#000 0 1px 0,#000 -1px 0 0,#000 0 -1px 0;')
}
// 定义全局自增ID
var atomicLong = 1
export function timeFix () {
const time = new Date()
const hour = time.getHours()
return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好'
}
export function isIE () {
const bw = window.navigator.userAgent
const compare = (s) => bw.indexOf(s) >= 0
const ie11 = (() => 'ActiveXObject' in window)()
return compare('MSIE') || ie11
}
/** 生成自增序列号(不重复) **/
export function genRowKey () {
return new Date().getTime() + '_' + (atomicLong++)
}
.textOverflow() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
}
.textOverflowMulti(@line: 3, @bg: #fff) {
position: relative;
max-height: @line * 1.5em;
margin-right: -1em;
padding-right: 1em;
overflow: hidden;
line-height: 1.5em;
text-align: justify;
&::before {
position: absolute;
right: 14px;
bottom: 0;
padding: 0 1px;
background: @bg;
content: '...';
}
&::after {
position: absolute;
right: 14px;
width: 1em;
height: 1em;
margin-top: 0.2em;
background: white;
content: '';
}
}
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
}
\ No newline at end of file
<template>
<a-modal
title="修改头像"
:visible="visible"
:maskClosable="false"
:confirmLoading="confirmLoading"
:width="800"
:footer="null"
@cancel="cancelHandel">
<a-row>
<a-col :xs="24" :md="12" :style="{height: '350px'}">
<vue-cropper
ref="cropper"
:img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
@realTime="realTime"
>
</vue-cropper>
</a-col>
<a-col :xs="24" :md="12" :style="{height: '350px'}">
<div class="avatar-upload-preview">
<img :src="previews.url" :style="previews.img"/>
</div>
</a-col>
</a-row>
<br>
<a-row>
<a-col :lg="2" :md="2">
<a-upload name="file" :beforeUpload="beforeUpload" :showUploadList="false">
<a-button icon="upload">选择图片</a-button>
</a-upload>
</a-col>
<a-col :lg="{span: 1, offset: 2}" :md="2">
<a-button icon="plus" @click="changeScale(1)"/>
</a-col>
<a-col :lg="{span: 1, offset: 1}" :md="2">
<a-button icon="minus" @click="changeScale(-1)"/>
</a-col>
<a-col :lg="{span: 1, offset: 1}" :md="2">
<a-button icon="undo" @click="rotateLeft"/>
</a-col>
<a-col :lg="{span: 1, offset: 1}" :md="2">
<a-button icon="redo" @click="rotateRight"/>
</a-col>
<a-col :lg="{span: 2, offset: 6}" :md="2">
<a-button type="primary" @click="finish('blob')">保存</a-button>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { upload, updateUserInfo } from '@/api/manage'
import store from '@/store'
export default {
data () {
return {
recordId: store.state.user.userId, // 拿到ID
userLoad: upload.avatar, // 图片上传地址
visible: false,
id: null,
confirmLoading: false,
fileList: [],
uploading: false,
options: {
// img: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
img: '',
autoCrop: true,
autoCropWidth: 200,
autoCropHeight: 200,
fixedBox: true
},
previews: {}
}
},
methods: {
edit (id) {
this.visible = true
this.id = id
/* 获取原始头像 */
},
close () {
this.id = null
this.visible = false
},
cancelHandel () {
this.close()
},
changeScale (num) {
num = num || 1
this.$refs.cropper.changeScale(num)
},
rotateLeft () {
this.$refs.cropper.rotateLeft()
},
rotateRight () {
this.$refs.cropper.rotateRight()
},
beforeUpload (file) {
const reader = new FileReader()
// 把Array Buffer转化为blob 如果是base64不需要
// 转化为base64
reader.readAsDataURL(file)
reader.onload = () => {
this.options.img = reader.result
}
// 转化为blob
// reader.readAsArrayBuffer(file)
return false
},
// 上传图片(点击上传按钮)
finish (type) {
console.log('finish')
const _this = this
// 通过FormData构造函数创建一个空对象
const formData = new FormData()
// 输出
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
// 通过该方法可以获取当前文件的一个内存URL
const img = window.URL.createObjectURL(data)
this.model = true
this.modelSrc = img
formData.append('avatarUrl', img)
// this.$http.post('https://www.mocky.io/v2/5cc8019d300000980a055e76', formData, { contentType: false, processData: false, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
updateUserInfo(formData)
// .then(res => {
// console.log(res)
// }).catch(err => {
// console.log(err)
// })
.then((response) => {
console.log('upload response:', response)
// var res = response.data
// if (response.status === 'done') {
// _this.imgFile = ''
// _this.headImg = res.realPathList[0] // 完整路径
// _this.uploadImgRelaPath = res.relaPathList[0] // 非完整路径
// _this.$message.success('上传成功')
// this.visible = false
// }
_this.$message.success('上传成功')
_this.$emit('ok', response.url)
_this.visible = false
}).catch(err => {
console.log(err)
})
console.log(_this.userLoad, _this.recordId, this.modelSrc)
})
} else {
this.$refs.cropper.getCropData((data) => {
this.model = true
this.modelSrc = data
})
}
},
okHandel () {
const vm = this
vm.confirmLoading = true
setTimeout(() => {
vm.confirmLoading = false
vm.close()
vm.$message.success('上传头像成功')
}, 2000)
},
realTime (data) {
this.previews = data
}
}
}
</script>
<style lang="less" scoped>
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 180px;
height: 180px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
</style>
<template>
<div style="background: #fff">
<a-tabs @change="selectTabs">
<a-tab-pane key="1" tab="基本信息">
<div class="account-settings-info-view">
<a-row :gutter="16">
<a-col :md="16" :lg="16">
<a-form-model ref="infoFormModel" :model="saveObject" :label-col="{span: 9}" :wrapper-col="{span: 10}" :rules="rules">
<a-form-model-item label="用户登录名:">
<a-input v-model="saveObject.loginUsername" disabled/>
</a-form-model-item>
<a-form-model-item label="用户姓名:" prop="realname">
<a-input v-model="saveObject.realname" />
</a-form-model-item>
<a-form-model-item label="手机号:" prop="telphone">
<a-input v-model="saveObject.telphone" disabled />
</a-form-model-item>
<a-form-model-item label="请选择性别:">
<a-radio-group v-model="saveObject.sex">
<a-radio :value="1"></a-radio>
<a-radio :value="2"></a-radio>
</a-radio-group>
</a-form-model-item>
</a-form-model>
<a-form-item style="display:flex;justify-content:center">
<a-button type="primary" @click="changeInfo" icon="check-circle" :loading="btnLoading">更新基本信息</a-button>
</a-form-item>
</a-col>
<a-col :md="8" :lg="8" :style="{ minHeight: '180px',margin:'0 auto' }">
<!-- 原始头像上传,带有图片裁剪
<div class="ant-upload-preview" @click="$refs.modal.edit(1)" >
<a-icon type="cloud-upload-o" class="upload-icon"/>
<div class="mask">
<a-icon type="plus" />
</div>
<img :src="saveObject.avatarUrl"/>
</div> -->
<div class="ant-upload-preview" >
<img :src="saveObject.avatarUrl" style="border: 1px solid rgba(0,0,0,0.08)"/>
<JeepayUpload
style="margin-top:10px"
:action="action"
accept=".jpg, .jpeg, .png"
@uploadSuccess="uploadSuccess($event, '')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginLeft:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '更换头像' }} </a-button>
</template>
</JeepayUpload>
</div>
</a-col>
</a-row>
<avatar-modal ref="modal" @ok="setavatar"/>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="安全信息">
<div class="account-settings-info-view">
<a-row :gutter="16">
<a-col :md="16" :lg="16">
<a-form-model ref="pwdFormModel" :model="updateObject" :label-col="{span: 9}" :wrapper-col="{span: 10}" :rules="rulesPass">
<a-form-model-item label="原密码:" prop="originalPwd">
<a-input-password v-model="updateObject.originalPwd" placeholder="请输入原密码" />
</a-form-model-item>
<a-form-model-item label="新密码:" prop="newPwd">
<a-input-password v-model="updateObject.newPwd" placeholder="请输入新密码" />
</a-form-model-item>
<a-form-model-item label="确认新密码:" prop="confirmPwd">
<a-input-password v-model="updateObject.confirmPwd" placeholder="确认新密码" />
</a-form-model-item>
</a-form-model>
<a-form-item style="display:flex;justify-content:center">
<a-button type="primary" icon="safety-certificate" @click="confirm" :loading="btnLoading">更新密码</a-button>
</a-form-item>
</a-col>
</a-row>
</div>
</a-tab-pane>
</a-tabs>
</div>
</template>
<script>
import JeepayUpload from '@/components/JeepayUpload/JeepayUpload'
import { getInfo } from '@/api/login'
import { Base64 } from 'js-base64'
import { updateUserInfo, updateUserPass, getUserInfo, upload } from '@/api/manage'
import AvatarModal from './AvatarModal'
import store from '@/store'
export default {
components: {
AvatarModal, JeepayUpload
},
data () {
return {
action: upload.avatar, // 上传图标地址
btnLoading: false,
saveObject: {
loginUsername: '', // 登录名
realname: '', // 真实姓名
sex: '',
avatarUrl: '' // 头像地址
},
updateObject: {
originalPwd: '', // 原密码
newPwd: '', // 新密码
confirmPwd: '' // 确认密码
},
recordId: store.state.user.userId, // 拿到ID
rules: {
realname: [{ required: true, message: '请输入真实姓名', trigger: 'blur' }]
},
rulesPass: {
originalPwd: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
newPwd: [{ min: 6, max: 12, required: true, message: '请输入6-12位新密码', trigger: 'blur' }],
confirmPwd: [{ required: true, message: '请输入确认新密码', trigger: 'blur' }, {
validator: (rule, value, callBack) => {
this.updateObject.newPwd === value ? callBack() : callBack('新密码与确认密码不一致')
}
}]
}
}
},
created () {
this.detail()
},
methods: {
setavatar (url) {
this.option.img = url
},
detail () { // 获取基本信息
const that = this
getUserInfo().then(res => {
that.saveObject = res
})
},
changeInfo () { // 更新基本信息事件
const that = this
this.$refs.infoFormModel.validate(valid => {
if (valid) { // 验证通过
this.$infoBox.confirmPrimary('确认更新信息吗?', '', () => {
// 请求接口
that.btnLoading = true // 打开按钮上的 loading
updateUserInfo(that.saveObject).then(res => {
that.btnLoading = true // 打开按钮上的 loading
return getInfo()
})
.then(bizData => {
bizData.avatarUrl = that.saveObject.avatarUrl
bizData.realname = that.saveObject.realname
that.btnLoading = false
store.commit('SET_USER_INFO', bizData) // 调用vuex设置用户基本信息
that.$message.success('修改成功')
console.log('cg')
}).catch(err => {
console.log(err)
that.btnLoading = false // 打开按钮上的 loading
})
})
}
})
},
confirm (e) { // 确认更新密码
const that = this
this.$refs.pwdFormModel.validate(valid => {
if (valid) { // 验证通过
this.$infoBox.confirmPrimary('确认更新密码吗?', '', () => {
// 请求接口
that.btnLoading = true // 打开按钮上的 loading
that.confirmLoading = true // 显示loading
that.updateObject.recordId = that.recordId // 用户ID
that.updateObject.originalPwd = Base64.encode(that.updateObject.originalPwd)
that.updateObject.confirmPwd = Base64.encode(that.updateObject.confirmPwd)
this.$delete(this.updateObject, 'newPwd')
updateUserPass(that.updateObject).then(res => {
that.$message.success('修改成功')
// 退出登录
this.$store.dispatch('Logout').then(() => {
this.$router.push({ name: 'login' })
})
}).catch(res => {
that.confirmLoading = false
that.btnLoading = false
})
})
}
})
},
selectTabs () { // 清空必填提示
if (this.$refs.pwdFormModel !== undefined) {
this.$refs.pwdFormModel.resetFields()
}
},
uploadSuccess (value, name) {
this.saveObject.avatarUrl = value
this.$forceUpdate()
}
}
}
</script>
<style lang="less" scoped>
.avatar-upload-wrapper {
height: 200px;
width: 100%;
}
.ant-upload-preview {
text-align:center ;
position: relative;
margin: 0 auto;
width: 100%;
// max-width: 180px;
border-radius: 50%;
// box-shadow: 0 0 4px #ccc;
.upload-icon {
position: absolute;
top: 0;
right: 10px;
font-size: 1.4rem;
padding: 0.5rem;
background: rgba(222, 221, 221, 0.7);
border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.2);
}
.mask {
opacity: 0;
position: absolute;
background: rgba(0,0,0,0.4);
cursor: pointer;
transition: opacity 0.4s;
&:hover {
opacity: 1;
}
i {
font-size: 2rem;
position: absolute;
top: 50%;
left: 50%;
margin-left: -1rem;
margin-top: -1rem;
color: #d6d6d6;
}
}
img, .mask {
width: 150px;
height: 150px;
border-radius: 50%;
overflow: hidden;
}
}
</style>
<template>
<div id="chart-card">
<div class="chart-top">
<div class="chart-item top-left">
<div class="chart-data" style="position:relative">
<!-- 骨架屏与图表有冲突,故不使用内嵌方式。 因为内边距的原因,采取v-if的方式 -->
<a-skeleton active :loading="true" v-if="skeletonIsShow" style="padding:20px" :paragraph="{ rows: 6 }" />
<div v-show="!skeletonIsShow">
<div class="analy-title" style="padding:20px;box-sizing:border-box;padding-bottom:10px">
<span>今日交易金额</span>
<a-tooltip>
<template slot="title">
{{ mainTips.todayAmountTip }}
</template>
<a-icon type="question-circle" />
</a-tooltip>
</div>
<div v-show="ispayAmount">
<div class="pay-amount-text">
<span class="pay-amount">¥{{ mainChart.todayAmount }}</span>
<span>共{{ mainChart.todayPayCount }}笔</span>
</div>
<div id="payAmount" style="height:60px"></div>
</div>
<div class="payAmountSpan" v-show="ispayAmount">
<span>昨日交易金额:¥{{ this.mainChart.yesterdayAmount }}</span>
<span>近七天交易金额:¥{{ this.mainChart.payWeek }}</span>
</div>
<empty v-show="!ispayAmount"/>
</div>
</div>
</div>
<div class="top-middle">
<div class="middle-top">
<div class="chart-item middle-larger">
<div class="chart-data">
<a-skeleton active :loading="skeletonIsShow" :paragraph="{ rows: 1 }">
<div class="analy-title">
<span>交易总金额</span>
<a-tooltip>
<template slot="title">
{{ mainTips.totalAmountTip }}
</template>
<a-icon type="question-circle" />
</a-tooltip>
</div>
<a-card :bordered="false">
<a-statistic style="margin-top: 10px" :precision="2" :value="'¥' + this.mainChart.totalAmount" />
</a-card>
</a-skeleton>
</div>
</div>
</div>
<div class="middle-bottom">
<div class="chart-item middle-larger">
<div class="chart-data">
<a-skeleton active :loading="skeletonIsShow" :paragraph="{ rows: 1 }">
<div class="analy-title">
<span>交易总笔数</span>
<a-tooltip>
<template slot="title">
{{ mainTips.totalPayCountTip }}
</template>
<a-icon type="question-circle" />
</a-tooltip>
</div>
<a-card :bordered="false">
<a-statistic style="margin-top: 10px" :value="this.mainChart.totalPayCount + ' 笔'" />
</a-card>
</a-skeleton>
</div>
</div>
</div>
</div>
<div class="chart-item top-right">
<div class="chart-data user-greet">
<a-skeleton active :loading="skeletonIsShow" :paragraph="{ rows: 6 }">
<div class="user-greet-title">
<div class="user-greet-all">
<div class="user-greet-img">
<img :src="greetImg" alt="" >
</div>
<div class="user-greet-all" >
<span style="">{{ mainTips.helloTitle }}</span>
<div>
<span style="font-size: 12px;" v-if="isAdmin === 1">超级管理员</span>
<span style="font-size: 12px;" v-else>操作员</span>
</div>
</div>
</div>
<!-- <span class="there-spot"></span> -->
<img src="@/assets/svg/more.svg" class="there-spot" alt="更多" slot="extra" @click="showDrawer">
</div>
<div class="quick-start">
<p class="quick-start-title ">快速开始</p>
<ul class="quick-start-ul">
<template v-for="menu in quickMenuList">
<li :key="menu.entId">
<router-link :to="menu.menuUri" tag="span">{{ menu.entName }}</router-link>
</li>
</template>
</ul>
</div>
</a-skeleton>
</div>
</div>
</div>
<div class="chart-bottom">
<div class="chart-item bottom-left">
<div class="chart-data">
<a-skeleton active :loading="skeletonIsShow" :paragraph="{ rows: 6 }"/>
<div v-show="!skeletonIsShow">
<div>
<div class="pay-count-title">
<span class="chart-title">交易统计</span>
<div class="chart-padding" style="border: 1px solid #ddd;" >
<a-range-picker
ref="jeeRange"
style="width:100%"
:ranges="{ '最近一个月': [moment().subtract(1, 'months'),moment()] }"
:default-value="[moment().subtract(7, 'days'),moment()]"
show-time
format="YYYY-MM-DD"
@change="transactionChange"
:disabled-date="disabledDate"
@ok="payCountOk"
:allowClear="false"
>
<div class="change-date-layout">
{{ jeeDate ? jeeDate : '最近七天' }}
<div class="pay-icon">
<div v-if="lastSevenDays" class="change-date-icon"><a-icon type="down" /></div>
<div v-else @click.stop="iconClick" class="change-date-icon" ><a-icon type="close-circle" /></div>
</div>
</div>
</a-range-picker>
</div>
</div>
<div style="position: relative;">
<div v-show="isPayCount">
<div id="payCount" >
</div>
<span style="right: 30px; position: absolute;top: 0;">单位(元)</span>
</div>
<empty v-show="!isPayCount"/>
</div>
</div>
</div>
</div>
</div>
<div class="chart-item bottom-right">
<div class="chart-data">
<a-skeleton active :loading="skeletonIsShow" :paragraph="{ rows: 6 }"/>
<div v-show="!skeletonIsShow">
<div class="pay-count-title">
<span class="chart-title">支付方式</span>
<div class="chart-padding" style="border: 1px solid #ddd;">
<a-range-picker
style="width:100%"
ref="jeeRangePie"
:ranges="{ '最近一个月': [moment().subtract(1, 'months'),moment()] }"
@change="payOnChange"
:default-value="[moment().subtract(7, 'days'),moment()]"
show-time
format="YYYY-MM-DD"
:disabled-date="disabledDate"
@ok="payTypeOk"
:allowClear="false"
>
<div class="change-date-layout">
{{ jeeDatePie ? jeeDatePie : '最近七天' }}
<div class="pay-icon">
<div v-if="!pieDays" class="change-date-icon"><a-icon type="down" /></div>
<div v-else @click.stop="iconPieClick" class="change-date-icon" ><a-icon type="close-circle" /></div>
</div>
</div>
</a-range-picker>
</div>
</div>
<div>
<!-- 如果没数据就展示一个图标 -->
<div v-show="isPayType" id="payType" style="height:300px"></div>
<empty v-show="!isPayType" />
</div>
</div>
</div>
</div>
</div>
<!-- 抽屉 -->
<template>
<div>
<a-drawer
title="商户信息"
placement="right"
:closable="true"
:visible="visible"
@close="onClose"
>
<a-row>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="商户号">
{{ userInfo.mchNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24" v-if="userInfo.isvNo">
<a-descriptions>
<a-descriptions-item label="服务商号">
{{ userInfo.isvNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="商户名称">
{{ userInfo.mchName }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="商户简称">
{{ userInfo.mchShortName }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="商户类型">
<a-tag :color="userInfo.type === 1 ? 'green' : 'orange'">
{{ userInfo.type === 1 ? '普通商户' : '特约商户' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="联系人姓名">
{{ userInfo.contactName }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="联系人手机号">
{{ userInfo.contactTel }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :span="24">
<a-descriptions>
<a-descriptions-item label="联系人邮箱">
{{ userInfo.contactEmail }}
</a-descriptions-item>
</a-descriptions>
</a-col>
</a-row>
</a-drawer>
</div>
</template>
</div>
</template>
<script>
import { TinyArea, Column, Pie, measureTextWidth } from '@antv/g2plot'
import { getMainUserInfo, getPayAmountWeek, getNumCount, getPayCount, getPayType } from '@/api/manage'
import moment from 'moment'
import store from '@/store'
import { timeFix } from '@/utils/util'
import empty from './empty' // 空数据展示的组件,首页自用
export default {
mounted () {
this.tinyArea = new TinyArea('payAmount', {
autoFit: true,
data: this.mainChart.payAmountData,
smooth: true
})
this.columnPlot = new Column('payCount', {
data: this.mainChart.payCount,
xField: 'date',
yField: 'payAmount',
seriesField: 'type',
isGroup: 'true',
marginRatio: 0,
height: 300,
// appendPadding: 16,
appendPadding: [0, 0, 0, 10], // 增加额外的边距,防止左侧的辅助文字被遮挡
theme: {
colors10: ['#FFB238', '#F55536']
},
label: {
// 可配置附加的布局方法
layout: [
],
style: {
fillOpacity: 0
}
}
})
this.piePlot = new Pie('payType', {
width: '100%',
appendPadding: [10, 16, 10, 10], // 增加额外的边距,防止左侧的辅助文字被遮挡
data: this.mainChart.payType,
angleField: 'typeAmount', // 金额 // 笔数 typeCount
colorField: 'typeName',
radius: 1,
innerRadius: 0.64,
autoFit: true,
color: ['#FF6B3B', '#626681', '#FFC100', '#9FB40F', '#76523B', '#DAD5B5', '#0E8E89', '#E19348', '#F383A2', '#247FEA', '#2BCB95', '#B1ABF4', '#1D42C2', '#1D9ED1', '#D64BC0', '#255634', '#8C8C47', '#8CDAE5', '#8E283B', '#791DC9'],
meta: {
value: {
formatter: (v) => `${v} ¥`
}
},
label: {
type: 'inner',
offset: '-50%',
style: {
textAlign: 'center'
},
autoRotate: false,
content: '{value}'
},
statistic: {
title: {
offsetY: -4,
customHtml: (container, view, datum) => {
const { width, height } = container.getBoundingClientRect()
const d = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2))
const text = datum ? datum.typeName : '总计'
return this.renderStatistic(d, text, { fontSize: 28 })
}
},
content: {
offsetY: 4,
style: {
fontSize: '32px'
},
customHtml: (container, view, datum, data) => {
const { width } = container.getBoundingClientRect()
// 在这里保留小数点后两位
const fixedTwo = data.reduce((r, d) => r + d.typeAmount, 0).toFixed(2)
const text = datum ? ${datum.typeAmount}` : ${fixedTwo}`
return this.renderStatistic(width, text, { fontSize: 32 })
}
}
},
// 添加 中心统计文本 交互
interactions: [{ type: 'element-selected' }, { type: 'element-active' }, { type: 'pie-statistic-active' }]
})
this.mainTips.helloTitle = `${timeFix()},` + this.$store.state.user.userName
this.init()
// 去掉交易统计 日期选择框,原生的边框
this.$refs.jeeRange.$refs.picker.$el.firstChild.style.border = 'none'
this.$refs.jeeRangePie.$refs.picker.$el.firstChild.style.border = 'none'
},
components: { empty },
data () {
return {
skeletonIsShow: true, // 骨架屏是否显示
skeletonReqNum: 0, // 当所有数据请求完毕后关闭骨架屏(共四个请求)
lastSevenDays: true, // 最近七天是否显示
pieDays: false, // 饼状图的关闭按钮是否展示
isAdmin: store.state.user.isAdmin, // 是否为超级管理员
visible: false,
recordUser: store.state.user,
userInfo: {}, // 当前用户信息
searchData: {}, // 时间选择条件
greetImg: store.state.user.avatarImgPath, // 头像图片地址
isPayType: true, // 支付方式是否存在数据
isPayCount: true, // 交易统计是否存在数据
ispayAmount: true, // 今日交易金额是否存在数据
jeeDate: undefined, // 自定义日期选择框所用状态-交易统计
jeeDatePie: undefined, // 自定义日期选择框所用状态-支付方式
mainTips: { // 主页提示
todayAmountTip: '今日成功交易金额及笔数', // 今日交易提示文字
totalAmountTip: '成功交易总金额', // 交易总金额提示文字
totalPayCountTip: '成功交易总笔数', // 交易总笔数提示文字
helloTitle: ''
},
mainChart: { // 主页统计数据
payAmountData: [], // 近七天交易图表
payCount: [], // 交易统计图表
payType: [], // 支付方式统计图表
todayAmount: 0.00, // 今日交易金额
todayPayCount: 0, // 今日交易笔数
yesterdayAmount: 0.00, // 昨日交易金额
payWeek: 0.00, // 近7天总交易金额
totalPayCount: 0, // 交易总笔数
totalAmount: 0.00 // 交易总金额
},
tinyArea: {},
columnPlot: null, // 柱状图数据
piePlot: null // 环图数据
}
},
computed: {
// 快速菜单集合
quickMenuList: function () {
const result = []
const putResult = function (item) {
for (let i = 0; i < item.length; i++) {
if (item[i].menuUri && item[i].quickJump === 1) {
result.push(item[i])
}
if (item[i].children) {
putResult(item[i].children)
}
}
}
putResult(this.$store.state.user.allMenuRouteTree)
return result
}
},
methods: {
init () {
const that = this
if (this.$access('ENT_MCH_MAIN_PAY_AMOUNT_WEEK')) {
// 周总交易金额
getPayAmountWeek().then(res => {
that.mainChart.payAmountData = res.dataArray
res.dataArray.length === 0 ? this.ispayAmount = false : this.ispayAmount = true
that.mainChart.todayAmount = res.todayAmount
that.mainChart.todayPayCount = res.todayPayCount
that.mainChart.payWeek = res.payWeek
that.mainChart.yesterdayAmount = (res.yesterdayAmount)
that.initPayAmount()
that.skeletonClose(that)
}).catch(err => {
console.error(err)
that.skeletonClose(that)
this.ispayAmount = false
})
} else {
this.ispayAmount = false
that.skeletonClose(that)
}
if (this.$access('ENT_MCH_MAIN_NUMBER_COUNT')) {
// 数据统计
getNumCount().then(res => {
that.skeletonClose(that)
that.mainChart.totalAmount = res.totalAmount
that.mainChart.totalPayCount = res.totalCount
}).catch(err => {
console.error(err)
that.skeletonClose(that)
})
} else {
that.skeletonClose(that)
}
// 交易统计
if (this.$access('ENT_MCH_MAIN_PAY_COUNT')) {
getPayCount(that.searchData).then(res => {
that.mainChart.payCount = res
res.length === 0 ? this.isPayCount = false : this.isPayCount = true
that.initPayCount()
that.skeletonClose(that)
}).catch(err => {
console.error(err)
this.isPayCount = false
that.skeletonClose(that)
})
} else {
this.isPayCount = false
that.skeletonClose(that)
}
if (this.$access('ENT_MCH_MAIN_PAY_TYPE_COUNT')) {
// 支付类型统计
getPayType(that.searchData).then(res => {
that.mainChart.payType = res[0]
res[0].length === 0 ? this.isPayType = false : this.isPayType = true
that.initPayType()
that.skeletonClose(that)
}).catch(err => {
console.error(err)
this.isPayType = false
that.skeletonClose(that)
})
} else {
this.isPayType = false
that.skeletonClose(that)
}
if (this.$access('ENT_MCH_MAIN_USER_INFO')) {
// 初始化用户信息
that.getUserInfo()
}
},
getUserInfo () {
const that = this
getMainUserInfo().then(res => {
that.userInfo = res
})
},
initPayAmount () {
this.tinyArea.render()
this.tinyArea.changeData(this.mainChart.payAmountData)
},
initPayCount () {
this.columnPlot.render()
this.columnPlot.changeData(this.mainChart.payCount)
},
initPayType () {
this.piePlot.render()
this.piePlot.changeData(this.mainChart.payType)
},
renderStatistic: function (containerWidth, text, style) {
const { width: textWidth, height: textHeight } = measureTextWidth(text, style)
const R = containerWidth / 2
// r^2 = (w / 2)^2 + (h - offsetY)^2
let scale = 0.7
if (containerWidth < textWidth) {
scale = Math.min(Math.sqrt(Math.abs(Math.pow(R, 2) / (Math.pow(textWidth / 2, 2) + Math.pow(textHeight, 2)))), 1)
}
const textStyleStr = `width:${containerWidth}px`
return `<div style="${textStyleStr};font-size:${scale}em;line-height:${scale < 1 ? 1 : 'inherit'};">${text}</div>`
},
showDrawer () {
if (this.userInfo.mchNo) {
this.visible = true
} else {
this.$message.warning('当前功能已被限制')
}
},
onClose () {
this.visible = false
},
payOnChange (date, dateString) {
this.searchData.createdStart = dateString[0] // 开始时间
this.searchData.createdEnd = dateString[1] // 结束时间
this.pieDays = true
this.jeeDatePie = dateString[0] + ' ~ ' + dateString[1]
},
// 支付方式,日期选择器,关闭按钮点击事件
iconPieClick () {
this.searchData.createdStart = moment().subtract(7, 'days').format('YYYY-MM-DD') // 开始时间
this.searchData.createdEnd = moment().format('YYYY-MM-DD') // 结束时间
this.payTypeOk()
this.jeeDatePie = '最近七天'
this.pieDays = false
},
moment,
disabledDate (current) {
// 当天之前的三十天,可选。 当天也可选
return current < moment().subtract(32, 'days') || current > moment().endOf('day')
},
// 交易统计,日期选择器,关闭按钮点击事件
iconClick (dates) {
this.searchData.createdStart = moment().subtract(7, 'days').format('YYYY-MM-DD') // 开始时间
this.searchData.createdEnd = moment().format('YYYY-MM-DD') // 结束时间
this.payCountOk()
this.jeeDate = '最近七天'
this.lastSevenDays = true
},
// 交易统计日期改变的回调
transactionChange (dates, dateStrings) {
this.searchData.createdStart = dateStrings[0] // 开始时间
this.searchData.createdEnd = dateStrings[1] // 结束时间
this.jeeDate = dateStrings[0] + ' ~ ' + dateStrings[1]
this.lastSevenDays = false
},
// 交易统计点击确定按钮的回调
payCountOk () {
const that = this
getPayCount(that.searchData).then(res => {
res.length === 0 ? this.isPayCount = false : this.isPayCount = true
that.columnPlot.changeData(res)
})
},
payTypeOk () {
const that = this
getPayType(that.searchData).then(res => {
res[0].length === 0 ? that.isPayType = false : that.isPayType = true
that.piePlot.changeData(res[0])
})
},
skeletonClose (that) {
// 每次请求成功,skeletonReqNum + 1,当大于等于4时, 取消骨架屏展示
that.skeletonReqNum++
that.skeletonReqNum >= 4 ? that.skeletonIsShow = false : that.skeletonIsShow = true
}
}
}
</script>
<style lang="less" scoped>
@import './index.less'; // 响应式布局
.user-greet {
font-size: 19px;
font-weight: 500;
.quick-start {
box-sizing: border-box;
padding-top: 20px;
.quick-start-title {
font-size: 16px;
font-weight: 500;
text-align: left;
margin-bottom:0;
}
.quick-start-ul {
font-size: 13px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
padding: 0;
margin-bottom:0;
li {
margin-right: 20px;
margin-top: 10px;
text-align: left;
:hover {
color: @jee-inside-link
}
}
li:hover {
cursor:pointer;
}
}
}
}
.user-greet-title{
box-sizing:border-box;
padding-bottom:20px;
display:flex;
justify-content:space-between;
border-bottom:1px solid #ddd;
.user-greet-all {
display:flex;
flex-direction:row;
.user-greet-img {
width:60px;
height:60px;
border-radius:50%;
overflow:hidden;
background:#ddd;
margin-right:10px;
img{
width:60px;
height:60px;
border:1px solid rgba(0,0,0,0.08)
}
}
.user-greet-all {
display:flex;
flex-direction:column;
justify-content: space-around;
}
}
}
.analy-title {
display:flex;
justify-content:space-between;
padding-bottom:0;
align-items: center;
}
.there-spot:hover {
cursor:pointer;
}
.ant-calendar-picker-input {
border:none !important
}
.payAmountSpan {
display:flex;
justify-content:space-between;
width: 100%;
box-sizing: border-box;
position: absolute;
bottom:20px;
padding:0 20px;
box-sizing: border-box;
}
.chart-data {
padding:20px;
box-sizing: border-box;
}
.top-left {
.chart-data {
padding:0;
}
}
.pay-count-title {
display:flex;
flex-wrap: wrap;
justify-content:space-between;
align-items:center;
.pay-count-date{
display:flex;
justify-content:space-around;
}
}
.pay-amount-text {
display: flex;
padding: 0 20px 0 16px;
box-sizing: border-box;
height: 33px;
line-height: 33px;
align-items: baseline;
margin-bottom: 10px;
.pay-amount {
font-size: 33px;
margin-right: 10px;
}
}
.chart-padding {
box-sizing: border-box;
max-width:330px;
min-width:260px;
flex-grow: 1;
flex-shrink:1;
margin-bottom: 20px;
}
.change-date-layout {
padding-left: 11px;
align-items: center;
display:flex;
justify-content:space-between;
.change-date-icon {
width:50px;
height:36px;
display:flex;
align-items:center;
justify-content:center;
}
}
.chart-title {
font-size: 16px;
font-weight: 500;
margin-right:20px;
margin-bottom:20px;
}
</style>
<template>
<div class="empty">
<img src="@/assets/svg/empty.svg" alt="" style="width:100px;">
<p style="padding-right:5px;">暂无数据</p>
</div>
</template>
<script>
export default {
name: 'Empty'
}
</script>
<style scoped lang="less">
.empty {
width: 100%;
height:300px; // 与环状图一个高度
display: flex;
justify-content:center;
align-items: center;
flex-direction: column;
text-align:center;
}
</style>
#chart-card {
width: 100%;
}
#chart-card .chart-data {
min-height: 100px;
height: 100%;
width: 100%;
border-radius: 6px;
background-color: #fff;
}
#chart-card .chart-top,
#chart-card .chart-bottom {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
#chart-card .chart-top .middle-smaller,
#chart-card .chart-bottom .middle-smaller,
#chart-card .chart-top .middle-larger,
#chart-card .chart-bottom .middle-larger {
height: 100%;
}
#chart-card .chart-top .top-left,
#chart-card .chart-bottom .top-left {
min-height: 238px;
}
#chart-card .chart-item {
width: 100%;
box-sizing: border-box;
padding: 12px;
}
@media screen and (max-width: 767px) {
#chart-card .chart-top .top-left {
order: 1;
}
#chart-card .chart-top .top-middle {
order: 2;
width: 100%;
}
#chart-card .chart-top .top-middle .middle-top,
#chart-card .chart-top .top-middle .middle-bottom {
display: flex;
flex-direction: column;
}
#chart-card .chart-top .top-middle .middle-top .middle-larger,
#chart-card .chart-top .top-middle .middle-bottom .middle-larger,
#chart-card .chart-top .top-middle .middle-top .middle-smaller,
#chart-card .chart-top .top-middle .middle-bottom .middle-smaller {
width: 100%;
}
#chart-card .chart-top .top-right {
order: 0;
}
}
@media screen and (min-width: 768px) {
#chart-card .top-left,
#chart-card .top-middle {
order: 1;
}
#chart-card .top-middle {
width: 100%;
}
#chart-card .top-middle .middle-top,
#chart-card .top-middle .middle-bottom {
display: flex;
flex-direction: row;
}
#chart-card .top-middle .middle-top .middle-larger,
#chart-card .top-middle .middle-bottom .middle-larger {
flex-grow: 1;
}
#chart-card .top-middle .middle-top .middle-smaller,
#chart-card .top-middle .middle-bottom .middle-smaller {
max-width: 170px;
min-width: 130px;
}
#chart-card .top-right {
order: 0;
}
}
@media screen and (min-width: 1200px) {
#chart-card .top-left {
order: 1;
width: 50%;
}
#chart-card .top-middle {
width: 50%;
order: 2;
}
#chart-card .top-middle .middle-top,
#chart-card .top-middle .middle-bottom {
display: flex;
flex-direction: row;
}
#chart-card .top-middle .middle-top .middle-larger,
#chart-card .top-middle .middle-bottom .middle-larger {
flex-grow: 1;
}
#chart-card .top-middle .middle-top .middle-smaller,
#chart-card .top-middle .middle-bottom .middle-smaller {
max-width: 170px;
min-width: 130px;
}
#chart-card .top-right {
width: 100%;
order: 0;
}
}
@media screen and (min-width: 1500px) {
#chart-card {
flex-direction: row;
}
#chart-card .chart-top {
width: 100%;
order: 0;
flex-wrap: nowrap;
}
#chart-card .chart-top .top-left,
#chart-card .chart-top .top-middle {
width: 500px;
min-width: 460px;
order: 0;
}
#chart-card .chart-top .top-middle {
order: 1;
}
#chart-card .chart-top .top-middle .middle-top,
#chart-card .chart-top .top-middle .middle-bottom {
display: flex;
width: 100%;
height: 50%;
flex-wrap: nowrap;
}
#chart-card .chart-top .top-middle .middle-smaller {
width: 150px;
min-width: 150px;
}
#chart-card .chart-top .top-middle .middle-larger {
width: 300px;
flex-grow: 1;
}
#chart-card .chart-top .top-right {
order: 2;
flex-grow: 1;
}
#chart-card .chart-bottom {
order: 1;
width: 100%;
flex-wrap: nowrap;
}
#chart-card .chart-bottom .bottom-right {
max-width: 500px;
min-width: 330px;
}
#chart-card .chart-bottom .bottom-left {
min-width: 900px;
flex-grow: 1;
}
}
#chart-card {
width: 100%;
.chart-data {
min-height: 100px;
height: 100%;
width: 100%;
border-radius: 6px;
background-color: #fff;
}
.chart-top, .chart-bottom {
display: flex;
flex-direction: row;
flex-wrap: wrap;
.middle-smaller, .middle-larger { // 较小的区域
height: 100%;
}
.top-left {
min-height: 238px;
}
}
.chart-item {
width: 100%;
box-sizing: border-box;
padding: 12px;
}
}
@media screen and (max-width:767px){
#chart-card {
.chart-top {
.top-left {
order: 1;
}
.top-middle {
order: 2;
width: 100%;
.middle-top, .middle-bottom {
display: flex;
flex-direction: column;
.middle-larger, .middle-smaller {
width: 100%;
}
}
}
.top-right {
order: 0;
}
}
}
}
@media screen and (min-width:768px){
#chart-card {
.top-left, .top-middle {
order: 1;
}
.top-middle {
width: 100%;
.middle-top, .middle-bottom {
display: flex;
flex-direction: row;
.middle-larger {
flex-grow: 1; // 设置为1,存在剩余空间放大
}
.middle-smaller {
max-width: 170px;
min-width: 130px;
}
}
}
.top-right {
order: 0;
}
}
}
@media screen and (min-width:1200px){
#chart-card {
.top-left {
order: 1;
width: 50%;
}
.top-middle {
width: 50%;
order: 2;
.middle-top, .middle-bottom {
display: flex;
flex-direction: row;
.middle-larger {
flex-grow: 1; // 设置为1,存在剩余空间放大
}
.middle-smaller {
max-width: 170px;
min-width: 130px;
}
}
}
.top-right {
width: 100%;
order: 0;
}
}
}
@media screen and (min-width:1500px){
#chart-card {
flex-direction: row;
.chart-top {
width:100%;
order: 0;
flex-wrap: nowrap; // 禁止换行
.top-left, .top-middle {
width:500px;
min-width: 460px;
order: 0;
}
.top-middle {
order: 1;
.middle-top, .middle-bottom {
display: flex;
width: 100%;
height: 50%;
flex-wrap: nowrap; // 禁止换行
}
.middle-smaller { // 较小的区域
width: 150px;
min-width: 150px;
}
.middle-larger { // 较大的区域
width: 300px;
flex-grow: 1; // 设置为1,存在剩余空间放大
}
}
.top-right {
order: 2;
flex-grow: 1; // 设置为1,存在剩余空间放大
}
}
.chart-bottom {
order: 1;
width:100%;
flex-wrap: nowrap;
.bottom-right {
max-width: 500px;
min-width: 330px;
}
.bottom-left {
min-width: 900px;
flex-grow: 1; // 设置为1,存在剩余空间放大
}
}
}
}
\ No newline at end of file
<template>
<a-modal v-model="isShow" :title=" isAdd ? '新增菜单' : '修改菜单' " @ok="handleOkFunc" :confirmLoading="confirmLoading">
<a-form-model ref="infoFormModel" :model="saveObject" :label-col="{span: 6}" :wrapper-col="{span: 15}" :rules="rules">
<a-form-model-item label="组名称:" prop="receiverGroupName">
<a-input v-model="saveObject.receiverGroupName" />
</a-form-model-item>
<a-form-model-item label="自动分账组" prop="autoDivisionFlag">
<a-radio-group v-model="saveObject.autoDivisionFlag">
<a-radio :value="1"></a-radio> <a-radio :value="0"></a-radio>
</a-radio-group>
<hr/>
<p style="color: indianred">1. 自动分账组: 当订单分账模式为自动分账,该组下的所有正常分账状态的账号将作为订单分账对象</p>
<p style="color: indianred">2. 每个商户仅有一个默认分账组, 当该组更新为自动分账时,其他组将改为否</p>
</a-form-model-item>
</a-form-model>
</a-modal>
</template>
<script>
import { API_URL_DIVISION_RECEIVER_GROUP, req } from '@/api/manage'
export default {
props: {
callbackFunc: { type: Function }
},
data () {
return {
confirmLoading: false, // 显示确定按钮loading图标
isAdd: true, // 新增 or 修改页面标识
isShow: false, // 是否显示弹层/抽屉
saveObject: { autoDivisionFlag: 0 }, // 数据对象
recordId: null, // 更新对象ID
rules: {
receiverGroupName: [
{ required: true, message: '请输入组名称', trigger: 'blur' }
]
}
}
},
created () {
},
methods: {
show: function (recordId) { // 弹层打开事件
this.isAdd = !recordId
this.saveObject = { autoDivisionFlag: 0 } // 数据清空
this.confirmLoading = false // 关闭loading
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
const that = this
if (!this.isAdd) { // 修改信息 延迟展示弹层
that.recordId = recordId
req.getById(API_URL_DIVISION_RECEIVER_GROUP, recordId).then(res => {
that.saveObject = res
that.isShow = true
})
} else {
that.isShow = true // 立马展示弹层信息
}
},
handleOkFunc: function () { // 点击【确认】按钮事件
const that = this
this.$refs.infoFormModel.validate(valid => {
if (valid) { // 验证通过
// 请求接口
that.confirmLoading = true // 显示loading
if (that.isAdd) {
req.add(API_URL_DIVISION_RECEIVER_GROUP, that.saveObject).then(res => {
that.$message.success('添加成功')
that.isShow = false
that.callbackFunc() // 刷新列表
}).catch(res => { that.confirmLoading = false })
} else {
req.updateById(API_URL_DIVISION_RECEIVER_GROUP, that.recordId, that.saveObject).then(res => {
that.$message.success('修改成功')
that.isShow = false
that.callbackFunc() // 刷新列表
}).catch(res => { that.confirmLoading = false })
}
}
})
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div v-if="$access('ENT_DIVISION_RECEIVER_GROUP_LIST')" class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div
class="table-layer"
>
<jeepay-text-up :placeholder="'组ID'" :msg="searchData.receiverGroupId" v-model="searchData.receiverGroupId" />
<jeepay-text-up :placeholder="'组名称'" :msg="searchData.receiverGroupName" v-model="searchData.receiverGroupName" />
<span class="table-page-search-submitButtons">
<a-button type="primary" @click="searchFunc" icon="search" :loading="btnLoading">查询</a-button>
<a-button style="margin-left: 8px;" @click="() => this.searchData = {}" icon="reload">重置</a-button>
</span>
</div>
</a-form>
<div>
<a-button v-if="$access('ENT_DIVISION_RECEIVER_GROUP_ADD')" type="primary" icon="plus" @click="addFunc" class="mg-b-30">新建</a-button>
</div>
</div>
<!-- 列表渲染 -->
<JeepayTable
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
@btnLoadClose="btnLoading=false"
rowKey="receiverGroupId"
>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a v-if="$access('ENT_DIVISION_RECEIVER_GROUP_EDIT')" @click="editFunc(record.receiverGroupId)">修改</a>
<a style="color: red" v-if="$access('ENT_DIVISION_RECEIVER_GROUP_DELETE')" @click="delFunc(record.receiverGroupId)">删除</a>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 新增 / 修改 页面组件 -->
<InfoAddOrEdit ref="infoAddOrEdit" :callbackFunc="searchFunc" />
</page-header-wrapper>
</template>
<script>
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_DIVISION_RECEIVER_GROUP, req } from '@/api/manage'
import InfoAddOrEdit from './AddOrEdit'
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'receiverGroupId', dataIndex: 'receiverGroupId', title: '组ID' },
{ key: 'receiverGroupName', dataIndex: 'receiverGroupName', title: '组名称' },
{ key: 'autoDivisionFlag', dataIndex: 'autoDivisionFlag', title: '自动分账组', customRender: (text, record, index) => text === 1 ? '是' : '否' },
{ key: 'createdBy', dataIndex: 'createdBy', title: '创建人' },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建时间' },
{ key: 'op', title: '操作', width: '200px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
name: 'RolePage',
components: { JeepayTable, JeepayTableColumns, InfoAddOrEdit, JeepayTextUp },
data () {
return {
tableColumns: tableColumns,
searchData: {},
btnLoading: false
}
},
mounted () {
},
methods: {
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_DIVISION_RECEIVER_GROUP, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.btnLoading = true // 打开查询按钮上的loading
this.$refs.infoTable.refTable(true)
},
addFunc: function () { // 业务通用【新增】 函数
this.$refs.infoAddOrEdit.show()
},
editFunc: function (recordId) { // 业务通用【修改】 函数
this.$refs.infoAddOrEdit.show(recordId)
},
delFunc: function (recordId) { // 业务通用【删除】 函数
const that = this
this.$infoBox.confirmDanger('确认删除?', '', () => {
// 需要【按钮】loading 请返回 promise对象, 不需要请直接返回null
return req.delById(API_URL_DIVISION_RECEIVER_GROUP, recordId).then(res => {
that.$message.success('删除成功!')
that.$refs.infoTable.refTable(false)
})
})
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div v-if="$access('ENT_DIVISION_RECEIVER_LIST')" class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<a-form-item label="" class="table-head-layout" :wrapper-col="{span: 16}">
<a-select v-model="searchData.appId" placeholder="选择应用">
<a-select-option key="" >全部应用</a-select-option>
<a-select-option v-for="(item) in mchAppList" :key="item.appId" >{{ item.appName }} [{{ item.appId }}]</a-select-option>
</a-select>
</a-form-item>
<jeepay-text-up placeholder="分账接收者ID[精准]" :msg="searchData.receiverId" v-model="searchData.receiverId" />
<jeepay-text-up placeholder="接收者账号别名[模糊]" :msg="searchData.receiverAlias" v-model="searchData.receiverAlias" />
<jeepay-text-up placeholder="组ID[精准]" :msg="searchData.receiverGroupId" v-model="searchData.receiverGroupId" />
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.state" placeholder="账号状态(本系统)" default-value="">
<a-select-option value="">全部</a-select-option>
<a-select-option value="1">正常分账</a-select-option>
<a-select-option value="0">暂停分账</a-select-option>
</a-select>
</a-form-item>
<span class="table-page-search-submitButtons">
<a-button type="primary" @click="searchFunc" icon="search" :loading="btnLoading">查询</a-button>
<a-button style="margin-left: 8px;" @click="() => this.searchData = {}" icon="reload">重置</a-button>
</span>
</div>
</a-form>
<div>
<a-button v-if="$access('ENT_DIVISION_RECEIVER_ADD')" type="danger" icon="plus" @click="addFunc" class="mg-b-30">新建</a-button>
</div>
</div>
<!-- 列表渲染 -->
<JeepayTable
ref="infoTable"
:initData="false"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
@btnLoadClose="btnLoading=false"
rowKey="receiverId"
>
<!-- 渠道类型 -->
<template slot="ifCodeSlot" slot-scope="{record}">
<template v-if="record.ifCode === 'wxpay'" ><span style="color: green"><a-icon type="wechat" /> 微信</span></template>
<template v-else-if="record.ifCode == 'alipay'" ><span style="color: dodgerblue"><a-icon type="alipay-circle" /> 支付宝</span></template>
<template v-else >{{record.ifCode}}</template>
</template>
<!-- 状态(本系统) -->
<template slot="stateSlot" slot-scope="{record}">
<div v-if="record.state == 0" ><a-badge status="error" text="暂停分账" /></div>
<div v-else-if="record.state == 1" ><a-badge status="processing" text="正常分账" /></div>
<div v-else ><a-badge status="warning" text="未知" /></div>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a v-if="$access('ENT_DIVISION_RECEIVER_EDIT')" @click="editFunc(record.receiverId)">修改</a>
</JeepayTableColumns>
</template>
</JeepayTable>
<!-- 新增收款账号页面 -->
<ReceiverAdd ref="receiverAdd" :callbackFunc="searchFunc"/>
<!-- 修改 页面组件 -->
<ReceiverEdit ref="receiverEdit" :callbackFunc="searchFunc"/>
</a-card>
</page-header-wrapper>
</template>
<script>
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_DIVISION_RECEIVER, API_URL_MCH_APP, req } from '@/api/manage'
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import ReceiverAdd from './ReceiverAdd'
import ReceiverEdit from './ReceiverEdit'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'receiverId', dataIndex: 'receiverId', title: '绑定ID' },
{ key: 'ifCode', title: '渠道类型', scopedSlots: { customRender: 'ifCodeSlot' } },
{ key: 'receiverAlias', dataIndex: 'receiverAlias', title: '账号别名' },
{ key: 'receiverGroupName', dataIndex: 'receiverGroupName', title: '组名称' },
{ key: 'accNo', dataIndex: 'accNo', title: '分账接收账号' },
{ key: 'accName', dataIndex: 'accName', title: '分账接收账号名称' },
{ key: 'relationTypeName', dataIndex: 'relationTypeName', title: '分账关系类型' },
{ title: '状态', scopedSlots: { customRender: 'stateSlot' }, align: 'center' },
{ key: 'bindSuccessTime', dataIndex: 'bindSuccessTime', title: '绑定成功时间' },
{ key: 'divisionProfit', dataIndex: 'divisionProfit', title: '默认分账比例', customRender: (text, record, index) => (text * 100).toFixed(2) + '%' },
{ key: 'op', title: '操作', width: '200px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
components: { JeepayTable, JeepayTableColumns, JeepayTextUp, ReceiverAdd, ReceiverEdit },
data () {
return {
tableColumns: tableColumns,
searchData: { appId: '' },
btnLoading: false,
mchAppList: [] // 商户app列表
}
},
mounted () {
const that = this // 提前保留this
// 请求接口,获取所有的appid,只有此处进行pageSize=-1传参
req.list(API_URL_MCH_APP, { pageSize: -1 }).then(res => {
that.mchAppList = res.records
// 默认选中第一个 & 更新列表
if (that.mchAppList && that.mchAppList.length > 0) {
that.searchData.appId = that.mchAppList[0].appId + ''
that.searchFunc()
}
})
},
methods: {
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_DIVISION_RECEIVER, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.btnLoading = true // 打开查询按钮上的loading
this.$refs.infoTable.refTable(true)
},
addFunc: function () { // 业务通用【新增】 函数
if (this.mchAppList.length <= 0) {
return this.$message.error('当前商户无任何应用,请先创建应用后再试。')
}
if (!this.searchData.appId) {
return this.$message.error('请先选择应用。')
}
// 打开弹层
this.$refs.receiverAdd.show(this.mchAppList.filter((item) => item.appId === this.searchData.appId)[0])
},
editFunc: function (recordId) { // 业务通用【修改】 函数
this.$refs.receiverEdit.show(recordId)
}
}
}
</script>
<template>
<a-drawer
v-if="visible"
:visible="visible"
@close="onClose"
:closable="true"
:maskClosable="false"
:body-style="{ paddingBottom: '80px' }"
:drawer-style="{ backgroundColor: '#f0f2f5' }"
width="80%"
>
<a-descriptions title="绑定分账接收者账号">
<a-descriptions-item label="当前应用">
<span style="color: red">{{ appInfo.appName }} [{{ appInfo.appId }}]</span>
</a-descriptions-item>
<a-descriptions-item label="选择要加入到的账号分组">
<a-select style="width: 210px" placeholder="账号分组" v-model="selectedReceiverGroupId">
<a-select-option v-for="(item) in allReceiverGroup" :key="item.receiverGroupId" :value="item.receiverGroupId">{{ item.receiverGroupName }}</a-select-option>
</a-select>
</a-descriptions-item>
</a-descriptions>
<a-divider></a-divider>
<a-card title="微信账号" v-show="appSupportIfCodes.indexOf('wxpay') >= 0">
<a slot="extra" href="#">
<a-button style="background: green; color: white" icon="wechat" @click="addReceiverRow('wxpay')">添加【微信官方】分账接收账号</a-button>
</a>
<a-table :columns="accTableColumns" :data-source="receiverTableData.filter((item) => item.ifCode == 'wxpay')" :pagination="false" rowKey="rowKey">
<!-- 账号类型 -->
<template slot="reqBindStateSlot" slot-scope="record">
<div style="color: salmon " v-show="record.reqBindState == 0">
<a-icon type="info-circle" /> 待绑定
</div>
<div style="color: green; " v-show="record.reqBindState == 1">
<a-icon type="check-circle" /> 绑定成功
</div>
<div style="color: red; " v-show="record.reqBindState == 2">
<a-icon type="close-circle" /> 绑定异常
</div>
</template>
<!-- 账号别名 -->
<template slot="receiverAliasSlot" slot-scope="record">
<a-input v-model="record.receiverAlias" style="width: 150px" placeholder="(选填)默认为账号"/>
</template>
<!-- 账号类型 -->
<template slot="accTypeSlot" slot-scope="record">
<a-select style="width: 110px" v-model="record.accType" placeholder="账号类型" default-value="0">
<a-select-option value="0">个人</a-select-option>
<a-select-option value="1">微信商户</a-select-option>
</a-select>
</template>
<!-- 接收方账号 -->
<template slot="accNoSlot" slot-scope="record">
<a-input v-model="record.accNo" style="width: 150px"/>
<a-button type="link" v-if="record.accType == 0" @click="showChannelUserModal('wxpay', record)">扫码获取</a-button>
</template>
<!-- 接收方姓名 -->
<template slot="accNameSlot" slot-scope="record">
<a-input v-model="record.accName"/>
</template>
<!-- 分账关系 -->
<template slot="relationTypeSlot" slot-scope="record">
<a-select style="width: 110px" labelInValue placeholder="分账关系类型" :defaultValue="{key: 'PARTNER'}" @change="changeRelationType(record, $event)">
<a-select-option key="PARTNER">合作伙伴</a-select-option>
<a-select-option key="SERVICE_PROVIDER">服务商</a-select-option>
<a-select-option key="STORE">门店</a-select-option>
<a-select-option key="STAFF">员工</a-select-option>
<a-select-option key="STORE_OWNER">店主</a-select-option>
<a-select-option key="HEADQUARTER">总部</a-select-option>
<a-select-option key="BRAND">品牌方</a-select-option>
<a-select-option key="DISTRIBUTOR">分销商</a-select-option>
<a-select-option key="USER">用户</a-select-option>
<a-select-option key="SUPPLIER">供应商</a-select-option>
<a-select-option key="CUSTOM">自定义</a-select-option>
</a-select>
</template>
<!-- 关系名称 -->
<template slot="relationTypeNameSlot" slot-scope="record">
<a-input :disabled="record.relationType !== 'CUSTOM'" v-model="record.relationTypeName"/>
</template>
<!-- 默认分账比例 -->
<template slot="divisionProfitSlot" slot-scope="record">
<a-input v-model="record.divisionProfit" style="width: 65px"/> %
</template>
<template slot="opSlot" slot-scope="record"><a-button type="link" @click="delRow(record)">删除</a-button></template>
</a-table>
</a-card>
<br />
<a-card title="支付宝账号" v-show="appSupportIfCodes.indexOf('alipay') >= 0">
<a slot="extra" href="#">
<a-button style="background: dodgerblue; color: white" icon="alipay-circle" @click="addReceiverRow('alipay')" >添加【支付宝官方】分账接收账号</a-button>
</a>
<a-table :columns="accTableColumns" :data-source="receiverTableData.filter((item) => item.ifCode == 'alipay')" :pagination="false" rowKey="rowKey">
<!-- 账号类型 -->
<template slot="reqBindStateSlot" slot-scope="record">
<div style="color: salmon " v-show="record.reqBindState == 0">
<a-icon type="info-circle" /> 待绑定
</div>
<div style="color: green; " v-show="record.reqBindState == 1">
<a-icon type="check-circle" /> 绑定成功
</div>
<div style="color: red; " v-show="record.reqBindState == 2">
<a-icon type="close-circle" /> 绑定异常
</div>
</template>
<!-- 账号别名 -->
<template slot="receiverAliasSlot" slot-scope="record">
<a-input v-model="record.receiverAlias" style="width: 150px" placeholder="(选填)默认为账号"/>
</template>
<!-- 账号类型 -->
<template slot="accTypeSlot" slot-scope="record">
<a-select style="width: 110px" v-model="record.accType" placeholder="账号类型" default-value="0">
<a-select-option value="0">个人</a-select-option>
<a-select-option value="1">商户</a-select-option>
</a-select>
</template>
<!-- 接收方账号 -->
<template slot="accNoSlot" slot-scope="record">
<a-input v-model="record.accNo" style="width: 150px"/>
<a-button type="link" v-if="record.accType == 0" @click="showChannelUserModal('alipay', record)">扫码获取</a-button>
</template>
<!-- 接收方姓名 -->
<template slot="accNameSlot" slot-scope="record">
<a-input v-model="record.accName"/>
</template>
<!-- 分账关系 -->
<template slot="relationTypeSlot" slot-scope="record">
<a-select style="width: 110px" labelInValue placeholder="分账关系类型" :defaultValue="{key: 'PARTNER'}" @change="changeRelationType(record, $event)">
<a-select-option key="PARTNER">合作伙伴</a-select-option>
<a-select-option key="SERVICE_PROVIDER">服务商</a-select-option>
<a-select-option key="STORE">门店</a-select-option>
<a-select-option key="STAFF">员工</a-select-option>
<a-select-option key="STORE_OWNER">店主</a-select-option>
<a-select-option key="HEADQUARTER">总部</a-select-option>
<a-select-option key="BRAND">品牌方</a-select-option>
<a-select-option key="DISTRIBUTOR">分销商</a-select-option>
<a-select-option key="USER">用户</a-select-option>
<a-select-option key="SUPPLIER">供应商</a-select-option>
<a-select-option key="CUSTOM">自定义</a-select-option>
</a-select>
</template>
<!-- 关系名称 -->
<template slot="relationTypeNameSlot" slot-scope="record">
<a-input :disabled="record.relationType !== 'CUSTOM'" v-model="record.relationTypeName"/>
</template>
<!-- 默认分账比例 -->
<template slot="divisionProfitSlot" slot-scope="record">
<a-input v-model="record.divisionProfit" style="width: 65px"/> %
</template>
<template slot="opSlot" slot-scope="record"><a-button type="link" @click="delRow(record)">删除</a-button></template>
</a-table>
</a-card>
<div class="drawer-btn-center ">
<a-button type="primary" icon="rocket" :style="{ marginRight: '8px' }" @click="reqBatchBindReceiver(0)">发起绑定请求</a-button>
<a-button icon="close" @click="onClose">关闭</a-button>
</div>
<ChannelUserModal ref="channelUserModal" @changeChannelUserId="changeChannelUserIdFunc($event)"/>
</a-drawer>
</template>
<script>
// eslint-disable-next-line no-unused-vars
import { genRowKey } from '@/utils/util'
import ChannelUserModal from '@/components/ChannelUser/ChannelUserModal'
import { API_URL_DIVISION_RECEIVER, API_URL_DIVISION_RECEIVER_GROUP, req, getIfCodeByAppId } from '@/api/manage'
// eslint-disable-next-line no-unused-vars
const accTableColumns = [
{ key: 'reqBindState', title: '状态', scopedSlots: { customRender: 'reqBindStateSlot' } },
{ key: 'receiverAlias', title: '账号别名', scopedSlots: { customRender: 'receiverAliasSlot' } },
{ key: 'accType', title: '账号类型', scopedSlots: { customRender: 'accTypeSlot' } },
{ key: 'accNo', width: '300px', title: '接收方账号', scopedSlots: { customRender: 'accNoSlot' } },
{ key: 'accName', width: '180px', title: '接收方姓名', scopedSlots: { customRender: 'accNameSlot' } },
{ key: 'relationType', title: '分账关系', scopedSlots: { customRender: 'relationTypeSlot' } },
{ key: 'relationTypeName', width: '200px', title: '关系名称', scopedSlots: { customRender: 'relationTypeNameSlot' } },
{ key: 'divisionProfit', title: '默认分账比例', scopedSlots: { customRender: 'divisionProfitSlot' } },
{ key: 'op', title: '操作', scopedSlots: { customRender: 'opSlot' } }
]
const defaultReceiverTemplate = {
reqBindState: 0, // 默认待绑定
receiverAlias: '',
receiverGroupId: '',
appId: '',
ifCode: '',
accType: '0',
accNo: '',
accName: '',
relationType: 'PARTNER', // 默认合作伙伴, 需要同时更改select的defaultValue
relationTypeName: '合作伙伴',
divisionProfit: ''
}
export default {
components: { ChannelUserModal },
props: {
callbackFunc: {
type: Function,
default: () => ({})
}
},
data () {
return {
visible: false, // 是否显示抽屉
appInfo: null, // 应用app信息
accTableColumns: accTableColumns, // 表头模板(微信支付宝公用)
allReceiverGroup: [], // 当前商户所有的接收账号的分组情况
selectedReceiverGroupId: '', // 当前选择的分组ID
appSupportIfCodes: [], // 应用支持的支付方式
receiverTableData: [] // 微信支付的分账用户列表集合
}
},
methods: {
// 弹层打开事件
show (appInfo) {
const that = this // 提前保留this
this.appSupportIfCodes = [] // 初始化
this.receiverTableData = [] // 置空表格
// 请求接口,获取所有分组信息,只有此处进行pageSize=-1传参
req.list(API_URL_DIVISION_RECEIVER_GROUP, { pageSize: -1 }).then(res => {
that.allReceiverGroup = res.records
if (that.allReceiverGroup && that.allReceiverGroup.length > 0) { // 默认选中第一个 & 更新列表
that.selectedReceiverGroupId = that.allReceiverGroup[0].receiverGroupId
}
})
// 查询支持的分账接口
getIfCodeByAppId(appInfo.appId).then((res) => {
that.appSupportIfCodes = res
})
this.appInfo = appInfo // 应用信息
this.visible = true // 显示弹层
},
// 抽屉关闭
onClose () {
this.callbackFunc() // 刷新列表
this.visible = false
},
// 删除某一行
delRow (item) {
const index = this.receiverTableData.indexOf(item)
if (index > -1) {
this.receiverTableData.splice(index, 1)
}
},
changeRelationType (record, value) {
record.relationType = value.key
if (value.key !== 'CUSTOM') {
record.relationTypeName = value.label
} else {
record.relationTypeName = ''
}
},
// 显示获取用户ID的弹层
showChannelUserModal (ifCode, record) {
this.$refs.channelUserModal.showModal(this.appInfo.appId, ifCode, record)
},
// 接收到当前渠道用户ID信息
changeChannelUserIdFunc ({ channelUserId, extObject }) {
console.log(channelUserId, extObject)
extObject.accNo = channelUserId
},
// 添加一行账号信息
addReceiverRow (ifCode) {
if (!this.selectedReceiverGroupId) {
return this.$message.error('请选选择要加入的分组')
}
this.receiverTableData.push(Object.assign({}, defaultReceiverTemplate, { rowKey: genRowKey(), ifCode: ifCode, appId: this.appInfo.appId }))
},
// 单条绑定 返回是否成功
reqBatchBindReceiver (i) {
const that = this
if (that.receiverTableData.length <= 0) {
return that.$message.error('请先添加账号')
}
// 完成了所有的绑定操作
if (i >= that.receiverTableData.length) {
return this.$message.success('已完成所有账号的绑定操作')
}
// 当前的账号
const currentReceiver = that.receiverTableData[i]
currentReceiver.receiverGroupId = that.selectedReceiverGroupId // 设置分组ID
if (currentReceiver.reqBindState === 1) { // 已经绑定成功, 不在重复发起
return that.reqBatchBindReceiver(++i) // 递归继续绑定
}
if (!currentReceiver.accNo) {
return this.$message.error(`第${i + 1 }条: 接收方账号不能为空`)
}
if (currentReceiver.relationType === 'CUSTOM' && !currentReceiver.relationTypeName) {
return this.$message.error(`第${i + 1 }条: 自定义类型时接收方账号名称不能为空`)
}
if (!currentReceiver.divisionProfit || currentReceiver.divisionProfit <= 0 || currentReceiver.divisionProfit > 100) {
return this.$message.error(`第${i + 1 }条: 默认分账比例请设置在[0.01% ~ 100% ] 之间`)
}
req.add(API_URL_DIVISION_RECEIVER, currentReceiver).then(apiRes => {
// 绑定成功
if (apiRes.bindState === 1) {
that.reqBatchBindReceiver(++i) // 递归继续绑定
currentReceiver.reqBindState = 1 // 成功
} else {
currentReceiver.reqBindState = 2 // 异常
that.$infoBox.modalError(`第${i + 1 }条: 绑定异常`, <div><div>错误码:{ apiRes.errCode}</div><div>错误信息:{ apiRes.errMsg}</div></div>)
}
}).catch(() => {
currentReceiver.reqBindState = 2 // 异常
})
}
}
}
</script>
<template>
<a-drawer :visible="isShow" title="修改分账用户信息" width="30%" :maskClosable="false" @close="isShow = false">
<a-form-model ref="infoFormModel" :model="saveObject" :label-col="{span: 6}" :wrapper-col="{span: 15}" :rules="rules">
<a-form-model-item label="账号别名:" prop="receiverAlias">
<a-input v-model="saveObject.receiverAlias" />
</a-form-model-item>
<a-form-model-item label="默认分账比例:" prop="divisionProfit">
<a-input v-model="saveObject.divisionProfit" style="width: 100px" /> %
</a-form-model-item>
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
<a-radio :value="1">正常分账</a-radio> <a-radio :value="0">暂停分账</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="分组变更:" prop="receiverGroupId">
<a-select style="width: 210px" placeholder="账号分组" v-model="saveObject.receiverGroupId">
<a-select-option v-for="(item) in allReceiverGroup" :key="item.receiverGroupId" :value="item.receiverGroupId">{{ item.receiverGroupName }}</a-select-option>
</a-select>
</a-form-model-item>
</a-form-model>
<div class="drawer-btn-center">
<a-button :style="{ marginRight: '8px' }" @click="isShow = false" icon="close">取消</a-button>
<a-button type="primary" @click="handleOkFunc" :loading="confirmLoading" icon="check">保存</a-button>
</div>
</a-drawer>
</template>
<script>
import { API_URL_DIVISION_RECEIVER, API_URL_DIVISION_RECEIVER_GROUP, req } from '@/api/manage'
export default {
props: {
callbackFunc: { type: Function }
},
data () {
return {
confirmLoading: false, // 显示确定按钮loading图标
isShow: false, // 是否显示弹层/抽屉
saveObject: {}, // 数据对象
recordId: null, // 更新对象ID
allReceiverGroup: [], // 当前商户所有的接收账号的分组情况
rules: {
receiverAlias: [{ required: true, message: '请输入别名', trigger: 'blur' }],
receiverGroupId: [{ required: true, message: '请选择分组', trigger: 'blur' }],
divisionProfit: [{ required: true, message: '请录入默认分账比例', trigger: 'blur' }],
state: [{ required: true, message: '请选择状态', trigger: 'blur' }]
}
}
},
created () {
},
methods: {
show: function (recordId) { // 弹层打开事件
this.saveObject = {} // 数据清空
this.confirmLoading = false // 关闭loading
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
const that = this
that.recordId = recordId
// 查询账号信息
req.getById(API_URL_DIVISION_RECEIVER, recordId).then(res => {
res.divisionProfit = (res.divisionProfit * 100).toFixed(2)
that.saveObject = res
})
// 请求接口,获取所有分组信息,只有此处进行pageSize=-1传参
req.list(API_URL_DIVISION_RECEIVER_GROUP, { pageSize: -1 }).then(res => { that.allReceiverGroup = res.records })
this.isShow = true
},
handleOkFunc: function () { // 点击【确认】按钮事件
const that = this
this.$refs.infoFormModel.validate(valid => {
if (valid) { // 验证通过
that.confirmLoading = true // 显示loading
var reqObject = {
receiverAlias: that.saveObject.receiverAlias,
receiverGroupId: that.saveObject.receiverGroupId,
divisionProfit: that.saveObject.divisionProfit,
state: that.saveObject.state
}
req.updateById(API_URL_DIVISION_RECEIVER, that.recordId, reqObject).then(res => {
that.$message.success('修改成功')
that.isShow = false
that.callbackFunc() // 刷新列表
}).catch(res => { that.confirmLoading = false })
}
})
}
}
}
</script>
<!-- 详情抽屉 -->
<template>
<a-drawer
width="50%"
:closable="true"
:visible="visible"
title="记录详情"
@close="visible = false"
>
<a-row justify="space-between" type="flex">
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账记录ID">{{ detailData.recordId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="商户号">{{ detailData.mchNo }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="应用ID">{{ detailData.appId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="支付接口代码">{{ detailData.ifCode }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="系统支付订单号">{{ detailData.payOrderId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="支付订单渠道支付订单号">{{ detailData.payOrderChannelOrderNo }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="订单金额">{{ detailData.payOrderAmount / 100 }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账基数">{{ detailData.payOrderDivisionAmount / 100 }} (订单金额-手续费-退款金额)</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="系统分账批次号">{{ detailData.batchOrderId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="上游分账批次号">{{ detailData.channelBatchOrderId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="状态">
<a-tag v-if="detailData.state === 0" :key="detailData.state" color="orange">分账中</a-tag>
<a-tag v-if="detailData.state === 1" :key="detailData.state" color="blue">分账成功</a-tag>
<a-tag v-if="detailData.state === 2" :key="detailData.state" color="volcano">分账失败</a-tag>
<a-tag v-if="detailData.state === 3" :key="detailData.state" color="purple">已受理</a-tag>
</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账接收者ID">{{ detailData.receiverId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="收款账号组ID">{{ detailData.receiverGroupId }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="收款账号别名">{{ detailData.receiverAlias }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账接收账号类型">{{ detailData.accType == 0 ? '个人' : '商户' }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账接收账号">{{ detailData.accNo }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账接收账号名称">{{ detailData.accName }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账关系类型">{{ detailData.relationType }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账关系类型名称">{{ detailData.relationTypeName }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="实际分账比例">{{ (detailData.divisionProfit * 100).toFixed(2) }}%</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="分账金额">{{ detailData.calDivisionAmount / 100 }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="创建时间">{{ detailData.createdAt }}</a-descriptions-item></a-descriptions></a-col>
<a-col :sm="12"><a-descriptions><a-descriptions-item label="更新时间">{{ detailData.updatedAt }}</a-descriptions-item></a-descriptions></a-col>
</a-row>
<a-divider />
<a-row justify="start" type="flex">
<a-col :sm="24">
<a-form-model-item label="上游返回数据包">
<a-input type="textarea" disabled="disabled" style="height: 100px;color: black" v-model="detailData.channelRespResult"/>
</a-form-model-item>
</a-col>
</a-row>
</a-drawer>
</template>
<script>
import { API_URL_PAY_ORDER_DIVISION_RECORD_LIST, req } from '@/api/manage'
export default {
data () {
return {
visible: false,
detailData: {}
}
},
methods: {
show: function (recordId) {
const that = this
req.getById(API_URL_PAY_ORDER_DIVISION_RECORD_LIST, recordId).then(res => {
that.detailData = res
})
this.visible = true
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<a-form-item label="" class="table-head-layout" style="max-width:350px;min-width:300px">
<a-range-picker
@change="onChange"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
>
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<jeepay-text-up placeholder="分账接受者ID" :msg="searchData.receiverId" v-model="searchData.receiverId" />
<jeepay-text-up placeholder="分账账号组ID" :msg="searchData.receiverGroupId" v-model="searchData.receiverGroupId" />
<jeepay-text-up placeholder="应用AppId" :msg="searchData.appId" v-model="searchData.appId"/>
<jeepay-text-up placeholder="支付订单号" :msg="searchData.payOrderId" v-model="searchData.payOrderId"/>
<jeepay-text-up placeholder="分账接收账号" :msg="searchData.accNo" v-model="searchData.accNo"/>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.state" placeholder="分账状态" default-value="">
<a-select-option value="">全部</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>
</a-form-item>
<span class="table-page-search-submitButtons">
<a-button type="primary" icon="search" @click="queryFunc" :loading="btnLoading">搜索</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="() => this.searchData = {}">重置</a-button>
</span>
</div>
</a-form>
</div>
<!-- 列表渲染 -->
<JeepayTable
@btnLoadClose="btnLoading=false"
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="recordId"
>
<template slot="amountSlot" slot-scope="{record}"><b>{{ record.calDivisionAmount/100 }}</b></template> <!-- 自定义插槽 -->
<template slot="stateSlot" slot-scope="{record}">
<a-tag v-if="record.state === 0" :key="record.state" color="orange">分账中</a-tag>
<a-tag v-if="record.state === 1" :key="record.state" color="blue">分账成功</a-tag>
<a-tag v-if="record.state === 2" :key="record.state" color="volcano">分账失败</a-tag>
<a-tag v-if="record.state === 3" :key="record.state" color="purple">已受理</a-tag>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a-button type="link" v-if="$access('ENT_DIVISION_RECORD_VIEW')" @click="detailFunc(record.recordId)">详情</a-button>
<a-button type="link" v-if="record.state == 2 && $access('ENT_DIVISION_RECORD_RESEND')" @click="redivFunc(record.recordId)">重试</a-button>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<Detail ref="recordDetail" />
</page-header-wrapper>
</template>
<script>
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_PAY_ORDER_DIVISION_RECORD_LIST, req, resendDivision } from '@/api/manage'
import moment from 'moment'
import Detail from './Detail'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'calDivisionAmount', title: '分账金额', scopedSlots: { customRender: 'amountSlot' } },
{ key: 'batchOrderId', title: '分账批次号', dataIndex: 'batchOrderId' },
{ key: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
{ key: 'ifCode', title: '接口代码', dataIndex: 'ifCode' },
{ key: 'payOrderAmount', dataIndex: 'payOrderAmount', title: '订单金额', customRender: (text) => (text / 100).toFixed(2) },
{ key: 'payOrderDivisionAmount', dataIndex: 'payOrderDivisionAmount', title: '分账基数', customRender: (text) => (text / 100).toFixed(2) },
{ key: 'receiverAlias', title: '账号别名', dataIndex: 'receiverAlias' },
{ key: 'accNo', title: '接收账号', dataIndex: 'accNo' },
{ key: 'accName', title: '账号姓名', dataIndex: 'accName' },
{ key: 'relationTypeName', title: '分账关系类型', dataIndex: 'relationTypeName' },
{ key: 'divisionProfit', dataIndex: 'divisionProfit', title: '分账比例', customRender: (text, record, index) => (text * 100).toFixed(2) + '%' },
{ key: 'state', title: '分账状态', scopedSlots: { customRender: 'stateSlot' } },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期' },
{ key: 'op', title: '操作', width: '100px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
components: { JeepayTable, JeepayTableColumns, JeepayTextUp, Detail },
data () {
return {
btnLoading: false,
tableColumns: tableColumns,
searchData: {},
createdStart: '', // 选择开始时间
createdEnd: '' // 选择结束时间
}
},
computed: {
},
mounted () {
},
methods: {
queryFunc () {
this.btnLoading = true
this.$refs.infoTable.refTable(true)
},
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_PAY_ORDER_DIVISION_RECORD_LIST, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.$refs.infoTable.refTable(true)
},
detailFunc: function (recordId) {
this.$refs.recordDetail.show(recordId)
},
// 重新发起分账
redivFunc: function (recordId) {
const that = this
this.$infoBox.confirmPrimary('确认重新分账?', '重新分账将按照订单维度重新发起(仅限分账失败订单)。', () => {
resendDivision(recordId).then(res => {
that.$refs.infoTable.refTable(false)
that.$message.warning('请等待接口最新状态')
})
})
},
moment,
onChange (date, dateString) {
this.searchData.createdStart = dateString[0] // 开始时间
this.searchData.createdEnd = dateString[1] // 结束时间
},
disabledDate (current) { // 今日之后日期不可选
return current && current > moment().endOf('day')
},
onClose () {
this.visible = false
}
}
}
</script>
<template>
<div class="result-err">
<img src="~@/assets/svg/403.svg" alt="">
<div>
抱歉,您无权访问此页。
</div>
<a-button type="primary" @click="toHome" style="margin-top:30px">
返回首页
</a-button>
</div>
</template>
<script>
export default {
name: 'Exception403',
methods: {
toHome () {
this.$router.push({ path: '/' })
}
}
}
</script>
<template>
<div class="result-err">
<img src="~@/assets/svg/404.svg" alt="">
<div>
抱歉,您访问的页面不存在。
</div>
<a-button type="primary" @click="toHome" style="margin-top:30px">
返回首页
</a-button>
</div>
</template>
<script>
export default {
name: 'Exception404',
methods: {
toHome () {
this.$router.push({ path: '/' })
}
}
}
</script>
<template>
<div class="result-err">
<img src="~@/assets/svg/500.svg" alt="">
<div>
对不起,服务器错误。
</div>
<a-button type="primary" @click="toHome" style="margin-top:30px">
返回首页
</a-button>
</div>
</template>
<script>
export default {
name: 'Exception500',
methods: {
toHome () {
this.$router.push({ path: '/' })
}
}
}
</script>
<template>
<a-drawer
:visible="visible"
:title=" isAdd ? '新增应用' : '修改应用'"
width="40%"
:maskClosable="false"
@close="onClose">
<a-form-model ref="infoFormModel" :model="saveObject" layout="vertical" :rules="rules">
<a-row :gutter="16">
<a-col :span="12" v-if="!isAdd">
<a-form-model-item label="应用 AppId" prop="appId">
<a-input v-model="saveObject.appId" placeholder="请输入" :disabled="!isAdd" />
</a-form-model-item>
</a-col>
<a-col :span="12">
<a-form-model-item label="应用名称" prop="appName">
<a-input v-model="saveObject.appName" placeholder="请输入" />
</a-form-model-item>
</a-col>
<a-col :span="12">
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
<a-radio :value="1">
启用
</a-radio>
<a-radio :value="0">
停用
</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="私钥 AppSecret" prop="appSecret" >
<a-input v-model="saveObject.appSecret" :placeholder="saveObject.appSecret_ph" type="textarea" />
<a-button type="primary" ghost @click="randomKey(false, 128, 0)"><a-icon type="file-sync" />随机生成私钥</a-button>
</a-form-model-item>
</a-col>
<a-col :span="12">
<a-form-model-item label="备注" prop="remark">
<a-input v-model="saveObject.remark" placeholder="请输入" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<div class="drawer-btn-center">
<a-button @click="onClose" icon="close" :style="{ marginRight: '8px' }">取消</a-button>
<a-button type="primary" @click="onSubmit" icon="check" >保存</a-button>
</div>
</a-drawer>
</template>
<script>
import { API_URL_MCH_APP, req } from '@/api/manage'
export default {
props: {
callbackFunc: { type: Function, default: () => () => ({}) }
},
data () {
return {
isAdd: true, // 新增 or 修改
visible: false, // 抽屉开关
appId: '', // 应用AppId
saveObject: {}, // 数据对象
rules: {
appName: [{ required: true, message: '请输入应用名称', trigger: 'blur' }]
}
}
},
methods: {
// 抽屉显示
show (appId) {
this.isAdd = !appId
// 数据清空
this.saveObject = {
'state': 1,
'appSecret': '',
'appSecret_ph': '请输入'
}
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
const that = this
that.rules.appSecret = []
if (!this.isAdd) { // 修改信息 延迟展示弹层
that.appId = appId
// 拉取详情
req.getById(API_URL_MCH_APP, appId).then(res => {
that.saveObject = res
that.saveObject.appSecret_ph = res.appSecret
that.saveObject.appSecret = ''
})
this.visible = true
} else {
// 新增时,appSecret必填
that.rules.appSecret.push({
required: true,
message: '请输入私钥或点击随机生成私钥',
trigger: 'blur'
})
that.visible = true // 展示弹层信息
}
},
// 表单提交
onSubmit () {
const that = this
this.$refs.infoFormModel.validate(valid => {
if (valid) { // 验证通过
delete that.saveObject.appSecret_ph
// 请求接口
if (that.isAdd) {
req.add(API_URL_MCH_APP, that.saveObject).then(res => {
that.$message.success('新增成功')
that.visible = false
that.callbackFunc() // 刷新列表
})
} else {
if (that.saveObject.appSecret === '') {
delete that.saveObject.appSecret
}
req.updateById(API_URL_MCH_APP, that.appId, that.saveObject).then(res => {
that.$message.success('修改成功')
that.visible = false
that.callbackFunc() // 刷新列表
})
}
}
})
},
randomKey: function (randomFlag, min, max) { // 生成随机128位私钥
let str = ''
let range = min
const arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
// 随机产生
if (randomFlag) {
range = Math.round(Math.random() * (max - min)) + min
}
for (var i = 0; i < range; i++) {
var pos = Math.round(Math.random() * (arr.length - 1))
str += arr[ pos ]
}
this.saveObject.appSecret = str
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>
<template>
<a-modal v-model="isShow" title="支付宝子商户扫码授权" @ok="handleOkFunc" @cancel="handleOkFunc">
<div style="text-align: center">
<p>方式1: <br/> 使用商家账号登录【支付宝】APP, 扫描如下二维码, 按提示授权: </p>
<img style="margin-bottom: 10px" :src="apiResData.authQrImgUrl">
<hr/>
<p style="margin-top: 10px">
方式2: <br/> <a-button size="small" class="copy-btn" v-clipboard:copy="apiResData.authUrl" v-clipboard:success="onCopySuccess" >点击复制</a-button>
或点击以下链接,按照页面提示自主授权:
</p>
<a target="_blank" :href="apiResData.authUrl">{{ apiResData.authUrl }}</a>
</div>
</a-modal>
</template>
<script>
import { queryAlipayIsvsubMchAuthUrl } from '@/api/manage'
export default {
props: {
callbackFunc: { type: Function }
},
data () {
return {
isShow: false, // 是否显示弹层/抽屉
appId: '',
apiResData: {}
}
},
created () {
},
methods: {
show: function (appId) { // 弹层打开事件
this.apiResData = {}
this.appId = appId
const that = this
queryAlipayIsvsubMchAuthUrl(appId).then(res => {
that.apiResData = res
this.isShow = true
})
},
handleOkFunc: function () { // 点击【确认】按钮事件
this.isShow = false
if (this.callbackFunc) {
this.callbackFunc()
}
},
onCopySuccess () {
this.$message.success('复制成功')
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<jeepay-text-up :placeholder="'应用AppId'" :msg="searchData.appId" v-model="searchData.appId"/>
<jeepay-text-up :placeholder="'应用名称'" :msg="searchData.appName" v-model="searchData.appName"/>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.state" placeholder="状态" default-value="">
<a-select-option value="">全部</a-select-option>
<a-select-option value="0">禁用</a-select-option>
<a-select-option value="1">启用</a-select-option>
</a-select>
</a-form-item>
<span class="table-page-search-submitButtons" style="flex-grow: 0; flex-shrink: 0;">
<a-button type="primary" icon="search" @click="queryFunc" :loading="btnLoading">查询</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="() => this.searchData = {}">重置</a-button>
</span>
</div>
</a-form>
<div>
<a-button v-if="$access('ENT_MCH_APP_ADD')" type="primary" icon="plus" @click="addFunc" class="mg-b-30">新建</a-button>
</div>
</div>
<!-- 列表渲染 -->
<JeepayTable
@btnLoadClose="btnLoading=false"
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="appId"
>
<template slot="appIdSlot" slot-scope="{record}">
<b>{{ record.appId }}</b>
</template> <!-- 自定义插槽 -->
<template slot="stateSlot" slot-scope="{record}">
<a-badge :status="record.state === 0?'error':'processing'" :text="record.state === 0?'禁用':'启用'" />
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a-button type="link" v-if="$access('ENT_MCH_APP_EDIT')" @click="editFunc(record.appId)">修改</a-button>
<a-button type="link" v-if="$access('ENT_MCH_PAY_CONFIG_LIST')" @click="showPayIfConfigList(record.appId)">支付配置</a-button>
<a-button type="link" v-if="$access('ENT_MCH_PAY_TEST')">
<router-link :to="{name:'ENT_MCH_PAY_TEST', params:{appId:record.appId}}">
支付测试
</router-link>
</a-button>
<a-button type="link" v-if="$access('ENT_MCH_TRANSFER')">
<router-link :to="{name:'ENT_MCH_TRANSFER', params:{appId:record.appId}}">
发起转账
</router-link>
</a-button>
<a-button type="link" v-if="$access('ENT_MCH_APP_DEL')" style="color: red" @click="delFunc(record.appId)">删除</a-button>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 新增应用 -->
<MchAppAddOrEdit ref="mchAppAddOrEdit" :callbackFunc="searchFunc"/>
<!-- 支付参数配置页面组件 -->
<MchPayIfConfigList ref="mchPayIfConfigList" />
</page-header-wrapper>
</template>
<script>
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_MCH_APP, req } from '@/api/manage'
import MchAppAddOrEdit from './AddOrEdit'
import MchPayIfConfigList from './MchPayIfConfigList'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'appId', fixed: 'left', width: '320px', title: '应用AppId', scopedSlots: { customRender: 'appIdSlot' } },
{ key: 'appName', title: '应用名称', dataIndex: 'appName' },
{ key: 'state', title: '状态', scopedSlots: { customRender: 'stateSlot' } },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期' },
{ key: 'op', title: '操作', width: '260px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
name: 'MchAppPage',
components: { JeepayTable, JeepayTableColumns, JeepayTextUp, MchAppAddOrEdit, MchPayIfConfigList },
data () {
return {
btnLoading: false,
tableColumns: tableColumns,
searchData: {}
}
},
methods: {
queryFunc () {
this.btnLoading = true
this.$refs.infoTable.refTable(true)
},
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_MCH_APP, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.$refs.infoTable.refTable(true)
},
addFunc: function () { // 业务通用【新增】 函数
this.$refs.mchAppAddOrEdit.show()
},
editFunc: function (recordId) { // 业务通用【修改】 函数
this.$refs.mchAppAddOrEdit.show(recordId)
},
delFunc (appId) {
const that = this
this.$infoBox.confirmDanger('确认删除?', '', () => {
req.delById(API_URL_MCH_APP, appId).then(res => {
that.$message.success('删除成功!')
that.searchFunc()
})
})
},
showPayIfConfigList: function (recordId) { // 支付参数配置
this.$refs.mchPayIfConfigList.show(recordId)
}
}
}
</script>
<style lang="less" scoped>
</style>
<template>
<a-drawer
title="填写参数"
width="40%"
:closable="true"
:maskClosable="false"
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
@close="onClose"
>
<a-form-model ref="infoFormModel" :model="saveObject" layout="vertical" :rules="rules">
<a-row :gutter="16">
<a-col :span="12">
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
<a-radio :value="1">
启用
</a-radio>
<a-radio :value="0">
停用
</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="备注" prop="remark">
<a-input v-model="saveObject.remark" placeholder="请输入" type="textarea" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<a-divider orientation="left">
<a-tag color="#FF4B33">
{{ saveObject.ifCode }} 商户参数配置
</a-tag>
</a-divider>
<a-form-model ref="mchParamFormModel" :model="ifParams" layout="vertical" :rules="ifParamsRules">
<a-row :gutter="16">
<a-col v-for="(item, key) in mchParams" :key="key" :span="item.type === 'text' ? 12 : 24">
<a-form-model-item :label="item.desc" :prop="item.name" v-if="item.type === 'text' || item.type === 'textarea'">
<a-input v-if="item.star === '1'" v-model="ifParams[item.name]" :placeholder="ifParams[item.name + '_ph']" :type="item.type" />
<a-input v-else v-model="ifParams[item.name]" placeholder="请输入" :type="item.type" />
</a-form-model-item>
<a-form-model-item :label="item.desc" :prop="item.name" v-else-if="item.type === 'radio'">
<a-radio-group v-model="ifParams[item.name]">
<a-radio v-for="(radioItem, radioKey) in item.values" :key="radioKey" :value="radioItem.value">
{{ radioItem.title }}
</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item :label="item.desc" :prop="item.name" v-else-if="item.type === 'file'">
<a-input v-model="ifParams[item.name]" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams[item.name]"
@uploadSuccess="uploadSuccess($event, item.name)"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<div class="drawer-btn-center" v-if="$access('ENT_MCH_PAY_CONFIG_ADD')">
<a-button :style="{ marginRight: '8px' }" @click="onClose" icon="close">取消</a-button>
<a-button type="primary" @click="onSubmit" icon="check" :loading="btnLoading">保存</a-button>
</div>
</a-drawer>
</template>
<script>
import JeepayCard from '@/components/JeepayCard/JeepayCard'
import JeepayUpload from '@/components/JeepayUpload/JeepayUpload'
import { API_URL_MCH_PAYCONFIGS_LIST, req, getMchPayConfigUnique, upload } from '@/api/manage'
export default {
components: {
JeepayCard,
JeepayUpload
},
props: {
callbackFunc: { type: Function, default: () => ({}) }
},
data () {
return {
btnLoading: false,
visible: false, // 抽屉开关
appId: null, // 商户号
ifCode: null, // 接口代码
mchType: null, // 商户类型:1-普通商户 2-特约商户
action: upload.cert, // 上传文件地址
mchParams: {}, // 支付接口定义描述
saveObject: {}, // 保存的对象
ifParams: {}, // 参数配置对象
rules: {
infoId: [{ required: true, trigger: 'blur' }],
ifCode: [{ required: true, trigger: 'blur' }]
// ifRate: [{ required: false, pattern: /^(([1-9]{1}\d{0,1})|(0{1}))(\.\d{1,4})?$/, message: '请输入0-100之间的数字,最多四位小数', trigger: 'blur' }]
},
ifParamsRules: {}
}
},
watch: {
ifParams: function (o, n) {
this.$set(this.ifParams, 'appSecret', this.ifParams.appSecret) // 解决appSecret 双向绑定数据不显示的问题
}
},
methods: {
// 弹层打开事件
show: function (appId, record) {
this.appId = appId
this.ifCode = record.ifCode
this.mchType = record.mchType
this.saveObject = {} // 要保存的对象
this.ifParams = {} // 参数配置对象
this.mchParams = {} // 支付接口定义描述
this.saveObject.infoId = appId
this.saveObject.ifCode = record.ifCode
this.saveObject.state = record.ifConfigState === 0 ? 0 : 1
if (this.$refs.mchParamFormModel !== undefined) {
this.$refs.mchParamFormModel.resetFields()
}
this.getMchPayConfig(record)
},
// 支付参数配置
getMchPayConfig (record) {
const that = this
// 获取支付参数
getMchPayConfigUnique(that.saveObject.infoId, that.saveObject.ifCode).then(res => {
if (res && res.ifParams) {
that.saveObject = res
that.ifParams = JSON.parse(res.ifParams)
}
const newItems = [] // 重新加载支付接口配置定义描述json
let radioItems = [] // 存放单选框value title
const mchParams = record.mchParams // 根据商户类型获取接口定义描述
JSON.parse(mchParams).forEach(item => {
radioItems = []
if (item.type === 'radio') {
const valueItems = item.values.split(',')
const titleItems = item.titles.split(',')
for (const i in valueItems) {
// 检查参数是否为数字类型 然后赋值给radio值
let radioVal = valueItems[i]
if (!isNaN((radioVal))) { radioVal = Number(radioVal) }
radioItems.push({
value: radioVal,
title: titleItems[i]
})
}
}
if (item.star === '1') {
that.ifParams[item.name + '_ph'] = that.ifParams[item.name] ? that.ifParams[item.name] : '请输入'
that.ifParams[item.name] = ''
}
newItems.push({
name: item.name,
desc: item.desc,
type: item.type,
verify: item.verify,
values: radioItems,
star: item.star // 脱敏标识 1-是
})
})
that.mchParams = newItems // 重新赋值接口定义描述
that.visible = true // 打开支付参数配置抽屉
that.generoterRules()
})
},
// 表单提交
onSubmit () {
const that = this
this.$refs.infoFormModel.validate(valid => {
this.$refs.mchParamFormModel.validate(valid2 => {
if (valid && valid2) { // 验证通过
that.btnLoading = true
const reqParams = {}
reqParams.infoId = that.saveObject.infoId
reqParams.ifCode = that.saveObject.ifCode
// reqParams.ifRate = that.saveObject.ifRate
reqParams.state = that.saveObject.state
reqParams.remark = that.saveObject.remark
// 支付参数配置不能为空
if (Object.keys(that.ifParams).length === 0) {
this.$message.error('参数不能为空!')
return
}
// 脱敏数据为空时,删除该key
that.mchParams.forEach(item => {
if (item.star === '1' && that.ifParams[item.name] === '') {
that.ifParams[item.name] = undefined
}
that.ifParams[item.name + '_ph'] = undefined
})
reqParams.ifParams = JSON.stringify(that.ifParams)
// 请求接口
if (Object.keys(reqParams).length === 0) {
this.$message.error('参数不能为空!')
return
}
req.add(API_URL_MCH_PAYCONFIGS_LIST, reqParams).then(res => {
that.$message.success('保存成功')
that.visible = false
that.btnLoading = false
that.callbackFunc()
})
}
})
})
},
// 上传文件成功回调方法,参数value为文件地址,name是自定义参数
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
// 动态生成支付参数表单检验规则
generoterRules () {
const rules = {}
let newItems = []
this.mchParams.forEach(item => {
newItems = []
if (item.verify === 'required' && item.star !== '1') {
newItems.push({
required: true,
message: '请输入' + item.desc,
trigger: 'blur'
})
rules[item.name] = newItems
}
})
this.ifParamsRules = rules
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
.jeepay-card-content {
width: 100%;
position: relative;
background-color: @jee-card-back;
border-radius: 6px;
overflow:hidden;
}
.jeepay-card-ops {
width: 100%;
height: 50px;
background-color: @jee-card-back;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
border-top: 1px solid @jee-back;
position: absolute;
bottom: 0;
}
.jeepay-card-content-header {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.jeepay-card-content-body {
display: flex;
flex-direction: column;
justify-content: start;
align-items: center;
}
.title {
font-size: 16px;
font-family: PingFang SC, PingFang SC-Bold;
font-weight: 700;
color: #1a1a1a;
letter-spacing: 1px;
}
</style>
<template>
<a-drawer
:visible="visible"
@close="onClose"
:closable="true"
:body-style="{ paddingBottom: '80px' }"
:drawer-style="{ backgroundColor: '#f0f2f5' }"
width="80%"
>
<template slot="title">
<a-steps :current="currentStep" type="navigation" style="width:80%">
<a-step title="支付参数配置" @click="stepChange(0)" />
<a-step title="支付通道配置" @click="stepChange(1)" />
</a-steps>
</template>
<div v-if="currentStep === 0">
<JeepayCard
ref="infoCard"
:reqCardListFunc="reqCardListFunc"
:span="jeepayCard.span"
:height="jeepayCard.height"
>
<div slot="cardContentSlot" slot-scope="{record}">
<div :style="{'height': jeepayCard.height + 'px'}" class="jeepay-card-content">
<!-- 卡片自定义样式 -->
<div class="jeepay-card-content-header" :style="{backgroundColor: record.bgColor, height: jeepayCard.height/2 + 'px'}">
<img v-if="record.icon" :src="record.icon" :style="{height: jeepayCard.height/5 + 'px'}">
</div>
<div class="jeepay-card-content-body" :style="{height: (jeepayCard.height/2 - 50) + 'px'}">
<div class="title">
{{ record.ifName }}
</div>
<a-badge :status="record.ifConfigState ===1 ? 'processing' : 'error'" :text="record.ifConfigState ===1 ? '启用' : '未开通'" ></a-badge>
</div>
<!-- 卡片底部操作栏 -->
<div class="jeepay-card-ops">
<a v-if="record.mchType == 2 && record.ifCode == 'alipay' && $access('ENT_MCH_PAY_CONFIG_ADD')" @click="toAlipayAuthPageFunc(record)">扫码授权 <a-icon key="right" type="right" style="fontSize: 13px"></a-icon></a>
<a v-if="$access('ENT_MCH_PAY_CONFIG_ADD')" @click="editPayIfConfigFunc(record)">填写参数 <a-icon key="right" type="right" style="fontSize: 13px"></a-icon></a>
<a v-else>暂无操作</a>
</div>
</div>
</div>
</JeepayCard>
</div>
<div v-else-if="currentStep === 1">
<a-card>
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="10">
<a-col :md="4">
<a-form-item label="">
<a-input placeholder="支付方式代码" v-model="searchData2.wayCode"/>
</a-form-item>
</a-col>
<a-col :md="4">
<a-form-item label="">
<a-input placeholder="支付方式名称" v-model="searchData2.wayName"/>
</a-form-item>
</a-col>
<a-col :sm="6">
<span class="table-page-search-submitButtons">
<a-button type="primary" icon="search" @click="searchFunc(true)">查询</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="() => this.searchData2 = {}">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 列表渲染 -->
<JeepayTable
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData2"
rowKey="wayCode"
>
<template slot="stateSlot" slot-scope="{record}">
<a-badge :status="record.passageState === 0?'error':'processing'" :text="record.passageState === 0?'禁用':'启用'" />
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a-button type="link" v-if="$access('ENT_MCH_PAY_PASSAGE_CONFIG')" @click="editPayPassageFunc(record)">配置</a-button>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
</div>
<div class="drawer-btn-center ">
<a-button :style="{ marginRight: '8px' }" @click="onClose" icon="close">关闭</a-button>
<a-button type="primary" icon="arrow-left" v-if="$access('ENT_MCH_PAY_CONFIG_LIST') && currentStep ===1" @click="stepChange(0)">上一步</a-button>
<a-button type="primary" icon="arrow-right" v-if="$access('ENT_MCH_PAY_PASSAGE_LIST') && currentStep ===0" @click="stepChange(1)">下一步</a-button>
</div>
<!-- 支付参数配置JSON渲染页面组件 -->
<MchPayConfigAddOrEdit ref="mchPayConfigAddOrEdit" :callbackFunc="refCardList" />
<!-- 支付参数配置自定义页面组件 wxpay -->
<WxpayPayConfig ref="wxpayPayConfig" :callbackFunc="refCardList" />
<!-- 支付参数配置自定义页面组件 alipay -->
<AlipayPayConfig ref="alipayPayConfig" :callbackFunc="refCardList" />
<!-- 支付通道配置页面组件 -->
<MchPayPassageAddOrEdit ref="mchPayPassageAddOrEdit" :callbackFunc="searchFunc"/>
<!-- 支付宝授权弹层 -->
<AlipayAuth ref="alipayAuthPage" :callbackFunc="refCardList"/>
</a-drawer>
</template>
<script>
import JeepayCard from '@/components/JeepayCard/JeepayCard'
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_MCH_PAYCONFIGS_LIST, API_URL_MCH_PAYPASSAGE_LIST, req, getAvailablePayInterfaceList } from '@/api/manage'
import MchPayConfigAddOrEdit from './MchPayConfigAddOrEdit'
import MchPayPassageAddOrEdit from './MchPayPassageAddOrEdit'
import WxpayPayConfig from './custom/WxpayPayConfig'
import AlipayPayConfig from './custom/AlipayPayConfig'
import AlipayAuth from './AlipayAuth'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'wayCode', title: '支付方式代码', dataIndex: 'wayCode' },
{ key: 'wayName', title: '支付方式名称', dataIndex: 'wayName' },
{ key: 'passageState', title: '状态', scopedSlots: { customRender: 'stateSlot' } },
{ key: 'op', title: '操作', width: '200px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
components: {
JeepayCard,
JeepayTable,
JeepayTableColumns,
MchPayConfigAddOrEdit,
MchPayPassageAddOrEdit,
WxpayPayConfig,
AlipayPayConfig,
AlipayAuth
},
data () {
return {
currentStep: 0, // 当前步骤条index
btnLoading: false,
appId: null, // 应用appId
visible: false, // 抽屉开关
jeepayCard: { // 卡片配置
height: 300,
span: { xxl: 6, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 }
},
tableColumns: tableColumns,
searchData2: {}
}
},
methods: {
// 弹层打开事件
show (appId) {
this.appId = appId
this.ifCode = null
this.currentStep = 0
this.visible = true
this.refCardList()
},
// 步骤条切换
stepChange (current) {
this.currentStep = current
},
// 请求支付接口定义数据
reqCardListFunc () {
return req.list(API_URL_MCH_PAYCONFIGS_LIST, { 'appId': this.appId })
},
// 刷新支付接口card列表
refCardList () {
if (this.$refs.infoCard) {
this.$refs.infoCard.refCardList()
}
},
// 请求支付通道数据
reqTableDataFunc (params) {
const that = this
return req.list(API_URL_MCH_PAYPASSAGE_LIST, Object.assign(params, { appId: that.appId }))
},
searchFunc (isToFirst = false) { // 点击【查询】按钮点击事件
this.$refs.infoTable.refTable(isToFirst)
},
// 支付参数配置
editPayIfConfigFunc (record) {
if (!record) {
return
}
if (record.subMchIsvConfig === 0) {
this.$error({
title: '提示',
content: '服务商未配置,请联系服务商配置支付参数!'
})
} else if (record.configPageType === 1) {
this.$refs.mchPayConfigAddOrEdit.show(this.appId, record)
} else if (record.configPageType === 2) {
this.$refs[record.ifCode + 'PayConfig'].show(this.appId, record)
}
},
// 支付通道配置
editPayPassageFunc (record) {
const that = this
getAvailablePayInterfaceList(that.appId, record.wayCode).then(resData => {
if (!resData || resData.length === 0) {
that.$error({
title: '提示',
content: '暂无可用支付接口配置'
})
} else {
that.$refs.mchPayPassageAddOrEdit.show(that.appId, record.wayCode)
}
})
},
// 抽屉关闭
onClose () {
this.visible = false
},
// 支付宝子商户 扫码授权
toAlipayAuthPageFunc (record) {
if (!record) {
return
}
if (record.subMchIsvConfig === 0) {
this.$error({
title: '提示',
content: '当前应用所属商户为特约商户,请先配置服务商支付参数!'
})
return
}
this.$refs.alipayAuthPage.show(this.appId)
}
}
}
</script>
<style lang="less" scoped>
.jeepay-card-content {
width: 100%;
position: relative;
background-color: @jee-card-back;
border-radius: 6px;
overflow:hidden;
}
.jeepay-card-ops {
width: 100%;
height: 50px;
background-color: @jee-card-back;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
border-top: 1px solid @jee-back;
position: absolute;
bottom: 0;
}
.jeepay-card-content-header {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.jeepay-card-content-body {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.title {
font-size: 16px;
font-family: PingFang SC, PingFang SC-Bold;
font-weight: 700;
color: #1a1a1a;
letter-spacing: 1px;
}
</style>
<template>
<a-drawer
:visible="visible"
title="配置支付通道"
@close="onClose"
:closable="true"
:maskClosable="false"
:body-style="{ paddingBottom: '80px' }"
:drawer-style="{ backgroundColor: '#f0f2f5' }"
width="40%"
>
<a-list :data-source="[]" v-if="cardList.length === 0" />
<div v-else>
<a-row :gutter="[24,24]" style="width:100%">
<a-col
v-for="(record, key) in cardList"
:key="key"
:xxl="24/jeepayCard.span.xxl"
:xl="24/jeepayCard.span.xl"
:lg="24/jeepayCard.span.lg"
:md="24/jeepayCard.span.md"
:sm="24/jeepayCard.span.sm"
:xs="24/jeepayCard.span.xs"
>
<div :style="{'height': jeepayCard.height + 'px'}" class="jeepay-card-content">
<!-- 卡片自定义样式 -->
<div class="jeepay-card-content-header" :style="{backgroundColor: record.bgColor, height: (jeepayCard.height-50)/2 + 'px'}">
<img v-if="record.icon" :src="record.icon" :style="{height: (jeepayCard.height-50)/5 + 'px'}">
</div>
<div class="jeepay-card-content-body" :style="{height: ((jeepayCard.height-50)/2) + 'px'}">
<div class="title" :style="{height: ((jeepayCard.height-50)/4) + 'px', lineHeight: ((jeepayCard.height-50)/4) + 'px'}">
{{ record.ifName }}
</div>
<a-form layout="inline" :labelCol="{span:8}" :wrapperCol="{span:14}">
<a-form-item label="费率:" :validate-status="record.error" :help="record.help">
<a-input v-model="record.rate" :disabled="!record.state && record.passageId != ''" suffix="%" />
</a-form-item>
</a-form>
</div>
<!-- 卡片底部操作栏 -->
<div class="jeepay-card-ops">
<a-switch checked-children="启用" un-checked-children="停用" v-model="record.state"></a-switch>
</div>
</div>
</a-col>
</a-row>
<div
:style="{
position: 'absolute',
right: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'center',
zIndex: 1
}"
>
<a-button icon="close" :style="{ marginRight: '8px' }" @click="onClose">
取消
</a-button>
<a-button type="primary" icon="check" v-if="$access('ENT_MCH_PAY_PASSAGE_ADD')" @click="handleOkFunc">
保存
</a-button>
</div>
</div>
</a-drawer>
</template>
<script>
import JeepayCard from '@/components/JeepayCard/JeepayCard'
import { API_URL_MCH_PAYPASSAGE_LIST, req, getAvailablePayInterfaceList } from '@/api/manage'
export default {
components: {
JeepayCard
},
props: {
callbackFunc: { type: Function, default: () => ({}) }
},
data () {
return {
cardList: [],
appId: null, // 应用appId
wayCode: null, // 支付方式代码
visible: false, // 是否显示弹层/抽屉
jeepayCard: {
height: 300,
span: { xxl: 3, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }
}
}
},
methods: {
// 弹层打开事件
show: function (appId, wayCode) {
this.appId = appId
this.wayCode = wayCode
this.visible = true
this.cardList = []
this.refCardList()
},
// 请求支付接口定义数据
refCardList () {
const that = this
getAvailablePayInterfaceList(this.appId, this.wayCode).then(resData => {
if (resData === undefined || resData.length === 0) {
that.cardList = []
return
}
const newItems = []
resData.forEach(item => {
newItems.push({
passageId: item.passageId ? item.passageId : '',
ifCode: item.ifCode,
ifName: item.ifName,
icon: item.icon,
bgColor: item.bgColor,
rate: item.rate,
state: item.state === 1
})
})
that.cardList = newItems
that.$forceUpdate()
})
},
handleOkFunc: function () { // 点击【确认】按钮事件
const that = this
const reqParams = []
try {
that.cardList.forEach(item => {
item.error = ''
item.help = ''
const reg = /^(([1-9]{1}\d{0,1})|(0{1}))(\.\d{1,4})?$/
// 状态开启则费率必填
if (item.state) {
if (!item.rate) {
item.error = 'error'
item.help = '请输入费率'
throw new Error('error')
}
if (!reg.test(item.rate) || item.rate > 100) {
item.error = 'error'
item.help = '最多四位小数'
throw new Error('error')
}
}
reqParams.push({
id: item.passageId,
appId: that.appId,
wayCode: that.wayCode,
ifCode: item.ifCode,
rate: item.rate,
state: item.state ? 1 : 0
})
})
} catch (e) {
if (e.message === 'error') {
this.$forceUpdate()
return
}
}
// 请求接口
req.add(API_URL_MCH_PAYPASSAGE_LIST, { 'reqParams': JSON.stringify(reqParams) }).then(res => {
that.$message.success('保存成功')
that.visible = false
that.callbackFunc()
})
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
.jeepay-card-content {
width: 100%;
position: relative;
background-color: @jee-card-back;
border-radius: 6px;
overflow:hidden;
}
.jeepay-card-ops {
width: 100%;
height: 50px;
background-color: @jee-card-back;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
border-top: 1px solid @jee-back;
position: absolute;
bottom: 0;
}
.jeepay-card-content-header {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.jeepay-card-content-body {
display: flex;
flex-direction: column;
justify-content: start;
align-items: center;
}
.title {
font-size: 16px;
font-family: PingFang SC, PingFang SC-Bold;
font-weight: 700;
color: #1a1a1a;
letter-spacing: 1px;
}
</style>
<template>
<a-drawer
title="填写参数"
width="40%"
:closable="true"
:maskClosable="false"
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
@close="onClose"
>
<a-form-model ref="infoFormModel" :model="saveObject" layout="vertical" :rules="rules">
<a-row :gutter="16">
<a-col :span="12">
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
<a-radio :value="1">
启用
</a-radio>
<a-radio :value="0">
停用
</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="备注" prop="remark">
<a-input v-model="saveObject.remark" placeholder="请输入" type="textarea" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<a-divider orientation="left">
<a-tag color="#FF4B33">
{{ saveObject.ifCode }} 商户参数配置
</a-tag>
</a-divider>
<a-form-model ref="mchParamFormModel" :model="ifParams" layout="vertical" :rules="ifParamsRules">
<a-row :gutter="16" v-if="mchType === 1">
<a-col span="12">
<a-form-model-item label="环境配置" prop="sandbox">
<a-radio-group v-model="ifParams.sandbox">
<a-radio :value="1">沙箱环境</a-radio>
<a-radio :value="0">生产环境</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="应用AppID" prop="appId">
<a-input v-model="ifParams.appId" placeholder="请输入" />
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="应用私钥" prop="privateKey">
<a-input v-model="ifParams.privateKey" :placeholder="ifParams.privateKey_ph" type="textarea" />
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="支付宝公钥" prop="alipayPublicKey">
<a-input v-model="ifParams.alipayPublicKey" :placeholder="ifParams.alipayPublicKey_ph" type="textarea" />
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="接口签名方式(推荐使用RSA2)" prop="signType">
<a-radio-group v-model="ifParams.signType" defaultValue="RSA">
<a-radio value="RSA">RSA</a-radio>
<a-radio value="RSA2">RSA2</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="公钥证书" prop="useCert">
<a-radio-group v-model="ifParams.useCert" defaultValue="1">
<a-radio :value="1">使用证书(请使用RSA2私钥)</a-radio>
<a-radio :value="0">不使用证书</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="应用公钥证书(.crt格式)" prop="appPublicCert">
<a-input v-model="ifParams.appPublicCert" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams.appPublicCert"
@uploadSuccess="uploadSuccess($event, 'appPublicCert')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="支付宝公钥证书(.crt格式)" prop="alipayPublicCert">
<a-input v-model="ifParams.alipayPublicCert" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams.alipayPublicCert"
@uploadSuccess="uploadSuccess($event, 'alipayPublicCert')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="支付宝根证书(.crt格式)" prop="alipayRootCert">
<a-input v-model="ifParams.alipayRootCert" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams.alipayRootCert"
@uploadSuccess="uploadSuccess($event, 'alipayRootCert')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
</a-row>
<a-row :gutter="16" v-else-if="mchType === 2">
<a-col span="12">
<a-form-model-item label="子商户app_auth_token" prop="appAuthToken">
<a-input v-model="ifParams.appAuthToken" placeholder="请输入子商户app_auth_token" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<div class="drawer-btn-center" v-if="$access('ENT_MCH_PAY_CONFIG_ADD')">
<a-button :style="{ marginRight: '8px' }" @click="onClose" icon="close">取消</a-button>
<a-button type="primary" @click="onSubmit" icon="check" :loading="btnLoading">保存</a-button>
</div>
</a-drawer>
</template>
<script>
import JeepayCard from '@/components/JeepayCard/JeepayCard'
import JeepayUpload from '@/components/JeepayUpload/JeepayUpload'
import { API_URL_MCH_PAYCONFIGS_LIST, req, getMchPayConfigUnique, upload } from '@/api/manage'
export default {
components: {
JeepayCard,
JeepayUpload
},
props: {
callbackFunc: { type: Function, default: () => ({}) }
},
data () {
return {
btnLoading: false,
visible: false, // 抽屉开关
isAdd: true,
mchType: 1,
action: upload.cert, // 上传文件地址
saveObject: {}, // 保存的对象
ifParams: {}, // 参数配置对象
rules: {
// ifRate: [{ required: false, pattern: /^(([1-9]{1}\d{0,1})|(0{1}))(\.\d{1,4})?$/, message: '请输入0-100之间的数字,最多四位小数', trigger: 'blur' }]
},
ifParamsRules: {
appId: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && !value) {
callback(new Error('请输入应用AppID'))
}
callback()
} }],
privateKey: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && this.isAdd && !value) {
callback(new Error('请输入应用私钥'))
}
callback()
} }],
alipayPublicKey: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && this.isAdd && this.ifParams.useCert === 0 && !value) {
callback(new Error('请输入支付宝公钥'))
}
callback()
} }],
appPublicCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && this.ifParams.useCert === 1 && !this.ifParams.appPublicCert) {
callback(new Error('请上传应用公钥证书(.crt格式)'))
}
callback()
} }],
alipayPublicCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && this.ifParams.useCert === 1 && !this.ifParams.alipayPublicCert) {
callback(new Error('请上传支付宝公钥证书(.crt格式)'))
}
callback()
} }],
alipayRootCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && this.ifParams.useCert === 1 && !this.ifParams.alipayRootCert) {
callback(new Error('请上传支付宝根证书(.crt格式)'))
}
callback()
} }],
appAuthToken: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 2 && !value) {
callback(new Error('请输入子商户app_auth_token'))
}
callback()
} }]
}
}
},
methods: {
// 弹层打开事件
show: function (appId, record) {
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
if (this.$refs.mchParamFormModel !== undefined) {
this.$refs.mchParamFormModel.resetFields()
}
this.mchType = record.mchType
// 数据初始化
this.saveObject = {
infoId: appId,
ifCode: record.ifCode,
state: record.ifConfigState === 0 ? 0 : 1
}
// 参数配置对象,数据初始化
this.ifParams = {
sandbox: 0,
signType: 'RSA2',
useCert: 0,
privateKey: '',
privateKey_ph: '请输入',
alipayPublicKey: '',
alipayPublicKey_ph: '请输入',
appPublicCert: '',
alipayPublicCert: '',
alipayRootCert: ''
}
this.visible = true
this.getMchPayConfig()
},
// 支付参数配置
getMchPayConfig () {
const that = this
// 获取支付参数
getMchPayConfigUnique(that.saveObject.infoId, that.saveObject.ifCode).then(res => {
if (res && res.ifParams) {
that.saveObject = res
that.ifParams = JSON.parse(res.ifParams)
that.ifParams.privateKey_ph = that.ifParams.privateKey
that.ifParams.privateKey = ''
that.ifParams.alipayPublicKey_ph = that.ifParams.alipayPublicKey
that.ifParams.alipayPublicKey = ''
that.isAdd = false
} else if (res === undefined) {
that.isAdd = true
}
})
},
// 表单提交
onSubmit () {
const that = this
this.$refs.infoFormModel.validate(valid => {
this.$refs.mchParamFormModel.validate(valid2 => {
if (valid && valid2) { // 验证通过
that.btnLoading = true
const reqParams = {}
reqParams.infoId = that.saveObject.infoId
reqParams.ifCode = that.saveObject.ifCode
// reqParams.ifRate = that.saveObject.ifRate
reqParams.state = that.saveObject.state
reqParams.remark = that.saveObject.remark
// 支付参数配置不能为空
if (Object.keys(that.ifParams).length === 0) {
this.$message.error('参数不能为空!')
return
}
// 脱敏数据为空时,删除该key
that.clearEmptyKey('privateKey')
that.clearEmptyKey('alipayPublicKey')
reqParams.ifParams = JSON.stringify(that.ifParams)
// 请求接口
if (Object.keys(reqParams).length === 0) {
this.$message.error('参数不能为空!')
return
}
req.add(API_URL_MCH_PAYCONFIGS_LIST, reqParams).then(res => {
that.$message.success('保存成功')
that.visible = false
that.btnLoading = false
that.callbackFunc()
})
}
})
})
},
// 脱敏数据为空时,删除对应key
clearEmptyKey (key) {
if (!this.ifParams[key]) {
this.ifParams[key] = undefined
}
this.ifParams[key + '_ph'] = undefined
},
// 上传文件成功回调方法,参数value为文件地址,name是自定义参数
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>
<template>
<a-drawer
title="填写参数"
width="40%"
:closable="true"
:maskClosable="false"
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
@close="onClose"
>
<a-form-model ref="infoFormModel" :model="saveObject" layout="vertical" :rules="rules">
<a-row :gutter="16">
<a-col :span="12">
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
<a-radio :value="1">
启用
</a-radio>
<a-radio :value="0">
停用
</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="备注" prop="remark">
<a-input v-model="saveObject.remark" placeholder="请输入" type="textarea" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<a-divider orientation="left">
<a-tag color="#FF4B33">
{{ saveObject.ifCode }} 商户参数配置
</a-tag>
</a-divider>
<a-form-model ref="mchParamFormModel" :model="ifParams" layout="vertical" :rules="ifParamsRules">
<a-row :gutter="16" v-if="mchType === 1">
<a-col span="12">
<a-form-model-item label="微信支付商户号" prop="mchId">
<a-input v-model="ifParams.mchId" placeholder="请输入" />
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="应用AppID" prop="appId">
<a-input v-model="ifParams.appId" placeholder="请输入" />
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="应用AppSecret" prop="appSecret">
<a-input v-model="ifParams.appSecret" :placeholder="ifParams.appSecret_ph" />
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="oauth2地址(置空将使用官方)" prop="oauth2Url">
<a-input v-model="ifParams.oauth2Url" placeholder="请输入" />
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="微信支付API版本" prop="apiVersion">
<a-radio-group v-model="ifParams.apiVersion" defaultValue="V2">
<a-radio value="V2">V2</a-radio>
<a-radio value="V3">V3</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="APIv2密钥" prop="key">
<a-input v-model="ifParams.key" :placeholder="ifParams.key_ph" type="textarea" />
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="APIv3秘钥" prop="apiV3Key">
<a-input v-model="ifParams.apiV3Key" :placeholder="ifParams.apiV3Key_ph" type="textarea" />
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="序列号" prop="serialNo">
<a-input v-model="ifParams.serialNo" :placeholder="ifParams.serialNo_ph" type="textarea" />
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="API证书(apiclient_cert.p12)" prop="cert">
<a-input v-model="ifParams.cert" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams.cert"
@uploadSuccess="uploadSuccess($event, 'cert')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="证书文件(apiclient_cert.pem)" prop="apiClientCert">
<a-input v-model="ifParams.apiClientCert" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams.apiClientCert"
@uploadSuccess="uploadSuccess($event, 'apiClientCert')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
<a-col span="24">
<a-form-model-item label="私钥文件(apiclient_key.pem)" prop="apiClientKey">
<a-input v-model="ifParams.apiClientKey" disabled="disabled" />
<JeepayUpload
:action="action"
:fileUrl="ifParams.apiClientKey"
@uploadSuccess="uploadSuccess($event, 'apiClientKey')"
>
<template slot="uploadSlot" slot-scope="{loading}">
<a-button style="marginTop:5px;"> <a-icon :type="loading ? 'loading' : 'upload'" /> {{ loading ? '正在上传' : '点击上传' }} </a-button>
</template>
</JeepayUpload>
</a-form-model-item>
</a-col>
</a-row>
<a-row :gutter="16" v-else-if="mchType === 2">
<a-col span="12">
<a-form-model-item label="子商户ID" prop="subMchId">
<a-input v-model="ifParams.subMchId" placeholder="请输入" />
</a-form-model-item>
</a-col>
<a-col span="12">
<a-form-model-item label="子账户appID(线上支付必填)" prop="subMchAppId">
<a-input v-model="ifParams.subMchAppId" placeholder="请输入" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
<div class="drawer-btn-center" v-if="$access('ENT_MCH_PAY_CONFIG_ADD')">
<a-button :style="{ marginRight: '8px' }" @click="onClose" icon="close">取消</a-button>
<a-button type="primary" @click="onSubmit" icon="check" :loading="btnLoading">保存</a-button>
</div>
</a-drawer>
</template>
<script>
import JeepayCard from '@/components/JeepayCard/JeepayCard'
import JeepayUpload from '@/components/JeepayUpload/JeepayUpload'
import { API_URL_MCH_PAYCONFIGS_LIST, req, getMchPayConfigUnique, upload } from '@/api/manage'
export default {
components: {
JeepayCard,
JeepayUpload
},
props: {
callbackFunc: { type: Function, default: () => ({}) }
},
data () {
return {
btnLoading: false,
visible: false, // 抽屉开关
isAdd: true,
mchType: 1,
action: upload.cert, // 上传文件地址
saveObject: {}, // 保存的对象
ifParams: { apiVersion: 'V2' }, // 参数配置对象
rules: {
// ifRate: [{ required: false, pattern: /^(([1-9]{1}\d{0,1})|(0{1}))(\.\d{1,4})?$/, message: '请输入0-100之间的数字,最多四位小数', trigger: 'blur' }]
},
ifParamsRules: {
mchId: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && !value) {
callback(new Error('请输入微信支付商户号'))
}
callback()
} }],
appId: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 1 && !value) {
callback(new Error('请输入应用AppID'))
}
callback()
} }],
appSecret: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.isAdd && this.mchType === 1 && !value) {
callback(new Error('请输入应用AppSecret'))
}
callback()
} }],
key: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V2' && this.isAdd && this.mchType === 1 && !value) {
callback(new Error('请输入API密钥'))
}
callback()
} }],
apiV3Key: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.isAdd && this.mchType === 1 && !value) {
callback(new Error('请输入API V3秘钥'))
}
callback()
} }],
serialNo: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.isAdd && this.mchType === 1 && !value) {
callback(new Error('请输入序列号'))
}
callback()
} }],
cert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.isAdd && !value) {
callback(new Error('请上传API证书(apiclient_cert.p12)'))
}
callback()
} }],
apiClientCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.isAdd && !value) {
callback(new Error('请上传证书文件(apiclient_cert.pem)'))
}
callback()
} }],
apiClientKey: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.mchType === 1 && !this.ifParams.apiClientKey) {
callback(new Error('请上传私钥文件(apiclient_key.pem)'))
}
callback()
} }],
subMchId: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.mchType === 2 && !value) {
callback(new Error('请输入子商户ID'))
}
callback()
} }]
}
}
},
methods: {
// 弹层打开事件
show: function (appId, record) {
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
if (this.$refs.mchParamFormModel !== undefined) {
this.$refs.mchParamFormModel.resetFields()
}
this.mchType = record.mchType
// 数据初始化
this.saveObject = {
infoId: appId,
ifCode: record.ifCode,
state: record.ifConfigState === 0 ? 0 : 1
}
// 参数配置对象,数据初始化
this.ifParams = {
apiVersion: 'V2',
appSecret: '',
appSecret_ph: '请输入',
key: '',
key_ph: '请输入',
apiV3Key: '',
apiV3Key_ph: '请输入',
serialNo: '',
serialNo_ph: '请输入'
}
this.visible = true
this.getMchPayConfig()
},
// 支付参数配置
getMchPayConfig () {
const that = this
// 获取支付参数
getMchPayConfigUnique(that.saveObject.infoId, that.saveObject.ifCode).then(res => {
if (res && res.ifParams) {
that.saveObject = res
that.ifParams = JSON.parse(res.ifParams)
that.ifParams.appSecret_ph = that.ifParams.appSecret
that.ifParams.appSecret = ''
that.ifParams.key_ph = that.ifParams.key
that.ifParams.key = ''
that.ifParams.apiV3Key_ph = that.ifParams.apiV3Key
that.ifParams.apiV3Key = ''
that.ifParams.serialNo_ph = that.ifParams.serialNo
that.ifParams.serialNo = ''
that.isAdd = false
} else if (res === undefined) {
that.isAdd = true
}
})
},
// 表单提交
onSubmit () {
const that = this
this.$refs.infoFormModel.validate(valid => {
this.$refs.mchParamFormModel.validate(valid2 => {
if (valid && valid2) { // 验证通过
that.btnLoading = true
const reqParams = {}
reqParams.infoId = that.saveObject.infoId
reqParams.ifCode = that.saveObject.ifCode
// reqParams.ifRate = that.saveObject.ifRate
reqParams.state = that.saveObject.state
reqParams.remark = that.saveObject.remark
// 支付参数配置不能为空
if (Object.keys(that.ifParams).length === 0) {
this.$message.error('参数不能为空!')
return
}
// 脱敏数据为空时,删除该key
that.clearEmptyKey('appSecret')
that.clearEmptyKey('key')
that.clearEmptyKey('apiV3Key')
that.clearEmptyKey('serialNo')
reqParams.ifParams = JSON.stringify(that.ifParams)
// 请求接口
if (Object.keys(reqParams).length === 0) {
this.$message.error('参数不能为空!')
return
}
req.add(API_URL_MCH_PAYCONFIGS_LIST, reqParams).then(res => {
that.$message.success('保存成功')
that.visible = false
that.btnLoading = false
that.callbackFunc()
})
}
})
})
},
// 脱敏数据为空时,删除对应key
clearEmptyKey (key) {
if (!this.ifParams[key]) {
this.ifParams[key] = undefined
}
this.ifParams[key + '_ph'] = undefined
},
// 上传文件成功回调方法,参数value为文件地址,name是自定义参数
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>
<template>
<page-header-wrapper>
<a-card>
<div class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<a-form-item label="" class="table-head-layout" style="max-width:350px;min-width:300px">
<a-range-picker
@change="onChange"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
>
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<jeepay-text-up :placeholder="'支付/商户/渠道订单号'" :msg="searchData.unionOrderId" v-model="searchData.unionOrderId" />
<!-- <jeepay-text-up :placeholder="'支付订单号'" :msg="searchData.payOrderId" v-model="searchData.payOrderId" />-->
<!-- <jeepay-text-up :placeholder="'商户订单号'" :msg="searchData.mchOrderNo" v-model="searchData.mchOrderNo" />-->
<jeepay-text-up :placeholder="'应用AppId'" :msg="searchData.appId" v-model="searchData.appId"/>
<a-form-item v-if="$access('ENT_PAY_ORDER_SEARCH_PAY_WAY')" label="" class="table-head-layout">
<a-select v-model="searchData.wayCode" placeholder="支付方式" default-value="">
<a-select-option value="">全部</a-select-option>
<a-select-option :key="item.wayCode" v-for="item in payWayList" :value="item.wayCode">
{{ item.wayName }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.state" placeholder="支付状态" default-value="">
<a-select-option value="">全部</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-option value="4">已撤销</a-select-option>
<a-select-option value="5">已退款</a-select-option>
<a-select-option value="6">订单关闭</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.divisionState" placeholder="分账状态" default-value="">
<a-select-option value="">全部</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>
</a-form-item>
<span class="table-page-search-submitButtons">
<a-button type="primary" icon="search" @click="queryFunc" :loading="btnLoading">搜索</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="() => this.searchData = {}">重置</a-button>
</span>
</div>
</a-form>
</div>
<!-- 列表渲染 -->
<JeepayTable
@btnLoadClose="btnLoading=false"
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="payOrderId"
:tableRowCrossColor="true"
>
<template slot="amountSlot" slot-scope="{record}"><b>{{ record.amount/100 }}</b></template> <!-- 自定义插槽 -->
<template slot="refundAmountSlot" slot-scope="{record}">{{ record.refundAmount/100 }}</template> <!-- 自定义插槽 -->
<template slot="stateSlot" slot-scope="{record}">
<a-tag
:key="record.state"
:color="record.state === 0?'blue':record.state === 1?'orange':record.state === 2?'green':record.state === 6?'':'volcano'"
>
{{ record.state === 0?'订单生成':record.state === 1?'支付中':record.state === 2?'支付成功':record.state === 3?'支付失败':record.state === 4?'已撤销':record.state === 5?'已退款':record.state === 6?'订单关闭':'未知' }}
</a-tag>
</template>
<template slot="divisionStateSlot" slot-scope="{record}">
<span v-if="record.divisionState == 0">-</span>
<a-tag color="orange" v-else-if="record.divisionState == 1">待分账</a-tag>
<a-tag color="red" v-else-if="record.divisionState == 2">分账处理中</a-tag>
<a-tag color="green" v-else-if="record.divisionState == 3">任务已结束</a-tag>
<span v-else>未知</span>
</template>
<template slot="orderSlot" slot-scope="{record}">
<div class="order-list">
<p><span style="color:#729ED5;background:#e7f5f7">支付</span>{{ record.payOrderId }}</p>
<p style="margin-bottom: 0">
<span style="color:#56cf56;background:#d8eadf">商户</span>
<a-tooltip placement="bottom" style="font-weight: normal;" v-if="record.mchOrderNo.length > record.payOrderId.length">
<template slot="title">
<span>{{ record.mchOrderNo }}</span>
</template>
{{ changeStr2ellipsis(record.mchOrderNo, record.payOrderId.length) }}
</a-tooltip>
<span style="font-weight: normal;" v-else>{{ record.mchOrderNo }}</span>
</p>
<p v-if="record.channelOrderNo" style="margin-bottom: 0;margin-top: 10px">
<span style="color:#fff;background:#E09C4D">渠道</span>
<a-tooltip placement="bottom" style="font-weight: normal;" v-if="record.channelOrderNo.length > record.payOrderId.length">
<template slot="title">
<span>{{ record.channelOrderNo }}</span>
</template>
{{ changeStr2ellipsis(record.channelOrderNo, record.payOrderId.length) }}
</a-tooltip>
<span style="font-weight: normal;" v-else>{{ record.channelOrderNo }}</span>
</p>
</div>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a-button type="link" v-if="$access('ENT_PAY_ORDER_VIEW')" @click="detailFunc(record.payOrderId)">详情</a-button>
<a-button type="link" v-if="$access('ENT_PAY_ORDER_REFUND')" style="color: red" v-show="(record.state === 2 && record.refundState !== 2)" @click="openFunc(record, record.payOrderId)">退款</a-button>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 退款弹出框 -->
<refund-modal ref="refundModalInfo" :callbackFunc="searchFunc"></refund-modal>
<!-- 日志详情抽屉 -->
<template>
<a-drawer
width="50%"
placement="right"
:closable="true"
:visible="visible"
:title="visible === true? '订单详情':''"
@close="onClose"
>
<a-row justify="space-between" type="flex">
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="服务商号">
{{ detailData.isvNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付订单号">
<a-tag color="purple">
{{ detailData.payOrderId }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商户号">
{{ detailData.mchNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商户订单号">
{{ detailData.mchOrderNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商户名称">
{{ detailData.mchName }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="应用APPID">
{{ detailData.appId }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="订单状态">
<a-tag :color="detailData.state === 0?'blue':detailData.state === 1?'orange':detailData.state === 2?'green':detailData.state === 6?'':'volcano'">
{{ detailData.state === 0?'订单生成':detailData.state === 1?'支付中':detailData.state === 2?'支付成功':detailData.state === 3?'支付失败':detailData.state === 4?'已撤销':detailData.state === 5?'已退款':detailData.state === 6?'订单关闭':'未知' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付金额">
<a-tag color="green">
{{ detailData.amount/100 }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions><a-descriptions-item label="手续费"><a-tag color="pink">{{ detailData.mchFeeAmount/100 }}</a-tag></a-descriptions-item></a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions><a-descriptions-item label="商家费率">{{ (detailData.mchFeeRate*100).toFixed(2) }}%</a-descriptions-item></a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付错误码">
{{ detailData.errCode }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付错误描述">
{{ detailData.errMsg }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="订单失效时间">
{{ detailData.expiredTime }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付成功时间">
{{ detailData.successTime }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="创建时间">
{{ detailData.createdAt }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="更新时间">
{{ detailData.updatedAt }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-divider />
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商品标题">
{{ detailData.subject }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商品描述">
{{ detailData.body }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="接口代码">
{{ detailData.ifCode }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="货币代码">
{{ detailData.currency }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付方式">
{{ detailData.wayCode }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="客户端IP">
{{ detailData.clientIp }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="用户标识">
{{ detailData.channelUser }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道订单号">
{{ detailData.channelOrderNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="异步通知地址">
{{ detailData.notifyUrl }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="页面跳转地址">
{{ detailData.returnUrl }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="退款次数">
{{ detailData.refundTimes }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="退款总额">
<a-tag color="cyan" v-if="detailData.refundAmount">
{{ detailData.refundAmount/100 }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-divider />
<a-col :sm="12">
<a-descriptions><a-descriptions-item label="订单分账模式">
<span v-if="detailData.divisionMode == 0">该笔订单不允许分账</span>
<span v-else-if="detailData.divisionMode == 1">支付成功按配置自动完成分账</span>
<span v-else-if="detailData.divisionMode == 2">商户手动分账(解冻商户金额)</span>
<span v-else>未知</span>
</a-descriptions-item></a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions><a-descriptions-item label="分账状态">
<a-tag color="blue" v-if="detailData.divisionState == 0">未发生分账</a-tag>
<a-tag color="orange" v-else-if="detailData.divisionState == 1">待分账</a-tag>
<a-tag color="red" v-else-if="detailData.divisionState == 2">分账处理中</a-tag>
<a-tag color="green" v-else-if="detailData.divisionState == 3">任务已结束</a-tag>
<a-tag color="#f50" v-else>未知</a-tag>
</a-descriptions-item></a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions><a-descriptions-item label="最新分账发起时间">{{ detailData.divisionLastTime }}</a-descriptions-item></a-descriptions>
</a-col>
</a-row>
<a-divider />
<a-row justify="start" type="flex">
<a-col :sm="24">
<a-form-model-item label="扩展参数">
<a-input
type="textarea"
disabled="disabled"
style="height: 100px;color: black"
v-model="detailData.extParam"
/>
</a-form-model-item>
</a-col>
</a-row>
</a-drawer>
</template>
</page-header-wrapper>
</template>
<script>
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import RefundModal from './RefundModal' // 退款弹出框
import { API_URL_PAY_ORDER_LIST, API_URL_PAYWAYS_LIST, req } from '@/api/manage'
import moment from 'moment'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'amount', title: '支付金额', scopedSlots: { customRender: 'amountSlot' } },
{ key: 'refundAmount', title: '退款金额', scopedSlots: { customRender: 'refundAmountSlot' } },
{ key: 'mchFeeAmount', dataIndex: 'mchFeeAmount', title: '手续费', customRender: (text) => '¥' + (text / 100).toFixed(2) },
{ key: 'orderNo', title: '订单号', scopedSlots: { customRender: 'orderSlot' }, width: '260px' },
// { key: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
// { key: 'mchOrderNo', title: '商户订单号', dataIndex: 'mchOrderNo' },
{ key: 'wayName', title: '支付方式', dataIndex: 'wayName', width: 150 },
{ key: 'state', title: '支付状态', scopedSlots: { customRender: 'stateSlot' } },
{ key: 'divisionState', title: '分账状态', scopedSlots: { customRender: 'divisionStateSlot' }, align: 'center' },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期' },
{ key: 'op', title: '操作', width: '120px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
name: 'IsvListPage',
components: { JeepayTable, JeepayTableColumns, JeepayTextUp, RefundModal },
data () {
return {
btnLoading: false,
tableColumns: tableColumns,
searchData: {},
createdStart: '', // 选择开始时间
createdEnd: '', // 选择结束时间
visible: false,
detailData: {},
payWayList: []
}
},
computed: {
},
mounted () {
if (this.$access('ENT_PAY_ORDER_SEARCH_PAY_WAY')) {
this.initPayWay()
}
},
methods: {
queryFunc () {
this.btnLoading = true
this.$refs.infoTable.refTable(true)
},
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_PAY_ORDER_LIST, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.$refs.infoTable.refTable(false)
},
// 打开退款弹出框
openFunc (record, recordId) {
if (record.refundState === 2) {
return this.$infoBox.modalError('订单无可退款金额', '')
}
this.$refs.refundModalInfo.show(recordId)
},
detailFunc: function (recordId) {
const that = this
req.getById(API_URL_PAY_ORDER_LIST, recordId).then(res => {
that.detailData = res
})
this.visible = true
},
moment,
onChange (date, dateString) {
this.searchData.createdStart = dateString[0] // 开始时间
this.searchData.createdEnd = dateString[1] // 结束时间
},
disabledDate (current) { // 今日之后日期不可选
return current && current > moment().endOf('day')
},
onClose () {
this.visible = false
},
initPayWay: function () {
const that = this
req.list(API_URL_PAYWAYS_LIST, { 'pageSize': -1 }).then(res => { // 支付方式下拉列表
that.payWayList = res.records
})
},
changeStr2ellipsis (orderNo, baseLength) {
const halfLengh = parseInt(baseLength / 2)
return orderNo.substring(0, halfLengh - 1) + '...' + orderNo.substring(orderNo.length - halfLengh, orderNo.length)
}
}
}
</script>
<style lang="less" scoped>
///deep/ .ant-table-fixed{
// tr{
// th{
// padding: 0px 0px;
// }
// }
// }
.order-list {
-webkit-text-size-adjust:none;
font-size: 12px;
display: flex;
flex-direction: column;
p {
white-space:nowrap;
span {
display: inline-block;
font-weight: 800;
height: 16px;
line-height: 16px;
width: 35px;
border-radius: 5px;
text-align: center;
margin-right: 2px;
}
}
}
</style>
<template>
<div>
<a-modal
title="退款"
:visible="visible"
:confirm-loading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
:closable="false"
>
<a-row>
<a-col :sm="24">
<a-descriptions>
<a-descriptions-item label="支付订单号">
<a-tag color="purple">
{{ detailData.payOrderId }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-descriptions>
<a-descriptions-item label="支付金额">
<a-tag color="green">
{{ detailData.amount/100 }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-descriptions>
<a-descriptions-item label="可退金额">
<a-tag color="pink">
{{ nowRefundAmount }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
</a-row>
<a-form-model :rules="rules" :model="refund" ref="refundInfo" >
<a-form-model-item label="退款金额" prop="refundAmount">
<a-input-number v-model="refund.refundAmount" :precision="2" style="width:100%"/>
</a-form-model-item>
<a-form-model-item label="退款原因" prop="refundReason">
<a-input v-model="refund.refundReason" type="textarea" />
</a-form-model-item>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import { API_URL_PAY_ORDER_LIST, req, payOrderRefund } from '@/api/manage'
export default {
props: {
callbackFunc: { type: Function, default: () => () => ({}) }
},
data () {
return {
recordId: '',
labelCol: { span: 4 },
wrapperCol: { span: 16 },
visible: false,
confirmLoading: false,
detailData: {
},
refund: {
// refundReason: '', // 退款原因
// refundAmount: '' // 退款金额
},
rules: {
refundReason: [{ min: 0, max: 256, required: true, trigger: 'blur', message: '请输入退款原因,最长不超过256个字符' }],
refundAmount: [{ required: true, message: '请输入金额', trigger: 'blur' },
{
validator: (rule, value, callBack) => {
if (value < 0.01 || value > this.nowRefundAmount) {
callBack('退款金额不能小于0.01,或者大于可退金额')
}
callBack()
}
}]
}
}
},
computed: {
nowRefundAmount () {
return (this.detailData.amount - this.detailData.refundAmount) / 100
}
},
methods: {
show (recordId) {
if (this.$refs.refundInfo !== undefined) {
this.$refs.refundInfo.resetFields()
}
this.recordId = recordId
this.visible = true
this.refund = {}
const that = this
req.getById(API_URL_PAY_ORDER_LIST, recordId).then(res => {
that.detailData = res
})
},
handleOk (e) {
this.$refs.refundInfo.validate(valid => {
if (valid) {
this.confirmLoading = true
const that = this
// 退款接口
payOrderRefund(that.recordId, that.refund.refundAmount, that.refund.refundReason).then(res => {
that.visible = false // 关闭弹窗
that.confirmLoading = false // 取消按钮转圈
if (res.state === 0 || res.state === 3) { // 订单生成 || 失败
const refundErrorModal = that.$infoBox.modalError('退款失败', (h) => that.buildModalText(res, h, () => { refundErrorModal.destroy() }))
} else if (res.state === 1) { // 退款中
const refundErrorModal = that.$infoBox.modalWarning('退款中', (h) => that.buildModalText(res, h, () => { refundErrorModal.destroy() }))
that.callbackFunc()
} else if (res.state === 2) { // 退款成功
that.$message.success('退款成功')
that.callbackFunc()
} else {
const refundErrorModal = that.$infoBox.modalWarning('退款状态未知', (h) => that.buildModalText(res, h, () => { refundErrorModal.destroy() }))
}
}).catch(() => {
that.confirmLoading = false // 取消按钮转圈
})
}
})
},
handleCancel (e) {
this.visible = false
},
buildModalText (res, h, callbackFunc) {
// 跳转退款列表Btn
const toRefundPageBtn = h('a', { on: { click: () => {
callbackFunc()
this.$router.push({ name: 'ENT_REFUND_ORDER' })
} } })
toRefundPageBtn.text = '退款列表'
return h('div', [
h('div', res.errCode ? `错误码:${res.errCode}` : ''),
h('div', res.errMsg ? `错误信息:${res.errMsg}` : ''),
h('div', [h('span', '请到'), toRefundPageBtn, h('span', '中查看详细信息')])
])
}
}
}
</script>
<style scoped lang="less">
</style>
<template>
<page-header-wrapper>
<a-card>
<div class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<a-form-item label="" class="table-head-layout" style="max-width:350px;min-width:300px">
<a-range-picker
@change="onChange"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
>
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<jeepay-text-up :placeholder="'支付/退款列订单号'" :msg="searchData.unionOrderId" v-model="searchData.unionOrderId" />
<!-- <jeepay-text-up :placeholder="'退款订单号'" :msg="searchData.refundOrderId" v-model="searchData.refundOrderId" />-->
<!-- <jeepay-text-up :placeholder="'商户退款单号'" :msg="searchData.mchRefundNo" v-model="searchData.mchRefundNo" />-->
<!-- <jeepay-text-up :placeholder="'支付订单号'" :msg="searchData.payOrderId" v-model="searchData.payOrderId" />-->
<!-- <jeepay-text-up :placeholder="'渠道订单号'" :msg="searchData.channelPayOrderNo" v-model="searchData.channelPayOrderNo" />-->
<jeepay-text-up :placeholder="'应用AppId'" :msg="searchData.appId" v-model="searchData.appId"/>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.state" placeholder="退款状态" default-value="">
<a-select-option value="">全部</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>
</a-form-item>
<span class="table-page-search-submitButtons">
<a-button type="primary" icon="search" @click="queryFunc" :loading="btnLoading">搜索</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="() => this.searchData = {}">重置</a-button>
</span>
</div>
</a-form>
</div>
<!-- 列表渲染 -->
<JeepayTable
@btnLoadClose="btnLoading=false"
ref="infoTable"
:initData="true"
:closable="true"
:searchData="searchData"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
rowKey="refundOrderId"
:tableRowCrossColor="true"
>
<template slot="payAmountSlot" slot-scope="{record}"><b>{{ record.payAmount/100 }}</b></template> <!-- 自定义插槽 -->
<template slot="refundAmountSlot" slot-scope="{record}"><b>{{ record.refundAmount/100 }}</b></template> <!-- 自定义插槽 -->
<template slot="stateSlot" slot-scope="{record}">
<div><a-tag
:key="record.state"
:color="record.state === 0?'blue':record.state === 1?'orange':record.state === 2?'green':'volcano'"
>
{{ record.state === 0?'订单生成':record.state === 1?'退款中':record.state === 2?'退款成功':record.state === 3?'退款失败':record.state === 4?'任务关闭':'未知' }}
</a-tag>
</div>
</template>
<template slot="payOrderSlot" slot-scope="{record}">
<div class="order-list">
<p><span style="color:#729ED5;background:#e7f5f7">支付</span>{{ record.payOrderId }}</p>
<p v-if="record.channelPayOrderNo" style="margin-bottom: 0;">
<span style="color:#fff;background:#E09C4D">渠道</span>
<a-tooltip placement="bottom" style="font-weight: normal;" v-if="record.channelPayOrderNo.length > record.payOrderId.length">
<template slot="title">
<span>{{ record.channelPayOrderNo }}</span>
</template>
{{ changeStr2ellipsis(record.channelPayOrderNo, record.payOrderId.length) }}
</a-tooltip>
<span style="font-weight: normal;" v-else>{{ record.channelPayOrderNo }}</span>
</p>
</div>
</template>
<template slot="refundOrderSlot" slot-scope="{record}">
<div class="order-list">
<p><span style="color:#729ED5;background:#e7f5f7">退款</span>{{ record.refundOrderId }}</p>
<p style="margin-bottom: 0;">
<span style="color:#56cf56;background:#d8eadf">商户</span>
<a-tooltip placement="bottom" style="font-weight: normal;" v-if="record.mchRefundNo.length > record.refundOrderId.length">
<template slot="title">
<span>{{ record.mchRefundNo }}</span>
</template>
{{ changeStr2ellipsis(record.mchRefundNo, record.refundOrderId.length) }}
</a-tooltip>
<span style="font-weight: normal;" v-else>{{ record.mchRefundNo }}</span>
</p>
</div>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a-button type="link" v-if="$access('ENT_REFUND_ORDER_VIEW')" @click="detailFunc(record.refundOrderId)">详情</a-button>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 日志详情抽屉 -->
<template>
<a-drawer
width="50%"
placement="right"
:closable="true"
:visible="visible"
:title="visible === true? '退款订单详情':''"
@close="onClose"
>
<a-row justify="space-between" type="flex">
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="所属系统">
{{ detailData.mchType === 1?'普通商户':detailData.mchType === 2?'特约商户':'未知' }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="服务商号">
{{ detailData.isvNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="退款订单号">
<a-tag color="purple">
{{ detailData.refundOrderId }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商户号">
{{ detailData.mchNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付订单号">
{{ detailData.payOrderId }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商户退款单号">
{{ detailData.mchRefundNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道支付订单号">
{{ detailData.channelPayOrderNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="应用APPID">
{{ detailData.appId }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="支付金额">
<a-tag color="green">
{{ detailData.payAmount/100 }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="退款金额">
<a-tag color="green">
{{ detailData.refundAmount/100 }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="订单状态">
<a-tag :color="detailData.state === 0?'blue':detailData.state === 1?'orange':detailData.state === 2?'green':'volcano'">
{{ detailData.state === 0?'订单生成':detailData.state === 1?'退款中':detailData.state === 2?'退款成功':detailData.state === 3?'退款失败':detailData.state === 4?'任务关闭':'未知' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-descriptions>
<a-descriptions-item label="退款成功时间">
{{ detailData.successTime }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="创建时间">
{{ detailData.createdAt }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="更新时间">
{{ detailData.updatedAt }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-divider />
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="接口代码">
{{ detailData.ifCode }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="货币代码">
{{ detailData.currency }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="方式代码">
{{ detailData.wayCode }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="客户端IP">
{{ detailData.clientIp }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-descriptions>
<a-descriptions-item label="异步通知地址">
{{ detailData.notifyUrl }}
</a-descriptions-item>
</a-descriptions>
</a-col>
</a-row>
<a-divider />
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道订单号">
{{ detailData.channelOrderNo }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道错误码">
{{ detailData.errCode }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道错误描述">
{{ detailData.errMsg }}
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-form-model-item label="渠道额外参数">
<a-input
type="textarea"
disabled="disabled"
style="height: 100px;color: black"
v-model="detailData.channelExtra"
/>
</a-form-model-item>
</a-col>
<a-divider />
<a-col :sm="24">
<a-form-model-item label="扩展参数">
<a-input
type="textarea"
disabled="disabled"
style="height: 100px;color: black"
v-model="detailData.extParam"
/>
</a-form-model-item>
</a-col>
<a-col :sm="24">
<a-form-model-item label="备注">
<a-input
type="textarea"
disabled="disabled"
style="height: 100px;color: black"
v-model="detailData.remark"
/>
</a-form-model-item>
</a-col>
</a-drawer>
</template>
</page-header-wrapper>
</template>
<script>
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_REFUND_ORDER_LIST, req } from '@/api/manage'
import moment from 'moment'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'payAmount', title: '支付金额', scopedSlots: { customRender: 'payAmountSlot' } },
{ key: 'refundAmount', title: '退款金额', scopedSlots: { customRender: 'refundAmountSlot' } },
{ key: 'pay', title: '退款订单号', scopedSlots: { customRender: 'refundOrderSlot' }, width: '260px' },
{ key: 'refund', title: '支付订单号', scopedSlots: { customRender: 'payOrderSlot' }, width: '260px' },
// { key: 'refundOrderId', title: '退款订单号', dataIndex: 'refundOrderId' },
// { key: 'mchRefundNo', title: '商户退款单号', dataIndex: 'mchRefundNo' },
// { key: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
// { key: 'channelPayOrderNo', title: '渠道订单号', dataIndex: 'channelPayOrderNo' },
{ key: 'state', title: '支付状态', scopedSlots: { customRender: 'stateSlot' } },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期' },
{ key: 'op', title: '操作', width: '100px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
name: 'IsvListPage',
components: { JeepayTable, JeepayTableColumns, JeepayTextUp },
data () {
return {
btnLoading: false,
tableColumns: tableColumns,
searchData: {},
createdStart: '', // 选择开始时间
createdEnd: '', // 选择结束时间
visible: false,
detailData: {}
}
},
computed: {
},
mounted () {
},
methods: {
queryFunc () {
this.btnLoading = true
this.$refs.infoTable.refTable(true)
},
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_REFUND_ORDER_LIST, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.$refs.infoTable.refTable(true)
},
detailFunc: function (recordId) {
const that = this
req.getById(API_URL_REFUND_ORDER_LIST, recordId).then(res => {
that.detailData = res
})
this.visible = true
},
moment,
onChange (date, dateString) {
this.searchData.createdStart = dateString[0] // 开始时间
this.searchData.createdEnd = dateString[1] // 结束时间
},
disabledDate (current) { // 今日之后日期不可选
return current && current > moment().endOf('day')
},
onClose () {
this.visible = false
},
changeStr2ellipsis (orderNo, baseLength) {
const halfLengh = parseInt(baseLength / 2)
return orderNo.substring(0, halfLengh - 1) + '...' + orderNo.substring(orderNo.length - halfLengh, orderNo.length)
}
}
}
</script>
<style lang="less" scoped>
.order-list {
-webkit-text-size-adjust:none;
font-size: 12px;
display: flex;
flex-direction: column;
p {
white-space:nowrap;
span {
display: inline-block;
font-weight: 800;
height: 16px;
line-height: 16px;
width: 35px;
border-radius: 5px;
text-align: center;
margin-right: 2px;
}
}
}
</style>
<!-- 订单详情抽屉 -->
<template>
<a-drawer
width="50%"
placement="right"
:closable="true"
:visible="isShow"
title="转账订单详情"
@close="isShow = false"
>
<a-row justify="space-between" type="flex">
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="应用APPID">{{ detailData.appId }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="转账订单号">
<a-tag color="purple">{{ detailData.transferId }}</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="商户转账单号">{{ detailData.mchOrderNo }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道订单号">{{ detailData.channelOrderNo }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="金额">
<a-tag color="green">{{ detailData.amount/100 }}</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="货币代码">{{ detailData.currency }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="收款账号">
<a-tag color="green">{{ detailData.accountNo }}</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="收款人姓名">{{ detailData.accountName }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="转账备注">{{ detailData.transferDesc }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="订单状态">
<a-tag :color="detailData.state === 0?'blue':detailData.state === 1?'orange':detailData.state === 2?'green':'volcano'">
{{ detailData.state === 0?'订单生成':detailData.state === 1?'转账中':detailData.state === 2?'转账成功':detailData.state === 3?'转账失败':detailData.state === 4?'任务关闭':'未知' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="转账成功时间">{{ detailData.successTime }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="创建时间">{{ detailData.createdAt }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="更新时间">{{ detailData.updatedAt }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-divider />
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="接口代码">{{ detailData.ifCode }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="入账类型">{{ detailData.entryType }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="客户端IP">{{ detailData.clientIp }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-descriptions>
<a-descriptions-item label="异步通知地址">{{ detailData.notifyUrl }}</a-descriptions-item>
</a-descriptions>
</a-col>
</a-row>
<a-divider />
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道订单号">{{ detailData.channelOrderNo }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道错误码">{{ detailData.errCode }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="渠道错误描述">{{ detailData.errMsg }}</a-descriptions-item>
</a-descriptions>
</a-col>
<a-col :sm="24">
<a-form-model-item label="渠道额外参数">
<a-input
type="textarea"
disabled="disabled"
style="height: 100px;color: black"
v-model="detailData.channelExtra"
/>
</a-form-model-item>
</a-col>
<a-divider />
<a-col :sm="24">
<a-form-model-item label="扩展参数">
<a-input
type="textarea"
disabled="disabled"
style="height: 100px;color: black"
v-model="detailData.extParam"
/>
</a-form-model-item>
</a-col>
</a-drawer>
</template>
<script>
import { API_URL_TRANSFER_ORDER_LIST, req } from '@/api/manage'
export default {
data () {
return {
detailData: {},
isShow: false, // 是否显示弹层/抽屉
recordId: null // 更新对象ID
}
},
methods: {
show: function (recordId) {
const that = this
req.getById(API_URL_TRANSFER_ORDER_LIST, recordId).then(res => {
that.detailData = res
})
this.isShow = true
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<a-form-item label="" class="table-head-layout" style="max-width:350px;min-width:300px">
<a-range-picker
@change="onChange"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
>
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<jeepay-text-up :placeholder="'转账/商户/渠道订单号'" :msg="searchData.unionOrderId" v-model="searchData.unionOrderId" />
<!-- <jeepay-text-up :placeholder="'转账订单号'" :msg="searchData.transferId" v-model="searchData.transferId" />-->
<!-- <jeepay-text-up :placeholder="'商户订单号'" :msg="searchData.mchOrderNo" v-model="searchData.mchOrderNo" />-->
<!-- <jeepay-text-up :placeholder="'渠道支付订单号'" :msg="searchData.channelOrderNo" v-model="searchData.channelOrderNo" />-->
<jeepay-text-up :placeholder="'应用AppId'" :msg="searchData.appId" v-model="searchData.appId"/>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.state" placeholder="转账状态" default-value="">
<a-select-option value="">全部</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>
</a-form-item>
<span class="table-page-search-submitButtons">
<a-button type="primary" icon="search" @click="queryFunc" :loading="btnLoading">搜索</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="() => this.searchData = {}">重置</a-button>
</span>
</div>
</a-form>
</div>
<!-- 列表渲染 -->
<JeepayTable
@btnLoadClose="btnLoading=false"
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="transferId"
:tableRowCrossColor="true"
>
<template slot="transferAmountSlot" slot-scope="{record}"><b>{{ record.amount/100 }}</b></template> <!-- 自定义插槽 -->
<template slot="stateSlot" slot-scope="{record}">
<a-tag
:key="record.state"
:color="record.state === 0?'blue':record.state === 1?'orange':record.state === 2?'green':'volcano'"
>
{{ record.state === 0?'订单生成':record.state === 1?'转账中':record.state === 2?'转账成功':record.state === 3?'转账失败':record.state === 4?'任务关闭':'未知' }}
</a-tag>
</template>
<template slot="orderSlot" slot-scope="{record}">
<div class="order-list">
<p><span style="color:#729ED5;background:#e7f5f7">转账</span>{{ record.transferId }}</p>
<p style="margin-bottom: 0">
<span style="color:#56cf56;background:#d8eadf">商户</span>
<a-tooltip v-if="record.mchOrderNo.length > record.transferId.length" placement="bottom" style="font-weight: normal;">
<template slot="title">
<span>{{ record.mchOrderNo }}</span>
</template>
{{ changeStr2ellipsis(record.mchOrderNo, record.transferId.length) }}
</a-tooltip>
<span style="font-weight: normal;" v-else>{{ record.mchOrderNo }}</span>
</p>
<p v-if="record.channelOrderNo" style="margin-bottom: 0;margin-top: 10px">
<span style="color:#fff;background:#E09C4D">渠道</span>
<a-tooltip v-if="record.channelOrderNo.length > record.transferId.length" placement="bottom" style="font-weight: normal;">
<template slot="title">
<span>{{ record.channelOrderNo }}</span>
</template>
{{ changeStr2ellipsis(record.channelOrderNo, record.transferId.length) }}
</a-tooltip>
<span style="font-weight: normal;" v-else>{{ record.channelOrderNo }}</span>
</p>
</div>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a-button type="link" v-if="$access('ENT_TRANSFER_ORDER_VIEW')" @click="detailFunc(record.transferId)">详情</a-button>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 订单详情 页面组件 -->
<TransferOrderDetail ref="transferOrderDetail" />
</page-header-wrapper>
</template>
<script>
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import TransferOrderDetail from './TransferOrderDetail'
import { API_URL_TRANSFER_ORDER_LIST, req } from '@/api/manage'
import moment from 'moment'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ title: '转账金额', scopedSlots: { customRender: 'transferAmountSlot' } },
{ key: 'orderNo', title: '订单号', scopedSlots: { customRender: 'orderSlot' }, width: 260 },
// { title: '转账订单号', dataIndex: 'transferId' },
// { title: '商户转账单号', dataIndex: 'mchOrderNo' },
// { title: '渠道订单号', dataIndex: 'channelOrderNo' },
{ title: '收款账号', dataIndex: 'accountNo', width: 200 },
{ title: '收款人姓名', dataIndex: 'accountName' },
{ title: '转账备注', dataIndex: 'transferDesc' },
{ title: '状态', scopedSlots: { customRender: 'stateSlot' }, width: 100 },
{ title: '创建日期', dataIndex: 'createdAt' },
{ title: '操作', width: '100px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
name: 'IsvListPage',
components: { JeepayTable, JeepayTableColumns, JeepayTextUp, TransferOrderDetail },
data () {
return {
btnLoading: false,
tableColumns: tableColumns,
searchData: {},
createdStart: '', // 选择开始时间
createdEnd: '' // 选择结束时间
}
},
methods: {
queryFunc () {
this.btnLoading = true
this.$refs.infoTable.refTable(true)
},
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_TRANSFER_ORDER_LIST, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.$refs.infoTable.refTable(true)
},
detailFunc: function (recordId) {
this.$refs.transferOrderDetail.show(recordId)
},
moment,
onChange (date, dateString) {
this.searchData.createdStart = dateString[0] // 开始时间
this.searchData.createdEnd = dateString[1] // 结束时间
},
disabledDate (current) { // 今日之后日期不可选
return current && current > moment().endOf('day')
},
changeStr2ellipsis (orderNo, baseLength) {
const halfLengh = parseInt(baseLength / 2)
return orderNo.substring(0, halfLengh - 1) + '...' + orderNo.substring(orderNo.length - halfLengh, orderNo.length)
}
}
}
</script>
<style lang="less" scoped>
.order-list {
-webkit-text-size-adjust:none;
font-size: 12px;
display: flex;
flex-direction: column;
p {
white-space:nowrap;
span {
display: inline-block;
font-weight: 800;
height: 16px;
line-height: 16px;
width: 35px;
border-radius: 5px;
text-align: center;
margin-right: 2px;
}
}
}
</style>
<template>
<div>
<a-card style="box-sizing:border-box;padding:30px">
<!-- 选择下单的应用列表 -->
<a-form>
<div style="display:flex;flex-direction:row">
<!-- <p style="margin-top:9px;margin-right:10px;"></p> -->
<a-form-item label="" class="table-head-layout">
<a-select v-model="appId" @change="changeAppId" style="width:300px">
<a-select-option key="" >应用APPID</a-select-option>
<a-select-option v-for="(item) in mchAppList" :key="item.appId" >{{ item.appName }} [{{ item.appId }}]</a-select-option>
</a-select>
</a-form-item>
</div>
</a-form>
<!-- 未配置支付方式提示框 -->
<a-divider v-if="!appId">请选择应用APPID</a-divider>
<a-divider v-else-if="noConfigText">您尚未配置任何支付方式</a-divider>
<a-divider v-else></a-divider>
<!-- 支付测试面板 v-if=""-->
<div style="width: 100%;" class="paydemo" v-if="payTestShow()">
<div class="paydemo-type-content">
<div class="paydemo-type-name article-title" v-show="showTitle('WX')" >微信支付</div>
<div class="paydemo-type-body">
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('WX_NATIVE') >= 0" @click="changeCurrentWayCode('WX_NATIVE', 'codeImgUrl')" :class="{this:(currentWayCode === 'WX_NATIVE')}">
<img src="@/assets/payTestImg/wx_native.svg" class="paydemo-type-img"><span class="color-change">微信二维码</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('WX_BAR') >= 0" @click="changeCurrentWayCode('WX_BAR', '')" :class="{this:(currentWayCode === 'WX_BAR')}">
<img src="@/assets/payTestImg/wx_bar.svg" class="paydemo-type-img"><span class="color-change">微信条码</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('WX_JSAPI') >= 0" @click="changeCurrentWayCode('WX_JSAPI', 'codeImgUrl')" :class="{this:(currentWayCode === 'WX_JSAPI')}">
<img src="@/assets/payTestImg/wx_jsapi.svg" class="paydemo-type-img"><span class="color-change">公众号/小程序</span>
</div>
<div class="paydemo-type-h5" v-show="appPaywayList.indexOf('WX_H5') >= 0" @click="changeCurrentWayCode('WX_H5', 'payurl')" :class="{this:(currentWayCode === 'WX_H5')}">
<img src="@/assets/payTestImg/wx_h5.svg" class="paydemo-type-img"><span class="color-change">微信H5</span>
</div>
</div>
<div class="paydemo-type-name article-title" v-show="showTitle('ALI')">支付宝支付</div>
<div class="paydemo-type-body">
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('ALI_QR') >= 0" @click="changeCurrentWayCode('ALI_QR', 'codeImgUrl')" :class="{this:(currentWayCode === 'ALI_QR')}">
<img src="@/assets/payTestImg/ali_qr.svg" class="paydemo-type-img"><span class="color-change">支付宝二维码</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('ALI_BAR') >= 0" @click="changeCurrentWayCode('ALI_BAR', '')" :class="{this:(currentWayCode === 'ALI_BAR')}">
<img src="@/assets/payTestImg/ali_bar.svg" class="paydemo-type-img"><span class="color-change">支付宝条码</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('ALI_JSAPI') >= 0" @click="changeCurrentWayCode('ALI_JSAPI', 'codeImgUrl')" :class="{this:(currentWayCode === 'ALI_JSAPI')}">
<img src="@/assets/payTestImg/ali_jsapi.svg" class="paydemo-type-img"><span class="color-change">支付宝生活号</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('ALI_PC') >= 0" @click="changeCurrentWayCode('ALI_PC', 'payurl')" :class="{this:(currentWayCode === 'ALI_PC')}">
<img src="@/assets/payTestImg/ali_pc.svg" class="paydemo-type-img"><span class="color-change">支付宝PC网站</span>
</div>
<div>
<div class="paydemo-type-h5" v-show="appPaywayList.indexOf('ALI_WAP') >= 0" @click="changeCurrentWayCode('ALI_WAP', 'payurl')" :class="{this:(currentWayCode === 'ALI_WAP')}">
<img src="@/assets/payTestImg/ali_wap.svg" class="paydemo-type-img"><span class="color-change">支付宝WAP</span>
</div>
</div>
</div>
<div class="paydemo-type-name article-title" v-show="showQtTitle()">其它支付</div>
<div class="paydemo-type-body">
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('WX_JSAPI') >= 0 || appPaywayList.indexOf('ALI_JSAPI') >= 0" @click="changeCurrentWayCode('QR_CASHIER', 'codeImgUrl')" :class="{this:(currentWayCode === 'QR_CASHIER')}">
<img src="@/assets/payTestImg/qr_cashier.svg" class="paydemo-type-img"><span class="color-change">聚合主扫</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('WX_BAR') >= 0 || appPaywayList.indexOf('ALI_BAR') >= 0" @click="changeCurrentWayCode('AUTO_BAR', 'codeImgUrl')" :class="{this:(currentWayCode === 'AUTO_BAR')}">
<img src="@/assets/payTestImg/auto_bar.svg" class="paydemo-type-img"><span class="color-change">聚合被扫</span>
</div>
<div class="paydemo-type color-change" v-show="appPaywayList.indexOf('PP_PC') >= 0" @click="changeCurrentWayCode('PP_PC', 'payurl')" :class="{this:(currentWayCode === 'PP_PC')}">
<img src="@/assets/payTestImg/pp_pc.svg" class="paydemo-type-img"><span class="color-change">PayPal支付</span>
</div>
</div>
</div>
<a-divider ></a-divider>
<!-- 订单信息 -->
<div class="paydemo-type-content">
<div class="paydemo-type-name article-title">支付信息</div>
<form class="layui-form">
<div class="paydemo-form-item">
<label>订单编号:</label><span id="payMchOrderNo">{{ mchOrderNo }}</span>
<span @click="randomOrderNo" class=" paydemo-btn" style="padding:0 3px">刷新订单号</span>
</div>
<div class="paydemo-form-item">
<label>订单标题:</label>
<a-input v-model="orderTitle" style="width: 200px"/>
</div>
<div class="paydemo-form-item">
<label>分账方式:</label>
<a-radio-group v-model="divisionMode" style="display:flex">
<div style="display:flex">
<a-radio :value="0">订单不分账</a-radio>
<a-radio :value="1">支付完成自动分账</a-radio>
<a-radio :value="2">手动分账(冻结商户资金, 只能通过API发起分账后解冻)</a-radio>
</div>
</a-radio-group>
</div>
<a-divider ></a-divider>
<div class="paydemo-form-item">
<span>支付金额(元):</span>
<a-radio-group name="radioGroup" :default-value="0.01" style="display:flex">
<div @click="amountInput=false" style="display:flex">
<a-radio :value="0.01" @click="paytestAmount=0.01">¥0.01</a-radio>
<a-radio :value="0.15" @click="paytestAmount=0.15">¥0.15</a-radio>
<a-radio :value="0.21" @click="paytestAmount=0.21">¥0.21</a-radio>
<a-radio :value="0.29" @click="paytestAmount=0.29">¥0.29</a-radio>
<a-radio :value="0.64" @click="paytestAmount=0.64">¥0.64</a-radio>
</div>
<a-radio @click="amountInputShow">
<span style="margin-right:3px">自定义金额</span>
<a-input-number
ref="amountInputFocus"
:max="100000"
:min="0.01"
v-show="amountInput"
v-model="paytestAmount"
:precision="2">
</a-input-number>
</a-radio>
</a-radio-group>
</div>
<div style="margin-top:20px;text-align: left">
<!-- <span style="color: #FD482C;font-size: 18px;padding-right: 10px;" id="amountShow">{{ paytestAmount }}</span> -->
<a-button @click="immediatelyPay" style="padding:5px 20px;background-color: #1953ff;border-radius: 5px;color:#fff">立即支付</a-button>
</div>
</form>
</div>
</div>
</a-card>
<!-- 二维码弹窗 -->
<pay-test-modal ref="payTestModal" @closeBarCode="$refs.payTestBarCode.visible = false" ></pay-test-modal>
<!-- 条码弹框 -->
<pay-test-bar-code ref="payTestBarCode" @barCodeValue="barCodeChange" @CodeAgainChange="testCodeChange"></pay-test-bar-code>
</div>
</template>
<script>
import { API_URL_MCH_APP, req, payTest, payTestOrder } from '@/api/manage' // 接口
import PayTestModal from './PayTestModal' // 二维码对话框组件
import PayTestBarCode from './PayTestBarCode' // 条码对话框组件
export default {
props: {},
components: { PayTestModal, PayTestBarCode },
data () {
return {
mchAppList: [], // app列表
appId: '', // 已选择的appId
appPaywayList: [], // 商户app支持的支付方式
currentWayCode: '', // 以何种方式进行支付,默认是微信二维码
currentPayDataType: '', // 支付参数
mchOrderNo: '', // 模拟商户订单号
authCode: '', // 条码的值
paytestAmount: '0.01', // 支付金额,默认为0.01
amountInput: false, // 自定金额输入框是否展示
noConfigText: false, // 尚无任何配置分割线提示文字
divisionMode: 0, // 订单分账模式
orderTitle: '接口调试' // 订单标题
}
},
mounted () {
// 获取传入的参数,如果参数存在,则为appId 重新赋值
const appId = this.$route.params.appId
if (appId) {
this.appId = appId // appId赋值
this.appPaywayListHandle(appId) // 调用appPaywayListHandle,展示支付方式
}
const that = this // 提前保留this
// 请求接口,获取所有的appid,只有此处进行pageSize=-1传参
req.list(API_URL_MCH_APP, { pageSize: -1 }).then(res => {
that.mchAppList = res.records
if (that.mchAppList.length > 0) {
// 赋予默认值
that.appId = that.mchAppList[0].appId
// 根据appId的值,动态显示支付方式
this.appPaywayListHandle(that.appId)
}
})
// 在进入页面时刷新订单号
this.randomOrderNo()
},
methods: {
// 支付板块是否展示
payTestShow () {
// 如果未选择appid,或者支付方式列表为0,则不显示支付体验板块
if (this.appId === '' || this.appPaywayList.length === 0) {
return false
} else {
return true
}
},
changeCurrentWayCode (wayCode, currentPayDataType) { // 切换支付方式
this.currentWayCode = wayCode
this.currentPayDataType = currentPayDataType
},
// 变更 appId的事件
changeAppId (value) {
this.appPaywayListHandle(value) // 根据appId的值,动态显示支付方式
},
// 刷新订单号
randomOrderNo () {
this.mchOrderNo = 'M' + new Date().getTime() + Math.floor(Math.random() * (9999 - 1000) + 1000)
},
// 获取条码的值
barCodeChange (value) {
this.authCode = value
this.immediatelyPay()
},
// 根据不同的appId展示不同的支付方式(在下拉框切换时和在携带参数进入页面时调用)
appPaywayListHandle (value) {
if (!value) {
this.appPaywayList = []
return false
}
const that = this
payTest(value).then(res => {
that.appPaywayList = res
if (res.length === 0) {
that.noConfigText = true
} else {
that.noConfigText = false
}
})
},
// 立即支付按钮
immediatelyPay () {
// 判断支付金额是否为0
if (!this.paytestAmount || this.paytestAmount === 0.00) {
return this.$message.error('请输入支付金额')
}
// 判断是否选择支付方式
if (this.currentWayCode === '') {
return this.$message.error('请选择支付方式')
}
// 请输入订单标题
if (!this.orderTitle || this.orderTitle.length > 20) {
return this.$message.error('请输入正确的订单标题[20字以内]')
}
// 判断是否为条码支付
if (!this.$refs.payTestBarCode.getVisible() && (this.currentWayCode === 'WX_BAR' || this.currentWayCode === 'ALI_BAR' || this.currentWayCode === 'AUTO_BAR')) {
this.$refs.payTestBarCode.showModal()
return
}
const that = this
payTestOrder({
// jsapi 默认使用聚合二维码支付
wayCode: (this.currentWayCode === 'WX_JSAPI' || this.currentWayCode === 'ALI_JSAPI') ? 'QR_CASHIER' : this.currentWayCode, // 支付方式
amount: this.paytestAmount, // 支付金额
appId: this.appId, // appId
mchOrderNo: this.mchOrderNo, // 订单编号
payDataType: this.currentPayDataType, // 支付参数(二维码,条码)
authCode: this.authCode,
divisionMode: this.divisionMode,
orderTitle: this.orderTitle
}).then(res => {
that.$refs.payTestModal.showModal(this.currentWayCode, res) // 打开弹窗
that.randomOrderNo() // 刷新订单号
}).catch(() => {
that.$refs.payTestBarCode.processCatch()
that.randomOrderNo() // 刷新订单号
})
},
// 此处判断,微信,支付宝,聚合码,哪种支付方式一个都没配置,如果未配置,则不显示该板块,若等于-1 则表示不存在
showTitle (parameterA) {
if (this.appPaywayList.toString().indexOf(parameterA) === -1) {
return false
} else {
return true
}
},
// 聚合支付标题显示
showQtTitle () {
if (this.appPaywayList.toString().indexOf('WX') !== -1 || this.appPaywayList.toString().indexOf('ALI') !== -1 || this.appPaywayList.toString().indexOf('PP_PC') !== -1) {
return true
} else {
return false
}
},
// 自定义金额输入框是否展示
amountInputShow () {
this.$nextTick(() => { // 输入框默认展示焦点
this.$refs.amountInputFocus.focus()
})
this.amountInput = true
this.paytestAmount = ''
},
// 条码弹窗点击x或者蒙版关闭
testCodeChange () {
this.randomOrderNo() // 刷新订单号
}
// handleCloseBarCode () {
// this.$refs.payTestBarCode.visible = false
// }
}
}
</script>
<style scoped lang="css">
@import './payTest.css';
</style>
<template>
<div>
<a-modal v-model="visible" title="条码支付" @cancel="handleChose" :footer="null" :width="350">
<div>
<p>请输入用户条形码:</p>
<div style="display:flex;flex-direction:row;margin-bottom:14px;">
<a-input v-model="barCodeValue" ref="barCodeInput" @keyup.enter="handleOk"></a-input>
<a-button @click="handleOk" type="primary" style="margin-left:10px;" :loading="loading">确认支付</a-button>
</div>
<p>或者使用(扫码枪/扫码盒)扫码:</p>
<div style="text-align:center">
<img src="@/assets/payTestImg/scan.svg" alt="">
</div>
</div>
</a-modal>
</div>
</template>
<script>
export default {
data () {
return {
visible: false,
barCodeValue: '', // 条码的值
loading: false // 按钮的loading状态
}
},
methods: {
showModal () {
this.loading = false
this.barCodeValue = ''// 清空条码的值
this.visible = true
this.$nextTick(() => { // 弹窗展示后,输入框默认展示焦点
this.$refs.barCodeInput.focus()
})
},
// 按钮的点击事件,当使用扫码设备扫码后,也会自动吊起该事件
handleOk () {
if (this.barCodeValue === '') {
return
}
// 传递条码值给父组件
this.loading = true
this.$emit('barCodeValue', this.barCodeValue)
},
handleChose () {
// 点击×关闭,或者点击蒙版关闭时,设置父组件barCodeAgain的值为false
this.$emit('CodeAgainChange')
},
getVisible () {
return this.visible
},
processCatch () {
this.loading = false
}
}
}
</script>
<template>
<div>
<a-modal v-model="visible" title="等待支付" @ok="handleClose" :footer="null" :width="300">
<div style="width:100%;margin-bottom:20px;text-align:center">
<img v-if="apiRes.payDataType == 'codeImgUrl'" :src="apiRes.payData" alt="">
<span v-else-if="apiRes.payDataType == 'payurl'">等待用户支付 <hr> 如浏览器未正确跳转请点击: <a :href="apiRes.payData" target="_blank">支付地址</a><a-button size="small" class="copy-btn" v-clipboard:copy="apiRes.payData" v-clipboard:success="onCopy" >复制链接</a-button></span>
<span v-else>等待用户支付,请稍后</span>
</div>
<p class="describe">
<img src="@/assets/payTestImg/wx_app.svg" alt="" v-show="wxApp"><!-- 微信图标 -->
<img src="@/assets/payTestImg/ali_app.svg" alt="" v-show="aliApp"><!-- 支付宝图标 -->
<span>{{ payText }}</span>
</p>
</a-modal>
</div>
</template>
<script>
import ReconnectingWebSocket from 'reconnectingwebsocket'
import { getWebSocketPrefix } from '@/api/manage'
export default {
name: 'PayTestModal',
data () {
return {
visible: false,
payText: '', // 二维码底部描述文字
wxApp: false, // 微信二维码图片是否展示
aliApp: false, // 支付宝二维码图片是否展示
apiRes: {}, // 接口返回数据包
payOrderWebSocket: null // 支付订单webSocket对象
}
},
methods: {
onCopy () {
this.$message.success('复制成功')
},
// 二维码以及条码弹窗
showModal (wayCode, apiRes) {
const that = this
// 关闭上一个webSocket监听
if (this.payOrderWebSocket) {
this.payOrderWebSocket.close()
}
this.apiRes = apiRes
this.wxApp = false
this.aliApp = false
this.visible = true // 打开弹窗
// 根据不同的支付方式,展示不同的信息
this.payText = ''
if (wayCode === 'WX_NATIVE' || wayCode === 'WX_JSAPI') { // 微信二维码
this.wxApp = true
this.payText = '请使用微信"扫一扫"扫码支付'
} else if (wayCode === 'ALI_QR' || wayCode === 'ALI_JSAPI') { // 支付宝二维码
this.aliApp = true
this.payText = '请使用支付宝"扫一扫"扫码支付'
} else if (wayCode === 'QR_CASHIER') { // 聚合支付二维码
this.wxApp = true
this.aliApp = true
this.payText = '支持微信、支付宝扫码'
}
// 此处判断接口中返回的orderState,值为0,1 代表支付中,直接放行无需处理,2 成功 3 失败
if (apiRes.orderState === 2 || apiRes.orderState === 3) {
if (apiRes.orderState === 2) {
that.handleClose()
const succModal = that.$infoBox.modalSuccess('支付成功', <div>2s后自动关闭...</div>)
setTimeout(() => { succModal.destroy() }, 2000)
that.$emit('closeBarCode') // 关闭条码框
} else if (apiRes.orderState === 3) {
that.handleClose()
that.$emit('closeBarCode') // 关闭条码框
that.$infoBox.modalError('支付失败', <div><div>错误码:{ apiRes.errCode}</div>
<div>错误信息:{ apiRes.errMsg}</div></div>)
}
return
}
// h5 或者 wap
if (wayCode === 'WX_H5' || wayCode === 'ALI_WAP') {
this.payText = '请复制链接到手机端打开'
} else {
// 跳转到PC网站
if (apiRes.payDataType === 'payurl') {
window.open(apiRes.payData)
}
}
// 如果上面未关闭条码框,则代表进入webScoket,那么先在此处关闭条码框
that.$emit('closeBarCode') // 关闭条码框
// 监听响应结果
this.payOrderWebSocket = new ReconnectingWebSocket(getWebSocketPrefix() + '/api/anon/ws/payOrder/' + apiRes.payOrderId + '/' + new Date().getTime())
this.payOrderWebSocket.onopen = () => {}
this.payOrderWebSocket.onmessage = (msgObject) => {
const resMsgObject = JSON.parse(msgObject.data)
if (resMsgObject.state === 2) {
that.handleClose()
const succModal = that.$infoBox.modalSuccess('支付成功', <div>2s后自动关闭...</div>)
setTimeout(() => { succModal.destroy() }, 2000)
} else {
that.handleClose()
that.$infoBox.modalError('支付失败', <div><div>错误码:{ apiRes.errCode}</div>
<div>错误信息:{ apiRes.errMsg}</div></div>)
}
}
},
handleClose (e) {
if (this.payOrderWebSocket) {
this.payOrderWebSocket.close()
}
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
.describe {
img {
width: 30px;
height: 25px;
}
}
</style>
.paydemo .content {
max-width: 1120px;
margin: 0 auto;
}
.paydemo .paydemo-type-content {
padding:20px 0;
margin-bottom:20px;
background-color: #FFFFFF;
border-radius: 6px;
}
.paydemo .paydemo-type-name {
font-size: 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.paydemo .paydemo-type-body {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 20px;
}
.paydemo .paydemo-type {
padding: 12px;
border: solid 1px #e2e2e2;
margin-right: 10px;
cursor: pointer;
}
.paydemo .paydemo-type-h5 {
padding: 12px;
border: solid 1px #e2e2e2;
margin-right: 10px;
cursor: pointer;
}
.paydemo .codeImg_wx_h5 {
position: absolute;
z-index:1001;
display: flex;
flex-direction: column;
align-items: center;
background-color: #ffffff;
width: 160px;
}
.paydemo .paydemo-type-img {
width: 40px;
height: 40px;
vertical-align: center;
margin-right: 10px;
}
.paydemo .this {
color: #1953ff;
border-color: #1953ff;
}
.paydemo .layui-input {
width: 50%;
display: inline;
font-size: 14px;
}
.paydemo .paydemo-form-item {
height: 38px;
margin-bottom: 5px;
display: flex;
flex-direction: row;
align-items: center;
}
.paydemo .layui-form-radio i:hover, .layui-form-radioed i {
color: #1953ff;
}
.paydemo .layui-anim.layui-icon {
margin-top: 3px;
}
.paydemo .layui-form-radio {
margin-top: 2px;
}
.paydemo .paydemo-scan {
padding-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.paydemo .layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: #1953ff;
}
.paydemo .layui-laypage a:hover {
color: #1953ff;
}
.paydemo-type-content p {
margin-bottom: 10px;
}
.paydemo .layui-table-view .layui-table {
width: 100%;
}
.paydemo .paydemo-btn {
border: 1px solid #e2e2e2;
background-color: #ffffff;
color:#000000;
margin-left: 8px;
display: inline-flex;
flex-direction: row;
align-items: center;
cursor:pointer;
}
.paydemo #paydemo-amount {
display: flex;
align-items: inherit;
}
.paydemo #paydemo-amount .layui-unselect{
margin-right:15px
}
#randomOrderNo:hover {
cursor:pointer;
}
.paydemo-form-item label {
display: flex;
align-items: center;
margin-right:15px;
flex-wrap:wrap
}
.paydemo-form-item label input {
margin-left:3px;
}
.paydemo .article-title {
font-weight: 600
}
\ No newline at end of file
<template>
<a-drawer :visible="isShow" :title=" isAdd ? '新增角色' : '修改角色' " width="30%" :maskClosable="false" @close="isShow = false">
<a-form-model ref="infoFormModel" :model="saveObject" :label-col="{span: 4}" :wrapper-col="{span: 15}" :rules="rules">
<a-form-model-item label="角色名称:" prop="roleName">
<a-input v-model="saveObject.roleName" />
</a-form-model-item>
</a-form-model>
<!-- 角色权限分配 -->
<RoleDist ref="roleDist" />
<div class="drawer-btn-center">
<a-button :style="{ marginRight: '8px' }" @click="isShow = false" icon="close">取消</a-button>
<a-button type="primary" @click="handleOkFunc" :loading="confirmLoading" icon="check">保存</a-button>
</div>
</a-drawer>
</template>
<script>
import { API_URL_ROLE_LIST, req } from '@/api/manage'
import RoleDist from './RoleDist'
export default {
components: { RoleDist },
props: {
callbackFunc: { type: Function }
},
data () {
return {
confirmLoading: false, // 显示确定按钮loading图标
isAdd: true, // 新增 or 修改页面标识
isShow: false, // 是否显示弹层/抽屉
saveObject: {}, // 数据对象
recordId: null, // 更新对象ID
rules: {
roleName: [
{ required: true, message: '请输入角色名称', trigger: 'blur' }
]
}
}
},
created () {
},
methods: {
show: function (recordId) { // 弹层打开事件
this.isAdd = !recordId
this.saveObject = {} // 数据清空
this.confirmLoading = false // 关闭loading
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
const that = this
// 初始化角色权限分配功能
this.$nextTick(() => that.$refs.roleDist.initTree(recordId))
if (!this.isAdd) { // 修改信息 延迟展示弹层
that.recordId = recordId
req.getById(API_URL_ROLE_LIST, recordId).then(res => { that.saveObject = res })
this.isShow = true
} else {
that.isShow = true // 立马展示弹层信息
}
},
handleOkFunc: function () { // 点击【确认】按钮事件
const that = this
this.$refs.infoFormModel.validate(valid => {
if (valid) { // 验证通过
// 请求接口
that.confirmLoading = true // 显示loading
// 保存选择的权限信息
const selectedEntIdList = that.$refs.roleDist.getSelectedEntIdList()
that.saveObject.entIdListStr = selectedEntIdList ? JSON.stringify(selectedEntIdList) : ''
if (that.isAdd) {
req.add(API_URL_ROLE_LIST, that.saveObject).then(res => {
that.$message.success('新增成功')
that.isShow = false
that.callbackFunc() // 刷新列表
}).catch(res => { that.confirmLoading = false })
} else {
req.updateById(API_URL_ROLE_LIST, that.recordId, that.saveObject).then(res => {
that.$message.success('修改成功')
that.isShow = false
that.callbackFunc() // 刷新列表
}).catch(res => { that.confirmLoading = false })
}
}
})
}
}
}
</script>
<template>
<div style="padding-bottom:50px;">
<p v-if="hasEnt">请选择权限: </p>
<!-- 树状结构 -->
<a-tree :tree-data="treeData" :replaceFields="replaceFields" v-model="checkedKeys" :checkable="true"/>
</div>
</template>
<script>
import { getEntTree, API_URL_ROLE_ENT_RELA_LIST, req } from '@/api/manage'
export default {
data () {
return {
hasEnt: this.$access('ENT_UR_ROLE_DIST'),
recordId: null, // 更新对象ID
treeData: [],
replaceFields: { key: 'entId', title: 'entName' }, // 配置替换字段
checkedKeys: [], // 已选中的节点
allEntList: {} // 由于antd vue关联操作,无法直接获取到父ID, 需要内部自行维护一套数据结构 {entId: {pid, children}}
}
},
methods: {
initTree: function (recordId) { // 弹层打开事件
const that = this
// 判断是否有权限访问
if (!this.hasEnt) {
return false
}
// 重置数据
that.checkedKeys = []
that.treeData = []
that.allEntList = {}
that.recordId = recordId
// 获取全部权限的树状结构
getEntTree().then(res => {
that.treeData = res
// 存储所有的菜单权限集合
this.recursionTreeData(res, (item) => {
that.allEntList[item.entId] = { pid: item.pid, children: item.children || [] }
})
// 查询所有的已分配的权限集合 (默认为 0 , 无数据)
req.list(API_URL_ROLE_ENT_RELA_LIST, { roleId: recordId || 'NONE', pageSize: -1 }).then(res2 => {
const checkedEntIdList = [] // 所有已分配的权限集合(兼容antd vue 仅保留子节点)
res2.records.map(item => {
if (that.allEntList[item.entId] && that.allEntList[item.entId].children.length <= 0) { // 说明是子节点
checkedEntIdList.push(item.entId)
}
})
that.checkedKeys = checkedEntIdList
})
})
},
getSelectedEntIdList: function () { // 获取已选择的列表集合
// 判断是否有权限访问
if (!this.hasEnt) {
return false
}
const that = this
const reqData = []
this.checkedKeys.map(item => {
const pidList = [] // 当前权限的所有的父节点IDList
that.getAllPid(item, pidList)
pidList.map(pid => {
if (reqData.indexOf(pid) < 0) {
reqData.push(pid)
}
})
})
return reqData
},
// 递归遍历树状结构数据
recursionTreeData (entTreeData, func) {
for (let i = 0; i < entTreeData.length; i++) {
const thisEnt = entTreeData[i]
if (thisEnt.children && thisEnt.children.length > 0) {
this.recursionTreeData(thisEnt.children, func)
}
func(thisEnt)
}
},
getAllPid (entId, array) { // 获取所有的PID
if (this.allEntList[entId] && entId !== 'ROOT') {
array.push(entId)
this.getAllPid(this.allEntList[entId].pid, array)
}
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div v-if="$access('ENT_UR_ROLE_SEARCH')" class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div
class="table-layer"
>
<jeepay-text-up :placeholder="'角色ID'" :msg="searchData.roleId" v-model="searchData.roleId" />
<jeepay-text-up :placeholder="'角色名称'" :msg="searchData.roleName" v-model="searchData.roleName" />
<span class="table-page-search-submitButtons">
<a-button type="primary" @click="searchFunc" icon="search" :loading="btnLoading">查询</a-button>
<a-button style="margin-left: 8px;" @click="() => this.searchData = {}" icon="reload">重置</a-button>
</span>
</div>
</a-form>
<div>
<a-button v-if="$access('ENT_UR_ROLE_ADD')" type="primary" icon="plus" @click="addFunc" class="mg-b-30">新建</a-button>
</div>
</div>
<!-- 列表渲染 -->
<JeepayTable
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
@btnLoadClose="btnLoading=false"
rowKey="roleId"
>
<template slot="roleIdSlot" slot-scope="{record}"><b>{{ record.roleId }}</b></template> <!-- 自定义插槽 -->
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a v-if="$access('ENT_UR_ROLE_EDIT')" @click="editFunc(record.roleId)">修改</a>
<a style="color: red" v-if="$access('ENT_UR_ROLE_DEL')" @click="delFunc(record.roleId)">删除</a>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 新增 / 修改 页面组件 -->
<InfoAddOrEdit ref="infoAddOrEdit" :callbackFunc="searchFunc" />
</page-header-wrapper>
</template>
<script>
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import { API_URL_ROLE_LIST, req } from '@/api/manage'
import InfoAddOrEdit from './AddOrEdit'
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{
key: 'roleId', // key为必填项,用于标志该列的唯一
title: '角色ID',
sorter: true,
scopedSlots: { customRender: 'roleIdSlot' }
},
{
key: 'roleName',
title: '角色名称',
dataIndex: 'roleName',
sorter: true
},
{
key: 'op',
title: '操作',
width: '200px',
fixed: 'right',
align: 'center',
scopedSlots: { customRender: 'opSlot' }
}
]
export default {
name: 'RolePage',
components: { JeepayTable, JeepayTableColumns, InfoAddOrEdit, JeepayTextUp },
data () {
return {
tableColumns: tableColumns,
searchData: {},
btnLoading: false
}
},
mounted () {
},
methods: {
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_ROLE_LIST, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.btnLoading = true // 打开查询按钮上的loading
this.$refs.infoTable.refTable(true)
},
addFunc: function () { // 业务通用【新增】 函数
this.$refs.infoAddOrEdit.show()
},
editFunc: function (recordId) { // 业务通用【修改】 函数
this.$refs.infoAddOrEdit.show(recordId)
},
delFunc: function (recordId) { // 业务通用【删除】 函数
const that = this
this.$infoBox.confirmDanger('确认删除?', '', () => {
// 需要【按钮】loading 请返回 promise对象, 不需要请直接返回null
return req.delById(API_URL_ROLE_LIST, recordId).then(res => {
that.$message.success('删除成功!')
that.$refs.infoTable.refTable(false)
})
})
}
}
}
</script>
<template>
<a-drawer
:title=" isAdd ? '新增操作员' : '修改操作员' "
placement="right"
:closable="true"
@ok="handleOkFunc"
:visible="isShow"
width="600"
@close="onClose"
:maskClosable="false">
<!-- <a-modal :confirmLoading="confirmLoading"> -->
<a-form-model
ref="infoFormModel"
:model="saveObject"
:rules="rules"
style="padding-bottom:50px"
layout="vertical"
>
<a-row justify="space-between" type="flex">
<a-col :span="10">
<a-form-model-item label="用户登录名:" prop="loginUsername">
<a-input v-model="saveObject.loginUsername" :disabled="!isAdd" />
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="用户姓名:" prop="realname">
<a-input v-model="saveObject.realname" />
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="手机号:" prop="telphone">
<a-input v-model="saveObject.telphone" />
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="编号:" prop="userNo">
<a-input v-model="saveObject.userNo" />
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="请选择性别:" prop="sex">
<a-radio-group v-model="saveObject.sex">
<a-radio :value="1"></a-radio>
<a-radio :value="2"></a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="状态:" prop="state">
<a-radio-group v-model="saveObject.state">
<a-radio :value="1">启用</a-radio>
<a-radio :value="0">停用</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
</a-row>
<a-divider orientation="left" v-if="resetIsShow">
<a-tag color="#FF4B33">
账户安全
</a-tag>
</a-divider>
<div style="display:flex;flex-direction:row;">
<a-row justify="space-between" type="flex" style="width:100%">
<a-col :span="10">
<a-form-model-item label="" v-if="resetIsShow">
重置密码:<a-checkbox v-model="sysPassword.resetPass"></a-checkbox>
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="" v-if="sysPassword.resetPass">
恢复默认密码:<a-checkbox v-model="sysPassword.defaultPass" @click="isResetPass"></a-checkbox>
</a-form-model-item>
</a-col>
</a-row>
</div>
<div v-if="sysPassword.resetPass">
<div v-show="!this.sysPassword.defaultPass">
<a-row justify="space-between" type="flex">
<a-col :span="10">
<a-form-model-item label="新密码:" prop="newPwd">
<a-input-password
autocomplete="new-password"
v-model="newPwd"
:disabled="sysPassword.defaultPass" />
</a-form-model-item>
</a-col>
<a-col :span="10">
<a-form-model-item label="确认新密码:" prop="confirmPwd">
<a-input-password
autocomplete="new-password"
v-model="sysPassword.confirmPwd"
:disabled="sysPassword.defaultPass" />
</a-form-model-item>
</a-col>
</a-row>
</div>
</div>
<div class="drawer-btn-center">
<a-button :style="{ marginRight: '8px' }" @click="onClose" icon="close">取消</a-button>
<a-button type="primary" @click="handleOkFunc" icon="check" :loading="confirmLoading">保存</a-button>
</div>
</a-form-model>
</a-drawer>
</template>
<script>
import {
req,
API_URL_SYS_USER_LIST
} from '@/api/manage'
import { Base64 } from 'js-base64'
export default {
props: {
callbackFunc: {
type: Function,
default: () => ({})
}
},
data () {
return {
newPwd: '', // 新密码
resetIsShow: false, // 重置密码是否展现
sysPassword: {
resetPass: false, // 重置密码
defaultPass: true, // 使用默认密码
confirmPwd: '' // 确认密码
},
loading: false, // 按钮上的loading
value: 1, // 单选框默认的值
confirmLoading: false, // 显示确定按钮loading图标
isAdd: true, // 新增 or 修改页面标识
isShow: false, // 是否显示弹层/抽屉
saveObject: {}, // 数据对象
recordId: null, // 更新对象ID
rules: {
realname: [{
required: true,
message: '请输入用户姓名',
trigger: 'blur'
}],
telphone: [{
required: true,
pattern: /^[1][0-9]{10}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}],
userNo: [{
required: true,
message: '请输入编号',
trigger: 'blur'
}],
loginUsername: [],
newPwd: [{
required: false,
trigger: 'blur'
}, {
validator: (rule, value, callBack) => {
if (!this.sysPassword.defaultPass) {
if (this.newPwd.length < 6 || this.newPwd.length >
12) {
callBack('请输入6-12位新密码')
}
}
callBack()
}
}], // 新密码
confirmPwd: [{
required: false,
trigger: 'blur'
}, {
validator: (rule, value, callBack) => {
if (!this.sysPassword.defaultPass) {
this.newPwd === this.sysPassword.confirmPwd ? callBack()
: callBack('新密码与确认密码不一致')
} else {
callBack()
}
}
}] // 确认新密码
}
}
},
created () {},
methods: {
show (recordId) { // 弹层打开事件
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
this.isAdd = !recordId
// 数据恢复为默认数据
this.saveObject = {
state: 1,
sex: 1
}
this.rules.loginUsername = []
this.confirmLoading = false // 关闭loading
if (this.isAdd) {
this.rules.loginUsername.push({
required: true,
pattern: /^[a-zA-Z][a-zA-Z0-9]{5,17}$/,
message: '请输入字母开头,长度为6-18位的登录名',
trigger: 'blur'
})
}
const that = this
if (!this.isAdd) { // 修改信息 延迟展示弹层
that.resetIsShow = true // 展示重置密码板块
that.recordId = recordId
req.getById(API_URL_SYS_USER_LIST, recordId).then(res => {
that.saveObject = res
})
this.isShow = true
} else {
that.isShow = true // 立马展示弹层信息
}
},
handleOkFunc: function () { // 点击【确认】按钮事件
const that = this
this.$refs.infoFormModel.validate(valid => {
if (valid) { // 验证通过
// 请求接口
that.loading = true // 打开按钮上的 loading
that.confirmLoading = true // 显示loading
if (that.isAdd) {
req.add(API_URL_SYS_USER_LIST, that.saveObject).then(res => {
that.$message.success('新增成功')
that.isShow = false
that.loading = false
that.callbackFunc() // 刷新列表
}).catch((res) => {
that.confirmLoading = false
})
} else {
that.sysPassword.confirmPwd = Base64.encode(that.sysPassword.confirmPwd)
Object.assign(that.saveObject, that.sysPassword) // 拼接对象
console.log(that.sysPassword.confirmPwd)
req.updateById(API_URL_SYS_USER_LIST, that.recordId, that.saveObject).then(res => {
that.$message.success('修改成功')
that.isShow = false
that.callbackFunc() // 刷新列表
that.resetIsShow = false // 取消展示
that.sysPassword.resetPass = false
that.sysPassword.defaultPass = true // 是否使用默认密码默认为true
that.resetPassEmpty(that) // 清空密码
}).catch(res => {
that.confirmLoading = false
that.resetIsShow = false // 取消展示
that.sysPassword.resetPass = false
that.sysPassword.defaultPass = true // 是否使用默认密码默认为true
that.resetPassEmpty(that) // 清空密码
})
}
}
})
},
// 点击遮罩层关闭抽屉
onClose () {
this.isShow = false
this.resetIsShow = false // 取消重置密码板块展示
this.resetPassEmpty(this) // 清空密码
this.sysPassword.resetPass = false // 关闭密码输入
this.sysPassword.defaultPass = true // 是否使用默认密码默认为true
},
// 使用默认密码重置是否为true
isResetPass () {
if (!this.sysPassword.defaultPass) {
this.newPwd = ''
this.sysPassword.confirmPwd = ''
}
},
// 保存后清空密码
resetPassEmpty (that) {
that.newPwd = ''
that.sysPassword.confirmPwd = ''
}
}
}
</script>
<template>
<a-drawer :visible="isShow" title="分配角色" width="30%" :maskClosable="false" @close="isShow = false">
<div>
<div :style="{ borderBottom: '1px solid #E9E9E9' }">
<a-checkbox
:indeterminate="checkedVal.length != 0 && allRoleList.length != checkedVal.length"
:checked="checkedVal.length != 0 && allRoleList.length === checkedVal.length"
@change="onCheckAllChange">
全选
</a-checkbox>
</div>
<br />
<a-checkbox-group v-model="checkedVal" :options="allRoleList"/>
</div>
<div class="drawer-btn-center">
<a-button :style="{ marginRight: '8px' }" @click="isShow = false" icon="close">取消</a-button>
<a-button type="primary" @click="handleOkFunc" :loading="confirmLoading" icon="check">保存</a-button>
</div>
</a-drawer>
</template>
<script>
import { uSysUserRoleRela, req, reqLoad, API_URL_ROLE_LIST, API_URL_USER_ROLE_RELA_LIST } from '@/api/manage'
export default {
props: {
callbackFunc: { type: Function }
},
data () {
return {
confirmLoading: false, // 显示确定按钮loading图标
isShow: false, // 是否显示弹层/抽屉
recordId: null, // 更新对象ID
allRoleList: [], // 全部的角色集合 {label: '', value: ''}
checkedVal: [] // 已选择的数据集合
}
},
created () {
},
methods: {
show: function (recordId) { // 弹层打开事件
const that = this
// 重置数据
that.allRoleList = []
that.checkedVal = []
that.confirmLoading = false // 关闭loading
that.recordId = recordId
// 查询所有角色列表
reqLoad.list(API_URL_ROLE_LIST, { pageSize: -1 }).then(res => {
if (res.total <= 0) {
return this.$message.error(`当前暂无角色,请先行添加`)
}
that.allRoleList = []
res.records.map(role => {
that.allRoleList.push({ label: role.roleName, value: role.roleId })
that.isShow = true
})
// 查询已分配的列表
req.list(API_URL_USER_ROLE_RELA_LIST, { pageSize: -1, userId: recordId }).then(relaRes => {
relaRes.records.map(rela => {
that.checkedVal.push(rela.roleId)
})
})
})
},
handleOkFunc: function () { // 点击【确认】按钮事件
const that = this
that.confirmLoading = true // 显示loading
uSysUserRoleRela(this.recordId, this.checkedVal).then(res => {
that.$message.success('更新成功!')
this.isShow = false
if (that.callbackFunc !== undefined) {
that.callbackFunc() // 刷新列表
}
}).catch(res => { that.confirmLoading = false }) // 恢复loading
},
onCheckAllChange: function (e) { // 点击全选/非全选的状态更改
const that = this
this.checkedVal = []
if (e.target.checked) { // 全选
this.allRoleList.map(role => { that.checkedVal.push(role.value) })
}
}
}
}
</script>
<template>
<page-header-wrapper>
<a-card>
<div v-if="$access('ENT_UR_USER_SEARCH')" class="table-page-search-wrapper">
<a-form layout="inline" class="table-head-ground">
<div class="table-layer">
<jeepay-text-up :placeholder="'用户ID'" :msg="searchData.sysUserId" v-model="searchData.sysUserId" />
<jeepay-text-up :placeholder="'用户姓名'" :msg="searchData.realname" v-model="searchData.realname" />
<span class="table-page-search-submitButtons">
<a-button type="primary" @click="searchFunc" icon="search" :loading="btnLoading">查询</a-button>
<a-button style="margin-left: 8px;" @click="() => this.searchData = {}" icon="reload">重置</a-button>
</span>
</div>
</a-form>
<div>
<a-button v-if="$access('ENT_UR_USER_ADD')" type="primary" icon="plus" @click="addFunc" class="mg-b-30">新建</a-button>
</div>
</div>
<!-- 列表渲染 -->
<JeepayTable
@btnLoadClose="btnLoading=false"
ref="infoTable"
:initData="true"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="sysUserId"
>
<template slot="avatarSlot" slot-scope="{record}">
<a-avatar size="default" :src="record.avatarUrl" />
</template>
<template slot="stateSlot" slot-scope="{record}">
<JeepayTableColState :state="record.state" :showSwitchType="$access('ENT_UR_USER_EDIT')" :onChange="(state) => { return updateState(record.sysUserId, state)}"/>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
<JeepayTableColumns>
<a v-if="$access('ENT_UR_USER_UPD_ROLE')" @click="roleDist(record.sysUserId)" >变更角色</a>
<a v-if="$access('ENT_UR_USER_EDIT')" @click="editFunc(record.sysUserId)">修改</a>
<a v-if="$access('ENT_UR_USER_DELETE')" style="color: red" @click="delFunc(record.sysUserId)">删除</a>
</JeepayTableColumns>
</template>
</JeepayTable>
</a-card>
<!-- 新增 / 修改 页面组件 -->
<InfoAddOrEdit ref="infoAddOrEdit" :callbackFunc="searchFunc"/>
<!-- 分配角色 页面组件 -->
<RoleDist ref="roleDist"/>
</page-header-wrapper>
</template>
<script>
import JeepayTextUp from '@/components/JeepayTextUp/JeepayTextUp' // 文字上移组件
import JeepayTable from '@/components/JeepayTable/JeepayTable'
import JeepayTableColumns from '@/components/JeepayTable/JeepayTableColumns'
import JeepayTableColState from '@/components/JeepayTable/JeepayTableColState'
import { API_URL_SYS_USER_LIST, req, reqLoad } from '@/api/manage'
import InfoAddOrEdit from './AddOrEdit'
import RoleDist from './RoleDist'
const tableColumns = [
{ title: '用户ID', dataIndex: 'sysUserId', fixed: 'left' },
{ title: '姓名', dataIndex: 'realname' },
{ title: '性别', dataIndex: 'sex', customRender: (text, record, index) => { return record.sex === 1 ? '男' : record.sex === 2 ? '女' : '未知' } },
{ title: '头像', scopedSlots: { customRender: 'avatarSlot' } },
{ title: '编号', dataIndex: 'userNo' },
{ title: '手机号', dataIndex: 'telphone' },
{ title: '超管', dataIndex: 'isAdmin', customRender: (text, record, index) => { return record.isAdmin === 1 ? '是' : '否' } },
{ title: '状态', scopedSlots: { customRender: 'stateSlot' }, align: 'center' },
{ title: '创建时间', dataIndex: 'createdAt' },
{ title: '修改时间', dataIndex: 'updatedAt' },
{
key: 'op',
title: '操作',
width: '200px',
fixed: 'right',
align: 'center',
scopedSlots: { customRender: 'opSlot' }
}
]
export default {
components: { JeepayTable, JeepayTableColumns, InfoAddOrEdit, RoleDist, JeepayTableColState, JeepayTextUp },
data () {
return {
tableColumns: tableColumns,
searchData: {},
btnLoading: false
}
},
mounted () {
},
methods: {
// 请求table接口数据
reqTableDataFunc: (params) => {
return req.list(API_URL_SYS_USER_LIST, params)
},
searchFunc: function () { // 点击【查询】按钮点击事件
this.btnLoading = true // 打开查询按钮的loading
this.$refs.infoTable.refTable(true)
},
addFunc: function () { // 业务通用【新增】 函数
this.$refs.infoAddOrEdit.show()
},
delFunc: function (recordId) { // 业务通用【删除】 函数
const that = this
this.$infoBox.confirmDanger('确认删除?', '', () => {
return req.delById(API_URL_SYS_USER_LIST, recordId).then(res => {
that.$message.success('删除成功!')
that.$refs.infoTable.refTable(false)
})
})
},
editFunc: function (recordId) { // 业务通用【修改】 函数
this.$refs.infoAddOrEdit.show(recordId)
},
roleDist: function (recordId) { // 【分配权限】 按钮点击事件
this.$refs.roleDist.show(recordId)
},
updateState: function (recordId, state) { // 【更新状态】
const that = this
const title = state === 1 ? '确认[启用]该用户?' : '确认[停用]该用户?'
const content = state === 1 ? '启用后用户可进行登陆等一系列操作' : '停用后该用户将立即退出系统并不可再次登陆'
return new Promise((resolve, reject) => {
that.$infoBox.confirmDanger(title, content, () => {
return reqLoad.updateById(API_URL_SYS_USER_LIST, recordId, { state: state }).then(res => {
that.searchFunc()
resolve()
}).catch(err => reject(err))
},
() => {
reject(new Error())
})
})
}
}
}
</script>
.paydemo .content {
max-width: 1120px;
margin: 0 auto;
}
.paydemo .paydemo-type-content {
padding:20px 0;
margin-bottom:20px;
background-color: #FFFFFF;
border-radius: 6px;
}
.paydemo .paydemo-type-name {
font-size: 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.paydemo .paydemo-type-body {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 20px;
}
.paydemo .paydemo-type {
padding: 12px;
border: solid 1px #e2e2e2;
margin-right: 10px;
cursor: pointer;
}
.paydemo .paydemo-type-h5 {
padding: 12px;
border: solid 1px #e2e2e2;
margin-right: 10px;
cursor: pointer;
}
.paydemo .codeImg_wx_h5 {
position: absolute;
z-index:1001;
display: flex;
flex-direction: column;
align-items: center;
background-color: #ffffff;
width: 160px;
}
.paydemo .paydemo-type-img {
width: 40px;
height: 40px;
vertical-align: center;
margin-right: 10px;
}
.paydemo .this {
color: #1953ff;
border-color: #1953ff;
}
.paydemo .layui-input {
width: 50%;
display: inline;
font-size: 14px;
}
.paydemo .paydemo-form-item {
height: 38px;
margin-bottom: 5px;
display: flex;
flex-direction: row;
align-items: center;
}
.paydemo .layui-form-radio i:hover, .layui-form-radioed i {
color: #1953ff;
}
.paydemo .layui-anim.layui-icon {
margin-top: 3px;
}
.paydemo .layui-form-radio {
margin-top: 2px;
}
.paydemo .paydemo-scan {
padding-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.paydemo .layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: #1953ff;
}
.paydemo .layui-laypage a:hover {
color: #1953ff;
}
.paydemo-type-content p {
margin-bottom: 10px;
}
.paydemo .layui-table-view .layui-table {
width: 100%;
}
.paydemo .paydemo-btn {
border: 1px solid #e2e2e2;
background-color: #ffffff;
color:#000000;
margin-left: 8px;
display: inline-flex;
flex-direction: row;
align-items: center;
cursor:pointer;
}
.paydemo #paydemo-amount {
display: flex;
align-items: inherit;
}
.paydemo #paydemo-amount .layui-unselect{
margin-right:15px
}
#randomOrderNo:hover {
cursor:pointer;
}
.paydemo-form-item label {
display: flex;
align-items: center;
margin-right:15px;
flex-wrap:wrap
}
.paydemo-form-item label input {
margin-left:3px;
}
.paydemo .article-title {
font-weight: 600
}
\ No newline at end of file
<template>
<div>
<a-card style="box-sizing:border-box;padding:30px">
<!-- 选择下单的应用列表 -->
<a-form>
<div style="display:flex;flex-direction:row">
<a-form-item label="" class="table-head-layout">
<a-select v-model="reqData.appId" @change="changeAppId" style="width:300px">
<a-select-option key="" >请选择应用APPID</a-select-option>
<a-select-option v-for="(item) in mchAppList" :key="item.appId" >{{ item.appName }} [{{ item.appId }}]</a-select-option>
</a-select>
</a-form-item>
</div>
</a-form>
<!-- 未配置支付方式提示框 -->
<a-divider v-if="!reqData.appId">请选择应用APPID</a-divider>
<a-divider v-else-if="ifCodeList.length == 0">该应用尚未配置任何通道</a-divider>
<div v-else>
<div style="width: 100%;" class="paydemo">
<div class="paydemo-type-content">
<div class="paydemo-type-name article-title" >选择通道</div>
<div class="paydemo-type-body">
<template v-for="(item) in ifCodeList">
<div :key="item.ifCode" :class="{ 'paydemo-type': true, 'color-change': true, 'this':(reqData.ifCode === item.ifCode) }" @click="changeCurrentIfCode(item.ifCode)">
<span class="color-change">{{ item.ifName }}</span>
</div>
</template>
</div>
</div>
<div class="paydemo-form-item">
<span>入账方式:</span>
<a-radio-group v-model="reqData.entryType" style="display:flex">
<div style="display:flex">
<a-radio value="WX_CASH" :disabled="reqData.ifCode != 'wxpay'" >微信零钱</a-radio>
<a-radio value="ALIPAY_CASH" :disabled="reqData.ifCode != 'alipay'">支付宝余额</a-radio>
<a-radio value="BANK_CARD" disabled >银行卡(暂未支持)</a-radio>
</div>
</a-radio-group>
</div>
<a-divider ></a-divider>
<!-- 订单信息 -->
<div class="paydemo-type-content">
<div class="paydemo-type-name article-title">转账信息</div>
<form class="layui-form">
<div class="paydemo-form-item">
<label>订单编号:</label><span id="payMchOrderNo">{{ reqData.mchOrderNo }}</span>
<span @click="randomOrderNo" class=" paydemo-btn" style="padding:0 3px">刷新订单号</span>
</div>
<div class="paydemo-form-item">
<span>转账金额(元):</span>
<a-input-number :max="100000" :min="0.01" v-model="reqData.amount" :precision="2" />
</div>
<div class="paydemo-form-item">
<span>收款账号:</span><a-input v-model="reqData.accountNo" style="width: 200px; margin-right: 10px"/>
<a-button v-show="reqData.entryType=='WX_CASH'" size="small" type="danger" @click="showChannelUserQR">自动获取openID</a-button>
</div>
<div style="margin-left: 10px; color: red">提示:【微信官方】需要填入对应应用收款方的openID</div>
<div style="margin-left: 10px; color: red"> 【支付宝官方】需要填入支付宝登录账号</div>
<div class="paydemo-form-item" style="margin-top: 10px">
<span>收款人姓名:</span><a-input v-model="reqData.accountName" style="width: 200px"/>
<div style="margin-left: 10px; color: red">提示: 填入则验证,否则不验证收款人姓名</div>
</div>
<div class="paydemo-form-item">
<span>转账备注:</span><a-input v-model="reqData.transferDesc" style="width: 200px"/>
</div>
<div style="margin-top:20px;text-align: left">
<a-button @click="immediatelyPay" style="padding:5px 20px;background-color: #1953ff;border-radius: 5px;color:#fff">立即转账</a-button>
</div>
</form>
</div>
</div>
</div>
</a-card>
<!-- 获取用户二维码 -->
<ChannelUserModal ref="channelUserModal" @changeChannelUserId="changeChannelUserIdFunc($event)"/>
</div>
</template>
<script>
import { API_URL_MCH_APP, req, queryMchTransferIfCode, doTransfer } from '@/api/manage' // 接口
import ChannelUserModal from '@/components/ChannelUser/ChannelUserModal'
export default {
components: { ChannelUserModal },
data () {
return {
ifCodeList: [], // 支持的支付接口
reqData: {
appId: '', // 已选择的appId
mchOrderNo: '', // 模拟商户订单号
ifCode: '', // 当前选择的支付接口
entryType: '', // 当前选择的入账方式
amount: 0.01, // 转账金额
accountNo: '', // 收款账号
accountName: '', // 收款人姓名
transferDesc: '打款' // 转账备注
},
mchAppList: [] // app列表
}
},
mounted () {
// 获取传入的参数,如果参数存在,则为appId 重新赋值
const appId = this.$route.params.appId
if (appId) {
this.reqData.appId = appId // appId赋值
this.changeAppId(appId)
}
const that = this // 提前保留this
// 请求接口,获取所有的appid,只有此处进行pageSize=-1传参
req.list(API_URL_MCH_APP, { pageSize: -1 }).then(res => {
that.mchAppList = res.records
if (that.mchAppList.length > 0) {
// 赋予默认值
that.reqData.appId = that.mchAppList[0].appId
// 根据不同的appId展示不同的支付方式
this.changeAppId(that.reqData.appId)
}
})
// 在进入页面时刷新订单号
this.randomOrderNo()
},
methods: {
// 变更 appId的事件
changeAppId (value) {
if (!value) {
this.ifCodeList = []
return false
}
const that = this
queryMchTransferIfCode(value).then(res => { // 查询所有的支付接口
that.ifCodeList = res
})
},
// 刷新订单号
randomOrderNo () {
this.reqData.mchOrderNo = 'M' + new Date().getTime() + Math.floor(Math.random() * (9999 - 1000) + 1000)
},
// 立即支付按钮
immediatelyPay () {
// 判断金额
if (!this.reqData.amount || this.reqData.amount <= 0) {
return this.$message.error('请输入转账金额')
}
if (!this.reqData.ifCode) {
return this.$message.error('请选择转账通道')
}
if (!this.reqData.entryType) {
return this.$message.error('请选择入账方式')
}
if (!this.reqData.accountNo) {
return this.$message.error('请输入收款账号')
}
if (!this.reqData.transferDesc) {
return this.$message.error('请输入转账备注')
}
const that = this
doTransfer(this.reqData).then(apiRes => {
that.randomOrderNo() // 刷新订单号
if (apiRes.state === 2) {
const succModal = that.$infoBox.modalSuccess('转账成功', <div>2s后自动关闭...</div>)
setTimeout(() => { succModal.destroy() }, 2000)
} else if (apiRes.state === 1) {
that.$infoBox.modalWarning('转账处理中', <div>请前往转账订单列表查看最终状态</div>)
} else if (apiRes.state === 3) {
that.$infoBox.modalError('转账处理失败', <div><div>错误码:{ apiRes.errCode}</div>
<div>错误信息:{ apiRes.errMsg}</div></div>)
} else {
return this.$message.error('转账异常')
}
}).catch(() => {
that.randomOrderNo() // 刷新订单号
})
},
// 切换ifCode
changeCurrentIfCode (ifCode) {
this.reqData.ifCode = ifCode
if (ifCode === 'wxpay') {
this.reqData.entryType = 'WX_CASH'
} else if (ifCode === 'alipay') {
this.reqData.entryType = 'ALIPAY_CASH'
} else {
this.reqData.entryType = '' // 入账方式清除
}
},
// 显示自动获取渠道用户ID的二维码地址
showChannelUserQR () {
this.$refs.channelUserModal.showModal(this.reqData.appId, this.reqData.ifCode) // 打开弹窗
},
// 更新账户
changeChannelUserIdFunc ({ channelUserId }) {
this.$message.success('成功获取渠道用户ID')
this.reqData.accountNo = channelUserId
}
}
}
</script>
<style scoped lang="css">
@import './MchTransferPage.css';
</style>
<template>
<div class="main">
<a-form class="user-layout-login" ref="formLogin" :form="form" @submit="handleSubmit">
<!-- 错误提示信息 -->
<a-alert v-if="showLoginErrorInfo" type="error" showIcon style="margin-bottom: 24px;" :message="showLoginErrorInfo" />
<a-form-item>
<a-input
@focus="usernameIcon = require('@/assets/svg/select-user.svg')"
@blur="usernameIcon = require('@/assets/svg/user.svg')"
size="large"
type="text"
placeholder="请输入账户"
v-decorator="[
'username',
{rules: [{ required: true, message: '请输入帐户名' }], validateTrigger: 'change'}
]"
>
<img :src="usernameIcon" slot="prefix" class="user" alt="user" >
</a-input>
</a-form-item>
<a-form-item>
<a-input-password
@focus="passwordIcon = require('@/assets/svg/select-lock.svg')"
@blur="passwordIcon = require('@/assets/svg/lock.svg')"
size="large"
placeholder="请输入密码"
v-decorator="[
'password',
{rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'change'}
]"
>
<!-- <a-icon src="../../assets/svg/user.svg" alt=""> -->
<img :src="passwordIcon" slot="prefix" class="user" alt="user">
</a-input-password>
</a-form-item>
<div class="code">
<a-form-item>
<a-input
@focus="vercodeIcon = require('@/assets/svg/select-code.svg')"
@blur="vercodeIcon = require('@/assets/svg/code.svg')"
class="code-input"
size="large"
type="text"
placeholder="请输入人机验证码"
v-decorator="[
'usercode',
{rules: [{ required: true, message: '请输入人机验证码' }], validateTrigger: 'blur'}
]"
>
<img :src="vercodeIcon" slot="prefix" class="user" alt="user" />
</a-input>
</a-form-item>
<div class="code-img" style="position: relative;background:#ddd">
<img v-show="vercodeImgSrc" :src="vercodeImgSrc" @click="refVercode()"/>
<div class="vercode-mask" v-show="isOverdue" @click="refVercode()">已过期 请刷新</div>
</div>
</div>
<a-form-item>
<!-- 自动登录 -->
<!-- <a-checkbox v-decorator="['rememberMe', { valuePropName: 'checked' }]">自动登录</a-checkbox> -->
<a-checkbox v-model="isAutoLogin">自动登录</a-checkbox>
<!-- 忘记密码 -->
<a class="forge-password" style="float: right;">忘记密码?</a>
</a-form-item>
<a-form-item class="submit">
<a-button
size="large"
type="primary"
htmlType="submit"
class="login-button"
:loading="loginBtnLoadingFlag"
>登录
</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import { timeFix } from '@/utils/util'
import { vercode } from '@/api/login'
export default {
components: {
},
data () {
return {
isOverdue: false, // 设置过期样式
isAutoLogin: true, // 是否是自动登录
loginBtnLoadingFlag: false, // 登录按钮是否显示 加载状态
showLoginErrorInfo: '', // 是否显示登录错误面板信息
form: this.$form.createForm(this),
usernameIcon: require('@/assets/svg/user.svg'), // 三个icon图标
passwordIcon: require('@/assets/svg/lock.svg'),
vercodeIcon: require('@/assets/svg/code.svg'),
vercodeImgSrc: '', // 验证码图片
vercodeToken: '' // 验证码验证token
}
},
created () {
},
mounted () {
this.refVercode()
},
methods: {
onChange (e) {
this.isAutoLogin = !this.isAutoLogin
console.log(this.isAutoLogin)
},
...mapActions(['Login', 'Logout']),
// handler
handleSubmit (e) {
e.preventDefault() // 通知 Web 浏览器不要执行与事件关联的默认动作
const that = this
that.form.validateFields({ force: true }, (err, values) => {
if (!err) {
const loginParams = { ...values }
loginParams.username = values.username
loginParams.password = values.password
loginParams.vercode = values.usercode
loginParams.vercodeToken = that.vercodeToken
that.loginBtnLoadingFlag = true // 登录按钮显示加载loading
that.Login({ loginParams: loginParams, isSaveStorage: that.isAutoLogin })
.then((res) => this.loginSuccess(res))
.catch(err => {
that.showLoginErrorInfo = (err.msg || JSON.stringify(err))
that.loginBtnLoadingFlag = false
})
}
})
},
loginSuccess (res) {
// this.$router.push({ path: '/' })
// 通过这里的传参 来得知是否点击自动登录按钮
this.$router.push({ path: '/', query: { isAutoLogin: this.isAutoLogin } })
// 延迟 1 秒显示欢迎信息
setTimeout(() => {
this.$notification.success({
message: '欢迎',
description: `${timeFix()},欢迎回来`
})
}, 1000)
this.showLoginErrorInfo = ''
},
refVercode () { // 刷新图片验证码
const that = this
// 获取图形验证码
vercode().then(res => {
that.vercodeImgSrc = res.imageBase64Data
that.vercodeToken = res.vercodeToken
this.isOverdue = false
if (this.timer) clearInterval(this.timer) // 如果多次点击则清除已有的定时器
// 超过60秒提示过期刷新
this.timer = setInterval(() => {
res.expireTime--
if (res.expireTime <= 0) {
that.isOverdue = true
clearInterval(this.timer)
}
}, 1000)
})
}
}
}
</script>
<style lang="less" scoped>
.user-layout-login {
label {
font-size: 14px;
}
.forge-password {
font-size: 14px;
color: @jee-theme;
}
button.login-button {
padding: 0 15px;
font-size: 16px;
height: 40px;
width: 100%;
}
.user-login-other {
text-align: left;
margin-top: 24px;
line-height: 22px;
.item-icon {
font-size: 24px;
color: rgba(0, 0, 0, 0.2);
margin-left: 16px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #1890ff;
}
}
.register {
float: right;
}
}
.code {
display: flex;
justify-content: space-between;
.code-input {
width: 216px;
}
.code-img {
width: 137px;
height: 40px;
background-color: #ddd;
img{
width: 137px;
height: 40px;
}
}
}
.submit {
margin-top: 50px;
}
}
.vercode-mask {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #000;
opacity: 0.8;
text-align:center;
line-height: 40px;
color:#fff;
&:hover {
cursor: pointer;
}
}
</style>
module.exports = {
env: {
jest: true
}
}
const path = require('path')
const webpack = require('webpack')
const GitRevisionPlugin = require('git-revision-webpack-plugin')
const GitRevision = new GitRevisionPlugin()
const buildDate = JSON.stringify(new Date().toLocaleString())
function resolve (dir) {
return path.join(__dirname, dir)
}
// check Git
function getGitHash () {
try {
return GitRevision.version()
} catch (e) { }
return 'unknown'
}
// const isProd = process.env.NODE_ENV === 'production'
// vue.config.js
const vueConfig = {
publicPath: process.env.VUE_APP_BASE_URL, // 前端资源访问根目录, 可配置到cdn目录下。 建议使用命令行环境变量进行替换。
configureWebpack: {
// webpack plugins
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.DefinePlugin({
APP_VERSION: `"${require('./package.json').version}"`,
GIT_HASH: JSON.stringify(getGitHash()),
BUILD_DATE: buildDate
})
],
// 代码调试
devtool: 'source-map',
// if prod, add externals
externals: {}
},
chainWebpack: (config) => {
config.resolve.alias
.set('@$', resolve('src'))
const svgRule = config.module.rule('svg')
svgRule.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
svgRule.uses.clear()
svgRule
.oneOf('inline')
.resourceQuery(/inline/)
.use('vue-svg-icon-loader')
.loader('vue-svg-icon-loader')
.end()
.end()
.oneOf('external')
.use('file-loader')
.loader('file-loader')
.options({
limit: 100000,
name: 'assets/[name].[hash:8].[ext]'
})
},
css: {
loaderOptions: {
less: {
modifyVars: {
'primary-color': '#1a53ff',
'link-color': '#1A79FF',
'border-radius-base': '4px'
},
javascriptEnabled: true
}
}
},
devServer: {
// development server port 8000
port: 8000
// If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
// proxy: {
// '/api': {
// target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
// ws: false,
// changeOrigin: true
// }
// }
},
// disable source map in production
productionSourceMap: false,
lintOnSave: undefined,
// babel-loader no-ignore node_modules/*
transpileDependencies: [],
pluginOptions: { // 第三方插件配置
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, 'src/assets/styles/color.less')] // less所在文件路径
}
}
}
module.exports = vueConfig
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment