mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-23 03:24:34 +08:00
支付模块重构完成
This commit is contained in:
@@ -11,9 +11,8 @@ type OrderStatus int
|
||||
|
||||
const (
|
||||
OrderNotPaid = OrderStatus(0)
|
||||
OrderScanned = OrderStatus(1) // 已扫码
|
||||
OrderPaidSuccess = OrderStatus(2)
|
||||
OrderPaidFailed = OrderStatus(3)
|
||||
OrderPaidSuccess = OrderStatus(2) // 已支付
|
||||
OrderPaidFailed = OrderStatus(3) // 已关闭
|
||||
)
|
||||
|
||||
type OrderRemark struct {
|
||||
|
||||
@@ -81,19 +81,6 @@ func (h *ManagerHandler) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// if h.App.SysConfig.Base.EnabledVerify {
|
||||
// var check bool
|
||||
// if data.X != 0 {
|
||||
// check = h.captcha.SlideCheck(data)
|
||||
// } else {
|
||||
// check = h.captcha.Check(data)
|
||||
// }
|
||||
// if !check {
|
||||
// resp.ERROR(c, "请先完人机验证")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
var manager model.AdminUser
|
||||
res := h.DB.Model(&model.AdminUser{}).Where("username = ?", data.Username).First(&manager)
|
||||
if res.Error != nil {
|
||||
|
||||
@@ -15,9 +15,10 @@ import (
|
||||
"geekai/store/vo"
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProductHandler struct {
|
||||
@@ -43,9 +44,7 @@ func (h *ProductHandler) Save(c *gin.Context) {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Price float64 `json:"price"`
|
||||
Discount float64 `json:"discount"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Days int `json:"days"`
|
||||
Power int `json:"power"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
@@ -55,12 +54,10 @@ func (h *ProductHandler) Save(c *gin.Context) {
|
||||
}
|
||||
|
||||
item := model.Product{
|
||||
Name: data.Name,
|
||||
Price: data.Price,
|
||||
Discount: data.Discount,
|
||||
Days: data.Days,
|
||||
Power: data.Power,
|
||||
Enabled: data.Enabled}
|
||||
Name: data.Name,
|
||||
Price: data.Price,
|
||||
Power: data.Power,
|
||||
Enabled: data.Enabled}
|
||||
item.Id = data.Id
|
||||
if item.Id > 0 {
|
||||
item.CreatedAt = time.Unix(data.CreatedAt, 0)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"geekai/store/vo"
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
@@ -96,17 +95,8 @@ func (h *OrderHandler) Query(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
counter := 0
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
var item model.Order
|
||||
h.DB.Where("order_no = ?", orderNo).First(&item)
|
||||
if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status {
|
||||
order.Status = item.Status
|
||||
break
|
||||
}
|
||||
counter++
|
||||
}
|
||||
var item model.Order
|
||||
h.DB.Where("order_no = ?", orderNo).First(&item)
|
||||
|
||||
resp.SUCCESS(c, gin.H{"status": order.Status})
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package handler
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/middleware"
|
||||
@@ -77,17 +76,14 @@ func (h *PaymentHandler) RegisterRoutes() {
|
||||
|
||||
// 支付回调接口(公开)
|
||||
rg.POST("notify/alipay", h.AlipayNotify)
|
||||
rg.GET("notify/geek", h.GeekPayNotify)
|
||||
rg.POST("notify/wechat", h.WechatPayNotify)
|
||||
rg.GET("notify/epay", h.EPayNotify)
|
||||
rg.POST("notify/wxpay", h.WxpayNotify)
|
||||
|
||||
// 需要用户登录的接口
|
||||
rg.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
rg.POST("create", h.Pay)
|
||||
rg.POST("create", h.CreateOrder)
|
||||
}
|
||||
|
||||
// 同步订单状态
|
||||
h.StartSyncOrders()
|
||||
}
|
||||
|
||||
func (h *PaymentHandler) StartSyncOrders() {
|
||||
@@ -116,10 +112,11 @@ func (h *PaymentHandler) SyncOrders() error {
|
||||
}
|
||||
|
||||
for _, order := range orders {
|
||||
// 超时15分钟的订单,直接标记为已关闭
|
||||
//超时15分钟的订单,直接标记为已关闭
|
||||
if time.Now().After(order.CreatedAt.Add(time.Minute * 15)) {
|
||||
h.DB.Model(&model.Order{}).Where("id", order.Id).Update("checked", true)
|
||||
return errors.New("订单超时")
|
||||
logger.Errorf("订单超时:%v", order)
|
||||
continue
|
||||
}
|
||||
// 查询订单状态
|
||||
var res payment.OrderInfo
|
||||
@@ -127,18 +124,22 @@ func (h *PaymentHandler) SyncOrders() error {
|
||||
case payment.PayChannelEpay:
|
||||
res, err = h.epayService.Query(order.OrderNo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error with query order info: %v", err)
|
||||
logger.Errorf("error with query order info: %v", err)
|
||||
continue
|
||||
}
|
||||
// 微信支付
|
||||
case payment.PayChannelWX:
|
||||
res, err = h.wxpayService.Query(order.OrderNo)
|
||||
logger.Debugf("微信支付订单状态:%+v", res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error with query order info: %v", err)
|
||||
logger.Errorf("error with query order info: %v", err)
|
||||
continue
|
||||
}
|
||||
case payment.PayChannelAL:
|
||||
res, err = h.alipayService.Query(order.OrderNo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error with query order info: %v", err)
|
||||
logger.Errorf("error with query order info: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,24 +149,26 @@ func (h *PaymentHandler) SyncOrders() error {
|
||||
"checked": true,
|
||||
"status": types.OrderPaidFailed,
|
||||
})
|
||||
return errors.New("订单已关闭")
|
||||
logger.Errorf("订单已关闭:%v", order)
|
||||
continue
|
||||
}
|
||||
|
||||
// 订单未支付,不处理,继续轮询
|
||||
if !res.Success() {
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
|
||||
// 订单支付成功
|
||||
err = h.paySuccess(res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error with deal order: %v", err)
|
||||
logger.Errorf("error with deal order: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
func (h *PaymentHandler) CreateOrder(c *gin.Context) {
|
||||
var data struct {
|
||||
PayWay string `json:"pay_way,omitempty"` // 支付方式:支付宝,微信
|
||||
Pid int `json:"pid,omitempty"`
|
||||
@@ -210,7 +213,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
if h.config.WxPay.Domain != "" {
|
||||
data.Domain = h.config.WxPay.Domain
|
||||
}
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/wechat", data.Domain)
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/wxpay", data.Domain)
|
||||
payURL, err = h.wxpayService.Pay(payment.PayRequest{
|
||||
OutTradeNo: orderNo,
|
||||
TotalFee: fmt.Sprintf("%d", int(amount*100)),
|
||||
@@ -230,7 +233,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
if h.config.Epay.Domain != "" {
|
||||
data.Domain = h.config.Epay.Domain
|
||||
}
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Domain)
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain)
|
||||
params := payment.PayRequest{
|
||||
OutTradeNo: orderNo,
|
||||
Subject: product.Name,
|
||||
@@ -280,7 +283,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
if h.config.Epay.Domain != "" {
|
||||
data.Domain = h.config.Epay.Domain
|
||||
}
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Domain)
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain)
|
||||
params := payment.PayRequest{
|
||||
OutTradeNo: orderNo,
|
||||
Subject: product.Name,
|
||||
@@ -309,7 +312,6 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
|
||||
// 创建订单
|
||||
remark := types.OrderRemark{
|
||||
Days: product.Days,
|
||||
Power: product.Power,
|
||||
Name: product.Name,
|
||||
Price: product.Price,
|
||||
@@ -377,7 +379,7 @@ func (h *PaymentHandler) paySuccess(info payment.OrderInfo) error {
|
||||
order.Status = types.OrderPaidSuccess
|
||||
order.TradeNo = info.TradeId
|
||||
order.Checked = true
|
||||
err = h.DB.Updates(&order).Error
|
||||
err = h.DB.Debug().Updates(&order).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("error with update order info: %v", err)
|
||||
}
|
||||
@@ -418,14 +420,14 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
// GeekPayNotify 支付异步回调
|
||||
func (h *PaymentHandler) GeekPayNotify(c *gin.Context) {
|
||||
// EPayNotify 易支付支付异步回调
|
||||
func (h *PaymentHandler) EPayNotify(c *gin.Context) {
|
||||
var params = make(map[string]string)
|
||||
for k := range c.Request.URL.Query() {
|
||||
params[k] = c.Query(k)
|
||||
}
|
||||
|
||||
logger.Infof("收到GeekPay订单支付回调:%+v", params)
|
||||
logger.Infof("收到易支付订单支付回调:%+v", params)
|
||||
// 检查支付状态, 如果未支付,则返回成功
|
||||
if params["trade_status"] != "TRADE_SUCCESS" {
|
||||
c.String(http.StatusOK, "success")
|
||||
@@ -456,8 +458,8 @@ func (h *PaymentHandler) GeekPayNotify(c *gin.Context) {
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
// WechatPayNotify 微信商户支付异步回调
|
||||
func (h *PaymentHandler) WechatPayNotify(c *gin.Context) {
|
||||
// WxpayNotify 微信商户支付异步回调
|
||||
func (h *PaymentHandler) WxpayNotify(c *gin.Context) {
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
|
||||
@@ -303,6 +303,7 @@ func main() {
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) {
|
||||
h.RegisterRoutes()
|
||||
h.StartSyncOrders()
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) {
|
||||
h.RegisterRoutes()
|
||||
|
||||
@@ -118,6 +118,18 @@ func (s *MigrationService) TableMigration() {
|
||||
if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") {
|
||||
s.db.Migrator().DropColumn(&model.ChatModel{}, "description")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.Product{}, "discount") {
|
||||
s.db.Migrator().DropColumn(&model.Product{}, "discount")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.Product{}, "days") {
|
||||
s.db.Migrator().DropColumn(&model.Product{}, "days")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.Product{}, "app_url") {
|
||||
s.db.Migrator().DropColumn(&model.Product{}, "app_url")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.Product{}, "url") {
|
||||
s.db.Migrator().DropColumn(&model.Product{}, "url")
|
||||
}
|
||||
}
|
||||
|
||||
// 迁移配置数据
|
||||
|
||||
@@ -74,6 +74,7 @@ func (s *WxPayService) Pay(params PayRequest) (string, error) {
|
||||
bm.Set("total", utils.IntValue(params.TotalFee, 0)).
|
||||
Set("currency", "CNY")
|
||||
})
|
||||
logger.Debugf("wxpay params: %+v", bm)
|
||||
if params.Device == "mobile" {
|
||||
bm.SetBodyMap("scene_info", func(bm gopay.BodyMap) {
|
||||
bm.Set("payer_client_ip", params.ClientIP)
|
||||
|
||||
@@ -17,7 +17,7 @@ type Order struct {
|
||||
Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;default:0.00;comment:订单金额" json:"amount"`
|
||||
Status types.OrderStatus `gorm:"column:status;type:tinyint(1);not null;default:0;comment:订单状态(0:待支付,1:已扫码,2:支付成功)" json:"status"`
|
||||
Remark string `gorm:"column:remark;type:varchar(255);not null;comment:备注" json:"remark"`
|
||||
PayTime int64 `gorm:"column:pay_time;type:int;comment:支付时间" json:"pay_time"`
|
||||
PayTime int64 `gorm:"column:pay_time;type:int(11);comment:支付时间" json:"pay_time"`
|
||||
PayWay string `gorm:"column:pay_way;type:varchar(20);not null;comment:支付方式" json:"pay_way"`
|
||||
Channel string `gorm:"column:channel;type:varchar(30);not null;comment:支付类型渠道:支付宝,微信,聚合支付"` // 支付类型渠道
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
|
||||
|
||||
@@ -6,19 +6,15 @@ import (
|
||||
|
||||
// Product 充值产品
|
||||
type Product struct {
|
||||
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
|
||||
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
|
||||
Name string `gorm:"column:name;type:varchar(30);not null;comment:名称" json:"name"`
|
||||
Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"`
|
||||
Discount float64 `gorm:"column:discount;type:decimal(10,2);not null;default:0.00;comment:优惠金额" json:"discount"`
|
||||
Days int `gorm:"column:days;type:smallint;not null;default:0;comment:延长天数" json:"days"`
|
||||
Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"`
|
||||
Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"`
|
||||
Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"`
|
||||
SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"`
|
||||
AppUrl string `gorm:"column:app_url;type:varchar(255);comment:App跳转地址" json:"app_url"`
|
||||
Url string `gorm:"column:url;type:varchar(255);comment:跳转地址" json:"url"`
|
||||
Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"`
|
||||
Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"`
|
||||
Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"`
|
||||
Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"`
|
||||
SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (m *Product) TableName() string {
|
||||
|
||||
@@ -17,8 +17,9 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
rand2 "math/rand"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// RandString generate rand string with specified length
|
||||
@@ -72,7 +73,16 @@ func Str2stamp(str string) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
layout := "2006-01-02 15:04:05"
|
||||
var layout string
|
||||
if strings.Contains(str, "T") {
|
||||
layout = "2006-01-02T15:04:05-07:00"
|
||||
} else {
|
||||
if len(str) < 12 {
|
||||
str = str + " 00:00:00"
|
||||
}
|
||||
layout = "2006-01-02 15:04:05"
|
||||
}
|
||||
|
||||
t, err := time.ParseInLocation(layout, str, time.Local)
|
||||
if err != nil {
|
||||
return 0
|
||||
|
||||
@@ -81,7 +81,8 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
background: url('data:image/svg+xml;utf8,<svg width="100%25" height="100%25" viewBox="0 0 400 200" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="100" cy="100" r="80" fill="%23e0eaff"/><circle cx="300" cy="60" r="40" fill="%23f0f7ff"/><circle cx="320" cy="180" r="30" fill="%23e0eaff"/></svg>') no-repeat center/cover;
|
||||
background: url('data:image/svg+xml;utf8,<svg width="100%25" height="100%25" viewBox="0 0 400 200" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="100" cy="100" r="80" fill="%23e0eaff"/><circle cx="300" cy="60" r="40" fill="%23f0f7ff"/><circle cx="320" cy="180" r="30" fill="%23e0eaff"/></svg>')
|
||||
no-repeat center/cover;
|
||||
opacity: 0.08;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -99,115 +100,259 @@
|
||||
}
|
||||
|
||||
.list-box {
|
||||
.product-col {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
animation-fill-mode: both;
|
||||
|
||||
&:nth-child(1) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
&:nth-child(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
&:nth-child(4) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
&:nth-child(5) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
&:nth-child(6) {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
}
|
||||
|
||||
.product-item {
|
||||
// border: 1px solid #666666;
|
||||
background-color: var(--chat-bg);
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, var(--panel-bg) 0%, var(--chat-bg) 100%);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
margin-bottom: 24px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
|
||||
.image-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.product-header {
|
||||
position: relative;
|
||||
|
||||
.el-image {
|
||||
padding: 6px;
|
||||
|
||||
.el-image__inner {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-title {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
|
||||
.name {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
|
||||
.info-line {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 5px 0;
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.price, .expire, calls {
|
||||
display: flex;
|
||||
width: 90px;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.discount {
|
||||
color: #f56c6c;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.expire {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.power {
|
||||
color: #f2cb51;
|
||||
}
|
||||
}
|
||||
|
||||
.pay-way {
|
||||
padding: 10px 0;
|
||||
.image-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
|
||||
.el-button {
|
||||
margin: 10px 5px 0 5px;
|
||||
height: 32px;
|
||||
filter: none;
|
||||
.el-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
.icon-alipay, .icon-wechat-pay {
|
||||
color: #ffffff;
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.icon-qq {
|
||||
color: #15a6e8;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.image-overlay {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
|
||||
.vip-badge {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
|
||||
color: #333;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 4px 12px rgba(255, 215, 0, 0.4);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
.icon-jd-pay {
|
||||
color: var(--text-theme-color);
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-title {
|
||||
padding: 20px 20px 0;
|
||||
text-align: center;
|
||||
|
||||
.name {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: var(--text-theme-color);
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary-color, #666);
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-content {
|
||||
padding: 20px;
|
||||
|
||||
.price-section {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.price-info {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.currency {
|
||||
font-size: 18px;
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.icon-douyin {
|
||||
color: #0a0a0a;
|
||||
font-size: 22px;
|
||||
|
||||
.price-value {
|
||||
font-size: 32px;
|
||||
font-weight: 800;
|
||||
color: #f56c6c;
|
||||
line-height: 1;
|
||||
}
|
||||
.icon-paypal {
|
||||
|
||||
.price-unit {
|
||||
font-size: 14px;
|
||||
color: #009cde;
|
||||
color: var(--text-secondary-color, #666);
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.features-list {
|
||||
.feature-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary-color, #666);
|
||||
|
||||
i {
|
||||
color: #67c23a;
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-actions {
|
||||
padding: 0 20px 20px;
|
||||
|
||||
.payment-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
.payment-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transform: translateZ(0);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.2),
|
||||
transparent
|
||||
);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px) scale(0.98);
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 18px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 600;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
i,
|
||||
span {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
&.wechat-btn {
|
||||
background: linear-gradient(135deg, #07c160 0%, #06ad56 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 16px rgba(7, 193, 96, 0.3);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(7, 193, 96, 0.4);
|
||||
background: linear-gradient(135deg, #06ad56 0%, #07c160 100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.alipay-btn {
|
||||
background: linear-gradient(135deg, #1677ff 0%, #0e5fd8 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 16px rgba(22, 119, 255, 0.3);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(22, 119, 255, 0.4);
|
||||
background: linear-gradient(135deg, #0e5fd8 0%, #1677ff 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
// box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
box-shadow: 0 0 10px var(--shadow-color);
|
||||
background-color: var(--hover-deep-color);
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
||||
border-color: rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,4 +379,168 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加动画效果
|
||||
@keyframes pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式优化
|
||||
@media (max-width: 768px) {
|
||||
.member {
|
||||
.inner {
|
||||
.product-box {
|
||||
.list-box {
|
||||
.el-col {
|
||||
width: 100% !important;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
.product-header {
|
||||
.image-container {
|
||||
padding: 16px;
|
||||
|
||||
.el-image {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-title {
|
||||
padding: 16px 16px 0;
|
||||
|
||||
.name {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-content {
|
||||
padding: 16px;
|
||||
|
||||
.price-section {
|
||||
.price-info {
|
||||
.price-value {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-actions {
|
||||
padding: 0 16px 16px;
|
||||
|
||||
.payment-buttons {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.payment-btn {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.member {
|
||||
.inner {
|
||||
padding: 10px 0 10px 10px;
|
||||
|
||||
.product-box {
|
||||
padding: 0 10px;
|
||||
|
||||
.list-box {
|
||||
.product-item {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.product-header {
|
||||
.image-container {
|
||||
padding: 12px;
|
||||
|
||||
.el-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-title {
|
||||
padding: 12px 12px 0;
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-content {
|
||||
padding: 12px;
|
||||
|
||||
.price-section {
|
||||
.price-info {
|
||||
.price-value {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.currency {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.features-list {
|
||||
.feature-item {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-actions {
|
||||
padding: 0 12px 12px;
|
||||
|
||||
.payment-buttons {
|
||||
.payment-btn {
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<span>{{ scope.row.remark && scope.row.remark.power }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pay_method" label="支付渠道" />
|
||||
<el-table-column prop="channel_name" label="支付渠道" />
|
||||
<el-table-column prop="pay_name" label="支付名称" />
|
||||
<el-table-column label="支付时间">
|
||||
<template #default="scope">
|
||||
|
||||
@@ -340,7 +340,7 @@
|
||||
<VideoPause />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button @click="sendMessage" style="color: #754ff6" v-else>
|
||||
<el-button @click="sendMessage()" style="color: #754ff6" v-else>
|
||||
<el-tooltip class="box-item" effect="dark" content="发送">
|
||||
<el-icon><Promotion /></el-icon>
|
||||
</el-tooltip>
|
||||
@@ -832,7 +832,7 @@ const sendSSERequest = async (message) => {
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = (messageId) => {
|
||||
const sendMessage = (messageId = 0) => {
|
||||
if (!isLogin.value) {
|
||||
console.log('未登录')
|
||||
store.setShowLoginDialog(true)
|
||||
@@ -895,7 +895,7 @@ const sendMessage = (messageId) => {
|
||||
tools: toolSelected.value,
|
||||
stream: stream.value,
|
||||
files: files.value,
|
||||
last_msg_id: messageId,
|
||||
last_msg_id: messageId || 0,
|
||||
})
|
||||
|
||||
prompt.value = ''
|
||||
|
||||
@@ -35,69 +35,56 @@
|
||||
<div class="profile-bg"></div>
|
||||
|
||||
<div class="product-box">
|
||||
<!-- <div class="info" v-if="orderPayInfoText !== ''">
|
||||
<el-alert type="success" show-icon :closable="false" effect="dark">
|
||||
<strong>说明:</strong> {{ vipInfoText }}
|
||||
</el-alert>
|
||||
</div> -->
|
||||
|
||||
<el-row v-if="list.length > 0" :gutter="20" class="list-box">
|
||||
<el-col v-for="item in list" :key="item" :span="6">
|
||||
<el-row v-if="list.length > 0" :gutter="24" class="list-box">
|
||||
<el-col
|
||||
v-for="item in list"
|
||||
:key="item"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
:lg="6"
|
||||
class="product-col"
|
||||
>
|
||||
<div class="product-item">
|
||||
<div class="image-container">
|
||||
<el-image :src="vipImg" fit="cover" />
|
||||
</div>
|
||||
<div class="product-title">
|
||||
<span class="name">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<div class="info-line">
|
||||
<span class="label">商品原价:</span>
|
||||
<span class="price"
|
||||
><del>¥{{ item.price }}</del></span
|
||||
>
|
||||
<div class="product-header">
|
||||
<div class="image-container">
|
||||
<el-image :src="vipImg" fit="cover" />
|
||||
<div class="image-overlay">
|
||||
<div class="vip-badge">热销</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">优惠价:</span>
|
||||
<span class="discount">¥{{ item.discount }}</span>
|
||||
<div class="product-title">
|
||||
<h3 class="name">{{ item.name }}</h3>
|
||||
<p class="description">{{ item.description || '全模型通用算力' }}</p>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">有效期:</span>
|
||||
<span class="expire" v-if="item.days > 0">{{ item.days }}天</span>
|
||||
<span class="expire" v-else>长期有效</span>
|
||||
</div>
|
||||
|
||||
<div class="product-content">
|
||||
<div class="price-section">
|
||||
<div class="price-info">
|
||||
<span class="currency">¥</span>
|
||||
<span class="price-value">{{ item.price }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-line">
|
||||
<span class="label">算力值:</span>
|
||||
<span class="power">{{ item.power }}</span>
|
||||
<div class="features-list" v-if="item.features">
|
||||
<div class="feature-item" v-for="feature in item.features" :key="feature">
|
||||
<i class="iconfont icon-check"></i>
|
||||
<span>{{ feature }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pay-way">
|
||||
<span
|
||||
type="primary"
|
||||
v-for="payWay in payWays"
|
||||
@click="pay(item, payWay)"
|
||||
:key="payWay"
|
||||
>
|
||||
<el-button v-if="payWay.pay_type === 'alipay'" color="#15A6E8" circle>
|
||||
<i class="iconfont icon-alipay"></i>
|
||||
</el-button>
|
||||
<el-button v-else-if="payWay.pay_type === 'qqpay'" circle>
|
||||
<i class="iconfont icon-qq"></i>
|
||||
</el-button>
|
||||
<el-button v-else-if="payWay.pay_type === 'paypal'" class="paypal" round>
|
||||
<i class="iconfont icon-paypal"></i>
|
||||
</el-button>
|
||||
<el-button v-else-if="payWay.pay_type === 'jdpay'" color="#E1251B" circle>
|
||||
<i class="iconfont icon-jd-pay"></i>
|
||||
</el-button>
|
||||
<el-button v-else-if="payWay.pay_type === 'douyin'" class="douyin" circle>
|
||||
<i class="iconfont icon-douyin"></i>
|
||||
</el-button>
|
||||
<el-button v-else circle class="wechat" color="#67C23A">
|
||||
<i class="iconfont icon-wechat-pay"></i>
|
||||
</el-button>
|
||||
</span>
|
||||
<div class="product-actions">
|
||||
<div class="payment-buttons">
|
||||
<button class="payment-btn wechat-btn" @click="wxPay(item)">
|
||||
<i class="iconfont icon-wechat-pay"></i>
|
||||
<span>微信支付</span>
|
||||
</button>
|
||||
<button class="payment-btn alipay-btn" @click="alipay(item)">
|
||||
<i class="iconfont icon-alipay"></i>
|
||||
<span>支付宝</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,23 +141,20 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<!--支付二维码-->
|
||||
<el-dialog
|
||||
v-model="showDialog"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
hide-footer
|
||||
width="auto"
|
||||
v-model="showQrCode"
|
||||
:show-close="true"
|
||||
style="width: 334px; height: 368px"
|
||||
class="pay-dialog"
|
||||
>
|
||||
<div v-if="qrImg !== ''">
|
||||
<div class="product-info">
|
||||
请使用微信扫码支付:<span class="price">¥{{ price }}</span>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-center text-base">
|
||||
<span style="color: var(--el-text-color-regular)">{{ title }}</span>
|
||||
</div>
|
||||
<el-image :src="qrImg" fit="cover" />
|
||||
</div>
|
||||
<div style="padding-bottom: 10px; text-align: center">
|
||||
<el-button type="success" @click="payCallback(true)">支付成功</el-button>
|
||||
<el-button type="danger" @click="payCallback(false)">支付失败</el-button>
|
||||
</template>
|
||||
<div class="qr-container">
|
||||
<el-image :src="qrImg" style="height: 300px; width: 300px" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@@ -188,7 +172,9 @@ import { useSharedStore } from '@/store/sharedata'
|
||||
import { httpGet, httpPost } from '@/utils/http'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import QRCode from 'qrcode'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { onMounted, ref, onUnmounted } from 'vue'
|
||||
import { showLoading, closeLoading } from '@/utils/dialog'
|
||||
import { isMobile } from '@/utils/libs'
|
||||
|
||||
const list = ref([])
|
||||
const vipImg = ref('/images/menu/member.png')
|
||||
@@ -203,15 +189,13 @@ const isLogin = ref(false)
|
||||
const orderTimeout = ref(1800)
|
||||
const loading = ref(true)
|
||||
const loadingText = ref('加载中...')
|
||||
const orderPayInfoText = ref('')
|
||||
|
||||
const payWays = ref([])
|
||||
const vipInfoText = ref('')
|
||||
const store = useSharedStore()
|
||||
const userOrderKey = ref(0)
|
||||
const showDialog = ref(false)
|
||||
const showQrCode = ref(false)
|
||||
const qrImg = ref('')
|
||||
const price = ref(0)
|
||||
const title = ref('')
|
||||
const handler = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
checkSession()
|
||||
@@ -236,68 +220,78 @@ onMounted(() => {
|
||||
.then((res) => {
|
||||
rewardImg.value = res.data['reward_img']
|
||||
enableReward.value = res.data['enabled_reward']
|
||||
orderPayInfoText.value = res.data['order_pay_info_text']
|
||||
if (res.data['order_pay_timeout'] > 0) {
|
||||
orderTimeout.value = res.data['order_pay_timeout']
|
||||
}
|
||||
vipInfoText.value = res.data['vip_info_text']
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error('获取系统配置失败:' + e.message)
|
||||
})
|
||||
|
||||
httpGet('/api/payment/payWays')
|
||||
.then((res) => {
|
||||
payWays.value = res.data
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error('获取支付方式失败:' + e.message)
|
||||
})
|
||||
})
|
||||
|
||||
const pay = (product, payWay) => {
|
||||
if (!isLogin.value) {
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
loadingText.value = '正在生成支付订单...'
|
||||
let host = import.meta.env.VITE_API_HOST
|
||||
if (host === '') {
|
||||
host = `${location.protocol}//${location.host}`
|
||||
}
|
||||
httpPost(`${import.meta.env.VITE_API_HOST}/api/payment/doPay`, {
|
||||
product_id: product.id,
|
||||
pay_way: payWay.pay_way,
|
||||
pay_type: payWay.pay_type,
|
||||
user_id: user.value.id,
|
||||
host: host,
|
||||
device: 'jump',
|
||||
const selectedPid = ref(0)
|
||||
const wxPay = (product) => {
|
||||
selectedPid.value = product.id
|
||||
title.value = '请打开微信扫码支付'
|
||||
generateOrder('wxpay')
|
||||
}
|
||||
|
||||
const alipay = (product) => {
|
||||
selectedPid.value = product.id
|
||||
title.value = '请打开支付宝扫码支付'
|
||||
generateOrder('alipay')
|
||||
}
|
||||
|
||||
const generateOrder = (payWay) => {
|
||||
showLoading('正在生成支付订单...')
|
||||
// 生成支付订单
|
||||
httpPost('/api/payment/create', {
|
||||
pid: selectedPid.value,
|
||||
pay_way: payWay,
|
||||
domain: `${window.location.protocol}//${window.location.host}`,
|
||||
device: isMobile() ? 'mobile' : 'pc',
|
||||
})
|
||||
.then((res) => {
|
||||
showDialog.value = true
|
||||
loading.value = false
|
||||
if (payWay.pay_way === 'wechat') {
|
||||
price.value = Number(product.discount)
|
||||
QRCode.toDataURL(res.data, { width: 300, height: 300, margin: 2 }, (error, url) => {
|
||||
closeLoading()
|
||||
|
||||
if (isMobile()) {
|
||||
window.location.href = res.data.pay_url
|
||||
} else {
|
||||
QRCode.toDataURL(res.data.pay_url, { width: 300, height: 300, margin: 2 }, (error, url) => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
} else {
|
||||
qrImg.value = url
|
||||
}
|
||||
})
|
||||
} else {
|
||||
window.open(res.data, '_blank')
|
||||
// 查询订单状态
|
||||
if (handler.value) {
|
||||
clearTimeout(handler.value)
|
||||
}
|
||||
handler.value = setTimeout(() => queryOrder(res.data.order_no), 3000)
|
||||
showQrCode.value = true
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
setTimeout(() => {
|
||||
ElMessage.error('生成支付订单失败:' + e.message)
|
||||
loading.value = false
|
||||
}, 500)
|
||||
closeLoading()
|
||||
ElMessage.error('生成支付订单失败:' + e.message)
|
||||
})
|
||||
}
|
||||
|
||||
const queryOrder = async (orderNo) => {
|
||||
const res = await httpGet('/api/order/query?order_no=' + orderNo)
|
||||
if (res?.data.status === 2) {
|
||||
// 订单支付成功
|
||||
clearTimeout(handler.value)
|
||||
ElMessage.success('支付成功')
|
||||
showQrCode.value = false
|
||||
// 更新用户积分
|
||||
user.value.scores += res.data.credit
|
||||
} else {
|
||||
handler.value = setTimeout(() => queryOrder(orderNo), 3000)
|
||||
}
|
||||
}
|
||||
|
||||
const redeemCallback = (success) => {
|
||||
showRedeemVerifyDialog.value = false
|
||||
|
||||
@@ -306,15 +300,67 @@ const redeemCallback = (success) => {
|
||||
}
|
||||
}
|
||||
|
||||
const payCallback = (success) => {
|
||||
showDialog.value = false
|
||||
if (success) {
|
||||
userOrderKey.value += 1
|
||||
// 组件卸载时清理定时器
|
||||
onUnmounted(() => {
|
||||
if (handler.value) {
|
||||
clearTimeout(handler.value)
|
||||
handler.value = null
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../assets/css/custom-scroll.scss' as *;
|
||||
@use '../assets/css/member.scss' as *;
|
||||
@use '@/assets/css/custom-scroll.scss' as *;
|
||||
@use '@/assets/css/member.scss' as *;
|
||||
|
||||
// 支付弹窗样式优化
|
||||
.pay-dialog {
|
||||
.qr-container {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
.qr-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.success-text {
|
||||
background: #67c23a;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 支付按钮样式
|
||||
.pay-way {
|
||||
.row {
|
||||
margin: 0;
|
||||
|
||||
.col {
|
||||
padding: 0 5px;
|
||||
|
||||
button {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,14 +42,23 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="支付时间">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row['pay_time']">{{ dateFormat(scope.row['pay_time']) }}</span>
|
||||
<el-tag v-else>未支付</el-tag>
|
||||
<el-table-column label="订单状态">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === 2" type="success">已支付</el-tag>
|
||||
<el-tag v-else-if="row.status === 3" type="danger">已关闭</el-tag>
|
||||
<el-tag v-else type="warning">未支付</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pay_method" label="支付渠道" />
|
||||
<el-table-column prop="pay_name" label="支付名称" />
|
||||
|
||||
<!-- 支付时间 -->
|
||||
<el-table-column label="支付时间">
|
||||
<template #default="row">
|
||||
<span>{{ dateFormat(row.pay_time) || '--' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="pay_name" label="支付渠道" min-width="100" />
|
||||
<el-table-column prop="channel_name" label="支付渠道" min-width="100" />
|
||||
|
||||
<el-table-column label="操作" width="180">
|
||||
<template #default="scope">
|
||||
|
||||
@@ -14,14 +14,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="商品原价" />
|
||||
<el-table-column prop="discount" label="优惠价" />
|
||||
<el-table-column prop="days" label="有效期(天)">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.days === 0">长期有效</el-tag>
|
||||
<span v-else>{{ scope.row.days }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="商品价格" />
|
||||
<el-table-column prop="power" label="算力" />
|
||||
<el-table-column prop="sales" label="销量" />
|
||||
<el-table-column prop="enabled" label="启用状态">
|
||||
@@ -55,18 +48,10 @@
|
||||
<el-input v-model="item.name" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="商品原价:" prop="price">
|
||||
<el-form-item label="商品价格:" prop="price">
|
||||
<el-input v-model="item.price" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="优惠价:" prop="discount">
|
||||
<el-input v-model="item.discount" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有效期:" prop="days">
|
||||
<el-input v-model.number="item.days" autocomplete="off" placeholder="会员有效期(天)" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="算力:" prop="power">
|
||||
<el-input v-model.number="item.power" autocomplete="off" placeholder="增加算力值" />
|
||||
</el-form-item>
|
||||
|
||||
Reference in New Issue
Block a user