Compare commits

...

78 Commits
v1.1.1 ... main

Author SHA1 Message Date
dingzhiwei 04e4a3f407 升级版本v2.2.2 2023-11-22 18:08:36 +08:00
terrfly 7400a45a4d 支付宝绑定分账用户去掉微信文字描述; 2023-10-07 09:07:43 +08:00
dingzhiwei a0f1f2ad94 升级版本v2.1.0 2023-04-27 14:58:35 +08:00
terrfly c0a90482c0 前端添加 分账已受理状态; 2023-03-29 14:08:13 +08:00
zhuxiao 24b70acb75 优化转账结果提示信息 2023-03-23 10:38:57 +08:00
dingzhiwei a5eab29e4a 升级版本v2.0.0 2023-03-19 16:01:21 +08:00
dingzhiwei 61dd91dbc2 修改页面内容显示 2023-03-19 14:48:38 +08:00
dingzhiwei 1c96e5e8f7 修改页面内容显示 2023-03-10 00:52:06 +08:00
dingzhiwei 25b6f2d3f1 修改页面内容显示 2023-02-19 22:02:13 +08:00
xiaoyu 6c511ac26c 支付接口页面样式调整 2023-02-16 09:23:37 +08:00
dingzhiwei 2e2a78dcb7 升级版本v1.15.0 2022-09-15 20:21:43 +08:00
dingzhiwei a4a5ef9bd0 升级版本v1.14.0 2022-07-08 18:42:36 +08:00
xiaoyu b6c9b5646a 退款订单列表搜索框描述修改;支付订单退款操作后默认刷新当前页 2022-07-05 09:56:41 +08:00
terrfly f9882824f9 生产打包改为相对路径; 2022-06-10 09:47:29 +08:00
大森林 af82979538
!2 添加 Docker 支持
Merge pull request !2 from 青木/dev
2022-03-30 15:08:33 +00:00
陈泉 eee7122e77 修复编译时候 Python 缺失问题 2022-03-30 17:31:19 +08:00
陈泉 6efb7c7893 添加 Docker 支持 2022-03-30 16:56:58 +08:00
dingzhiwei e20d7f8dda 升级版本v1.13.0 2022-03-29 18:47:53 +08:00
zhuxiao 148dd4f368 优化微信支付配置证书文件apiclient_cert.pem 2022-03-02 09:51:00 +08:00
dingzhiwei 817bbdf896 升级版本v1.12.0 2022-01-26 15:24:03 +08:00
terrfly 9faf41d174 维度 2022-01-25 18:00:32 +08:00
terrfly ebe38dabf9 重新分账; 2022-01-25 16:54:50 +08:00
zhuxiao 40c62219fc 优化支付宝支付参数必填项判断条件 2022-01-04 12:39:22 +08:00
dingzhiwei f48c063667 升级版本v1.11.0 2021-12-23 17:03:30 +08:00
dingzhiwei 6cb3dbf176 增加paypal支付方式 2021-12-17 18:06:38 +08:00
xiaoyu 2e23da7c66 商户端订单列表显示问题修复 2021-11-20 08:38:28 +08:00
xiaoyu 4cbd8a6f1c 转账订单列表显示问题优化(暂时) 2021-11-19 19:14:06 +08:00
xiaoyu 9111ce544e 转账订单列表显示问题优化 2021-11-19 18:48:27 +08:00
terrfly 600210db9d Merge branch 'dev' into main
# Conflicts:
#	jeepay-ui-manager/src/views/order/pay/PayOrderList.vue
#	jeepay-ui-manager/src/views/order/refund/RefundOrderList.vue
#	jeepay-ui-manager/src/views/order/transfer/TransferOrderList.vue
#	jeepay-ui-merchant/src/views/order/pay/PayOrderList.vue
#	jeepay-ui-merchant/src/views/order/refund/RefundOrderList.vue
#	jeepay-ui-merchant/src/views/order/transfer/TransferOrderList.vue
2021-11-19 18:12:04 +08:00
terrfly 7988d0086e 修改版本号; 2021-11-19 17:54:00 +08:00
terrfly ee67b98f2f rowKey 2021-11-19 14:35:33 +08:00
terrfly 6e775b7db7 rowKey 2021-11-19 14:34:42 +08:00
terrfly 6050d0b8ba scrollX 删除自定义, 改为统一配置 2021-11-19 14:29:20 +08:00
xiaoyu 046a840914 修复部分页面报错问题,订单页已关闭状态显示修改 2021-11-19 14:22:22 +08:00
terrfly 24ced13584 表格默认左侧10px, 表格内padding 8px 2021-11-19 14:10:55 +08:00
terrfly c9503665a8 表格默认左侧10px, 表格内padding 8px 2021-11-19 11:50:48 +08:00
terrfly a4f412d1c4 删除更多按钮的行高,与其他按钮保持一致; 2021-11-19 10:32:17 +08:00
xiaoyu fb0968d106 商户订单列表退款金额列表名称修改 2021-11-18 16:14:23 +08:00
xiaoyu 355d700046 订单列表显示优化 2021-11-18 15:09:41 +08:00
terrfly bb1ba49186 订单页面增加隔行换色效果; 其他页面保持不变; 2021-11-18 12:44:58 +08:00
xiaoyu 8dda8992f0 订单列插槽显示优化 2021-11-18 11:35:13 +08:00
xiaoyu 7ed6de4832 Merge remote-tracking branch 'origin/dev' into dev 2021-11-18 11:34:05 +08:00
terrfly 0b767f5bc3 调整左侧菜单宽度; 2021-11-18 11:30:03 +08:00
xiaoyu 96af114bc2 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	jeepay-ui-merchant/src/views/order/pay/PayOrderList.vue
2021-11-18 11:14:08 +08:00
terrfly 04fd2bb1c2 调整antdv 的table默认padding高度; 减少操作列的默认padding; 商户订单列表页增加退款金额的显示; 2021-11-18 11:11:30 +08:00
xiaoyu 975260e9c3 列表页部分优化 2021-11-18 11:11:25 +08:00
xiaoyu 6dfb18e03e 退款订单页增加订单号列宽 2021-11-17 17:24:01 +08:00
xiaoyu c2d9bbab0b 订单页订单号多合一;登录验证码超时;商户测试默认应用 2021-11-17 16:53:52 +08:00
xiaoyu f7734f4560 订单页订单号多合一;登录验证码超时;商户测试默认应用 2021-11-16 16:42:47 +08:00
dingzhiwei 0cd74cd00b 修改版本为1.9.0 2021-09-30 19:43:49 +08:00
terrfly bd65e46249 v1.8.0 2021-09-10 16:24:26 +08:00
terrfly 95d3fd7a8f 添加商户系统的退款功能权限配置项 2021-09-10 14:41:35 +08:00
terrfly b7b694434d 1.7.0 2021-08-30 14:46:54 +08:00
terrfly 217cf81d88 v1.7.0 2021-08-27 15:48:42 +08:00
terrfly 7a24c6451e 更改分账字段; 2021-08-27 11:15:12 +08:00
terrfly c44723198d 完成订单的分账逻辑 2021-08-26 16:58:18 +08:00
terrfly f538736894 订单页的支付方式筛选项添加权限并可分配: 避免API权限导致页面出现异常 2021-08-18 11:20:12 +08:00
terrfly 67cc36a050 1.6.0 2021-08-16 19:06:31 +08:00
terrfly 9ca73eaf70 商户系统转账功能; 2021-08-13 22:10:10 +08:00
terrfly cae3570582 转账订单列表; 2021-08-13 11:46:18 +08:00
dingzhiwei 3a9e9ba00b Merge remote-tracking branch 'origin/dev' into dev 2021-07-28 11:38:09 +08:00
dingzhiwei 76159154e0 修改日志输出版本及域名 2021-07-28 11:38:00 +08:00
terrfly 554c12ccb3 同步版本号v1.5.0 2021-07-27 18:13:14 +08:00
terrfly 189979a5b7 修改错误提示信息; 2021-07-27 17:12:49 +08:00
zhuxiao 887f6c9b4b 优化支付宝支付参数必填项判断条件 2021-07-27 14:08:44 +08:00
zhuxiao 139546193b 新增支付宝支付参数自定义配置 2021-07-27 11:15:34 +08:00
xiaoyu e019d0a5b4 修复:1-应用配置单选回显问题 2-退款订单查询条件不生效问题 2021-07-23 17:52:04 +08:00
xiaoyu 28fada2229 商户、服务商页面rowKey问题 2021-07-20 12:18:12 +08:00
terrfly da06c75a92 修改版本号; 2021-07-19 18:00:08 +08:00
terrfly f802fa0f78 商户系统扫码完成授权; 2021-07-16 16:15:05 +08:00
terrfly 69ad996ed9 支付宝子商户 扫码授权 2021-07-16 15:05:19 +08:00
terrfly 559034b85e 默认不再使用静态资源CDN服务; 2021-07-16 15:03:38 +08:00
zhuxiao fef496cefe 新增支付参数自定义配置和敏感数据脱敏 2021-07-15 15:28:43 +08:00
xiaoyu bb6c8c539b Merge remote-tracking branch 'origin/dev' into dev 2021-07-09 11:04:42 +08:00
xiaoyu bde10e70ed 退款订单状态 4->订单任务关闭 2021-07-09 11:04:34 +08:00
ssyang1993 237f1e082e 1. 若无可退款金额,则不显示退款按钮 2021-07-02 17:31:15 +08:00
ssyang1993 8fa35952f9 1. 解决应用列表,因重复 key 产生的报错问题 2021-07-02 10:01:23 +08:00
ssyang1993 84619cc27f 1. 没有退款按钮,则添加占位符,使详情按钮左对齐 2021-07-01 09:30:37 +08:00
83 changed files with 53726 additions and 800 deletions

1
.gitignore vendored
View File

@ -53,3 +53,4 @@ unpackage/
# [package-lock.json。 已放开,请添加到假定未更改文件 ]
# package-lock.json
/.history/

47
Dockerfile Normal file
View File

@ -0,0 +1,47 @@
# syntax = docker/dockerfile:experimental
# 使用了 Docker 特性 Buildx 请开启相关特性
FROM node:16-alpine AS builder
ARG PLATFORM=$PLATFORM
WORKDIR /workspace
COPY . /workspace
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk update
RUN apk add --no-cache --virtual .build-deps alpine-sdk python3
RUN npm config set registry https://registry.npmmirror.com
RUN cd /workspace/jeepay-ui-${PLATFORM} && npm install && npm run build
FROM nginx:alpine
ARG PLATFORM=$PLATFORM
ENV BACKEND_HOST $BACKEND_HOST
WORKDIR /workspace
COPY --from=builder /workspace/jeepay-ui-${PLATFORM}/dist /workspace
RUN chmod a+r /workspace
RUN rm -rf /etc/nginx/conf.d/default.conf
COPY --from=builder /workspace/default.conf.template /etc/nginx/templates/default.conf.template
# 编译命令
# docker buildx build . --build-arg PLATFORM=cashier -t jeepay-payment:latest
# docker buildx build . --build-arg PLATFORM=manager -t jeepay-manager:latest
# docker buildx build . --build-arg PLATFORM=merchant -t jeepay-merchant:latest
#
# 如果你需要多平台镜像,你可以使用 --platform linux/amd64,linux/arm64
# 比如 docker buildx build . --build-arg PLATFORM=cashier -t jeepay-ui-cashier:latest --platform linux/amd64,linux/arm64
#
# 启动命令
# docker run -d -p 9226:80 -e BACKEND_HOST=172.20.0.9216 jeepay-ui-cashier:latest
# docker run -d -p 9227:80 -e BACKEND_HOST=172.20.0.9217 jeepay-ui-manager:latest
# docker run -d -p 9228:80 -e BACKEND_HOST=172.20.0.9218 jeepay-ui-merchant:latest

156
README.md
View File

@ -1,152 +1,42 @@
<p align="center">
<a href="https://www.jeepay.vip"><img src="https://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_logo.svg"></a>
</p>
<p align="center">
<strong>适合互联网企业使用的开源支付系统</strong>
</p>
<p align="center">
👉 <a href="https://www.jeepay.vip">https://www.jeepay.vip</a> 👈
</p>
# Jeepay UI
为Jeepay对应的前端项目包括运营平台、商户系统、聚合码收银台。前端技术以vue为主框架使用Ant Design Vue开发。
<p align="center">
<a target="_blank" href="https://spring.io/projects/spring-boot">
<img src="https://img.shields.io/badge/spring%20boot-2.4.5-yellowgreen" />
</a>
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
<img src="https://img.shields.io/badge/JDK-8+-green.svg" />
</a>
<a target="_blank" href="http://www.gnu.org/licenses/lgpl.html">
<img src="https://img.shields.io/badge/license-LGPL--3.0-blue" />
</a>
<a href='https://gitee.com/jeequan/jeepay/stargazers' target="_blank">
<img src='https://gitee.com/jeequan/jeepay/badge/star.svg?theme=gvp' alt='star'></img>
</a>
<a target="_blank" href='https://github.com/jeequan/jeepay'>
<img src="https://img.shields.io/github/stars/jeequan/jeepay.svg?style=social" alt="github star"/>
</a>
</p>
Java服务端项目https://gitee.com/jeequan/jeepay
<br/>
<p align="center">
<a href="https://jq.qq.com/?_wv=1027&k=94WnXmdL">
<img src="https://img.shields.io/badge/qq%E7%BE%A4%E2%91%A0-635647058-critical"/>
</a>
</p>
-------------------------------------------------------------------------------
## 📚 项目介绍
Jeepay是一套适合互联网企业使用的开源支付系统支持多渠道服务商和普通商户模式。已对接`微信支付``支付宝``云闪付`官方接口,支持聚合码支付。
Jeepay使用`Spring Boot`和`Ant Design Vue`开发,集成`Spring Security`实现权限管理功能是一套非常实用的web开发框架。
### 🎁 名称的由来
Jeepay = Jee + pay是由原XxPay支付系统作者带领团队开发“Jee”是公司计全科技名称的表示pay表示支付。中文名称为计全支付释为计出万全、支付安全让支付更加方便安全。
### 🍟 项目体验
- Jeepay支付流程体验[https://www.jeequan.com/demo/jeepay_cashier.html](https://www.jeequan.com/demo/jeepay_cashier.html "Jeepay支付体验")
- Jeepay运营平台和商户系统演体验[https://www.jeequan.com/doc/detail_84.html](https://www.jeequan.com/doc/detail_84.html "Jeepay支付系统体验")
- Jeepay项目文档[https://www.jeepay.vip](https://www.jeepay.vip "Jeepay项目文档")
### 🍎 项目特点
* 支持多渠道对接,支付网关自动路由
* 已对接`微信`服务商和普通商户接口,支持`V2`和`V3`接口
* 已对接`支付宝`服务商和普通商户接口支持RSA和RSA2签名
* 已对接`云闪付`服务商接口,可选择多家支付机构
* 提供http形式接口提供各语言的`sdk`实现,方便对接
* 接口请求和响应数据采用签名机制,保证交易安全可靠
* 系统安全,支持`分布式`部署,`高并发`
* 管理端包括`运营平台`和`商户系统`
* 管理平台操作界面简洁、易用
* 支付平台到商户系统的订单通知使用MQ实现保证了高可用消息可达
* 支付渠道的接口参数配置界面自动化生成
* 使用`spring security`实现权限管理
* 前后端分离架构,方便二次开发
* 由原`XxPay`团队开发,有着多年支付系统开发经验
## 🥞 系统架构
> Jeepay计全支付系统架构图
![Jeepay系统架构图](https://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_framework.png "Jeepay系统架构图")
> 核心技术栈
| 软件名称 | 描述 | 版本
|---|---|---
|Jdk | Java环境 | 1.8
|Spring Boot | 开发框架 | 2.4.5
|Redis | 分布式缓存 | 3.2.8 或 高版本
|MySQL | 数据库 | 5.7.X
|ActiveMQ | 消息中间件 | 5.15.8 或 高版本
|[Ant Design Vue](https://www.antdv.com/docs/vue/introduce-cn/) | Ant Design的Vue实现前端开发使用 | 2.1.2
|[MyBatis-Plus](https://mp.baomidou.com/) | MyBatis增强工具 | 3.4.2
|[WxJava](https://gitee.com/binary/weixin-java-tools) | 微新开发Java SDK | 4.0.0
|[Hutool](https://www.hutool.cn/) | Java工具类库 | 5.6.6
> 项目结构
> 目录结构
```lua
jeepay -- https://gitee.com/jeequan/jeepay
jeepay-ui
├── jeepay-ui-manager -- 运营平台前端vue代码
├── jeepay-ui-merchant -- 商户系统前端vue代码
└── jeepay-ui-cashier -- 支付收银台vue代码
├── jeepay-ui-cashier -- 聚合收银台项目
├── jeepay-ui-manager -- 运营平台web管理端
└── jeepay-ui-merchant -- 商户系统web管理端
```
> 参考命令
> 开发部署
node版本要求`<= 16 `
- 系统开发:[https://www.jeepay.vip/#/develop/dev_serv](https://www.jeepay.vip/#/develop/dev_serv)
- 通道对接:[https://www.jeepay.vip/#/develop/dev_channel](https://www.jeepay.vip/#/develop/dev_channel)
- 线上部署:[https://www.jeepay.vip/#/develop/deploy](https://www.jeepay.vip/#/develop/deploy)
- 接口文档:[https://www.jeepay.vip/#/interface/payment_api](https://www.jeepay.vip/#/interface/payment_api)
#### 参考命令
## 🍿 功能模块
``` bash
# 拉取源码完毕后请先安装依赖, 进入项目根目录命令行执行:
> Jeepay运营平台功能
> npm install
![Jeepay运营平台功能](https://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_mgr.png "Jeepay运营平台功能")
# 本地启动项目(开发环境):
> Jeepay商户系统功能
1. 打开根目录下文件".env.development", 修改请求服务器地址"VUE_APP_API_BASE_URL"
![Jeepay商户系统功能](https://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_mch.png "Jeepay商户系统功能")
2. 在项目根目录命令行执行:
## 🍯 系统截图
> npm run serve
`以下截图是从实际已完成功能界面截取,截图时间为2021-05-29 02:05`
# 打包(生产环境):
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/001.png "Jeepay演示界面")
1. 打开根目录下文件".env", 修改请求服务器地址"VUE_APP_API_BASE_URL"
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/002.png "Jeepay演示界面")
2. 在项目根目录命令行执行:
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/005.png "Jeepay演示界面")
> npm run build
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/006.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/009.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/010.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/011.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/012.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/013.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/014.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/015.png "Jeepay演示界面")
![Jeepay演示界面](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/yanshi/022.png "Jeepay演示界面")
## 🥪 关于我们
***
微信扫描下面二维码,关注官方公众号:计全科技,获取更多精彩内容。
![计全科技公众号](http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jee-qrcode.jpg "计全科技公众号")
3. 文件将输出到 [/dist]目录, 拷贝到web服务器即可。
```

52
default.conf.template Normal file
View File

@ -0,0 +1,52 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
root /workspace/;
try_files $uri $uri/ /index.html;
location /api/ {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_pass http://$BACKEND_HOST;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}
# robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
}
# assets, media
location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
expires 7d;
access_log off;
}
# svg, fonts
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
add_header Access-Control-Allow-Origin "*";
expires 7d;
access_log off;
}
# gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
}

View File

@ -1,3 +1,3 @@
NODE_ENV=production
VUE_APP_BASE_URL=/
VUE_APP_BASE_URL=
VUE_APP_API_BASE_URL=

View File

@ -1,6 +1,6 @@
{
"name": "jeepay-ui-cashier",
"version": "0.1.0",
"version": "1.10.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1949,6 +1949,44 @@
"integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.nlark.com/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1618995547052&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.nlark.com/chalk/download/chalk-4.1.2.tgz?cache=0&sync_timestamp=1627646734234&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fchalk%2Fdownload%2Fchalk-4.1.2.tgz",
"integrity": "sha1-qsTit3NKdAhnrrFr8CqtVWoeegE=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.nlark.com/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.nlark.com/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"debug": {
"version": "4.3.1",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-4.3.1.tgz?cache=0&sync_timestamp=1607566551397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.3.1.tgz",
@ -1975,6 +2013,23 @@
"integrity": "sha1-/wQLKwhTsjw9MQJ1I3BvGIXXa+4=",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.nlark.com/has-flag/download/has-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"json5": {
"version": "2.2.0",
"resolved": "https://registry.nlark.com/json5/download/json5-2.2.0.tgz",
"integrity": "sha1-Lf7+cgxrpSXZ69kJlQ8FFTFsiaM=",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz?cache=0&sync_timestamp=1604161937969&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsonfile%2Fdownload%2Fjsonfile-4.0.0.tgz",
@ -1984,6 +2039,18 @@
"graceful-fs": "^4.1.6"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&sync_timestamp=1607433856030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz",
@ -1998,6 +2065,28 @@
"requires": {
"minipass": "^3.1.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.nlark.com/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1626703414084&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.6.0",
"resolved": "https://registry.nlark.com/vue-loader/download/vue-loader-16.6.0.tgz?cache=0&sync_timestamp=1632217703123&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvue-loader%2Fdownload%2Fvue-loader-16.6.0.tgz",
"integrity": "sha1-B03Rs97jCTfQpawi3GP+86nPoV8=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
}
}
},
@ -14254,97 +14343,6 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.2.0",
"resolved": "https://registry.nlark.com/vue-loader/download/vue-loader-16.2.0.tgz?cache=0&sync_timestamp=1620717743226&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvue-loader%2Fdownload%2Fvue-loader-16.2.0.tgz",
"integrity": "sha1-BGpTMI3Ufljv4g3ewe3sAnzjtG4=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.nlark.com/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1618995547052&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.1",
"resolved": "https://registry.nlark.com/chalk/download/chalk-4.1.1.tgz?cache=0&sync_timestamp=1618995354302&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fchalk%2Fdownload%2Fchalk-4.1.1.tgz",
"integrity": "sha1-yAs/qyi/Y3HmhjMl7uZ+YYt35q0=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1618559744568&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"json5": {
"version": "2.2.0",
"resolved": "https://registry.npm.taobao.org/json5/download/json5-2.2.0.tgz",
"integrity": "sha1-Lf7+cgxrpSXZ69kJlQ8FFTFsiaM=",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.nlark.com/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1622293630895&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-router": {
"version": "3.5.1",
"resolved": "https://registry.nlark.com/vue-router/download/vue-router-3.5.1.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "jeepay-ui-cashier",
"version": "1.1.1",
"version": "1.10.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

View File

@ -35,15 +35,15 @@ router.beforeEach((to, from, next) => {
}
if(!config.cacheToken) {
next({ name: config.errorPageRouteName, params: { errInfo: "token参数有误" } })
next({ name: config.errorPageRouteName, params: { errInfo: "请通过二维码进入支付页面" } })
return false;
}
//获取不到支付类型, 需要跳转到错误页面
if( ! wayCode.getPayWay() ) {
next({ name: config.errorPageRouteName, params: { errInfo: "不支持的支付方式" } })
return false;
}
//获取不到支付类型, 需要跳转到错误页面
if( ! wayCode.getPayWay() ) {
next({ name: config.errorPageRouteName, params: { errInfo: "不支持的支付方式 请在微信/支付宝/银联应用内扫码进入" } })
return false;
}
next()
})

View File

@ -1,7 +1,7 @@
<template>
<div class="center">
<img src="../assets/icon/error.svg" alt="">
<p>支付失败请重新扫码进入</p>
<!-- <p>支付失败请重新扫码进入</p>-->
<p>错误: {{msg}}</p>
</div>
</template>
@ -20,7 +20,7 @@ export default {
},
mounted() {
this.msg = this.$route.params.errInfo
this.msg = this.$route.params.errInfo || '请重新扫码进入!'
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "jeepay-ui-manager",
"version": "1.1.1",
"version": "1.10.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

View File

@ -97,6 +97,8 @@ export const API_URL_ISV_PAYCONFIGS_LIST = '/api/isv/payConfigs'
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 upload = {
@ -291,3 +293,11 @@ export function mchNotifyResend (notifyId) {
method: 'POST'
})
}
/** 查询支付宝授权地址URL **/
export function queryAlipayIsvsubMchAuthUrl (mchAppId) {
return request.request({
url: '/api/mch/payConfigs/alipayIsvsubMchAuthUrls/' + mchAppId,
method: 'GET'
})
}

View File

@ -3,7 +3,7 @@
<template v-slot:links>
</template>
<template v-slot:copyright>
<a href="http://www.jeequan.com" target="_blank">@计全科技</a>
Copyright © 2023 <a href="http://www.jeequan.com" target="_blank">jeequan.com</a>. All rights reserved.
</template>
</global-footer>
</template>

View File

@ -117,7 +117,7 @@ export default {
}
.jeepay-card-add-text {
padding-top: 5px;
font-size: 16px;
font-size: 13px;
color: rgba(0, 0, 0, 0.35);
}
</style>

View File

@ -16,6 +16,12 @@
: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 -->
@ -41,7 +47,8 @@ export default {
pageSize: { type: Number, default: 10 }, //
rowSelection: Object, // checkbox
rowKey: { type: [String, Function] }, // rowKey checkbox
scrollX: { type: Number, default: 800 } //
scrollX: { type: Number, default: 500 }, //
tableRowCrossColor: { type: Boolean, default: false } //
},
data () {
@ -117,3 +124,23 @@ export default {
}
}
</script>
<style lang="less">
// antdv tablepadding
.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>

View File

@ -31,7 +31,7 @@ export default {
}
return <div style="display:flex; justify-content: space-evenly;"> {firstEL}
<a-dropdown>
<a-button class="ant-dropdown-link" type="link" style="line-height:32px">更多<a-icon type="down" /></a-button>
<a-button class="ant-dropdown-link" type="link" style="">更多<a-icon type="down" /></a-button>
<a-menu slot="overlay">
{menuEL}
</a-menu>
@ -41,3 +41,9 @@ export default {
}
}
</script>
<style lang="less" scoped>
// padding
button { padding: 8px !important;}
</style>

View File

@ -31,6 +31,7 @@ export const asyncRouteDefine = {
'MchAppPage': { defaultPath: '/apps', component: () => import ('@/views/mchApp/List') }, // 商户应用列表
'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') }, // 转账订单
'MchNotifyListPage': { defaultPath: '/notify', component: () => import('@/views/order/notify/MchNotifyList') }, // 商户通知列表
'SysConfigPage': { defaultPath: '/config', component: () => import('@/views/sys/config/SysConfig') }, // 系统配置
'SysLogPage': { defaultPath: '/log', component: () => import('@/views/sys/log/SysLog') } // 系统日志

View File

@ -9,6 +9,7 @@
:i18nRender="false"
v-bind="settings"
:breadcrumbRender="handleBreadcrumbRender"
:siderWidth="210"
>
<!-- 1.0.0+ 版本 pro-layout 提供 API
我们推荐使用这种方式进行 LOGO title 自定义
@ -34,6 +35,8 @@
<template v-slot:rightContentRender>
<right-content :top-menu="settings.layout === 'topmenu'" :is-mobile="isMobile" :theme="settings.theme" />
<a style="color: red; float: right; padding-right: 10px;" href="https://www.jeequan.com/product/jeepay4plus.html" target="_blank">Plus商业版</a>
<a style="color: red; float: right; padding-right: 10px;" href="https://www.jeequan.com/ifstore/list.html" target="_blank">接口市场</a>
</template>
<!-- custom footer / 自定义Footer -->
<template v-slot:footerRender>

View File

@ -15,6 +15,7 @@ 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.config.productionTip = false
@ -23,6 +24,7 @@ Vue.config.productionTip = false
Vue.component('pro-layout', ProLayout)
Vue.component('page-container', PageHeaderWrapper)
Vue.component('page-header-wrapper', PageHeaderWrapper)
Vue.use(VueClipboard) // 复制插件
/**
* @description 全局注册权限验证

View File

@ -7,8 +7,8 @@ export const printANSI = () => {
/ /_/ // __/ __/ /_/ / /_/ / /_/ /
\\____/ \\___/\\___/ .___/\\__,_/\\__, /
/_/ /____/
:: Jeepay :: (v1.0.0.RELEASE)
适合互联网企业使用的开源支付系统 : https://www.jeepay.vip
:: Jeepay :: (v2.2.2.RELEASE)
让支付接入更简单 : https://www.jeequan.com
`
console.log(`%c${text}`, 'color: #fc4d50')

View File

@ -32,8 +32,7 @@
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
:scrollX="1100"
rowKey="isvName"
rowKey="isvNo"
>
<template slot="isvNameSlot" slot-scope="{record}"><b>{{ record.isvName }}</b></template> <!-- 自定义插槽 -->
<template slot="stateSlot" slot-scope="{record}">

View File

@ -79,7 +79,8 @@
<a-row :gutter="16">
<a-col v-for="(item, key) in isvParams" :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-model="ifParams[item.name]" placeholder="请输入" :type="item.type" />
<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]">
@ -113,19 +114,26 @@
</div>
</a-drawer>
<!-- 支付参数配置页面组件 -->
<WxpayPayConfig ref="wxpayPayConfig" :callbackFunc="refCardList" />
<!-- 支付参数配置页面组件 -->
<AlipayPayConfig ref="alipayPayConfig" :callbackFunc="refCardList" />
</a-drawer>
</template>
<script>
import JeepayCard from '@/components/JeepayCard/JeepayCard'
import JeepayUpload from '@/components/JeepayUpload/JeepayUpload'
import WxpayPayConfig from './custom/WxpayPayConfig'
import AlipayPayConfig from './custom/AlipayPayConfig'
import { API_URL_ISV_PAYCONFIGS_LIST, getIsvPayConfigUnique, req, upload } from '@/api/manage'
export default {
components: {
JeepayCard,
JeepayUpload
JeepayUpload,
WxpayPayConfig,
AlipayPayConfig
},
data () {
return {
@ -149,13 +157,18 @@ export default {
ifParamsRules: {}
}
},
watch: {
ifParams: function (o, n) {
this.$set(this.ifParams, 'appSecret', this.ifParams.appSecret) // appSecret
}
},
methods: {
generoterRules () {
const rules = {}
let newItems = []
this.isvParams.forEach(item => {
newItems = []
if (item.verify === 'required') {
if (item.verify === 'required' && item.star !== '1') {
newItems.push({
required: true,
message: '请输入' + item.desc,
@ -183,56 +196,75 @@ export default {
},
//
editPayIfConfigFunc (record) {
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
if (this.$refs.isvParamFormModel !== undefined) {
this.$refs.isvParamFormModel.resetFields()
}
this.childrenVisible = true //
this.saveObject = {} //
this.ifParams = {} //
this.isvParams = {} //
this.saveObject.infoId = this.isvNo
this.saveObject.ifCode = record.ifCode
this.saveObject.state = record.ifConfigState === 0 ? 0 : 1
if (record.configPageType === 1) { // JSON
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
if (this.$refs.isvParamFormModel !== undefined) {
this.$refs.isvParamFormModel.resetFields()
}
this.childrenVisible = true //
this.saveObject = {} //
this.ifParams = {} //
this.isvParams = {} //
this.saveObject.infoId = this.isvNo
this.saveObject.ifCode = record.ifCode
this.saveObject.state = record.ifConfigState === 0 ? 0 : 1
if (!record) {
return
}
//
getIsvPayConfigUnique(this.saveObject.infoId, this.saveObject.ifCode).then(res => {
if (!res || !res.ifParams) {
if (!record) {
return
}
this.saveObject = res
this.ifParams = JSON.parse(res.ifParams)
})
const newItems = [] // json
JSON.parse(record.isvParams).forEach(item => {
const radioItems = [] // value title
if (item.type === 'radio') {
const valueItems = item.values.split(',')
const titleItems = item.titles.split(',')
for (const i in valueItems) {
radioItems.push({
value: valueItems[i],
title: titleItems[i]
})
const that = this
//
getIsvPayConfigUnique(this.saveObject.infoId, this.saveObject.ifCode).then(res => {
if (res && res.ifParams) {
this.saveObject = res
this.ifParams = JSON.parse(res.ifParams)
}
}
newItems.push({
name: item.name,
desc: item.desc,
type: item.type,
verify: item.verify,
values: radioItems
})
})
this.isvParams = newItems //
this.generoterRules()
const newItems = [] // json
JSON.parse(record.isvParams).forEach(item => {
const radioItems = [] // value title
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] : '请输入'
if (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.isvParams = newItems //
that.generoterRules()
that.$forceUpdate()
})
} else if (record.configPageType === 2) { // customif_code + PayConfig
this.$refs[record.ifCode + 'PayConfig'].show(this.isvNo, record)
}
},
//
onSubmit () {
@ -252,6 +284,13 @@ export default {
this.$message.error('参数不能为空!')
return
}
// key
this.isvParams.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)
//
req.add(API_URL_ISV_PAYCONFIGS_LIST, reqParams).then(res => {

View File

@ -0,0 +1,312 @@
<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="ifRate">
<a-input v-model="saveObject.ifRate" placeholder="请输入" suffix="%" />
</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="备注" 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="isvParamFormModel" :model="ifParams" layout="vertical" :rules="ifParamsRules">
<a-row :gutter="16">
<a-col span="24">
<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="合作伙伴身份PID" prop="pid">
<a-input v-model="ifParams.pid" 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="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-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_ISV_PAYCONFIGS_LIST, req, getIsvPayConfigUnique, upload } from '@/api/manage'
export default {
components: {
JeepayCard,
JeepayUpload
},
props: {
callbackFunc: { type: Function, default: () => ({}) }
},
data () {
return {
btnLoading: false,
visible: false, //
isAdd: true,
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: {
pid: [{ required: true, message: '请输入合作伙伴身份PID', trigger: 'blur' }],
appId: [{ required: true, message: '请输入应用AppID', trigger: 'blur' }],
privateKey: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.isAdd && !value) {
callback(new Error('请输入应用私钥'))
}
callback()
} }],
alipayPublicKey: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.useCert === 0 && this.isAdd && !value) {
callback(new Error('请输入支付宝公钥'))
}
callback()
} }],
appPublicCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.useCert === 1 && !this.ifParams.appPublicCert) {
callback(new Error('请上传应用公钥证书(.crt格式'))
}
callback()
} }],
alipayPublicCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.useCert === 1 && !this.ifParams.alipayPublicCert) {
callback(new Error('请上传支付宝公钥证书(.crt格式'))
}
callback()
} }],
alipayRootCert: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.useCert === 1 && !this.ifParams.alipayRootCert) {
callback(new Error('请上传支付宝根证书(.crt格式'))
}
callback()
} }]
}
}
},
methods: {
//
show: function (isvNo, record) {
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
if (this.$refs.isvParamFormModel !== undefined) {
this.$refs.isvParamFormModel.resetFields()
}
//
this.saveObject = {
infoId: isvNo,
ifCode: record.ifCode,
state: record.ifConfigState === 0 ? 0 : 1
}
//
this.ifParams = {
sandbox: 0,
signType: 'RSA2',
useCert: 0,
privateKey: '',
privateKey_ph: '请输入',
alipayPublicKey: '',
alipayPublicKey_ph: '请输入'
}
this.visible = true
this.getIsvPayConfig()
},
//
getIsvPayConfig () {
const that = this
//
getIsvPayConfigUnique(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.isvParamFormModel.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_ISV_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
},
// valuename
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,335 @@
<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="ifRate">
<a-input v-model="saveObject.ifRate" placeholder="请输入" suffix="%" />
</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="备注" 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="isvParamFormModel" :model="ifParams" layout="vertical" :rules="ifParamsRules">
<a-row :gutter="16">
<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-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_ISV_PAYCONFIGS_LIST, req, getIsvPayConfigUnique, upload } from '@/api/manage'
export default {
components: {
JeepayCard,
JeepayUpload
},
props: {
callbackFunc: { type: Function, default: () => ({}) }
},
data () {
return {
btnLoading: false,
visible: false, //
isAdd: true,
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: [{ required: true, message: '请输入微信支付商户号', trigger: 'blur' }],
appId: [{ required: true, message: '请输入应用AppID', trigger: 'blur' }],
appSecret: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.isAdd && !value) {
callback(new Error('请输入应用AppSecret'))
}
callback()
} }],
key: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V2' && this.isAdd && !value) {
callback(new Error('请输入API密钥'))
}
callback()
} }],
apiV3Key: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.isAdd && !value) {
callback(new Error('请输入API V3秘钥'))
}
callback()
} }],
serialNo: [{ trigger: 'blur',
validator: (rule, value, callback) => {
if (this.ifParams.apiVersion === 'V3' && this.isAdd && !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' && !value) {
callback(new Error('请上传私钥文件(apiclient_key.pem)'))
}
callback()
} }]
}
}
},
methods: {
//
show: function (isvNo, record) {
if (this.$refs.infoFormModel !== undefined) {
this.$refs.infoFormModel.resetFields()
}
if (this.$refs.isvParamFormModel !== undefined) {
this.$refs.isvParamFormModel.resetFields()
}
//
this.saveObject = {
infoId: isvNo,
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.getIsvPayConfig()
},
//
getIsvPayConfig () {
const that = this
//
getIsvPayConfigUnique(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.isvParamFormModel.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_ISV_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
},
// valuename
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -40,8 +40,7 @@
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
:scrollX="1200"
rowKey="mchName"
rowKey="mchNo"
>
<template slot="mchNameSlot" slot-scope="{record}">
<b v-if="!$access('ENT_MCH_INFO_VIEW')">{{ record.mchName }}</b>

View File

@ -38,7 +38,7 @@
</a-col>
<a-col :span="24">
<a-form-model-item label="私钥 AppSecret" prop="appSecret" >
<a-input v-model="saveObject.appSecret" placeholder="请输入" type="textarea" />
<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>
@ -74,8 +74,7 @@ export default {
saveObject: {}, //
rules: {
mchNo: [{ required: true, message: '请输入商户号', trigger: 'blur' }],
appName: [{ required: true, message: '请输入应用名称', trigger: 'blur' }],
appSecret: [{ required: true, message: '请输入私钥或点击随机生成私钥' }]
appName: [{ required: true, message: '请输入应用名称', trigger: 'blur' }]
}
}
},
@ -87,7 +86,8 @@ export default {
this.saveObject = {
'state': 1,
'appSecret': '',
'mchNo': mchNo
'mchNo': mchNo,
'appSecret_ph': '请输入'
}
if (this.$refs.infoFormModel !== undefined) {
@ -95,17 +95,25 @@ export default {
}
const that = this
that.rules.appSecret = []
if (!this.isAdd) { //
that.appId = appId
//
req.getById(API_URL_MCH_APP, appId).then(res => {
that.saveObject = res
if (!that.saveObject.appSecret) { //
that.saveObject.appSecret = ''
}
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 //
}
},
@ -114,6 +122,7 @@ export default {
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 => {
@ -122,6 +131,9 @@ export default {
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

View File

@ -0,0 +1,60 @@
<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>

View File

@ -34,8 +34,7 @@
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
:scrollX="1200"
rowKey="mchName"
rowKey="appId"
>
<template slot="appIdSlot" slot-scope="{record}">
<b>{{ record.appId }}</b>

View File

@ -10,11 +10,6 @@
>
<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="ifRate">
<a-input v-model="saveObject.ifRate" placeholder="请输入" suffix="%" />
</a-form-model-item>
</a-col>
<a-col :span="12">
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
@ -43,7 +38,7 @@
<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-model="ifParams[item.name]" placeholder="请输入" :type="item.type" />
<a-input v-model="ifParams[item.name]" :placeholder="item.star === '1' ? ifParams[item.name + '_ph'] : '请输入'" :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]">
@ -101,12 +96,17 @@ export default {
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' }]
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) {
@ -133,35 +133,47 @@ export default {
that.saveObject = res
that.ifParams = JSON.parse(res.ifParams)
}
})
const newItems = [] // json
let radioItems = [] // value title
const mchParams = this.mchType === 1 ? record.normalMchParams : record.isvsubMchParams //
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) {
radioItems.push({
value: valueItems[i],
title: titleItems[i]
})
const newItems = [] // json
let radioItems = [] // value title
const mchParams = this.mchType === 1 ? record.normalMchParams : record.isvsubMchParams //
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]
})
}
}
}
newItems.push({
name: item.name,
desc: item.desc,
type: item.type,
verify: item.verify,
values: radioItems
})
})
that.mchParams = newItems //
that.visible = true //
that.generoterRules()
if (item.star === '1') {
that.ifParams[item.name + '_ph'] = that.ifParams[item.name] ? that.ifParams[item.name] : '请输入'
if (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 () {
@ -173,7 +185,7 @@ export default {
const reqParams = {}
reqParams.infoId = that.saveObject.infoId
reqParams.ifCode = that.saveObject.ifCode
reqParams.ifRate = that.saveObject.ifRate
// reqParams.ifRate = that.saveObject.ifRate
reqParams.state = that.saveObject.state
reqParams.remark = that.saveObject.remark
//
@ -181,6 +193,13 @@ export default {
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) {
@ -208,7 +227,7 @@ export default {
let newItems = []
this.mchParams.forEach(item => {
newItems = []
if (item.verify === 'required') {
if (item.verify === 'required' && item.star !== '1') {
newItems.push({
required: true,
message: '请输入' + item.desc,

View File

@ -34,8 +34,11 @@
</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>
@ -73,6 +76,7 @@
: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?'禁用':'启用'" />
@ -91,10 +95,17 @@
<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>
@ -105,6 +116,9 @@ 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 = [
@ -120,7 +134,10 @@ export default {
JeepayTable,
JeepayTableColumns,
MchPayConfigAddOrEdit,
MchPayPassageAddOrEdit
MchPayPassageAddOrEdit,
WxpayPayConfig,
AlipayPayConfig,
AlipayAuth
},
data () {
return {
@ -155,7 +172,9 @@ export default {
},
// card
refCardList () {
this.$refs.infoCard.refCardList()
if (this.$refs.infoCard) {
this.$refs.infoCard.refCardList()
}
},
//
reqTableDataFunc (params) {
@ -175,9 +194,11 @@ export default {
title: '提示',
content: '当前应用所属商户为特约商户,请先配置服务商支付参数!'
})
return
} 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)
}
this.$refs.mchPayConfigAddOrEdit.show(this.appId, record)
},
//
editPayPassageFunc (record) {
@ -196,6 +217,21 @@ export default {
//
onClose () {
this.visible = false
},
//
toAlipayAuthPageFunc (record) {
if (!record) {
return
}
if (record.subMchIsvConfig === 0) {
this.$error({
title: '提示',
content: '当前应用所属商户为特约商户,请先配置服务商支付参数!'
})
return
}
this.$refs.alipayAuthPage.show(this.appId)
}
}
}

View File

@ -0,0 +1,328 @@
<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
},
// valuename
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,364 @@
<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
},
// valuename
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -32,6 +32,7 @@
<a-select-option value="">全部</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>
@ -54,7 +55,6 @@
:searchData="searchData"
:rowSelection="rowSelection"
rowKey="orderId"
:scrollX="1050"
>
<template slot="stateSlot" slot-scope="{record}">
<a-tag
@ -67,9 +67,9 @@
<template slot="orderTypeSlot" slot-scope="{record}">
<a-tag
:key="record.orderType"
:color="record.orderType === 1?'green':record.orderType === 2?'volcano':'orange'"
:color="record.orderType === 1?'green':record.orderType === 2?'volcano': record.orderType === 3? 'blue': 'orange'"
>
{{ record.orderType === 1?'支付':record.orderType === 2?'退款':'未知' }}
{{ record.orderType === 1?'支付':record.orderType === 2?'退款':record.orderType === 3? '转账':'未知' }}
</a-tag>
</template>
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->
@ -124,8 +124,8 @@
<a-col :sm="12">
<a-descriptions>
<a-descriptions-item label="订单类型">
<a-tag :color="detailData.orderType === 1?'green':detailData.orderType === 2?'volcano':'orange'">
{{ detailData.orderType === 1?'支付':detailData.orderType === 2?'退款':'未知' }}
<a-tag :color="detailData.orderType === 1?'green':detailData.orderType === 2?'volcano': detailData.orderType === 3? 'blue' : 'orange'">
{{ detailData.orderType === 1?'支付':detailData.orderType === 2?'退款':detailData.orderType === 3 ? '转账': '未知' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>

View File

@ -14,11 +14,20 @@
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<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="'支付/商户/渠道订单号'" :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="'商户号'" :msg="searchData.mchNo" v-model="searchData.mchNo" />
<jeepay-text-up :placeholder="'服务商号'" :msg="searchData.isvNo" v-model="searchData.isvNo" />
<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>
@ -39,11 +48,12 @@
</a-select>
</a-form-item>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.wayCode" placeholder="支付方式" default-value="">
<a-select v-model="searchData.divisionState" 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-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">
@ -63,32 +73,57 @@
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="payOrderId"
:scrollX="1350"
: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':'volcano'"
: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="refundStateSlot" slot-scope="{record}">
<a-tag
:key="record.refundState"
:color="record.refundState === 0?'blue':record.refundState === 1?'orange':record.refundState === 2?'green':'volcano'"
>
{{ record.refundState === 0?'未发起':record.refundState === 1?'部分退款':record.refundState === 2?'全额退款':'未知' }}
</a-tag>
<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="notifySlot" slot-scope="{record}">
<a-badge :status="record.notifyState === 1?'processing':'error'" :text="record.notifyState === 1?'已发送':'未发送'" />
</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)" @click="openFunc(record, 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>
@ -159,10 +194,16 @@
</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="订单状态">
<a-tag :color="detailData.state === 0?'blue':detailData.state === 1?'orange':detailData.state === 2?'green':'volcano'">
<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>
@ -322,6 +363,27 @@
</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">
@ -350,16 +412,19 @@ import moment from 'moment'
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'amount', title: '支付金额', ellipsis: true, width: '130px', fixed: 'left', scopedSlots: { customRender: 'amountSlot' } },
{ key: 'mchName', title: '商户名称', dataIndex: 'mchName', ellipsis: true, width: '100px' },
{ key: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
{ key: 'mchOrderNo', title: '商户订单号', dataIndex: 'mchOrderNo' },
{ key: 'wayName', title: '支付方式', dataIndex: 'wayName', width: 150 },
{ key: 'amount', title: '支付金额', ellipsis: true, width: 108, fixed: 'left', scopedSlots: { customRender: 'amountSlot' } },
{ key: 'refundAmount', title: '退款金额', width: 108, scopedSlots: { customRender: 'refundAmountSlot' } },
{ key: 'mchFeeAmount', dataIndex: 'mchFeeAmount', title: '手续费', customRender: (text) => '¥' + (text / 100).toFixed(2), width: 100 },
{ key: 'mchName', title: '商户名称', dataIndex: 'mchName', ellipsis: true, width: 100 },
{ key: 'orderNo', title: '订单号', scopedSlots: { customRender: 'orderSlot' }, width: 210 },
// { key: 'payOrderId', title: '', dataIndex: 'payOrderId' },
// { key: 'mchOrderNo', title: '', dataIndex: 'mchOrderNo' },
{ key: 'wayName', title: '支付方式', dataIndex: 'wayName', width: 120 },
{ key: 'state', title: '支付状态', scopedSlots: { customRender: 'stateSlot' }, width: 100 },
{ key: 'refundState', title: '退款状态', scopedSlots: { customRender: 'refundStateSlot' }, width: 100 },
{ key: 'notifyState', title: '回调状态', scopedSlots: { customRender: 'notifySlot' }, width: 100 },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期', width: 180 },
{ key: 'op', title: '操作', width: '160px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
{ key: 'divisionState', title: '分账状态', scopedSlots: { customRender: 'divisionStateSlot' }, width: 100 },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期', width: 120 },
{ key: 'op', title: '操作', width: 120, fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
@ -380,7 +445,9 @@ export default {
computed: {
},
mounted () {
this.initPayWay()
if (this.$access('ENT_PAY_ORDER_SEARCH_PAY_WAY')) {
this.initPayWay()
}
},
methods: {
queryFunc () {
@ -392,7 +459,7 @@ export default {
return req.list(API_URL_PAY_ORDER_LIST, params)
},
searchFunc: function () { //
this.$refs.infoTable.refTable(true)
this.$refs.infoTable.refTable(false)
},
// 退
@ -425,7 +492,33 @@ export default {
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>
.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>

View File

@ -14,9 +14,10 @@
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<jeepay-text-up :placeholder="'退款订单号'" :msg="searchData.refundOrderId" v-model="searchData.refundOrderId" />
<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="'支付/退款列订单号'" :msg="searchData.unionOrderId" v-model="searchData.unionOrderId" />
<!-- <jeepay-text-up :placeholder="'退款订单号'" :msg="searchData.refundOrderId" v-model="searchData.refundOrderId" />-->
<!-- <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="'商户号'" :msg="searchData.mchNo" v-model="searchData.mchNo" />
<jeepay-text-up :placeholder="'服务商号'" :msg="searchData.isvNo" v-model="searchData.isvNo" />
<jeepay-text-up :placeholder="'应用AppId'" :msg="searchData.appId" v-model="searchData.appId"/>
@ -53,9 +54,8 @@
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
:rowSelection="rowSelection"
rowKey="refundOrderId"
:scrollX="1350"
: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> <!-- 自定义插槽 -->
@ -64,9 +64,41 @@
: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 === 0?'订单生成':record.state === 1?'退款中':record.state === 2?'退款成功':record.state === 3?'退款失败':record.state === 4?'任务关闭':'未知' }}
</a-tag>
</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.payOrderId.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>
@ -165,7 +197,7 @@
<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 === 0?'订单生成':detailData.state === 1?'退款中':detailData.state === 2?'退款成功':detailData.state === 3?'退款失败':detailData.state === 4?'任务关闭':'未知' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
@ -294,14 +326,15 @@
// eslint-disable-next-line no-unused-vars
const tableColumns = [
{ key: 'payAmount', title: '支付金额', fixed: 'left', scopedSlots: { customRender: 'payAmountSlot' } },
{ key: 'refundAmount', title: '退款金额', scopedSlots: { customRender: 'refundAmountSlot' } },
{ key: 'refundOrderId', title: '退款订单号', dataIndex: 'refundOrderId' },
{ key: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
{ key: 'mchRefundNo', title: '商户退款单号', dataIndex: 'mchRefundNo' },
{ key: 'payAmount', title: '支付金额', ellipsis: true, fixed: 'left', scopedSlots: { customRender: 'payAmountSlot' }, width: 100 },
{ key: 'refundAmount', title: '退款金额', ellipsis: true, scopedSlots: { customRender: 'refundAmountSlot' }, width: 100 },
{ key: 'pay', title: '退款订单号', scopedSlots: { customRender: 'refundOrderSlot' }, width: 220 },
{ key: 'refund', title: '支付订单号', scopedSlots: { customRender: 'payOrderSlot' }, width: 220 },
// { key: 'payOrderId', title: '', dataIndex: 'payOrderId' },
// { key: 'mchRefundNo', title: '退', dataIndex: 'mchRefundNo' },
{ key: 'state', title: '状态', scopedSlots: { customRender: 'stateSlot' }, width: 100 },
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期' },
{ key: 'op', title: '操作', width: '100px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
{ key: 'createdAt', dataIndex: 'createdAt', title: '创建日期', width: 120 },
{ key: 'op', title: '操作', width: 100, fixed: 'right', scopedSlots: { customRender: 'opSlot' } }
]
export default {
@ -320,17 +353,6 @@
}
},
computed: {
rowSelection () {
const that = this
return {
onChange: (selectedRowKeys, selectedRows) => {
that.selectedIds = [] //
selectedRows.forEach(function (data) { //
that.selectedIds.push(data.payOrderId)
})
}
}
}
},
mounted () {
},
@ -363,7 +385,33 @@
},
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>

View File

@ -0,0 +1,194 @@
<!-- 订单详情抽屉 -->
<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="商户类型">
{{ detailData.mchType === 1?'普通商户':detailData.mchType === 2?'特约商户':'未知' }}
</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.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="服务商号">{{ detailData.isvNo }}</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>

View File

@ -0,0 +1,183 @@
<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="'商户号'" :msg="searchData.mchNo" v-model="searchData.mchNo" />
<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 placement="bottom" style="font-weight: normal;" v-if="record.mchOrderNo.length > record.transferId.length">
<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 placement="bottom" style="font-weight: normal;" v-if="record.channelOrderNo.length > record.transferId.length">
<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' }, width: 108 },
{ title: '商户名称', dataIndex: 'mchName' },
{ key: 'orderNo', title: '订单号', scopedSlots: { customRender: 'orderSlot' }, width: 260 },
// { 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>

View File

@ -43,17 +43,29 @@
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="24" v-if="saveObject.isIsvMode == 1">
<a-col :span="24">
<a-form-model-item label="支付参数配置页面类型" prop="configPageType">
<a-radio-group v-model="saveObject.configPageType">
<a-radio :value="1">
根据接口配置定义描述渲染页面
</a-radio>
<a-radio :value="2">
自定义页面
</a-radio>
</a-radio-group>
</a-form-model-item>
</a-col>
<a-col :span="24" v-if="saveObject.isIsvMode == 1 && this.saveObject.configPageType === 1">
<a-form-model-item label="服务商接口配置定义描述" prop="isvParams">
<a-input v-model="saveObject.isvParams" placeholder="请输入" type="textarea" />
</a-form-model-item>
</a-col>
<a-col :span="24" v-if="saveObject.isIsvMode == 1">
<a-col :span="24" v-if="saveObject.isIsvMode == 1 && this.saveObject.configPageType === 1">
<a-form-model-item label="特约商户接口配置定义描述" prop="isvsubMchParams">
<a-input v-model="saveObject.isvsubMchParams" placeholder="请输入" type="textarea" />
</a-form-model-item>
</a-col>
<a-col :span="24" v-if="saveObject.isMchMode == 1">
<a-col :span="24" v-if="saveObject.isMchMode == 1 && this.saveObject.configPageType === 1">
<a-form-model-item label="普通商户接口配置定义描述" prop="normalMchParams">
<a-input v-model="saveObject.normalMchParams" placeholder="请输入" type="textarea" />
</a-form-model-item>
@ -124,19 +136,19 @@ export default {
data () {
const validateNormalMchParams = (rule, value, callback) => { //
if (this.saveObject.isMchMode === 1 && !value) {
if (this.saveObject.isMchMode === 1 && this.saveObject.configPageType === 1 && !value) {
callback(new Error('请输入普通商户接口配置定义描述'))
}
callback()
}
const validateIsvParams = (rule, value, callback) => { //
if (this.saveObject.isIsvMode === 1 && !value) {
if (this.saveObject.isIsvMode === 1 && this.saveObject.configPageType === 1 && !value) {
callback(new Error('请输入服务商接口配置定义描述'))
}
callback()
}
const validateIsvsubMchParams = (rule, value, callback) => { //
if (this.saveObject.isIsvMode === 1 && !value) {
if (this.saveObject.isIsvMode === 1 && this.saveObject.configPageType === 1 && !value) {
callback(new Error('请输入特约商户接口配置定义描述'))
}
callback()
@ -176,7 +188,8 @@ export default {
this.saveObject = {
'isMchMode': 1,
'isIsvMode': 1,
'state': 1
'state': 1,
'configPageType': 1
}
if (this.$refs.infoFormModel !== undefined) {

View File

@ -1,5 +1,17 @@
<template>
<page-header-wrapper>
<template>
<a-card style="width: calc(100% - 24px);margin-bottom: 20px;">
<a-alert message="" type="info">
<template #description>
<p style="display: flex; justify-content: space-between; margin: 0 0 4px;">
计全科技已开放支付接口购买渠道官方团队开发源码提供下载后直接使用<a href="https://docs.jeequan.com/docs/jeepay/jeepay-1ejdnsuhveb16" target="_blank">接口下载安装说明</a>
<a href="https://www.jeequan.com/ifstore/list.html" target="_blank">前往接口市场 ></a>
</p>
</template>
</a-alert>
</a-card>
</template>
<JeepayCard
ref="infoCard"
:reqCardListFunc="reqCardListFunc"
@ -53,8 +65,8 @@ export default {
return {
jeepayCard: {
name: '支付接口',
height: 300,
span: { xxl: 6, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 },
height: 200,
span: { xxl: 8, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 },
addAuthority: this.$access('ENT_PC_IF_DEFINE_ADD')
}
}
@ -118,7 +130,7 @@ export default {
align-items: center;
}
.title {
font-size: 16px;
font-size: 13px;
font-family: PingFang SC, PingFang SC-Bold;
font-weight: 700;
color: #1a1a1a;

View File

@ -24,7 +24,6 @@
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="wayCode"
:scrollX="500"
>
<template slot="wayCodeSlot" slot-scope="{record}"><b>{{ record.wayCode }}</b></template> <!-- 自定义插槽 -->
<template slot="opSlot" slot-scope="{record}"> <!-- 操作列插槽 -->

View File

@ -25,7 +25,6 @@
:tableColumns="tableColumns"
:searchData="searchData"
@btnLoadClose="btnLoading=false"
:scrollX="500"
rowKey="roleName"
>
<template slot="roleIdSlot" slot-scope="{record}"><b>{{ record.roleId }}</b></template> <!-- 自定义插槽 -->

View File

@ -45,7 +45,6 @@
:searchData="searchData"
:rowSelection="rowSelection"
rowKey="sysLogId"
:scrollX="1200"
>
<template slot="userNameSlot" slot-scope="{record}"><b>{{ record.userName }}</b></template> <!-- 自定义插槽 -->
<template slot="sysTypeSlot" slot-scope="{record}">

View File

@ -27,7 +27,6 @@
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="sysUserId"
:scrollX="1350"
>
<template slot="avatarSlot" slot-scope="{record}">

View File

@ -53,7 +53,10 @@
<img :src="vercodeIcon" slot="prefix" class="user" alt="user" />
</a-input>
</a-form-item>
<div class="code-img"><img v-show="vercodeImgSrc" :src="vercodeImgSrc" @click="refVercode()"/></div>
<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>
@ -89,6 +92,7 @@ export default {
},
data () {
return {
isOverdue: false, //
isAutoLogin: true, //
loginBtnLoadingFlag: false, //
showLoginErrorInfo: '', //
@ -145,6 +149,17 @@ export default {
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)
})
}
}
@ -211,5 +226,19 @@ export default {
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>

View File

@ -16,25 +16,7 @@ function getGitHash () {
return 'unknown'
}
const isProd = process.env.NODE_ENV === 'production'
const assetsCDN = {
// webpack build externals
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
},
css: [],
// https://unpkg.com/browse/vue@2.6.10/
js: [
'//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js'
]
}
// const isProd = process.env.NODE_ENV === 'production'
// vue.config.js
const vueConfig = {
@ -51,7 +33,7 @@ const vueConfig = {
})
],
// if prod, add externals
externals: isProd ? assetsCDN.externals : {}
externals: {}
},
chainWebpack: (config) => {
@ -71,17 +53,9 @@ const vueConfig = {
.use('file-loader')
.loader('file-loader')
.options({
limit: 100000,
name: 'assets/[name].[hash:8].[ext]'
})
// if prod is on
// assets require on cdn
if (isProd) {
config.plugin('html').tap(args => {
args[0].cdn = assetsCDN
return args
})
}
},
css: {

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "jeepay-ui-merchant",
"version": "1.1.1",
"version": "1.10.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@ -34,6 +34,7 @@
"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",

View File

@ -86,6 +86,17 @@ 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 = {
@ -235,3 +246,62 @@ export function getWebSocketPrefix () {
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'
})
}

View File

@ -0,0 +1,19 @@
<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>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,82 @@
<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>

View File

@ -3,7 +3,7 @@
<template v-slot:links>
</template>
<template v-slot:copyright>
<a href="http://www.jeequan.com" target="_blank">@计全科技</a>
Copyright © 2023 <a href="http://www.jeequan.com" target="_blank">jeequan.com</a>. All rights reserved.
</template>
</global-footer>
</template>

View File

@ -16,6 +16,12 @@
: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 -->
@ -41,7 +47,8 @@ export default {
pageSize: { type: Number, default: 10 }, //
rowSelection: Object, // checkbox
rowKey: { type: [String, Function] }, // rowKey checkbox
scrollX: { type: Number, default: 980 } //
scrollX: { type: Number, default: 800 }, //
tableRowCrossColor: { type: Boolean, default: false } //
},
data () {
@ -117,3 +124,24 @@ export default {
}
}
</script>
<style lang="less">
// antdv tablepadding
.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>

View File

@ -30,7 +30,7 @@ export default {
}
return <div style="display:flex; justify-content: space-evenly;"> {firstEL}
<a-dropdown>
<a-button style="line-height:32px" type="link" class="ant-dropdown-link">更多<a-icon type="down" /></a-button>
<a-button style="" type="link" class="ant-dropdown-link">更多<a-icon type="down" /></a-button>
<a-menu slot="overlay">
{menuEL}
</a-menu>
@ -40,3 +40,9 @@ export default {
}
}
</script>
<style lang="less" scoped>
// padding
button { padding: 8px !important;}
</style>

View File

@ -26,7 +26,14 @@ export const asyncRouteDefine = {
'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') } // 退款订单列表
'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') } // 分账记录
}

View File

@ -9,6 +9,7 @@
:i18nRender="false"
v-bind="settings"
:breadcrumbRender="handleBreadcrumbRender"
:siderWidth="210"
>
<!-- 1.0.0+ 版本 pro-layout 提供 API
我们推荐使用这种方式进行 LOGO title 自定义
@ -34,6 +35,8 @@
<template v-slot:rightContentRender>
<right-content :top-menu="settings.layout === 'topmenu'" :is-mobile="isMobile" :theme="settings.theme" />
<a style="color: red; float: right; padding-right: 10px;" href="https://www.jeequan.com/product/jeepay4plus.html" target="_blank">Plus商业版</a>
<a style="color: red; float: right; padding-right: 10px;" href="https://www.jeequan.com/ifstore/list.html" target="_blank">接口市场</a>
</template>
<!-- custom footer / 自定义Footer -->
<template v-slot:footerRender>

View File

@ -7,8 +7,8 @@ export const printANSI = () => {
/ /_/ // __/ __/ /_/ / /_/ / /_/ /
\\____/ \\___/\\___/ .___/\\__,_/\\__, /
/_/ /____/
:: Jeepay :: (v1.0.0.RELEASE)
适合互联网企业使用的开源支付系统 : https://www.jeepay.vip
:: Jeepay :: (v2.2.2.RELEASE)
让支付接入更简单 : https://www.jeequan.com
`
console.log(`%c${text}`, 'color: #fc4d50')

View File

@ -1,3 +1,7 @@
// 定义全局自增ID
var atomicLong = 1
export function timeFix () {
const time = new Date()
const hour = time.getHours()
@ -10,3 +14,8 @@ export function isIE () {
const ie11 = (() => 'ActiveXObject' in window)()
return compare('MSIE') || ie11
}
/** 生成自增序列号(不重复) **/
export function genRowKey () {
return new Date().getTime() + '_' + (atomicLong++)
}

View File

@ -0,0 +1,96 @@
<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>

View File

@ -0,0 +1,108 @@
<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>

View File

@ -0,0 +1,159 @@
<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
// appidpageSize=-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>

View File

@ -0,0 +1,356 @@
<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', // , selectdefaultValue
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>

View File

@ -0,0 +1,109 @@
<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>

View File

@ -0,0 +1,72 @@
<!-- 详情抽屉 -->
<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>

View File

@ -0,0 +1,152 @@
<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>

View File

@ -33,7 +33,7 @@
</a-col>
<a-col :span="24">
<a-form-model-item label="私钥 AppSecret" prop="appSecret" >
<a-input v-model="saveObject.appSecret" placeholder="请输入" type="textarea" />
<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>
@ -68,8 +68,7 @@ export default {
appId: '', // AppId
saveObject: {}, //
rules: {
appName: [{ required: true, message: '请输入应用名称', trigger: 'blur' }],
appSecret: [{ required: true, message: '请输入私钥或点击随机生成私钥' }]
appName: [{ required: true, message: '请输入应用名称', trigger: 'blur' }]
}
}
},
@ -80,7 +79,8 @@ export default {
//
this.saveObject = {
'state': 1,
'appSecret': ''
'appSecret': '',
'appSecret_ph': '请输入'
}
if (this.$refs.infoFormModel !== undefined) {
@ -88,17 +88,24 @@ export default {
}
const that = this
that.rules.appSecret = []
if (!this.isAdd) { //
that.appId = appId
//
req.getById(API_URL_MCH_APP, appId).then(res => {
that.saveObject = res
if (!that.saveObject.appSecret) { //
that.saveObject.appSecret = ''
}
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 //
}
},
@ -107,6 +114,7 @@ export default {
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 => {
@ -115,6 +123,9 @@ export default {
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

View File

@ -0,0 +1,60 @@
<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>

View File

@ -33,8 +33,7 @@
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
:searchData="searchData"
:scrollX="1200"
rowKey="mchName"
rowKey="appId"
>
<template slot="appIdSlot" slot-scope="{record}">
<b>{{ record.appId }}</b>
@ -51,6 +50,11 @@
支付测试
</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>

View File

@ -10,11 +10,6 @@
>
<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="ifRate">
<a-input v-model="saveObject.ifRate" placeholder="请输入" suffix="%" />
</a-form-model-item>
</a-col>
<a-col :span="12">
<a-form-model-item label="状态" prop="state">
<a-radio-group v-model="saveObject.state">
@ -43,7 +38,8 @@
<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-model="ifParams[item.name]" placeholder="请输入" :type="item.type" />
<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]">
@ -101,12 +97,17 @@ export default {
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' }]
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) {
@ -133,35 +134,45 @@ export default {
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) {
radioItems.push({
value: valueItems[i],
title: titleItems[i]
})
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]
})
}
}
}
newItems.push({
name: item.name,
desc: item.desc,
type: item.type,
verify: item.verify,
values: radioItems
})
})
that.mchParams = newItems //
that.visible = true //
that.generoterRules()
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 () {
@ -173,7 +184,7 @@ export default {
const reqParams = {}
reqParams.infoId = that.saveObject.infoId
reqParams.ifCode = that.saveObject.ifCode
reqParams.ifRate = that.saveObject.ifRate
// reqParams.ifRate = that.saveObject.ifRate
reqParams.state = that.saveObject.state
reqParams.remark = that.saveObject.remark
//
@ -181,6 +192,13 @@ export default {
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) {
@ -208,7 +226,7 @@ export default {
let newItems = []
this.mchParams.forEach(item => {
newItems = []
if (item.verify === 'required') {
if (item.verify === 'required' && item.star !== '1') {
newItems.push({
required: true,
message: '请输入' + item.desc,

View File

@ -34,6 +34,8 @@
</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>
@ -73,6 +75,7 @@
: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?'禁用':'启用'" />
@ -91,10 +94,16 @@
<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>
@ -105,6 +114,9 @@ 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 = [
@ -120,7 +132,10 @@ export default {
JeepayTable,
JeepayTableColumns,
MchPayConfigAddOrEdit,
MchPayPassageAddOrEdit
MchPayPassageAddOrEdit,
WxpayPayConfig,
AlipayPayConfig,
AlipayAuth
},
data () {
return {
@ -155,7 +170,9 @@ export default {
},
// card
refCardList () {
this.$refs.infoCard.refCardList()
if (this.$refs.infoCard) {
this.$refs.infoCard.refCardList()
}
},
//
reqTableDataFunc (params) {
@ -175,9 +192,11 @@ export default {
title: '提示',
content: '服务商未配置,请联系服务商配置支付参数!'
})
return
} 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)
}
this.$refs.mchPayConfigAddOrEdit.show(this.appId, record)
},
//
editPayPassageFunc (record) {
@ -196,6 +215,21 @@ export default {
//
onClose () {
this.visible = false
},
//
toAlipayAuthPageFunc (record) {
if (!record) {
return
}
if (record.subMchIsvConfig === 0) {
this.$error({
title: '提示',
content: '当前应用所属商户为特约商户,请先配置服务商支付参数!'
})
return
}
this.$refs.alipayAuthPage.show(this.appId)
}
}
}

View File

@ -0,0 +1,328 @@
<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
},
// valuename
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,364 @@
<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
},
// valuename
uploadSuccess (value, name) {
this.ifParams[name] = value
this.$forceUpdate()
},
onClose () {
this.visible = false
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -14,9 +14,18 @@
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<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="'支付/商户/渠道订单号'" :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>
@ -31,11 +40,12 @@
</a-form-item>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.wayCode" placeholder="支付方式" default-value="">
<a-select v-model="searchData.divisionState" 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-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>
@ -56,24 +66,63 @@
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="payOrderId"
:scrollX="1000"
: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':'volcano'"
: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
@ -132,7 +181,7 @@
<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'">
<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>
@ -147,6 +196,12 @@
</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="支付错误码">
@ -276,6 +331,28 @@
</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">
@ -298,23 +375,28 @@
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: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
{ key: 'mchOrderNo', title: '商户订单号', dataIndex: 'mchOrderNo' },
{ 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: '100px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
{ key: 'op', title: '操作', width: '120px', fixed: 'right', align: 'center', scopedSlots: { customRender: 'opSlot' } }
]
export default {
name: 'IsvListPage',
components: { JeepayTable, JeepayTableColumns, JeepayTextUp },
components: { JeepayTable, JeepayTableColumns, JeepayTextUp, RefundModal },
data () {
return {
btnLoading: false,
@ -330,7 +412,9 @@ export default {
computed: {
},
mounted () {
this.initPayWay()
if (this.$access('ENT_PAY_ORDER_SEARCH_PAY_WAY')) {
this.initPayWay()
}
},
methods: {
queryFunc () {
@ -342,7 +426,14 @@ export default {
return req.list(API_URL_PAY_ORDER_LIST, params)
},
searchFunc: function () { //
this.$refs.infoTable.refTable(true)
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
@ -367,7 +458,42 @@ export default {
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>

View File

@ -0,0 +1,160 @@
<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>

View File

@ -14,10 +14,11 @@
<a-icon slot="suffixIcon" type="sync" />
</a-range-picker>
</a-form-item>
<jeepay-text-up :placeholder="'退款订单号'" :msg="searchData.refundOrderId" v-model="searchData.refundOrderId" />
<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="'服务商号'" :msg="searchData.isvNo" v-model="searchData.isvNo" />
<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="">
@ -28,13 +29,6 @@
<a-select-option value="3">退款失败</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="" class="table-head-layout">
<a-select v-model="searchData.mchType" placeholder="商户类型" default-value="">
<a-select-option value="">全部</a-select-option>
<a-select-option value="1">普通商户</a-select-option>
<a-select-option value="2">特约商户</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>
@ -50,10 +44,11 @@
ref="infoTable"
:initData="true"
:closable="true"
:searchData="searchData"
:reqTableDataFunc="reqTableDataFunc"
:tableColumns="tableColumns"
rowKey="refundOrderId"
:scrollX="1300"
: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> <!-- 自定义插槽 -->
@ -62,10 +57,42 @@
: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 === 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>
@ -164,7 +191,7 @@
<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 === 0?'订单生成':detailData.state === 1?'退款中':detailData.state === 2?'退款成功':detailData.state === 3?'退款失败':detailData.state === 4?'任务关闭':'未知' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
@ -295,10 +322,12 @@
const tableColumns = [
{ key: 'payAmount', title: '支付金额', scopedSlots: { customRender: 'payAmountSlot' } },
{ key: 'refundAmount', title: '退款金额', scopedSlots: { customRender: 'refundAmountSlot' } },
{ key: 'refundOrderId', title: '退款订单号', dataIndex: 'refundOrderId' },
{ key: 'channelPayOrderNo', title: '渠道订单号', dataIndex: 'channelPayOrderNo' },
{ key: 'payOrderId', title: '支付订单号', dataIndex: 'payOrderId' },
{ key: 'mchRefundNo', title: '商户退款单号', dataIndex: 'mchRefundNo' },
{ 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' } }
@ -351,7 +380,33 @@
},
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>

View File

@ -0,0 +1,172 @@
<!-- 订单详情抽屉 -->
<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>

View File

@ -0,0 +1,183 @@
<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>

View File

@ -69,14 +69,18 @@
</div>
</div>
<div class="paydemo-type-name article-title" v-show="showJhTitle()">聚合支付</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>
<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>
<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>
@ -92,9 +96,23 @@
<span @click="randomOrderNo" class=" paydemo-btn" style="padding:0 3px">刷新订单号</span>
</div>
<div class="paydemo-form-item">
<label>商品名称</label>
<span id="paySubject">支付接口演示</span>
<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>
@ -118,8 +136,8 @@
</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>
@ -154,7 +172,9 @@ export default {
authCode: '', //
paytestAmount: '0.01', // 0.01
amountInput: false, //
noConfigText: false // 线
noConfigText: false, // 线
divisionMode: 0, //
orderTitle: '接口调试' //
}
},
@ -170,6 +190,12 @@ export default {
// appidpageSize=-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)
}
})
//
@ -236,6 +262,11 @@ export default {
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()
@ -250,7 +281,9 @@ export default {
appId: this.appId, // appId
mchOrderNo: this.mchOrderNo, //
payDataType: this.currentPayDataType, //
authCode: this.authCode
authCode: this.authCode,
divisionMode: this.divisionMode,
orderTitle: this.orderTitle
}).then(res => {
that.$refs.payTestModal.showModal(this.currentWayCode, res) //
that.randomOrderNo() //
@ -269,8 +302,8 @@ export default {
}
},
//
showJhTitle () {
if (this.appPaywayList.toString().indexOf('WX') !== -1 || this.appPaywayList.toString().indexOf('ALI') !== -1) {
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

View File

@ -27,7 +27,6 @@
:tableColumns="tableColumns"
:searchData="searchData"
@btnLoadClose="btnLoading=false"
:scrollX="500"
rowKey="roleId"
>
<template slot="roleIdSlot" slot-scope="{record}"><b>{{ record.roleId }}</b></template> <!-- 自定义插槽 -->

View File

@ -30,7 +30,6 @@
:tableColumns="tableColumns"
:searchData="searchData"
rowKey="sysUserId"
:scrollX="1300"
>
<template slot="avatarSlot" slot-scope="{record}">

View File

@ -0,0 +1,128 @@
.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
}

View File

@ -0,0 +1,232 @@
<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
// appidpageSize=-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>

View File

@ -53,8 +53,9 @@
<img :src="vercodeIcon" slot="prefix" class="user" alt="user" />
</a-input>
</a-form-item>
<div class="code-img">
<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>
@ -90,6 +91,7 @@ export default {
},
data () {
return {
isOverdue: false, //
isAutoLogin: true, //
loginBtnLoadingFlag: false, //
showLoginErrorInfo: '', //
@ -154,6 +156,17 @@ export default {
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)
})
}
}
@ -220,5 +233,19 @@ export default {
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>

View File

@ -16,25 +16,7 @@ function getGitHash () {
return 'unknown'
}
const isProd = process.env.NODE_ENV === 'production'
const assetsCDN = {
// webpack build externals
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
},
css: [],
// https://unpkg.com/browse/vue@2.6.10/
js: [
'//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js'
]
}
// const isProd = process.env.NODE_ENV === 'production'
// vue.config.js
const vueConfig = {
@ -53,7 +35,7 @@ const vueConfig = {
// 代码调试
devtool: 'source-map',
// if prod, add externals
externals: isProd ? assetsCDN.externals : {}
externals: {}
},
chainWebpack: (config) => {
@ -74,17 +56,9 @@ const vueConfig = {
.use('file-loader')
.loader('file-loader')
.options({
limit: 100000,
name: 'assets/[name].[hash:8].[ext]'
})
// if prod is on
// assets require on cdn
if (isProd) {
config.plugin('html').tap(args => {
args[0].cdn = assetsCDN
return args
})
}
},
css: {