feat: subscribe and alipay
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crm/models"
|
||||||
|
"crm/response"
|
||||||
|
"crm/service"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubscribeApi struct {
|
||||||
|
subscribeService *service.SubscribeService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubscribeApi() *SubscribeApi {
|
||||||
|
subscribeApi := SubscribeApi{
|
||||||
|
subscribeService: &service.SubscribeService{},
|
||||||
|
}
|
||||||
|
return &subscribeApi
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅专业版,发起支付
|
||||||
|
func (s *SubscribeApi) Pay(context *gin.Context) {
|
||||||
|
var param models.SubscribePayParam
|
||||||
|
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||||
|
err := context.ShouldBind(¶m);
|
||||||
|
if int64(uid) <= 0 || err != nil {
|
||||||
|
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param.Uid = int64(uid)
|
||||||
|
payUrl, errCode := s.subscribeService.Pay(param)
|
||||||
|
response.Result(errCode, payUrl, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付成功回调
|
||||||
|
func (s *SubscribeApi) Callback(context *gin.Context) {
|
||||||
|
context.Request.ParseForm()
|
||||||
|
var outTradeNo = context.Request.Form.Get("out_trade_no")
|
||||||
|
paySuccessURL, _ := s.subscribeService.Callback(outTradeNo)
|
||||||
|
context.Redirect(http.StatusMovedPermanently, paySuccessURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付通知
|
||||||
|
func (s *SubscribeApi) Notify(context *gin.Context) {
|
||||||
|
context.Request.ParseForm()
|
||||||
|
var outTradeNo = context.Request.Form.Get("out_trade_no")
|
||||||
|
errCode := s.subscribeService.Notify(context.Request.Form, outTradeNo)
|
||||||
|
response.Result(errCode, nil, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订阅信息
|
||||||
|
func (s *SubscribeApi) GetInfo(context *gin.Context) {
|
||||||
|
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||||
|
if int64(uid) <= 0 {
|
||||||
|
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subscribeInfo, errCode := s.subscribeService.GetInfo(int64(uid))
|
||||||
|
response.Result(errCode, subscribeInfo, context)
|
||||||
|
}
|
||||||
@@ -18,16 +18,16 @@ func Router() {
|
|||||||
route := engine.Group("/api")
|
route := engine.Group("/api")
|
||||||
|
|
||||||
{
|
{
|
||||||
// 用户模块
|
// 用户模块,订阅模块
|
||||||
route.GET("/user/verifycode", api.NewUserApi().GetVerifyCode)
|
route.GET("/user/verifycode", api.NewUserApi().GetVerifyCode)
|
||||||
route.GET("/user/info", api.NewUserApi().GetInfo)
|
route.GET("/user/info", api.NewUserApi().GetInfo)
|
||||||
route.PUT("/user/mail", api.NewUserApi().UpdateMail)
|
route.PUT("/user/mail", api.NewUserApi().UpdateMail)
|
||||||
route.PUT("/user/buy", api.NewUserApi().Buy)
|
|
||||||
route.POST("/user/login", api.NewUserApi().Login)
|
route.POST("/user/login", api.NewUserApi().Login)
|
||||||
route.POST("/user/register", api.NewUserApi().Register)
|
route.POST("/user/register", api.NewUserApi().Register)
|
||||||
route.POST("/user/pass", api.NewUserApi().ForgotPass)
|
route.POST("/user/pass", api.NewUserApi().ForgotPass)
|
||||||
route.DELETE("/user/logout", api.NewUserApi().Logout)
|
route.DELETE("/user/logout", api.NewUserApi().Logout)
|
||||||
route.DELETE("/user/delete", api.NewUserApi().Delete)
|
route.DELETE("/user/delete", api.NewUserApi().Delete)
|
||||||
|
route.GET("/subscribe/callback", api.NewSubscribeApi().Callback)
|
||||||
|
|
||||||
// Jwt中间件
|
// Jwt中间件
|
||||||
route.Use(middleware.JwtAuth())
|
route.Use(middleware.JwtAuth())
|
||||||
@@ -58,6 +58,10 @@ func Router() {
|
|||||||
// 仪表盘模块
|
// 仪表盘模块
|
||||||
route.GET("/dashboard/sum", api.NewDashboardApi().Summary)
|
route.GET("/dashboard/sum", api.NewDashboardApi().Summary)
|
||||||
|
|
||||||
|
// 订阅模块
|
||||||
|
route.GET("/subscribe/info", api.NewSubscribeApi().GetInfo)
|
||||||
|
route.POST("/subscribe/pay", api.NewSubscribeApi().Pay)
|
||||||
|
route.POST("/subscribe/notify", api.NewSubscribeApi().Notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动、监听端口
|
// 启动、监听端口
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Subscribe struct {
|
||||||
|
Id int64 `gorm:"primaryKey"`
|
||||||
|
Uid int64 `gorm:"uid"`
|
||||||
|
Version int `gorm:"version"`
|
||||||
|
Expired int64 `gorm:"expired"`
|
||||||
|
Created int64 `gorm:"created"`
|
||||||
|
Updated int64 `gorm:"updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscribePayParam struct {
|
||||||
|
Uid int64 `json:"uid" binding:"-"`
|
||||||
|
Version int `json:"version" binding:"required,oneof=2 3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscribePayOrder struct {
|
||||||
|
Uid int64 `json:"uid"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscribePayUrl struct {
|
||||||
|
PayUrl string `json:"payUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscribeInfo struct {
|
||||||
|
Version int `json:"version"`
|
||||||
|
Expired int64 `json:"expired"`
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ const (
|
|||||||
ErrCodeCompanyIdNotExist = 10007 // 企业编号不存在
|
ErrCodeCompanyIdNotExist = 10007 // 企业编号不存在
|
||||||
ErrCodeEmailFormatInvalid = 10008 // 邮箱格式无效
|
ErrCodeEmailFormatInvalid = 10008 // 邮箱格式无效
|
||||||
ErrCodeUserPassResetFailed = 10009 // 用户密码重置失败
|
ErrCodeUserPassResetFailed = 10009 // 用户密码重置失败
|
||||||
|
|
||||||
|
ErrCodePayFailed = 20001 // 支付宝支付失败
|
||||||
)
|
)
|
||||||
|
|
||||||
var msg = map[int]string{
|
var msg = map[int]string{
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crm/global"
|
||||||
|
"crm/models"
|
||||||
|
"crm/response"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/smartwalle/alipay/v3"
|
||||||
|
"github.com/smartwalle/xid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Paying = 1 // 支付中
|
||||||
|
Payed = 2 // 已支付
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubscribeService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅专业版,发起支付
|
||||||
|
func (s *SubscribeService) Pay(param models.SubscribePayParam) (*models.SubscribePayUrl, int) {
|
||||||
|
|
||||||
|
// 构建订单支付信息
|
||||||
|
tradeNo := fmt.Sprintf("%d", xid.Next())
|
||||||
|
var p = alipay.TradePagePay{}
|
||||||
|
p.NotifyURL = global.Config.Alipay.NotifyURL
|
||||||
|
p.ReturnURL = global.Config.Alipay.ReturnURL
|
||||||
|
fmt.Println(p.ReturnURL)
|
||||||
|
p.Subject = "支付测试:" + tradeNo
|
||||||
|
p.OutTradeNo = tradeNo
|
||||||
|
p.ProductCode = "FAST_INSTANT_TRADE_PAY"
|
||||||
|
switch param.Version {
|
||||||
|
case 2:
|
||||||
|
p.TotalAmount = "18.00"
|
||||||
|
case 3:
|
||||||
|
p.TotalAmount = "198.00"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存订单信息
|
||||||
|
order, _ := json.Marshal(&models.SubscribePayOrder{
|
||||||
|
Uid: param.Uid,
|
||||||
|
Version: param.Version,
|
||||||
|
})
|
||||||
|
err := global.Rdb.Set(ctx, tradeNo, string(order), time.Minute*30).Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置支付状态
|
||||||
|
key := fmt.Sprintf("uid:%v:pay:status", param.Uid)
|
||||||
|
if err := global.Rdb.Set(ctx, key, Paying, time.Minute*10).Err(); err != nil {
|
||||||
|
return nil, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回支付链接
|
||||||
|
payUrl, err := global.Alipay.TradePagePay(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
subscribePayUrl := models.SubscribePayUrl{
|
||||||
|
PayUrl: payUrl.String(),
|
||||||
|
}
|
||||||
|
return &subscribePayUrl, response.ErrCodeSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付成功回调
|
||||||
|
func (s *SubscribeService) Callback(outTradeNo string) (string, int) {
|
||||||
|
|
||||||
|
// 验证订单信息
|
||||||
|
var p = alipay.TradeQuery{}
|
||||||
|
p.OutTradeNo = outTradeNo
|
||||||
|
rsp, err := global.Alipay.TradeQuery(p)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("验证订单 %s 信息发生错误: %s", outTradeNo, err.Error())
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
if !rsp.IsSuccess() {
|
||||||
|
log.Printf("验证订单 %s 信息发生错误: %s-%s", outTradeNo, rsp.Content.Msg, rsp.Content.SubMsg)
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订单信息
|
||||||
|
var order models.SubscribePayOrder
|
||||||
|
orderJson, err := global.Rdb.Get(ctx, outTradeNo).Result()
|
||||||
|
if err != nil {
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(orderJson), &order); err != nil {
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建订阅信息
|
||||||
|
var expired int64
|
||||||
|
switch order.Version {
|
||||||
|
case 2:
|
||||||
|
expired = time.Now().Unix() + int64(2592000)
|
||||||
|
case 3:
|
||||||
|
expired = time.Now().Unix() + int64(31104000)
|
||||||
|
}
|
||||||
|
subscribe := models.Subscribe{
|
||||||
|
Version: order.Version,
|
||||||
|
Expired: expired,
|
||||||
|
}
|
||||||
|
if global.Db.Table(SUBSCRIBE).Where("uid = ?", order.Uid).First(&models.Subscribe{}).RowsAffected == 0 {
|
||||||
|
subscribe.Uid = order.Uid
|
||||||
|
subscribe.Created = time.Now().Unix()
|
||||||
|
err := global.Db.Table(SUBSCRIBE).Create(&subscribe).Error
|
||||||
|
if err != nil {
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subscribe.Updated = time.Now().Unix()
|
||||||
|
err = global.Db.Model(&models.Subscribe{}).Where("uid = ?", order.Uid).Updates(&subscribe).Error
|
||||||
|
if err != nil {
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改支付状态
|
||||||
|
key := fmt.Sprintf("uid:%v:pay:status", order.Uid)
|
||||||
|
if err := global.Rdb.Set(ctx, key, Payed, time.Hour*10).Err(); err != nil {
|
||||||
|
return StringNull, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
return global.Config.Alipay.PaySuccessURL, response.ErrCodeSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步验签
|
||||||
|
func (s *SubscribeService) Notify(data url.Values, outTradeNo string) int {
|
||||||
|
ok, err := global.Alipay.VerifySign(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("异步通知验证签名发生错误", err)
|
||||||
|
return response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Println("异步通知验证签名未通过")
|
||||||
|
return response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("异步通知验证签名通过")
|
||||||
|
|
||||||
|
var p = alipay.TradeQuery{}
|
||||||
|
p.OutTradeNo = outTradeNo
|
||||||
|
rsp, err := global.Alipay.TradeQuery(p)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("异步通知验证订单 %s 信息发生错误: %s \n", outTradeNo, err.Error())
|
||||||
|
return response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
if !rsp.IsSuccess() {
|
||||||
|
log.Printf("异步通知验证订单 %s 信息发生错误: %s-%s \n", outTradeNo, rsp.Content.Msg, rsp.Content.SubMsg)
|
||||||
|
return response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("订单 %s 支付成功 \n", outTradeNo)
|
||||||
|
return response.ErrCodeSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订阅信息
|
||||||
|
func (s *SubscribeService) GetInfo(uid int64) (*models.SubscribeInfo, int) {
|
||||||
|
var si models.SubscribeInfo
|
||||||
|
if err := global.Db.Table(SUBSCRIBE).First(&si, "uid = ?", uid).Error; err != nil {
|
||||||
|
return nil, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
// 判断用户订阅是否过期
|
||||||
|
if si.Version == 2 && time.Now().Unix() > int64(si.Expired) {
|
||||||
|
err := global.Db.Model(&models.Subscribe{}).Where("uid = ?", uid).Update("version", 1).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := global.Db.Table(SUBSCRIBE).First(&si, "uid = ?", uid).Error; err != nil {
|
||||||
|
return nil, response.ErrCodeFailed
|
||||||
|
}
|
||||||
|
return &si, response.ErrCodeSuccess
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import request from '../axios/index'
|
||||||
|
|
||||||
|
// 订阅专业版
|
||||||
|
export function subscribePay(param) {
|
||||||
|
return request({
|
||||||
|
url: '/subscribe/pay',
|
||||||
|
method: 'post',
|
||||||
|
data: param,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订阅信息
|
||||||
|
export function getSubscribeInfo(param) {
|
||||||
|
return request({
|
||||||
|
url: '/subscribe/info',
|
||||||
|
method: 'get',
|
||||||
|
params: param,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ import { QuestionCircleTwoTone } from '@ant-design/icons-vue'
|
|||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
import { getSummary } from "../api/dashboard";
|
import { getSummary } from "../api/dashboard";
|
||||||
import { getUserInfo } from "../api/user";
|
import { getSubscribeInfo } from '../api/subscribe';
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -114,7 +114,7 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkVersion();
|
subscribeInfo();
|
||||||
initChart();
|
initChart();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -163,9 +163,9 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看用户系统版本
|
// 获取用户订阅信息
|
||||||
const checkVersion = () => {
|
const subscribeInfo = () => {
|
||||||
getUserInfo().then((res) => {
|
getSubscribeInfo().then((res) => {
|
||||||
if (res.data.code == 0 && res.data.data.version == 1) {
|
if (res.data.code == 0 && res.data.data.version == 1) {
|
||||||
router.push('/result')
|
router.push('/result')
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ export default {
|
|||||||
data,
|
data,
|
||||||
daysRange,
|
daysRange,
|
||||||
initChart,
|
initChart,
|
||||||
checkVersion,
|
subscribeInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+138
-40
@@ -1,27 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="margin: 20vh auto;">
|
<div>
|
||||||
|
<a-alert message="点击立即购买后,会跳转到支付宝支付页面(沙箱环境,账户名 emrpqt1589@sandbox.com 登录密码/支付密码 111111)" type="info"
|
||||||
|
show-icon /><br />
|
||||||
<a-row :gutter="30">
|
<a-row :gutter="30">
|
||||||
<a-col :span="6" :offset="6">
|
<a-col :span="8">
|
||||||
<a-card class="card" :bordered="false">
|
<a-card :class="version == 1 ? 'selected-free-card' : 'card'" :bordered="false">
|
||||||
<h2 class="title">免费版
|
<span class="member-tag" style="color: #33B9FE;">基础版</span>
|
||||||
|
<h2 class="title">免费
|
||||||
<check-circle-filled v-if="ver == 1" class="icon" />
|
<check-circle-filled v-if="ver == 1" class="icon" />
|
||||||
</h2>
|
</h2>
|
||||||
<div class="content">满足基础功能需求,永久免费</div><br />
|
<div class="content">满足基础功能需求,永久免费</div><br />
|
||||||
<div class="price">¥0</div><br />
|
<a-button size="large" class="btn-free" shape="round">免费使用</a-button>
|
||||||
<a-button size="large" class="btn">免费使用</a-button>
|
<br />
|
||||||
|
<div class="subscribe-list" v-for="item in ['客户管理', '合同管理', '产品管理']">
|
||||||
|
<check-circle-filled style="color: #33B9FE;font-size: 20px;;" />
|
||||||
|
<span style="margin-left: 10px;">{{ item }}</span>
|
||||||
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="6">
|
<a-col :span="8">
|
||||||
<a-card class="card" :bordered="false">
|
<a-card :class="version == 2 ? 'selected-card' : 'card'" :bordered="false">
|
||||||
<h2 class="title">个人版
|
<span class="member-tag" style="color: #3788FF;">专业版</span>
|
||||||
<check-circle-filled v-if="ver == 2" class="icon" />
|
<h2 class="title">按月付费,每月18元</h2>
|
||||||
</h2>
|
|
||||||
<div class="content">能力不设限,新功能优先体验</div><br />
|
<div class="content">能力不设限,新功能优先体验</div><br />
|
||||||
<div class="price">¥28<span style="font-size: 20px;font-weight: 500;"> / 月</span></div><br />
|
<a-button v-if="version == 1 || version == 3" type="primary" size="large" class="btn-buy"
|
||||||
<a-button v-if="ver == 1" type="primary" size="large" @click="onBuy" style="width: 100%;">立即订阅
|
@click="onPay(2)" shape="round" :disabled="disabled">立即购买</a-button>
|
||||||
</a-button>
|
<a-button v-if="version == 2" type="primary" size="large" class="btn-buy" shape="round">{{ expired
|
||||||
<a-button v-if="ver == 2" type="primary" size="large" style="width: 100%;">{{ expired }} 到期
|
}} 到期</a-button>
|
||||||
</a-button>
|
<br />
|
||||||
|
<div class="subscribe-list" v-for="item in ['客户管理', '合同管理', '产品管理', '仪表盘可体验30天']">
|
||||||
|
<check-circle-filled style="color: #476FFF;font-size: 20px;;" />
|
||||||
|
<span style="margin-left: 10px;">{{ item }}</span>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card :class="version == 3 ? 'selected-card' : 'card'" :bordered="false">
|
||||||
|
<span class="member-tag" style="color: #3788FF;">高级版</span>
|
||||||
|
<h2 class="title">按年付费,每年198元</h2>
|
||||||
|
<div class="content">能力不设限,新功能优先体验</div><br />
|
||||||
|
<a-button v-if="version == 1 || version == 2" type="primary" size="large" class="btn-buy"
|
||||||
|
@click="onPay(3)" shape="round" :disabled="disabled">立即购买</a-button>
|
||||||
|
<a-button v-if="version == 3" type="primary" size="large" class="btn-buy" shape="round">{{ expired
|
||||||
|
}} 到期</a-button>
|
||||||
|
<br />
|
||||||
|
<div class="subscribe-list" v-for="item in ['客户管理', '合同管理', '产品管理', '仪表盘可体验365天']">
|
||||||
|
<check-circle-filled style="color: #476FFF;font-size: 20px;;" />
|
||||||
|
<span style="margin-left: 10px;">{{ item }}</span>
|
||||||
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -30,50 +56,77 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
import { CheckCircleFilled } from '@ant-design/icons-vue';
|
import { CheckCircleFilled } from '@ant-design/icons-vue';
|
||||||
import { getUserInfo, userBuy } from '../api/user';
|
import { subscribePay, getSubscribeInfo } from '../api/subscribe';
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
||||||
CheckCircleFilled
|
CheckCircleFilled
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
|
|
||||||
const ver = ref(0)
|
const router = useRouter()
|
||||||
|
|
||||||
|
const version = ref(0)
|
||||||
const expired = ref(undefined)
|
const expired = ref(undefined)
|
||||||
|
const payUrl = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const disabled = ref(false)
|
||||||
|
const active = ref(30)
|
||||||
|
|
||||||
|
const payResult = ref(false)
|
||||||
|
const title = ref('')
|
||||||
|
const buttonText = ref(undefined)
|
||||||
|
|
||||||
|
const isClick = (index) => {
|
||||||
|
active.value = index
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化数据
|
// 初始化数据
|
||||||
onMounted(() => { sysVersion() })
|
onMounted(() => { subscribeInfo() })
|
||||||
|
|
||||||
// 点击订阅
|
// 点击支付
|
||||||
const onBuy = () => {
|
const onPay = (ver) => {
|
||||||
userBuy().then((res) => {
|
let param = {
|
||||||
|
version: ver
|
||||||
|
}
|
||||||
|
subscribePay(param).then((res) => {
|
||||||
if (res.data.code == 0) {
|
if (res.data.code == 0) {
|
||||||
ver.value = res.data.data.version
|
visible.value = false
|
||||||
expired.value = moment(res.data.data.expired * 1000).format('YYYY-MM-DD')
|
payResult.value = true
|
||||||
message.success('恭喜你!订阅成功')
|
window.open(res.data.data.payUrl, '_self')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户系统版本
|
// 获取用户订阅信息
|
||||||
const sysVersion = () => {
|
const subscribeInfo = () => {
|
||||||
getUserInfo().then((res) => {
|
getSubscribeInfo().then((res) => {
|
||||||
if (res.data.code == 0) {
|
if (res.data.code == 0) {
|
||||||
ver.value = res.data.data.version
|
version.value = res.data.data.version
|
||||||
expired.value = moment(res.data.data.expired * 1000).format('YYYY-MM-DD')
|
expired.value = moment(res.data.data.expired * 1000).format('YYYY-MM-DD')
|
||||||
|
if (res.data.data.version !== 1) {
|
||||||
|
disabled.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ver,
|
version,
|
||||||
expired,
|
expired,
|
||||||
onBuy,
|
onPay,
|
||||||
sysVersion,
|
payUrl,
|
||||||
|
visible,
|
||||||
|
disabled,
|
||||||
|
payResult,
|
||||||
|
title,
|
||||||
|
active,
|
||||||
|
buttonText,
|
||||||
|
isClick,
|
||||||
|
subscribeInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +138,7 @@ export default {
|
|||||||
line-height: 47px;
|
line-height: 47px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
font-size: 28px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@@ -102,20 +155,65 @@ export default {
|
|||||||
|
|
||||||
.price {
|
.price {
|
||||||
height: 54px;
|
height: 54px;
|
||||||
font-size: 40px;
|
font-size: 35px;
|
||||||
line-height: 54px;
|
line-height: 54px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #212930;
|
color: #212930;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
min-height: 450px;
|
||||||
box-shadow: 0 1px 16px 0 rgb(33 41 48 / 5%);
|
box-shadow: 0 1px 16px 0 rgb(33 41 48 / 5%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.selected-free-card {
|
||||||
|
min-height: 450px;
|
||||||
|
box-shadow: 0 1px 16px 0 rgb(33 41 48 / 5%);
|
||||||
|
border: 2px solid #ceebfa;
|
||||||
|
background: #f0faff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-card {
|
||||||
|
min-height: 450px;
|
||||||
|
box-shadow: 0 1px 16px 0 rgb(33 41 48 / 5%);
|
||||||
|
border: 2px solid #d6ddf9;
|
||||||
|
background: #f3f6fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribe-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-tag {
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-free {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
float: right;
|
height: 50px;
|
||||||
color: #476FFF;
|
background-color: #33B9FE;
|
||||||
border-color: #476FFF;
|
border-color: #33B9FE;
|
||||||
|
display: flex;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-buy {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user