fix: 订阅模式登录验证、永久会员类型区分、动态代码HTTP返回值修复、侧边栏滚动位置保持
- 修复订阅模式登录时错误检查余额的问题 - 区分无限余额和永久订阅两种永久会员类型 - 修复动态代码HTTP请求返回值在JS中无法正确访问的问题 - 添加侧边栏滚动位置保持功能 - 移除developer角色相关代码,统一使用admin - 添加缺失的i18n翻译key
This commit is contained in:
@@ -331,13 +331,26 @@ func initData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 迁移:将 developer 角色统一为 admin
|
// 迁移:将 developer 角色统一为 admin
|
||||||
var developerCount int64
|
var devRoleCount int64
|
||||||
DB.Model(&model.User{}).Where("role = ?", "developer").Count(&developerCount)
|
DB.Model(&model.User{}).Where("role = ?", "developer").Count(&devRoleCount)
|
||||||
if developerCount > 0 {
|
if devRoleCount > 0 {
|
||||||
log.Printf("Migrating %d developer users to admin role...", developerCount)
|
log.Printf("Migrating %d developer users to admin role...", devRoleCount)
|
||||||
DB.Model(&model.User{}).Where("role = ?", "developer").Update("role", "admin")
|
DB.Model(&model.User{}).Where("role = ?", "developer").Update("role", "admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 迁移:将 developer_id 列名改为 admin_id
|
||||||
|
if DB.Migrator().HasColumn(&model.AgentApplication{}, "developer_id") {
|
||||||
|
log.Println("Migrating agent_applications.developer_id to admin_id...")
|
||||||
|
DB.Exec("ALTER TABLE agent_applications RENAME COLUMN developer_id TO admin_id")
|
||||||
|
}
|
||||||
|
if DB.Migrator().HasColumn(&model.AgentApplicationRequest{}, "developer_id") {
|
||||||
|
log.Println("Migrating agent_application_requests.developer_id to admin_id...")
|
||||||
|
DB.Exec("ALTER TABLE agent_application_requests RENAME COLUMN developer_id TO admin_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 迁移:将 write_permission 默认值从 developer 改为 admin
|
||||||
|
DB.Exec("UPDATE cloud_variables SET write_permission = 'admin' WHERE write_permission = 'developer'")
|
||||||
|
|
||||||
// 清理没有关联应用的云端常量和变量
|
// 清理没有关联应用的云端常量和变量
|
||||||
var orphanConstants int64
|
var orphanConstants int64
|
||||||
DB.Model(&model.CloudConstant{}).Where("app_id IS NULL").Count(&orphanConstants)
|
DB.Model(&model.CloudConstant{}).Where("app_id IS NULL").Count(&orphanConstants)
|
||||||
|
|||||||
@@ -114,26 +114,6 @@ func AdminAuth() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeveloperAuth 开发者权限中间件(管理员即开发者)
|
|
||||||
func DeveloperAuth() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
role, exists := c.Get("role")
|
|
||||||
if !exists {
|
|
||||||
response.Error(c, http.StatusForbidden, "Access denied")
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if role != "admin" {
|
|
||||||
response.Error(c, http.StatusForbidden, "Admin access required")
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AgentAuth 代理商授权中间件
|
// AgentAuth 代理商授权中间件
|
||||||
func AgentAuth() gin.HandlerFunc {
|
func AgentAuth() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ type Ticket struct {
|
|||||||
Title string `gorm:"size:200" json:"title"`
|
Title string `gorm:"size:200" json:"title"`
|
||||||
Content string `gorm:"type:text" json:"content"`
|
Content string `gorm:"type:text" json:"content"`
|
||||||
Category string `gorm:"size:50" json:"category"` // account, payment, technical, feature, other
|
Category string `gorm:"size:50" json:"category"` // account, payment, technical, feature, other
|
||||||
Type string `gorm:"size:50" json:"type"` // user, agent, developer
|
Type string `gorm:"size:50" json:"type"` // user, agent, admin
|
||||||
Status string `gorm:"size:20;default:open" json:"status"` // open, processing, resolved, closed
|
Status string `gorm:"size:20;default:open" json:"status"` // open, processing, resolved, closed
|
||||||
Priority string `gorm:"size:20;default:normal" json:"priority"` // low, normal, high, urgent
|
Priority string `gorm:"size:20;default:normal" json:"priority"` // low, normal, high, urgent
|
||||||
AssignedTo *uint `json:"assigned_to"` // 分配给的应用开发者ID或平台管理员ID
|
AssignedTo *uint `json:"assigned_to"` // 分配给的应用开发者ID或平台管理员ID
|
||||||
@@ -464,7 +464,7 @@ type CloudVariable struct {
|
|||||||
OriginalName string `gorm:"size:255" json:"original_name"`
|
OriginalName string `gorm:"size:255" json:"original_name"`
|
||||||
FileMD5 string `gorm:"size:32" json:"file_md5"`
|
FileMD5 string `gorm:"size:32" json:"file_md5"`
|
||||||
Scope string `gorm:"size:20;default:app" json:"scope"`
|
Scope string `gorm:"size:20;default:app" json:"scope"`
|
||||||
WritePermission string `gorm:"size:20;default:developer" json:"write_permission"`
|
WritePermission string `gorm:"size:20;default:admin" json:"write_permission"`
|
||||||
Description string `gorm:"size:255" json:"description"`
|
Description string `gorm:"size:255" json:"description"`
|
||||||
Status string `gorm:"size:20;default:active" json:"status"`
|
Status string `gorm:"size:20;default:active" json:"status"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
@@ -555,7 +555,7 @@ type AgentApplication struct {
|
|||||||
ID uint `gorm:"primaryKey" json:"id"`
|
ID uint `gorm:"primaryKey" json:"id"`
|
||||||
AgentID uint `json:"agent_id"`
|
AgentID uint `json:"agent_id"`
|
||||||
ApplicationID uint `json:"application_id"`
|
ApplicationID uint `json:"application_id"`
|
||||||
DeveloperID uint `json:"developer_id"`
|
AdminID uint `json:"admin_id"`
|
||||||
Discount float64 `gorm:"default:1.0" json:"discount"`
|
Discount float64 `gorm:"default:1.0" json:"discount"`
|
||||||
Status string `gorm:"size:20;default:active" json:"status"`
|
Status string `gorm:"size:20;default:active" json:"status"`
|
||||||
IsReceived bool `gorm:"default:false" json:"is_received"`
|
IsReceived bool `gorm:"default:false" json:"is_received"`
|
||||||
@@ -565,7 +565,7 @@ type AgentApplication struct {
|
|||||||
|
|
||||||
Agent User `gorm:"foreignKey:AgentID;references:ID" json:"agent,omitempty"`
|
Agent User `gorm:"foreignKey:AgentID;references:ID" json:"agent,omitempty"`
|
||||||
Application Application `gorm:"foreignKey:ApplicationID;references:ID" json:"application,omitempty"`
|
Application Application `gorm:"foreignKey:ApplicationID;references:ID" json:"application,omitempty"`
|
||||||
Developer User `gorm:"foreignKey:DeveloperID;references:ID" json:"developer,omitempty"`
|
Admin User `gorm:"foreignKey:AdminID;references:ID" json:"admin,omitempty"`
|
||||||
CardTypes []AgentApplicationCardType `gorm:"foreignKey:AgentApplicationID" json:"card_types,omitempty"`
|
CardTypes []AgentApplicationCardType `gorm:"foreignKey:AgentApplicationID" json:"card_types,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +586,7 @@ type AgentApplicationCardType struct {
|
|||||||
type AgentApplicationRequest struct {
|
type AgentApplicationRequest struct {
|
||||||
ID uint `gorm:"primaryKey" json:"id"`
|
ID uint `gorm:"primaryKey" json:"id"`
|
||||||
AgentID uint `json:"agent_id"`
|
AgentID uint `json:"agent_id"`
|
||||||
DeveloperID uint `json:"developer_id"`
|
AdminID uint `json:"admin_id"`
|
||||||
ApplicationID uint `json:"application_id"`
|
ApplicationID uint `json:"application_id"`
|
||||||
Type string `gorm:"size:20" json:"type"`
|
Type string `gorm:"size:20" json:"type"`
|
||||||
Status string `gorm:"size:20;default:pending" json:"status"`
|
Status string `gorm:"size:20;default:pending" json:"status"`
|
||||||
@@ -597,7 +597,7 @@ type AgentApplicationRequest struct {
|
|||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|
||||||
Agent User `gorm:"foreignKey:AgentID" json:"agent,omitempty"`
|
Agent User `gorm:"foreignKey:AgentID" json:"agent,omitempty"`
|
||||||
Developer User `gorm:"foreignKey:DeveloperID" json:"developer,omitempty"`
|
Admin User `gorm:"foreignKey:AdminID" json:"admin,omitempty"`
|
||||||
Application Application `gorm:"foreignKey:ApplicationID" json:"application,omitempty"`
|
Application Application `gorm:"foreignKey:ApplicationID" json:"application,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-36
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -66,10 +66,10 @@ func checkAgentPermission(userID uint) bool {
|
|||||||
|
|
||||||
func handleGetAgentApps(c *gin.Context) {
|
func handleGetAgentApps(c *gin.Context) {
|
||||||
userID := c.GetUint("user_id")
|
userID := c.GetUint("user_id")
|
||||||
log.Printf("[DEBUG] handleGetAgentApps called for developer %d\n", userID)
|
log.Printf("[DEBUG] handleGetAgentApps called for Admin %d\n", userID)
|
||||||
|
|
||||||
var myAuthorizations []model.AgentApplication
|
var myAuthorizations []model.AgentApplication
|
||||||
if err := database.DB.Where("developer_id = ?", userID).
|
if err := database.DB.Where("admin_id = ?", userID).
|
||||||
Preload("CardTypes.CardType").
|
Preload("CardTypes.CardType").
|
||||||
Find(&myAuthorizations).Error; err != nil {
|
Find(&myAuthorizations).Error; err != nil {
|
||||||
response.Error(c, 500, "获取授权列表失败")
|
response.Error(c, 500, "获取授权列表失败")
|
||||||
@@ -84,7 +84,7 @@ func handleGetAgentApps(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Found %d my authorizations and %d received authorizations for developer %d\n",
|
log.Printf("[DEBUG] Found %d my authorizations and %d received authorizations for Admin %d\n",
|
||||||
len(myAuthorizations), len(receivedAuthorizations), userID)
|
len(myAuthorizations), len(receivedAuthorizations), userID)
|
||||||
|
|
||||||
var allAgentApps []model.AgentApplication
|
var allAgentApps []model.AgentApplication
|
||||||
@@ -92,17 +92,17 @@ func handleGetAgentApps(c *gin.Context) {
|
|||||||
allAgentApps = append(allAgentApps, receivedAuthorizations...)
|
allAgentApps = append(allAgentApps, receivedAuthorizations...)
|
||||||
|
|
||||||
var agentIDs []uint
|
var agentIDs []uint
|
||||||
var developerIDs []uint
|
var AdminIDs []uint
|
||||||
var applicationIDs []uint
|
var applicationIDs []uint
|
||||||
|
|
||||||
for _, aa := range allAgentApps {
|
for _, aa := range allAgentApps {
|
||||||
agentIDs = append(agentIDs, aa.AgentID)
|
agentIDs = append(agentIDs, aa.AgentID)
|
||||||
developerIDs = append(developerIDs, aa.DeveloperID)
|
AdminIDs = append(AdminIDs, aa.AdminID)
|
||||||
applicationIDs = append(applicationIDs, aa.ApplicationID)
|
applicationIDs = append(applicationIDs, aa.ApplicationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var users []model.User
|
var users []model.User
|
||||||
if err := database.DB.Where("id IN ?", append(agentIDs, developerIDs...)).Find(&users).Error; err != nil {
|
if err := database.DB.Where("id IN ?", append(agentIDs, AdminIDs...)).Find(&users).Error; err != nil {
|
||||||
log.Printf("[ERROR] Failed to query users: %v\n", err)
|
log.Printf("[ERROR] Failed to query users: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[DEBUG] Found %d users\n", len(users))
|
log.Printf("[DEBUG] Found %d users\n", len(users))
|
||||||
@@ -199,7 +199,7 @@ func handleGetAgentRequests(c *gin.Context) {
|
|||||||
userID := c.GetUint("user_id")
|
userID := c.GetUint("user_id")
|
||||||
requestType := c.Query("type")
|
requestType := c.Query("type")
|
||||||
|
|
||||||
query := database.DB.Where("developer_id = ?", userID)
|
query := database.DB.Where("admin_id = ?", userID)
|
||||||
if requestType == "invite" {
|
if requestType == "invite" {
|
||||||
query = query.Where("type = ?", "invite")
|
query = query.Where("type = ?", "invite")
|
||||||
} else if requestType == "request" {
|
} else if requestType == "request" {
|
||||||
@@ -221,7 +221,7 @@ func handleGetAgentRequests(c *gin.Context) {
|
|||||||
AgentID uint `json:"agent_id"`
|
AgentID uint `json:"agent_id"`
|
||||||
AgentName string `json:"agent_name"`
|
AgentName string `json:"agent_name"`
|
||||||
AgentEmail string `json:"agent_email"`
|
AgentEmail string `json:"agent_email"`
|
||||||
DeveloperID uint `json:"developer_id"`
|
AdminID uint `json:"admin_id"`
|
||||||
ApplicationID uint `json:"application_id"`
|
ApplicationID uint `json:"application_id"`
|
||||||
AppName string `json:"app_name"`
|
AppName string `json:"app_name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@@ -252,7 +252,7 @@ func handleGetAgentRequests(c *gin.Context) {
|
|||||||
AgentID: req.AgentID,
|
AgentID: req.AgentID,
|
||||||
AgentName: agentName,
|
AgentName: agentName,
|
||||||
AgentEmail: agentEmail,
|
AgentEmail: agentEmail,
|
||||||
DeveloperID: req.DeveloperID,
|
AdminID: req.AdminID,
|
||||||
ApplicationID: req.ApplicationID,
|
ApplicationID: req.ApplicationID,
|
||||||
AppName: appName,
|
AppName: appName,
|
||||||
Type: req.Type,
|
Type: req.Type,
|
||||||
@@ -282,7 +282,7 @@ func handleGetMyRequests(c *gin.Context) {
|
|||||||
|
|
||||||
var requests []model.AgentApplicationRequest
|
var requests []model.AgentApplicationRequest
|
||||||
if err := query.
|
if err := query.
|
||||||
Preload("Developer").
|
Preload("Admin").
|
||||||
Preload("Application").
|
Preload("Application").
|
||||||
Order("created_at DESC").
|
Order("created_at DESC").
|
||||||
Find(&requests).Error; err != nil {
|
Find(&requests).Error; err != nil {
|
||||||
@@ -293,8 +293,8 @@ func handleGetMyRequests(c *gin.Context) {
|
|||||||
type RequestResponse struct {
|
type RequestResponse struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
AgentID uint `json:"agent_id"`
|
AgentID uint `json:"agent_id"`
|
||||||
DeveloperID uint `json:"developer_id"`
|
AdminID uint `json:"admin_id"`
|
||||||
DeveloperName string `json:"developer_name"`
|
AdminName string `json:"admin_name"`
|
||||||
ApplicationID uint `json:"application_id"`
|
ApplicationID uint `json:"application_id"`
|
||||||
AppName string `json:"app_name"`
|
AppName string `json:"app_name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@@ -306,9 +306,9 @@ func handleGetMyRequests(c *gin.Context) {
|
|||||||
|
|
||||||
var result []RequestResponse
|
var result []RequestResponse
|
||||||
for _, req := range requests {
|
for _, req := range requests {
|
||||||
developerName := ""
|
AdminName := ""
|
||||||
if req.Developer.ID != 0 {
|
if req.Admin.ID != 0 {
|
||||||
developerName = req.Developer.Username
|
AdminName = req.Admin.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
appName := ""
|
appName := ""
|
||||||
@@ -319,8 +319,8 @@ func handleGetMyRequests(c *gin.Context) {
|
|||||||
result = append(result, RequestResponse{
|
result = append(result, RequestResponse{
|
||||||
ID: req.ID,
|
ID: req.ID,
|
||||||
AgentID: req.AgentID,
|
AgentID: req.AgentID,
|
||||||
DeveloperID: req.DeveloperID,
|
AdminID: req.AdminID,
|
||||||
DeveloperName: developerName,
|
AdminName: AdminName,
|
||||||
ApplicationID: req.ApplicationID,
|
ApplicationID: req.ApplicationID,
|
||||||
AppName: appName,
|
AppName: appName,
|
||||||
Type: req.Type,
|
Type: req.Type,
|
||||||
@@ -379,7 +379,7 @@ func handleInviteAgent(c *gin.Context) {
|
|||||||
|
|
||||||
request := model.AgentApplicationRequest{
|
request := model.AgentApplicationRequest{
|
||||||
AgentID: req.AgentID,
|
AgentID: req.AgentID,
|
||||||
DeveloperID: userID,
|
AdminID: userID,
|
||||||
ApplicationID: req.ApplicationID,
|
ApplicationID: req.ApplicationID,
|
||||||
Type: "invite",
|
Type: "invite",
|
||||||
Status: "pending",
|
Status: "pending",
|
||||||
@@ -405,7 +405,7 @@ func handleInviteAgent(c *gin.Context) {
|
|||||||
func handleRequestAuthorization(c *gin.Context) {
|
func handleRequestAuthorization(c *gin.Context) {
|
||||||
userID := c.GetUint("user_id")
|
userID := c.GetUint("user_id")
|
||||||
var req struct {
|
var req struct {
|
||||||
DeveloperID uint `json:"developer_id"`
|
AdminID uint `json:"admin_id"`
|
||||||
ApplicationID uint `json:"application_id"`
|
ApplicationID uint `json:"application_id"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
@@ -414,18 +414,18 @@ func handleRequestAuthorization(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var developer model.User
|
var Admin model.User
|
||||||
if err := database.DB.First(&developer, req.DeveloperID).Error; err != nil {
|
if err := database.DB.First(&Admin, req.AdminID).Error; err != nil {
|
||||||
response.Error(c, 404, "开发者不存在")
|
response.Error(c, 404, "开发者不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if developer.Role != "admin" {
|
if Admin.Role != "admin" {
|
||||||
response.Error(c, 400, "该用户不是管理员")
|
response.Error(c, 400, "该用户不是管理员")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var app model.Application
|
var app model.Application
|
||||||
if err := database.DB.Where("id = ? AND user_id = ?", req.ApplicationID, req.DeveloperID).First(&app).Error; err != nil {
|
if err := database.DB.Where("id = ? AND user_id = ?", req.ApplicationID, req.AdminID).First(&app).Error; err != nil {
|
||||||
response.Error(c, 404, "应用不存在")
|
response.Error(c, 404, "应用不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -437,15 +437,15 @@ func handleRequestAuthorization(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var existingRequest model.AgentApplicationRequest
|
var existingRequest model.AgentApplicationRequest
|
||||||
if err := database.DB.Where("agent_id = ? AND developer_id = ? AND application_id = ? AND status = ?",
|
if err := database.DB.Where("agent_id = ? AND admin_id = ? AND application_id = ? AND status = ?",
|
||||||
userID, req.DeveloperID, req.ApplicationID, "pending").First(&existingRequest).Error; err == nil {
|
userID, req.AdminID, req.ApplicationID, "pending").First(&existingRequest).Error; err == nil {
|
||||||
response.Error(c, 400, "您已有待处理的申请")
|
response.Error(c, 400, "您已有待处理的申请")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
request := model.AgentApplicationRequest{
|
request := model.AgentApplicationRequest{
|
||||||
AgentID: userID,
|
AgentID: userID,
|
||||||
DeveloperID: req.DeveloperID,
|
AdminID: req.AdminID,
|
||||||
ApplicationID: req.ApplicationID,
|
ApplicationID: req.ApplicationID,
|
||||||
Type: "request",
|
Type: "request",
|
||||||
Status: "pending",
|
Status: "pending",
|
||||||
@@ -459,8 +459,8 @@ func handleRequestAuthorization(c *gin.Context) {
|
|||||||
|
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"id": request.ID,
|
"id": request.ID,
|
||||||
"developer_id": request.DeveloperID,
|
"admin_id": request.AdminID,
|
||||||
"developer_name": developer.Username,
|
"admin_name": Admin.Username,
|
||||||
"app_id": request.ApplicationID,
|
"app_id": request.ApplicationID,
|
||||||
"app_name": app.Name,
|
"app_name": app.Name,
|
||||||
"type": request.Type,
|
"type": request.Type,
|
||||||
@@ -478,7 +478,7 @@ func handleApproveRequest(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req model.AgentApplicationRequest
|
var req model.AgentApplicationRequest
|
||||||
if err := database.DB.Where("id = ? AND developer_id = ?", requestID, userID).First(&req).Error; err != nil {
|
if err := database.DB.Where("id = ? AND admin_id = ?", requestID, userID).First(&req).Error; err != nil {
|
||||||
response.Error(c, 404, "申请不存在")
|
response.Error(c, 404, "申请不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -493,7 +493,7 @@ func handleApproveRequest(c *gin.Context) {
|
|||||||
agentApp := model.AgentApplication{
|
agentApp := model.AgentApplication{
|
||||||
AgentID: req.AgentID,
|
AgentID: req.AgentID,
|
||||||
ApplicationID: req.ApplicationID,
|
ApplicationID: req.ApplicationID,
|
||||||
DeveloperID: userID,
|
AdminID: userID,
|
||||||
Discount: 1.0,
|
Discount: 1.0,
|
||||||
Status: "active",
|
Status: "active",
|
||||||
IsReceived: true,
|
IsReceived: true,
|
||||||
@@ -544,7 +544,7 @@ func handleRejectRequest(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req model.AgentApplicationRequest
|
var req model.AgentApplicationRequest
|
||||||
if err := database.DB.Where("id = ? AND developer_id = ?", requestID, userID).First(&req).Error; err != nil {
|
if err := database.DB.Where("id = ? AND admin_id = ?", requestID, userID).First(&req).Error; err != nil {
|
||||||
response.Error(c, 404, "申请不存在")
|
response.Error(c, 404, "申请不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -572,7 +572,7 @@ func handleGetAgentAppDetail(c *gin.Context) {
|
|||||||
agentAppID := c.Param("id")
|
agentAppID := c.Param("id")
|
||||||
|
|
||||||
var agentApp model.AgentApplication
|
var agentApp model.AgentApplication
|
||||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).
|
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).
|
||||||
Preload("CardTypes.CardType").
|
Preload("CardTypes.CardType").
|
||||||
First(&agentApp).Error; err != nil {
|
First(&agentApp).Error; err != nil {
|
||||||
response.Error(c, 404, "授权记录不存在")
|
response.Error(c, 404, "授权记录不存在")
|
||||||
@@ -652,7 +652,7 @@ func handleUpdateAgentApp(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var agentApp model.AgentApplication
|
var agentApp model.AgentApplication
|
||||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||||
response.Error(c, 404, "授权记录不存在")
|
response.Error(c, 404, "授权记录不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -693,7 +693,7 @@ func handleUpdateAgentCardTypes(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var agentApp model.AgentApplication
|
var agentApp model.AgentApplication
|
||||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||||
response.Error(c, 404, "授权记录不存在")
|
response.Error(c, 404, "授权记录不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -731,7 +731,7 @@ func handleRemoveAgentApp(c *gin.Context) {
|
|||||||
agentAppID := c.Param("id")
|
agentAppID := c.Param("id")
|
||||||
|
|
||||||
var agentApp model.AgentApplication
|
var agentApp model.AgentApplication
|
||||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||||
response.Error(c, 404, "授权记录不存在")
|
response.Error(c, 404, "授权记录不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -48,9 +48,9 @@ func SetupCardRoutesWithoutPackage(r *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDeveloperPackageValid(developerID uint) bool {
|
func checkAdminPackageValid(AdminID uint) bool {
|
||||||
var user model.User
|
var user model.User
|
||||||
if err := database.DB.First(&user, developerID).Error; err != nil {
|
if err := database.DB.First(&user, AdminID).Error; err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ func checkDeveloperPackageValid(developerID uint) bool {
|
|||||||
|
|
||||||
var userPackage model.UserPackage
|
var userPackage model.UserPackage
|
||||||
if err := database.DB.Where("user_id = ? AND package_id = ? AND status = ?",
|
if err := database.DB.Where("user_id = ? AND package_id = ? AND status = ?",
|
||||||
developerID, user.CurrentPackageID, "active").First(&userPackage).Error; err != nil {
|
AdminID, user.CurrentPackageID, "active").First(&userPackage).Error; err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,7 +678,7 @@ func handleBatchGenerateCards(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[DEBUG] AgentApp found - ID: %d, DeveloperID: %d\n", agentApp.ID, agentApp.DeveloperID)
|
fmt.Printf("[DEBUG] AgentApp found - ID: %d, AdminID: %d\n", agentApp.ID, agentApp.AdminID)
|
||||||
|
|
||||||
var cardTypePerm *model.AgentApplicationCardType
|
var cardTypePerm *model.AgentApplicationCardType
|
||||||
for _, ct := range agentApp.CardTypes {
|
for _, ct := range agentApp.CardTypes {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package developer
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
@@ -116,13 +116,24 @@ func handleAppHeartbeat(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if shouldDeduct {
|
if shouldDeduct {
|
||||||
if user.Balance >= app.DeductionAmount {
|
if user.Balance == -1 {
|
||||||
user.Balance -= app.DeductionAmount
|
log.Printf("[DEBUG] User %d is permanent member, skip deduction", user.ID)
|
||||||
log.Printf("[DEBUG] Deducted %.2f from user %d, new balance: %.2f", app.DeductionAmount, user.ID, user.Balance)
|
} else if app.BillingType == "subscription" {
|
||||||
|
if user.ExpiryAt == nil || user.ExpiryAt.Before(now) {
|
||||||
|
log.Printf("[DEBUG] User %d subscription expired, ExpiryAt=%v", user.ID, user.ExpiryAt)
|
||||||
|
response.Error(c, 403, "订阅已过期")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] User %d subscription valid, skip balance deduction", user.ID)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[DEBUG] User %d has insufficient balance: %.2f < %.2f", user.ID, user.Balance, app.DeductionAmount)
|
if user.Balance >= app.DeductionAmount {
|
||||||
response.Error(c, 403, "余额不足")
|
user.Balance -= app.DeductionAmount
|
||||||
return
|
log.Printf("[DEBUG] Deducted %.2f from user %d, new balance: %.2f", app.DeductionAmount, user.ID, user.Balance)
|
||||||
|
} else {
|
||||||
|
log.Printf("[DEBUG] User %d has insufficient balance: %.2f < %.2f", user.ID, user.Balance, app.DeductionAmount)
|
||||||
|
response.Error(c, 403, "余额不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -456,11 +456,24 @@ func handleAppLogin(c *gin.Context) {
|
|||||||
log.Printf("[DEBUG] isTrialValid=%v, IsTrialUser=%v, TrialEndAt=%v, Balance=%f", isTrialValid, user.IsTrialUser, user.TrialEndAt, user.Balance)
|
log.Printf("[DEBUG] isTrialValid=%v, IsTrialUser=%v, TrialEndAt=%v, Balance=%f", isTrialValid, user.IsTrialUser, user.TrialEndAt, user.Balance)
|
||||||
|
|
||||||
if !isTrialValid {
|
if !isTrialValid {
|
||||||
if appModel.BillingType != "free" && user.Balance <= 0 {
|
if appModel.BillingType != "free" {
|
||||||
log.Printf("[DEBUG] User %d has no balance remaining", user.ID)
|
if user.Balance == -1 {
|
||||||
service.LogVerification(c, &appModel.ID, &user.ID, "login_failed", "登录失败: 余额不足 - "+req.Username, req.DeviceID, fmt.Errorf("余额不足,请充值后继续使用"))
|
log.Printf("[DEBUG] User %d is permanent member, allowing login", user.ID)
|
||||||
response.Error(c, 403, "余额不足,请充值后继续使用")
|
} else if appModel.BillingType == "subscription" {
|
||||||
return
|
if user.ExpiryAt == nil || user.ExpiryAt.Before(time.Now()) {
|
||||||
|
log.Printf("[DEBUG] User %d subscription expired, ExpiryAt=%v", user.ID, user.ExpiryAt)
|
||||||
|
service.LogVerification(c, &appModel.ID, &user.ID, "login_failed", "登录失败: 订阅已过期 - "+req.Username, req.DeviceID, fmt.Errorf("订阅已过期,请充值后继续使用"))
|
||||||
|
response.Error(c, 403, "订阅已过期,请充值后继续使用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if user.Balance <= 0 {
|
||||||
|
log.Printf("[DEBUG] User %d has no balance remaining", user.ID)
|
||||||
|
service.LogVerification(c, &appModel.ID, &user.ID, "login_failed", "登录失败: 余额不足 - "+req.Username, req.DeviceID, fmt.Errorf("余额不足,请充值后继续使用"))
|
||||||
|
response.Error(c, 403, "余额不足,请充值后继续使用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -549,18 +549,18 @@ func handleAppUploadVariableBinary(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
var developer model.User
|
var adminUser model.User
|
||||||
if err := database.DB.First(&developer, app.UserID).Error; err != nil {
|
if err := database.DB.First(&adminUser, app.UserID).Error; err != nil {
|
||||||
response.Error(c, 500, "获取开发者信息失败")
|
response.Error(c, 500, "获取管理员信息失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if developer.CurrentPackageID != nil {
|
if adminUser.CurrentPackageID != nil {
|
||||||
var permission model.PackagePermission
|
var permission model.PackagePermission
|
||||||
if err := database.DB.Where("package_id = ?", developer.CurrentPackageID).First(&permission).Error; err == nil {
|
if err := database.DB.Where("package_id = ?", adminUser.CurrentPackageID).First(&permission).Error; err == nil {
|
||||||
maxStorageBytes := int64(permission.MaxStorage) * 1024 * 1024
|
maxStorageBytes := int64(permission.MaxStorage) * 1024 * 1024
|
||||||
if developer.StorageUsed+header.Size > maxStorageBytes {
|
if adminUser.StorageUsed+header.Size > maxStorageBytes {
|
||||||
usedMB := float64(developer.StorageUsed) / 1024 / 1024
|
usedMB := float64(adminUser.StorageUsed) / 1024 / 1024
|
||||||
maxMB := float64(permission.MaxStorage)
|
maxMB := float64(permission.MaxStorage)
|
||||||
response.Error(c, 403, fmt.Sprintf("存储空间不足,已使用 %.2f MB / %.2f MB", usedMB, maxMB))
|
response.Error(c, 403, fmt.Sprintf("存储空间不足,已使用 %.2f MB / %.2f MB", usedMB, maxMB))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -250,19 +250,35 @@ func handleExecuteDynamicCode(c *gin.Context) {
|
|||||||
|
|
||||||
httpClient := httputil.NewHTTPClient(10 * time.Second)
|
httpClient := httputil.NewHTTPClient(10 * time.Second)
|
||||||
httpObj := map[string]interface{}{
|
httpObj := map[string]interface{}{
|
||||||
"get": func(url string, headers map[string]interface{}) *httputil.HTTPResponse {
|
"get": func(url string, headers map[string]interface{}) map[string]interface{} {
|
||||||
convertedHeaders := make(map[string]string)
|
convertedHeaders := make(map[string]string)
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
convertedHeaders[k] = fmt.Sprintf("%v", v)
|
convertedHeaders[k] = fmt.Sprintf("%v", v)
|
||||||
}
|
}
|
||||||
return httpClient.Get(url, convertedHeaders)
|
resp := httpClient.Get(url, convertedHeaders)
|
||||||
|
return map[string]interface{}{
|
||||||
|
"statusCode": resp.StatusCode,
|
||||||
|
"status": resp.Status,
|
||||||
|
"headers": resp.Headers,
|
||||||
|
"body": resp.Body,
|
||||||
|
"json": resp.JSON,
|
||||||
|
"error": resp.Error,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"post": func(url string, headers map[string]interface{}, body interface{}) *httputil.HTTPResponse {
|
"post": func(url string, headers map[string]interface{}, body interface{}) map[string]interface{} {
|
||||||
convertedHeaders := make(map[string]string)
|
convertedHeaders := make(map[string]string)
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
convertedHeaders[k] = fmt.Sprintf("%v", v)
|
convertedHeaders[k] = fmt.Sprintf("%v", v)
|
||||||
}
|
}
|
||||||
return httpClient.Post(url, convertedHeaders, body)
|
resp := httpClient.Post(url, convertedHeaders, body)
|
||||||
|
return map[string]interface{}{
|
||||||
|
"statusCode": resp.StatusCode,
|
||||||
|
"status": resp.Status,
|
||||||
|
"headers": resp.Headers,
|
||||||
|
"body": resp.Body,
|
||||||
|
"json": resp.JSON,
|
||||||
|
"error": resp.Error,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := vm.Set("http", httpObj); err != nil {
|
if err := vm.Set("http", httpObj); err != nil {
|
||||||
|
|||||||
@@ -91,8 +91,14 @@ func handleAppRecharge(c *gin.Context) {
|
|||||||
user.IsTrialUser = false
|
user.IsTrialUser = false
|
||||||
|
|
||||||
if card.CardType.Value == -1 {
|
if card.CardType.Value == -1 {
|
||||||
user.Balance = -1
|
if card.CardType.RechargeType == "subscription" {
|
||||||
user.ExpiryAt = nil
|
permanentExpiry := time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||||
|
user.ExpiryAt = &permanentExpiry
|
||||||
|
user.Balance = -1
|
||||||
|
} else {
|
||||||
|
user.Balance = -1
|
||||||
|
user.ExpiryAt = nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
switch card.CardType.RechargeType {
|
switch card.CardType.RechargeType {
|
||||||
case "subscription":
|
case "subscription":
|
||||||
|
|||||||
@@ -673,7 +673,7 @@ func HandleCreateOrder(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyURL := fmt.Sprintf("%s/api/v1/payment/callback/bepusdt", callbackBaseURL)
|
notifyURL := fmt.Sprintf("%s/api/v1/payment/callback/bepusdt", callbackBaseURL)
|
||||||
redirectURL := fmt.Sprintf("%s/developer/finance?order=%s", callbackBaseURL, orderNo)
|
redirectURL := fmt.Sprintf("%s/admin/finance?order=%s", callbackBaseURL, orderNo)
|
||||||
|
|
||||||
result, err := paymentService.CreateOrder(orderNo, pkg.Price, notifyURL, redirectURL, order.Title)
|
result, err := paymentService.CreateOrder(orderNo, pkg.Price, notifyURL, redirectURL, order.Title)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"verification-platform-backend/internal/middleware"
|
"verification-platform-backend/internal/middleware"
|
||||||
"verification-platform-backend/internal/router/agent"
|
"verification-platform-backend/internal/router/agent"
|
||||||
"verification-platform-backend/internal/router/app"
|
"verification-platform-backend/internal/router/app"
|
||||||
"verification-platform-backend/internal/router/developer"
|
"verification-platform-backend/internal/router/admin"
|
||||||
"verification-platform-backend/internal/router/extension"
|
"verification-platform-backend/internal/router/extension"
|
||||||
"verification-platform-backend/internal/router/frontend"
|
"verification-platform-backend/internal/router/frontend"
|
||||||
"verification-platform-backend/pkg/response"
|
"verification-platform-backend/pkg/response"
|
||||||
@@ -32,9 +32,9 @@ func SetupRoutes(r *gin.Engine) {
|
|||||||
devGroup := api.Group("/dev")
|
devGroup := api.Group("/dev")
|
||||||
{
|
{
|
||||||
devGroup.Use(middleware.JWT())
|
devGroup.Use(middleware.JWT())
|
||||||
devGroup.Use(middleware.DeveloperAuth())
|
devGroup.Use(middleware.AdminAuth())
|
||||||
developer.SetupRoutes(devGroup)
|
admin.SetupRoutes(devGroup)
|
||||||
developer.SetupRoutesWithoutPackage(devGroup)
|
admin.SetupRoutesWithoutPackage(devGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
agentGroup := api.Group("/agent")
|
agentGroup := api.Group("/agent")
|
||||||
|
|||||||
@@ -32,9 +32,14 @@ func NewHTTPClient(timeout time.Duration) *HTTPClient {
|
|||||||
timeout = DefaultTimeout
|
timeout = DefaultTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proxyURL, _ := url.Parse("http://127.0.0.1:10809")
|
||||||
|
|
||||||
return &HTTPClient{
|
return &HTTPClient{
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURL),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -145,7 +145,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
"如何获取API密钥?": {
|
"如何获取API密钥?": {
|
||||||
TitleEn: "How to Get API Key?",
|
TitleEn: "How to Get API Key?",
|
||||||
ContentEn: "# How to Get API Key?\n\n## Steps\n\n### 1. Register Account\nFirst, register a developer account on the platform.\n\n### 2. Create Application\n1. Go to Console > Applications\n2. Click \"Create Application\" button\n3. Fill in application name and description\n4. Click \"Create\"\n\n### 3. Get App Key\nAfter creating the application, you can find the App Key on the application details page.\n\n## App Key Format\nApp Key is a unique identifier in the format:\n```\napp_xxxxxxxxxxxxxxxx\n```\n\n## Security Notes\n- Do not share your App Key publicly\n- Regenerate App Key if it's compromised\n- Use environment variables to store App Key in production",
|
ContentEn: "# How to Get API Key?\n\n## Steps\n\n### 1. Register Account\nFirst, register a admin account on the platform.\n\n### 2. Create Application\n1. Go to Console > Applications\n2. Click \"Create Application\" button\n3. Fill in application name and description\n4. Click \"Create\"\n\n### 3. Get App Key\nAfter creating the application, you can find the App Key on the application details page.\n\n## App Key Format\nApp Key is a unique identifier in the format:\n```\napp_xxxxxxxxxxxxxxxx\n```\n\n## Security Notes\n- Do not share your App Key publicly\n- Regenerate App Key if it's compromised\n- Use environment variables to store App Key in production",
|
||||||
},
|
},
|
||||||
"卡密验证失败怎么办?": {
|
"卡密验证失败怎么办?": {
|
||||||
TitleEn: "What to Do When Card Key Verification Fails?",
|
TitleEn: "What to Do When Card Key Verification Fails?",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -18,15 +18,15 @@ func main() {
|
|||||||
}{
|
}{
|
||||||
"免费版": {
|
"免费版": {
|
||||||
NameEn: "Free",
|
NameEn: "Free",
|
||||||
DescriptionEn: "Basic features for individual developers",
|
DescriptionEn: "Basic features for individual admins",
|
||||||
},
|
},
|
||||||
"公益版": {
|
"公益版": {
|
||||||
NameEn: "Community",
|
NameEn: "Community",
|
||||||
DescriptionEn: "Free for individual developers",
|
DescriptionEn: "Free for individual admins",
|
||||||
},
|
},
|
||||||
"专业版": {
|
"专业版": {
|
||||||
NameEn: "Professional",
|
NameEn: "Professional",
|
||||||
DescriptionEn: "Advanced features for professional developers",
|
DescriptionEn: "Advanced features for professional admins",
|
||||||
},
|
},
|
||||||
"企业版": {
|
"企业版": {
|
||||||
NameEn: "Enterprise",
|
NameEn: "Enterprise",
|
||||||
@@ -46,7 +46,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
"开发者版": {
|
"开发者版": {
|
||||||
NameEn: "Developer",
|
NameEn: "Developer",
|
||||||
DescriptionEn: "Perfect for individual developers",
|
DescriptionEn: "Perfect for individual admins",
|
||||||
},
|
},
|
||||||
"团队版": {
|
"团队版": {
|
||||||
NameEn: "Team",
|
NameEn: "Team",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -131,7 +131,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
"faq-api-key": {
|
"faq-api-key": {
|
||||||
TitleEn: "How to Get API Key?",
|
TitleEn: "How to Get API Key?",
|
||||||
ContentEn: "# How to Get API Key?\n\n## Steps\n\n1. Login to developer dashboard\n2. Go to \"Application Management\" page\n3. Create new application or select existing one\n4. In application details page you can see:\n - **AppID**: Application unique identifier\n - **AppKey**: Application key\n - **SecretKey**: Encryption key\n\n## Notes\n\n- AppKey is only shown once when created, please save it in time\n- Click \"Reset Key\" button to reset AppKey if needed\n- Old key becomes invalid immediately after reset\n- SecretKey is for server-side response verification, do not use it on client side",
|
ContentEn: "# How to Get API Key?\n\n## Steps\n\n1. Login to admin dashboard\n2. Go to \"Application Management\" page\n3. Create new application or select existing one\n4. In application details page you can see:\n - **AppID**: Application unique identifier\n - **AppKey**: Application key\n - **SecretKey**: Encryption key\n\n## Notes\n\n- AppKey is only shown once when created, please save it in time\n- Click \"Reset Key\" button to reset AppKey if needed\n- Old key becomes invalid immediately after reset\n- SecretKey is for server-side response verification, do not use it on client side",
|
||||||
SummaryEn: "Detailed steps to get API key",
|
SummaryEn: "Detailed steps to get API key",
|
||||||
},
|
},
|
||||||
"faq-card-fail": {
|
"faq-card-fail": {
|
||||||
@@ -141,7 +141,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
"faq-agent": {
|
"faq-agent": {
|
||||||
TitleEn: "How to Implement Agent Authorization?",
|
TitleEn: "How to Implement Agent Authorization?",
|
||||||
ContentEn: "# How to Implement Agent Authorization?\n\n## What is Agent Authorization?\n\nAgent authorization allows developers to authorize their applications to other developers, who can then generate card keys and sell them.\n\n## Authorization Process\n\n1. **Apply for Authorization**\n - Authorized party submits application to authorizer\n - Enter application ID\n - Wait for authorizer approval\n\n2. **Approve Authorization**\n - Authorizer views application\n - Set card key type permissions after approval\n - Set agent discount\n\n3. **Generate Card Keys**\n - Authorized party selects authorized application\n - Select card key types with permission\n - Generate and sell card keys\n\n4. **Settle Revenue**\n - Sales revenue is settled proportionally\n - Authorized party receives income\n\n## Permission Management\n\n- Authorizer can modify card key type permissions at any time\n- Can pause or terminate authorization\n- Can view agent's sales data",
|
ContentEn: "# How to Implement Agent Authorization?\n\n## What is Agent Authorization?\n\nAgent authorization allows admins to authorize their applications to other admins, who can then generate card keys and sell them.\n\n## Authorization Process\n\n1. **Apply for Authorization**\n - Authorized party submits application to authorizer\n - Enter application ID\n - Wait for authorizer approval\n\n2. **Approve Authorization**\n - Authorizer views application\n - Set card key type permissions after approval\n - Set agent discount\n\n3. **Generate Card Keys**\n - Authorized party selects authorized application\n - Select card key types with permission\n - Generate and sell card keys\n\n4. **Settle Revenue**\n - Sales revenue is settled proportionally\n - Authorized party receives income\n\n## Permission Management\n\n- Authorizer can modify card key type permissions at any time\n- Can pause or terminate authorization\n- Can view agent's sales data",
|
||||||
SummaryEn: "Implementation process and permission management for agent authorization",
|
SummaryEn: "Implementation process and permission management for agent authorization",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|||||||
@@ -1,14 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { HTMLAttributes } from "vue"
|
import type { HTMLAttributes } from "vue"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { ref, onMounted, onUnmounted, nextTick } from "vue"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
class?: HTMLAttributes["class"]
|
class?: HTMLAttributes["class"]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const scrollRef = ref<HTMLElement | null>(null)
|
||||||
|
const scrollPositionKey = 'sidebar-scroll-position'
|
||||||
|
let saveTimeout: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
function saveScrollPosition() {
|
||||||
|
if (scrollRef.value) {
|
||||||
|
sessionStorage.setItem(scrollPositionKey, scrollRef.value.scrollTop.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll() {
|
||||||
|
if (saveTimeout) {
|
||||||
|
clearTimeout(saveTimeout)
|
||||||
|
}
|
||||||
|
saveTimeout = setTimeout(saveScrollPosition, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const saved = sessionStorage.getItem(scrollPositionKey)
|
||||||
|
if (saved) {
|
||||||
|
nextTick(() => {
|
||||||
|
if (scrollRef.value) {
|
||||||
|
scrollRef.value.scrollTop = parseFloat(saved)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrollRef.value) {
|
||||||
|
scrollRef.value.addEventListener('scroll', handleScroll, { passive: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (scrollRef.value) {
|
||||||
|
scrollRef.value.removeEventListener('scroll', handleScroll)
|
||||||
|
}
|
||||||
|
if (saveTimeout) {
|
||||||
|
clearTimeout(saveTimeout)
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
ref="scrollRef"
|
||||||
data-slot="sidebar-content"
|
data-slot="sidebar-content"
|
||||||
data-sidebar="content"
|
data-sidebar="content"
|
||||||
:class="cn('flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', props.class)"
|
:class="cn('flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', props.class)"
|
||||||
|
|||||||
@@ -1,22 +1,107 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '@/components/marketing-layout/the-footer.vue'
|
import { computed } from 'vue'
|
||||||
import Header from '@/components/marketing-layout/the-header.vue'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
import AdminSidebar from '@/components/admin-sidebar/index.vue'
|
||||||
|
import LanguageChange from '@/components/language-change.vue'
|
||||||
|
import ThemePopover from '@/components/custom-theme/theme-popover.vue'
|
||||||
|
import ToggleTheme from '@/components/toggle-theme.vue'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const breadcrumbs = computed(() => {
|
||||||
|
const path = router.currentRoute.value.path
|
||||||
|
const parts = path.split('/').filter(Boolean)
|
||||||
|
const crumbs = [{ title: '控制台', path: '/admin' }]
|
||||||
|
|
||||||
|
const titleMap: Record<string, string> = {
|
||||||
|
applications: '应用管理',
|
||||||
|
cards: '卡密管理',
|
||||||
|
users: '用户管理',
|
||||||
|
devices: '设备管理',
|
||||||
|
sessions: '在线实例',
|
||||||
|
announcements: '公告管理',
|
||||||
|
versions: '版本管理',
|
||||||
|
'card-types': '卡类管理',
|
||||||
|
'agent-apps': '代理授权',
|
||||||
|
agents: '代理管理',
|
||||||
|
finance: '财务管理',
|
||||||
|
logs: '日志记录',
|
||||||
|
tickets: '工单系统',
|
||||||
|
'cloud-constants': '云端常量',
|
||||||
|
'cloud-variables': '云端变量',
|
||||||
|
'cloud-function': '云端函数',
|
||||||
|
'risk-control': '风控管理',
|
||||||
|
extension: '扩展配置',
|
||||||
|
profile: '个人中心',
|
||||||
|
settings: '基本设置',
|
||||||
|
security: '安全设置',
|
||||||
|
email: '邮箱设置',
|
||||||
|
create: '创建',
|
||||||
|
edit: '编辑',
|
||||||
|
recharge: '充值',
|
||||||
|
request: '申请授权',
|
||||||
|
invite: '邀请授权',
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPath = '/admin'
|
||||||
|
for (let i = 1; i < parts.length; i++) {
|
||||||
|
const part = parts[i]
|
||||||
|
currentPath += `/${part}`
|
||||||
|
|
||||||
|
if (titleMap[part]) {
|
||||||
|
crumbs.push({
|
||||||
|
title: titleMap[part],
|
||||||
|
path: currentPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (!isNaN(Number(part)) && i === parts.length - 1) {
|
||||||
|
crumbs.push({
|
||||||
|
title: '详情',
|
||||||
|
path: currentPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crumbs
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen flex flex-col">
|
<UiSidebarProvider>
|
||||||
<Header />
|
<AdminSidebar />
|
||||||
|
<UiSidebarInset>
|
||||||
|
<header class="flex h-14 shrink-0 items-center gap-2 border-b px-4">
|
||||||
|
<UiSidebarTrigger class="-ml-1" />
|
||||||
|
<UiSeparator orientation="vertical" class="mr-2 h-4" />
|
||||||
|
<UiBreadcrumb>
|
||||||
|
<UiBreadcrumbList>
|
||||||
|
<template v-for="(crumb, index) in breadcrumbs" :key="crumb.path">
|
||||||
|
<UiBreadcrumbItem v-if="index < breadcrumbs.length - 1">
|
||||||
|
<UiBreadcrumbLink as-child>
|
||||||
|
<router-link :to="crumb.path">
|
||||||
|
{{ crumb.title }}
|
||||||
|
</router-link>
|
||||||
|
</UiBreadcrumbLink>
|
||||||
|
</UiBreadcrumbItem>
|
||||||
|
<UiBreadcrumbItem v-else>
|
||||||
|
<UiBreadcrumbPage>{{ crumb.title }}</UiBreadcrumbPage>
|
||||||
|
</UiBreadcrumbItem>
|
||||||
|
<UiBreadcrumbSeparator v-if="index < breadcrumbs.length - 1" />
|
||||||
|
</template>
|
||||||
|
</UiBreadcrumbList>
|
||||||
|
</UiBreadcrumb>
|
||||||
|
|
||||||
<main class="flex-1">
|
<div class="ml-auto flex items-center gap-2">
|
||||||
<div class="relative bg-background">
|
<LanguageChange />
|
||||||
<div class="py-8 lg:py-12">
|
<ToggleTheme />
|
||||||
<div class="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
|
<ThemePopover />
|
||||||
<router-view />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</header>
|
||||||
</main>
|
|
||||||
|
|
||||||
<Footer />
|
<main class="flex-1 overflow-auto p-4 md:p-6">
|
||||||
</div>
|
<router-view />
|
||||||
|
</main>
|
||||||
|
</UiSidebarInset>
|
||||||
|
</UiSidebarProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
import AdminSidebar from '@/components/admin-sidebar/index.vue'
|
|
||||||
import LanguageChange from '@/components/language-change.vue'
|
|
||||||
import ThemePopover from '@/components/custom-theme/theme-popover.vue'
|
|
||||||
import ToggleTheme from '@/components/toggle-theme.vue'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
|
||||||
const path = router.currentRoute.value.path
|
|
||||||
const parts = path.split('/').filter(Boolean)
|
|
||||||
const crumbs = [{ title: '控制台', path: '/admin' }]
|
|
||||||
|
|
||||||
const titleMap: Record<string, string> = {
|
|
||||||
applications: '应用管理',
|
|
||||||
cards: '卡密管理',
|
|
||||||
users: '用户管理',
|
|
||||||
devices: '设备管理',
|
|
||||||
sessions: '在线实例',
|
|
||||||
announcements: '公告管理',
|
|
||||||
versions: '版本管理',
|
|
||||||
'card-types': '卡类管理',
|
|
||||||
'agent-apps': '代理授权',
|
|
||||||
agents: '代理管理',
|
|
||||||
finance: '财务管理',
|
|
||||||
logs: '日志记录',
|
|
||||||
tickets: '工单系统',
|
|
||||||
'cloud-constants': '云端常量',
|
|
||||||
'cloud-variables': '云端变量',
|
|
||||||
'cloud-function': '云端函数',
|
|
||||||
'risk-control': '风控管理',
|
|
||||||
extension: '扩展配置',
|
|
||||||
profile: '个人中心',
|
|
||||||
settings: '基本设置',
|
|
||||||
security: '安全设置',
|
|
||||||
email: '邮箱设置',
|
|
||||||
create: '创建',
|
|
||||||
edit: '编辑',
|
|
||||||
recharge: '充值',
|
|
||||||
request: '申请授权',
|
|
||||||
invite: '邀请授权',
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentPath = '/admin'
|
|
||||||
for (let i = 1; i < parts.length; i++) {
|
|
||||||
const part = parts[i]
|
|
||||||
currentPath += `/${part}`
|
|
||||||
|
|
||||||
if (titleMap[part]) {
|
|
||||||
crumbs.push({
|
|
||||||
title: titleMap[part],
|
|
||||||
path: currentPath,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else if (!isNaN(Number(part)) && i === parts.length - 1) {
|
|
||||||
crumbs.push({
|
|
||||||
title: '详情',
|
|
||||||
path: currentPath,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return crumbs
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UiSidebarProvider>
|
|
||||||
<AdminSidebar />
|
|
||||||
<UiSidebarInset>
|
|
||||||
<header class="flex h-14 shrink-0 items-center gap-2 border-b px-4">
|
|
||||||
<UiSidebarTrigger class="-ml-1" />
|
|
||||||
<UiSeparator orientation="vertical" class="mr-2 h-4" />
|
|
||||||
<UiBreadcrumb>
|
|
||||||
<UiBreadcrumbList>
|
|
||||||
<template v-for="(crumb, index) in breadcrumbs" :key="crumb.path">
|
|
||||||
<UiBreadcrumbItem v-if="index < breadcrumbs.length - 1">
|
|
||||||
<UiBreadcrumbLink as-child>
|
|
||||||
<router-link :to="crumb.path">
|
|
||||||
{{ crumb.title }}
|
|
||||||
</router-link>
|
|
||||||
</UiBreadcrumbLink>
|
|
||||||
</UiBreadcrumbItem>
|
|
||||||
<UiBreadcrumbItem v-else>
|
|
||||||
<UiBreadcrumbPage>{{ crumb.title }}</UiBreadcrumbPage>
|
|
||||||
</UiBreadcrumbItem>
|
|
||||||
<UiBreadcrumbSeparator v-if="index < breadcrumbs.length - 1" />
|
|
||||||
</template>
|
|
||||||
</UiBreadcrumbList>
|
|
||||||
</UiBreadcrumb>
|
|
||||||
|
|
||||||
<div class="ml-auto flex items-center gap-2">
|
|
||||||
<LanguageChange />
|
|
||||||
<ToggleTheme />
|
|
||||||
<ThemePopover />
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="flex-1 overflow-auto p-4 md:p-6">
|
|
||||||
<router-view />
|
|
||||||
</main>
|
|
||||||
</UiSidebarInset>
|
|
||||||
</UiSidebarProvider>
|
|
||||||
</template>
|
|
||||||
@@ -22,8 +22,8 @@ export const agentAppSchema = z.object({
|
|||||||
|
|
||||||
export const agentRequestSchema = z.object({
|
export const agentRequestSchema = z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
developer_id: z.number(),
|
admin_id: z.number(),
|
||||||
developer_name: z.string(),
|
admin_name: z.string(),
|
||||||
agent_id: z.number(),
|
agent_id: z.number(),
|
||||||
agent_name: z.string(),
|
agent_name: z.string(),
|
||||||
application_id: z.number(),
|
application_id: z.number(),
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ const allItems = computed<CombinedItem[]>(() => {
|
|||||||
.filter(item => item.status === 'pending')
|
.filter(item => item.status === 'pending')
|
||||||
.map(item => ({
|
.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
agent_id: item.developer_id,
|
agent_id: item.admin_id,
|
||||||
agent_name: item.developer_name,
|
agent_name: item.admin_name,
|
||||||
application_id: item.application_id,
|
application_id: item.application_id,
|
||||||
app_name: item.app_name,
|
app_name: item.app_name,
|
||||||
status: item.status,
|
status: item.status,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const saving = ref(false)
|
|||||||
const applications = ref<Array<{ id: number, name: string }>>([])
|
const applications = ref<Array<{ id: number, name: string }>>([])
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
developer_id: '',
|
admin_id: '',
|
||||||
application_id: '',
|
application_id: '',
|
||||||
message: '',
|
message: '',
|
||||||
})
|
})
|
||||||
@@ -35,7 +35,7 @@ async function fetchApplications() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (!form.value.developer_id) {
|
if (!form.value.admin_id) {
|
||||||
toast.error('请输入管理员ID')
|
toast.error('请输入管理员ID')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ async function handleSubmit() {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
developer_id: Number(form.value.developer_id),
|
admin_id: Number(form.value.admin_id),
|
||||||
application_id: Number(form.value.application_id),
|
application_id: Number(form.value.application_id),
|
||||||
message: form.value.message,
|
message: form.value.message,
|
||||||
}),
|
}),
|
||||||
@@ -109,12 +109,12 @@ onMounted(() => {
|
|||||||
</UiCardHeader>
|
</UiCardHeader>
|
||||||
<UiCardContent class="space-y-6">
|
<UiCardContent class="space-y-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="developer_id">
|
<UiLabel for="admin_id">
|
||||||
管理员ID
|
管理员ID
|
||||||
</UiLabel>
|
</UiLabel>
|
||||||
<UiInput
|
<UiInput
|
||||||
id="developer_id"
|
id="admin_id"
|
||||||
v-model="form.developer_id"
|
v-model="form.admin_id"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="请输入管理员ID"
|
placeholder="请输入管理员ID"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -211,13 +211,13 @@ onMounted(() => {
|
|||||||
<UiCardHeader>
|
<UiCardHeader>
|
||||||
<UiCardTitle class="flex items-center gap-2">
|
<UiCardTitle class="flex items-center gap-2">
|
||||||
<Icon icon="lucide:credit-card" class="size-5" />
|
<Icon icon="lucide:credit-card" class="size-5" />
|
||||||
{{ t('admin.cardTypes.create.rechargeConfig') || '充值配置' }}
|
{{ t('admin.cardTypes.create.rechargeConfig') }}
|
||||||
</UiCardTitle>
|
</UiCardTitle>
|
||||||
<UiCardDescription>{{ t('admin.cardTypes.create.rechargeConfigDesc') || '设置卡密的充值类型和金额' }}</UiCardDescription>
|
<UiCardDescription>{{ t('admin.cardTypes.create.rechargeConfigDesc') }}</UiCardDescription>
|
||||||
</UiCardHeader>
|
</UiCardHeader>
|
||||||
<UiCardContent class="space-y-6">
|
<UiCardContent class="space-y-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>{{ t('admin.cardTypes.create.rechargeType') || '充值类型' }}</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.rechargeType') }}</UiLabel>
|
||||||
<UiRadioGroup v-model="form.recharge_type" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<UiRadioGroup v-model="form.recharge_type" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div
|
<div
|
||||||
class="flex items-start gap-3 p-4 rounded-lg border cursor-pointer transition-colors"
|
class="flex items-start gap-3 p-4 rounded-lg border cursor-pointer transition-colors"
|
||||||
@@ -227,10 +227,10 @@ onMounted(() => {
|
|||||||
<UiRadioGroupItem value="balance" class="mt-0.5" />
|
<UiRadioGroupItem value="balance" class="mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
{{ t('admin.cardTypes.create.balanceRecharge') || '余额充值' }}
|
{{ t('admin.cardTypes.create.balanceRecharge') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
{{ t('admin.cardTypes.create.balanceRechargeDesc') || '充值账户余额,适用于计时/计次模式' }}
|
{{ t('admin.cardTypes.create.balanceRechargeDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -243,10 +243,10 @@ onMounted(() => {
|
|||||||
<UiRadioGroupItem value="subscription" class="mt-0.5" />
|
<UiRadioGroupItem value="subscription" class="mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
{{ t('admin.cardTypes.create.subscriptionRecharge') || '订阅充值' }}
|
{{ t('admin.cardTypes.create.subscriptionRecharge') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
{{ t('admin.cardTypes.create.subscriptionRechargeDesc') || '充值会员时长,适用于订阅模式' }}
|
{{ t('admin.cardTypes.create.subscriptionRechargeDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,10 +272,10 @@ onMounted(() => {
|
|||||||
<div class="flex items-center justify-between pt-4 border-t">
|
<div class="flex items-center justify-between pt-4 border-t">
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
{{ t('admin.cardTypes.create.permanent') || '永久会员' }}
|
{{ t('admin.cardTypes.create.permanentLabel') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
{{ t('admin.cardTypes.create.permanentDesc') || '开启后,使用此卡密的用户将成为永久会员' }}
|
{{ t('admin.cardTypes.create.permanentLabelDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<UiSwitch v-model="form.is_permanent" />
|
<UiSwitch v-model="form.is_permanent" />
|
||||||
@@ -283,7 +283,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div v-if="!form.is_permanent" class="space-y-4 pt-4 border-t">
|
<div v-if="!form.is_permanent" class="space-y-4 pt-4 border-t">
|
||||||
<div v-if="form.recharge_type === 'balance'" class="space-y-2">
|
<div v-if="form.recharge_type === 'balance'" class="space-y-2">
|
||||||
<UiLabel>{{ t('admin.cardTypes.create.rechargeAmount') || '充值金额' }}</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.rechargeAmount') }}</UiLabel>
|
||||||
<UiNumberField v-model="form.value" :min="0.01" :step="0.01" class="max-w-[200px]">
|
<UiNumberField v-model="form.value" :min="0.01" :step="0.01" class="max-w-[200px]">
|
||||||
<UiNumberFieldContent>
|
<UiNumberFieldContent>
|
||||||
<UiNumberFieldDecrement />
|
<UiNumberFieldDecrement />
|
||||||
@@ -292,14 +292,14 @@ onMounted(() => {
|
|||||||
</UiNumberFieldContent>
|
</UiNumberFieldContent>
|
||||||
</UiNumberField>
|
</UiNumberField>
|
||||||
<p class="text-xs text-muted-foreground">
|
<p class="text-xs text-muted-foreground">
|
||||||
{{ t('admin.cardTypes.create.rechargeAmountDesc') || '用户充值后获得的余额数量' }}
|
{{ t('admin.cardTypes.create.rechargeAmountDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="form.recharge_type === 'subscription'" class="space-y-4">
|
<div v-if="form.recharge_type === 'subscription'" class="space-y-4">
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>{{ t('admin.cardTypes.create.rechargeDuration') || '充值时长' }}</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.rechargeDuration') }}</UiLabel>
|
||||||
<UiNumberField v-model="form.value" :min="1" :step="1">
|
<UiNumberField v-model="form.value" :min="1" :step="1">
|
||||||
<UiNumberFieldContent>
|
<UiNumberFieldContent>
|
||||||
<UiNumberFieldDecrement />
|
<UiNumberFieldDecrement />
|
||||||
@@ -310,7 +310,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>{{ t('admin.cardTypes.create.durationUnit') || '时长单位' }}</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.durationUnit') }}</UiLabel>
|
||||||
<UiSelect v-model="form.value_unit">
|
<UiSelect v-model="form.value_unit">
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue />
|
<UiSelectValue />
|
||||||
@@ -360,8 +360,8 @@ onMounted(() => {
|
|||||||
<span>{{ selectedApplication?.name || '-' }}</span>
|
<span>{{ selectedApplication?.name || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.rechargeType') || '充值类型' }}</span>
|
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.rechargeType') }}</span>
|
||||||
<span>{{ form.recharge_type === 'balance' ? (t('admin.cardTypes.create.balanceRecharge') || '余额充值') : (t('admin.cardTypes.create.subscriptionRecharge') || '订阅充值') }}</span>
|
<span>{{ form.recharge_type === 'balance' ? t('admin.cardTypes.create.balanceRecharge') : t('admin.cardTypes.create.subscriptionRecharge') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.previewPrice') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.previewPrice') }}</span>
|
||||||
@@ -386,7 +386,7 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<Icon v-if="saving" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
<Icon v-if="saving" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
||||||
<Icon v-else icon="lucide:check" class="mr-2 h-4 w-4" />
|
<Icon v-else icon="lucide:check" class="mr-2 h-4 w-4" />
|
||||||
{{ t('admin.cardTypes.create.saveBtn') || '保存修改' }}
|
{{ t('admin.cardTypes.create.saveBtn') }}
|
||||||
</UiButton>
|
</UiButton>
|
||||||
<UiButton
|
<UiButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ onMounted(() => {
|
|||||||
<UiCardHeader>
|
<UiCardHeader>
|
||||||
<UiCardTitle class="flex items-center gap-2">
|
<UiCardTitle class="flex items-center gap-2">
|
||||||
<Icon icon="lucide:credit-card" class="size-5" />
|
<Icon icon="lucide:credit-card" class="size-5" />
|
||||||
充值配置
|
{{ t('admin.cardTypes.create.rechargeConfig') }}
|
||||||
</UiCardTitle>
|
</UiCardTitle>
|
||||||
<UiCardDescription>设置卡密的充值类型和金额</UiCardDescription>
|
<UiCardDescription>{{ t('admin.cardTypes.create.rechargeConfigDesc') }}</UiCardDescription>
|
||||||
</UiCardHeader>
|
</UiCardHeader>
|
||||||
<UiCardContent class="space-y-6">
|
<UiCardContent class="space-y-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>充值类型</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.rechargeType') }}</UiLabel>
|
||||||
<UiRadioGroup v-model="form.recharge_type" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<UiRadioGroup v-model="form.recharge_type" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div
|
<div
|
||||||
class="flex items-start gap-3 p-4 rounded-lg border cursor-pointer transition-colors"
|
class="flex items-start gap-3 p-4 rounded-lg border cursor-pointer transition-colors"
|
||||||
@@ -194,10 +194,10 @@ onMounted(() => {
|
|||||||
<UiRadioGroupItem value="balance" class="mt-0.5" />
|
<UiRadioGroupItem value="balance" class="mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
余额充值
|
{{ t('admin.cardTypes.create.balanceRecharge') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
充值账户余额,适用于计时/计次模式
|
{{ t('admin.cardTypes.create.balanceRechargeDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -210,10 +210,10 @@ onMounted(() => {
|
|||||||
<UiRadioGroupItem value="subscription" class="mt-0.5" />
|
<UiRadioGroupItem value="subscription" class="mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
订阅充值
|
{{ t('admin.cardTypes.create.subscriptionRecharge') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
充值会员时长,适用于订阅模式
|
{{ t('admin.cardTypes.create.subscriptionRechargeDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -239,10 +239,10 @@ onMounted(() => {
|
|||||||
<div class="flex items-center justify-between pt-4 border-t">
|
<div class="flex items-center justify-between pt-4 border-t">
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
永久会员
|
{{ t('admin.cardTypes.create.permanentLabel') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
开启后,使用此卡密的用户将成为永久会员
|
{{ t('admin.cardTypes.create.permanentLabelDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<UiSwitch v-model="form.is_permanent" />
|
<UiSwitch v-model="form.is_permanent" />
|
||||||
@@ -250,7 +250,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div v-if="!form.is_permanent" class="space-y-4 pt-4 border-t">
|
<div v-if="!form.is_permanent" class="space-y-4 pt-4 border-t">
|
||||||
<div v-if="form.recharge_type === 'balance'" class="space-y-2">
|
<div v-if="form.recharge_type === 'balance'" class="space-y-2">
|
||||||
<UiLabel>充值金额</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.rechargeAmount') }}</UiLabel>
|
||||||
<UiNumberField v-model="form.value" :min="0.01" :step="0.01" class="max-w-[200px]">
|
<UiNumberField v-model="form.value" :min="0.01" :step="0.01" class="max-w-[200px]">
|
||||||
<UiNumberFieldContent>
|
<UiNumberFieldContent>
|
||||||
<UiNumberFieldDecrement />
|
<UiNumberFieldDecrement />
|
||||||
@@ -259,14 +259,14 @@ onMounted(() => {
|
|||||||
</UiNumberFieldContent>
|
</UiNumberFieldContent>
|
||||||
</UiNumberField>
|
</UiNumberField>
|
||||||
<p class="text-xs text-muted-foreground">
|
<p class="text-xs text-muted-foreground">
|
||||||
用户充值后获得的余额数量
|
{{ t('admin.cardTypes.create.rechargeAmountDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="form.recharge_type === 'subscription'" class="space-y-4">
|
<div v-if="form.recharge_type === 'subscription'" class="space-y-4">
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>充值时长</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.rechargeDuration') }}</UiLabel>
|
||||||
<UiNumberField v-model="form.value" :min="1" :step="1">
|
<UiNumberField v-model="form.value" :min="1" :step="1">
|
||||||
<UiNumberFieldContent>
|
<UiNumberFieldContent>
|
||||||
<UiNumberFieldDecrement />
|
<UiNumberFieldDecrement />
|
||||||
@@ -277,26 +277,26 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>时长单位</UiLabel>
|
<UiLabel>{{ t('admin.cardTypes.create.durationUnit') }}</UiLabel>
|
||||||
<UiSelect v-model="form.value_unit">
|
<UiSelect v-model="form.value_unit">
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue />
|
<UiSelectValue />
|
||||||
</UiSelectTrigger>
|
</UiSelectTrigger>
|
||||||
<UiSelectContent>
|
<UiSelectContent>
|
||||||
<UiSelectItem value="minute">
|
<UiSelectItem value="minute">
|
||||||
分钟
|
{{ t('admin.cardTypes.units.minute') }}
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
<UiSelectItem value="hour">
|
<UiSelectItem value="hour">
|
||||||
小时
|
{{ t('admin.cardTypes.units.hour') }}
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
<UiSelectItem value="day">
|
<UiSelectItem value="day">
|
||||||
天
|
{{ t('admin.cardTypes.units.day') }}
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
<UiSelectItem value="month">
|
<UiSelectItem value="month">
|
||||||
月
|
{{ t('admin.cardTypes.units.month') }}
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
<UiSelectItem value="year">
|
<UiSelectItem value="year">
|
||||||
年
|
{{ t('admin.cardTypes.units.year') }}
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
</UiSelectContent>
|
</UiSelectContent>
|
||||||
</UiSelect>
|
</UiSelect>
|
||||||
@@ -327,8 +327,8 @@ onMounted(() => {
|
|||||||
<span>{{ selectedApplication?.name || '-' }}</span>
|
<span>{{ selectedApplication?.name || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">充值类型</span>
|
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.rechargeType') }}</span>
|
||||||
<span>{{ form.recharge_type === 'balance' ? '余额充值' : '订阅充值' }}</span>
|
<span>{{ form.recharge_type === 'balance' ? t('admin.cardTypes.create.balanceRecharge') : t('admin.cardTypes.create.subscriptionRecharge') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.previewPrice') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.cardTypes.create.previewPrice') }}</span>
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ onMounted(() => {
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon icon="lucide:lock" class="size-4 text-primary" />
|
<Icon icon="lucide:lock" class="size-4 text-primary" />
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
{{ t('admin.cloudVariables.create.developerOnly') }}
|
{{ t('admin.cloudVariables.create.adminOnly') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -467,7 +467,7 @@ onMounted(() => {
|
|||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.cloudVariables.create.writePermission') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.cloudVariables.create.writePermission') }}</span>
|
||||||
<UiBadge variant="secondary">
|
<UiBadge variant="secondary">
|
||||||
{{ form.write_permission === 'admin' ? t('admin.cloudVariables.create.developerOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('admin.cloudVariables.create.userWritable') }}
|
{{ form.write_permission === 'admin' ? t('admin.cloudVariables.create.adminOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('admin.cloudVariables.create.userWritable') }}
|
||||||
</UiBadge>
|
</UiBadge>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm items-center">
|
<div class="flex justify-between text-sm items-center">
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export function getColumns(actions: {
|
|||||||
if (permission === 'user') {
|
if (permission === 'user') {
|
||||||
return h(Badge, { variant: 'outline', class: 'bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300' }, () => t('admin.cloudVariables.create.userWritable'))
|
return h(Badge, { variant: 'outline', class: 'bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300' }, () => t('admin.cloudVariables.create.userWritable'))
|
||||||
}
|
}
|
||||||
return h(Badge, { variant: 'outline' }, () => t('admin.cloudVariables.create.developerOnly'))
|
return h(Badge, { variant: 'outline' }, () => t('admin.cloudVariables.create.adminOnly'))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -364,11 +364,11 @@ onMounted(() => {
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Icon icon="lucide:lock" class="size-4 text-primary" />
|
<Icon icon="lucide:lock" class="size-4 text-primary" />
|
||||||
<p class="font-medium">
|
<p class="font-medium">
|
||||||
{{ t('admin.cloudVariables.create.developerOnly') }}
|
{{ t('admin.cloudVariables.create.adminOnly') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-muted-foreground mt-1">
|
<p class="text-sm text-muted-foreground mt-1">
|
||||||
{{ form.scope === 'app' ? t('admin.cloudVariables.create.developerOnlyAppDesc') : t('admin.cloudVariables.create.developerOnlyDesc') }}
|
{{ form.scope === 'app' ? t('admin.cloudVariables.create.adminOnlyAppDesc') : t('admin.cloudVariables.create.adminOnlyDesc') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -451,7 +451,7 @@ onMounted(() => {
|
|||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.cloudVariables.create.writePermission') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.cloudVariables.create.writePermission') }}</span>
|
||||||
<UiBadge variant="secondary">
|
<UiBadge variant="secondary">
|
||||||
{{ form.write_permission === 'admin' ? t('admin.cloudVariables.create.developerOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('admin.cloudVariables.create.userWritable') }}
|
{{ form.write_permission === 'admin' ? t('admin.cloudVariables.create.adminOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('admin.cloudVariables.create.userWritable') }}
|
||||||
</UiBadge>
|
</UiBadge>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm items-center">
|
<div class="flex justify-between text-sm items-center">
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ onUnmounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<BasicPage
|
<BasicPage
|
||||||
:title="t('admin.dashboard.title')"
|
:title="t('admin.dashboard.title')"
|
||||||
:description="`${t('admin.welcomeBack')}, ${currentUser?.username || t('admin.developer')}`"
|
:description="`${t('admin.welcomeBack')}, ${currentUser?.username || t('admin.adminRole')}`"
|
||||||
>
|
>
|
||||||
<div v-if="loading" class="flex items-center justify-center py-12">
|
<div v-if="loading" class="flex items-center justify-center py-12">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ async function fetchUser() {
|
|||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('获取用户信息失败:', error)
|
console.error('获取用户信息失败:', error)
|
||||||
toast.error(t('admin.users.editFailed'))
|
toast.error(t('admin.users.edit.failed'))
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@@ -68,15 +68,15 @@ const selectedApplication = computed(() => {
|
|||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
if (!form.value.username) {
|
if (!form.value.username) {
|
||||||
toast.error(t('admin.users.create.usernameRequired'))
|
toast.error(t('admin.users.edit.usernameRequired'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!form.value.email) {
|
if (!form.value.email) {
|
||||||
toast.error(t('admin.users.create.emailRequired'))
|
toast.error(t('admin.users.edit.emailRequired'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!form.value.application_id) {
|
if (!form.value.application_id) {
|
||||||
toast.error(t('admin.users.create.applicationRequired'))
|
toast.error(t('admin.users.edit.applicationRequired'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,12 +93,12 @@ async function handleSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await api.put(`/dev/app-users/${userId.value}`, payload)
|
await api.put(`/dev/app-users/${userId.value}`, payload)
|
||||||
toast.success(t('admin.users.editSuccess'))
|
toast.success(t('admin.users.edit.success'))
|
||||||
router.push('/admin/users')
|
router.push('/admin/users')
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
console.error('更新用户失败:', error)
|
console.error('更新用户失败:', error)
|
||||||
toast.error(error.message || t('admin.users.editFailed'))
|
toast.error(error.message || t('admin.users.edit.failed'))
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
saving.value = false
|
saving.value = false
|
||||||
@@ -113,11 +113,11 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicPage
|
<BasicPage
|
||||||
:title="t('admin.users.edit')"
|
:title="t('admin.users.edit.title')"
|
||||||
:description="t('admin.users.create.basicInfoDesc')"
|
:description="t('admin.users.edit.basicInfoDesc')"
|
||||||
:breadcrumbs="[
|
:breadcrumbs="[
|
||||||
{ title: t('admin.users.title'), href: '/admin/users' },
|
{ title: t('admin.users.title'), href: '/admin/users' },
|
||||||
{ title: t('admin.users.edit') },
|
{ title: t('admin.users.edit.title') },
|
||||||
]"
|
]"
|
||||||
sticky
|
sticky
|
||||||
>
|
>
|
||||||
@@ -132,18 +132,18 @@ onMounted(() => {
|
|||||||
<UiCardHeader>
|
<UiCardHeader>
|
||||||
<UiCardTitle class="flex items-center gap-2">
|
<UiCardTitle class="flex items-center gap-2">
|
||||||
<Icon icon="lucide:user-plus" class="size-5" />
|
<Icon icon="lucide:user-plus" class="size-5" />
|
||||||
{{ t('admin.users.create.basicInfo') }}
|
{{ t('admin.users.edit.basicInfo') }}
|
||||||
</UiCardTitle>
|
</UiCardTitle>
|
||||||
<UiCardDescription>{{ t('admin.users.create.basicInfoDesc') }}</UiCardDescription>
|
<UiCardDescription>{{ t('admin.users.edit.basicInfoDesc') }}</UiCardDescription>
|
||||||
</UiCardHeader>
|
</UiCardHeader>
|
||||||
<UiCardContent class="space-y-6">
|
<UiCardContent class="space-y-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="application">
|
<UiLabel for="application">
|
||||||
{{ t('admin.users.create.application') }}
|
{{ t('admin.users.edit.application') }}
|
||||||
</UiLabel>
|
</UiLabel>
|
||||||
<UiSelect v-model="form.application_id">
|
<UiSelect v-model="form.application_id">
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue :placeholder="t('admin.users.create.selectApplication')" />
|
<UiSelectValue :placeholder="t('admin.users.edit.selectApplication')" />
|
||||||
</UiSelectTrigger>
|
</UiSelectTrigger>
|
||||||
<UiSelectContent>
|
<UiSelectContent>
|
||||||
<UiSelectItem v-for="app in applications" :key="app.id" :value="String(app.id)">
|
<UiSelectItem v-for="app in applications" :key="app.id" :value="String(app.id)">
|
||||||
@@ -155,25 +155,25 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="username">
|
<UiLabel for="username">
|
||||||
{{ t('admin.users.create.username') }}
|
{{ t('admin.users.edit.username') }}
|
||||||
</UiLabel>
|
</UiLabel>
|
||||||
<UiInput id="username" v-model="form.username" :placeholder="t('admin.users.create.usernamePlaceholder')" />
|
<UiInput id="username" v-model="form.username" :placeholder="t('admin.users.edit.usernamePlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="email">
|
<UiLabel for="email">
|
||||||
{{ t('admin.users.create.email') }}
|
{{ t('admin.users.edit.email') }}
|
||||||
</UiLabel>
|
</UiLabel>
|
||||||
<UiInput id="email" v-model="form.email" type="email" :placeholder="t('admin.users.create.emailPlaceholder')" />
|
<UiInput id="email" v-model="form.email" type="email" :placeholder="t('admin.users.edit.emailPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel for="password">
|
<UiLabel for="password">
|
||||||
{{ t('admin.users.create.password') }}
|
{{ t('admin.users.edit.password') }}
|
||||||
</UiLabel>
|
</UiLabel>
|
||||||
<UiInput id="password" v-model="form.password" type="password" :placeholder="t('admin.users.create.passwordPlaceholder')" />
|
<UiInput id="password" v-model="form.password" type="password" :placeholder="t('admin.users.edit.passwordPlaceholder')" />
|
||||||
<p class="text-xs text-muted-foreground">
|
<p class="text-xs text-muted-foreground">
|
||||||
留空则不修改密码
|
{{ t('admin.users.edit.passwordHint') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</UiCardContent>
|
</UiCardContent>
|
||||||
@@ -185,21 +185,21 @@ onMounted(() => {
|
|||||||
<UiCardHeader>
|
<UiCardHeader>
|
||||||
<UiCardTitle class="flex items-center gap-2">
|
<UiCardTitle class="flex items-center gap-2">
|
||||||
<Icon icon="lucide:eye" class="size-5" />
|
<Icon icon="lucide:eye" class="size-5" />
|
||||||
{{ t('admin.users.create.preview') }}
|
{{ t('admin.users.edit.preview') }}
|
||||||
</UiCardTitle>
|
</UiCardTitle>
|
||||||
</UiCardHeader>
|
</UiCardHeader>
|
||||||
<UiCardContent class="space-y-4">
|
<UiCardContent class="space-y-4">
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.users.create.app') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.users.edit.app') }}</span>
|
||||||
<span>{{ selectedApplication?.name || '-' }}</span>
|
<span>{{ selectedApplication?.name || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.users.create.usernameLabel') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.users.edit.usernameLabel') }}</span>
|
||||||
<span>{{ form.username || '-' }}</span>
|
<span>{{ form.username || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.users.create.emailLabel') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.users.edit.emailLabel') }}</span>
|
||||||
<span>{{ form.email || '-' }}</span>
|
<span>{{ form.email || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,14 +217,14 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<Icon v-if="saving" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
<Icon v-if="saving" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
||||||
<Icon v-else icon="lucide:check" class="mr-2 h-4 w-4" />
|
<Icon v-else icon="lucide:check" class="mr-2 h-4 w-4" />
|
||||||
{{ t('admin.users.create.saveBtn') || '保存修改' }}
|
{{ t('admin.users.edit.saveBtn') }}
|
||||||
</UiButton>
|
</UiButton>
|
||||||
<UiButton
|
<UiButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@click="router.back()"
|
@click="router.back()"
|
||||||
>
|
>
|
||||||
{{ t('admin.users.create.cancel') }}
|
{{ t('admin.users.edit.cancel') }}
|
||||||
</UiButton>
|
</UiButton>
|
||||||
</div>
|
</div>
|
||||||
</UiCardContent>
|
</UiCardContent>
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicPage
|
<BasicPage
|
||||||
title="创建用户"
|
:title="t('admin.users.create.title')"
|
||||||
description="为应用创建新的用户账号,设置基本信息和应用关联"
|
:description="t('admin.users.create.description')"
|
||||||
:breadcrumbs="[
|
:breadcrumbs="[
|
||||||
{ title: t('admin.users.title'), href: '/admin/users' },
|
{ title: t('admin.users.title'), href: '/admin/users' },
|
||||||
{ title: t('admin.users.create.title') },
|
{ title: t('admin.users.create.title') },
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"ban": "Ban",
|
"ban": "Ban",
|
||||||
"unban": "Unban",
|
"unban": "Unban",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"confirm": "Confirm",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"totalRecords": "{count} records in total",
|
"totalRecords": "{count} records in total",
|
||||||
@@ -194,7 +195,7 @@
|
|||||||
"activeRate": "Active Rate",
|
"activeRate": "Active Rate",
|
||||||
"searchPlaceholder": "Search username, email, device ID...",
|
"searchPlaceholder": "Search username, email, device ID...",
|
||||||
"allRoles": "All Roles",
|
"allRoles": "All Roles",
|
||||||
"developer": "Admin",
|
"admin": "Admin",
|
||||||
"agent": "Agent",
|
"agent": "Agent",
|
||||||
"allStatus": "All Status",
|
"allStatus": "All Status",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
@@ -219,6 +220,15 @@
|
|||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordPlaceholder": "Leave empty to keep current",
|
"passwordPlaceholder": "Leave empty to keep current",
|
||||||
"passwordHint": "Leave empty to keep current password",
|
"passwordHint": "Leave empty to keep current password",
|
||||||
|
"preview": "Preview",
|
||||||
|
"app": "Application",
|
||||||
|
"usernameLabel": "Username",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"saveBtn": "Save Changes",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"usernameRequired": "Please enter username",
|
||||||
|
"emailRequired": "Please enter email",
|
||||||
|
"applicationRequired": "Please select application",
|
||||||
"deviceId": "Device ID",
|
"deviceId": "Device ID",
|
||||||
"deviceIdPlaceholder": "Device ID (optional)",
|
"deviceIdPlaceholder": "Device ID (optional)",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
@@ -259,6 +269,35 @@
|
|||||||
"balanceUpdateFailed": "Failed to update",
|
"balanceUpdateFailed": "Failed to update",
|
||||||
"invalidAmount": "Please enter a valid value"
|
"invalidAmount": "Please enter a valid value"
|
||||||
},
|
},
|
||||||
|
"create": {
|
||||||
|
"title": "Create User",
|
||||||
|
"description": "Create a new user account for an application with basic information and application association",
|
||||||
|
"basicInfo": "Basic Information",
|
||||||
|
"basicInfoDesc": "Fill in user basic information",
|
||||||
|
"application": "Application",
|
||||||
|
"selectApplication": "Select application",
|
||||||
|
"username": "Username",
|
||||||
|
"usernamePlaceholder": "Enter username",
|
||||||
|
"email": "Email",
|
||||||
|
"emailPlaceholder": "Enter email",
|
||||||
|
"password": "Password",
|
||||||
|
"passwordPlaceholder": "Enter password",
|
||||||
|
"preview": "Preview",
|
||||||
|
"app": "Application",
|
||||||
|
"usernameLabel": "Username",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"submit": "Create User",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"usernameRequired": "Please enter username",
|
||||||
|
"emailRequired": "Please enter email",
|
||||||
|
"passwordRequired": "Please enter password",
|
||||||
|
"applicationRequired": "Please select application",
|
||||||
|
"success": "User created successfully",
|
||||||
|
"failed": "Failed to create user",
|
||||||
|
"saveBtn": "Save Changes"
|
||||||
|
},
|
||||||
|
"editFailed": "Failed to load user info",
|
||||||
|
"editSuccess": "User updated successfully",
|
||||||
"resetPassword": "Reset Password",
|
"resetPassword": "Reset Password",
|
||||||
"resetPasswordDesc": "Set a new password for user \"{username}\"",
|
"resetPasswordDesc": "Set a new password for user \"{username}\"",
|
||||||
"newPassword": "New Password",
|
"newPassword": "New Password",
|
||||||
@@ -561,7 +600,7 @@
|
|||||||
"totalOrders": "Total Orders",
|
"totalOrders": "Total Orders",
|
||||||
"totalRevenue": "Total Revenue",
|
"totalRevenue": "Total Revenue",
|
||||||
"onlineUsers": "Online Users",
|
"onlineUsers": "Online Users",
|
||||||
"totalDevelopers": "Admins",
|
"totalAdmins": "Admins",
|
||||||
"totalApps": "Applications",
|
"totalApps": "Applications",
|
||||||
"totalVisitors": "Visitors",
|
"totalVisitors": "Visitors",
|
||||||
"totalKeys": "Keys",
|
"totalKeys": "Keys",
|
||||||
@@ -580,7 +619,7 @@
|
|||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"charts": {
|
"charts": {
|
||||||
"userGrowth": "User Growth",
|
"userGrowth": "User Growth",
|
||||||
"userGrowthDesc": "Showing user and developer growth trends",
|
"userGrowthDesc": "Showing user and admin growth trends",
|
||||||
"orderTrend": "Order Trend",
|
"orderTrend": "Order Trend",
|
||||||
"orderTrendDesc": "Showing order count and revenue trends",
|
"orderTrendDesc": "Showing order count and revenue trends",
|
||||||
"selectTimeRange": "Select time range",
|
"selectTimeRange": "Select time range",
|
||||||
@@ -900,7 +939,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"welcomeBack": "Welcome back",
|
"welcomeBack": "Welcome back",
|
||||||
"developer": "Admin",
|
"adminRole": "Admin",
|
||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
"totalApplications": "Total Applications",
|
"totalApplications": "Total Applications",
|
||||||
"totalCards": "Total Cards",
|
"totalCards": "Total Cards",
|
||||||
@@ -995,7 +1034,7 @@
|
|||||||
"loginPolicy": "Login Policy",
|
"loginPolicy": "Login Policy",
|
||||||
"loginPolicyDesc": "Set user login verification policy",
|
"loginPolicyDesc": "Set user login verification policy",
|
||||||
"looseMode": "Loose Mode",
|
"looseMode": "Loose Mode",
|
||||||
"looseModeDesc": "Expired users can login, developer controls features",
|
"looseModeDesc": "Expired users can login, admin controls features",
|
||||||
"strictMode": "Strict Mode",
|
"strictMode": "Strict Mode",
|
||||||
"strictModeDesc": "Must be unexpired to login",
|
"strictModeDesc": "Must be unexpired to login",
|
||||||
"hybridMode": "Hybrid Mode",
|
"hybridMode": "Hybrid Mode",
|
||||||
@@ -1081,8 +1120,11 @@
|
|||||||
"batchUpdateSuccess": "Batch updated successfully",
|
"batchUpdateSuccess": "Batch updated successfully",
|
||||||
"batchUpdateFailed": "Failed to batch update",
|
"batchUpdateFailed": "Failed to batch update",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
|
"editFailed": "Failed to load card type",
|
||||||
|
"editSuccess": "Card type updated successfully",
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Create Card Type",
|
"title": "Create Card Type",
|
||||||
|
"description": "Create a new card type with basic information and recharge configuration",
|
||||||
"basicInfo": "Basic Information",
|
"basicInfo": "Basic Information",
|
||||||
"basicInfoDesc": "Fill in the basic information of the card type",
|
"basicInfoDesc": "Fill in the basic information of the card type",
|
||||||
"billingConfig": "Billing Configuration",
|
"billingConfig": "Billing Configuration",
|
||||||
@@ -1127,7 +1169,22 @@
|
|||||||
"createFailed": "Failed to create",
|
"createFailed": "Failed to create",
|
||||||
"saveSuccess": "Saved successfully",
|
"saveSuccess": "Saved successfully",
|
||||||
"saveFailed": "Failed to save",
|
"saveFailed": "Failed to save",
|
||||||
"editTitle": "Edit Card Type"
|
"editTitle": "Edit Card Type",
|
||||||
|
"rechargeConfig": "Recharge Configuration",
|
||||||
|
"rechargeConfigDesc": "Set card recharge type and amount",
|
||||||
|
"rechargeType": "Recharge Type",
|
||||||
|
"balanceRecharge": "Balance Recharge",
|
||||||
|
"balanceRechargeDesc": "Recharge account balance, suitable for time/count mode",
|
||||||
|
"subscriptionRecharge": "Subscription Recharge",
|
||||||
|
"subscriptionRechargeDesc": "Recharge membership duration, suitable for subscription mode",
|
||||||
|
"permanentLabel": "Permanent Member",
|
||||||
|
"permanentLabelDesc": "When enabled, users with this card will become permanent members",
|
||||||
|
"rechargeAmount": "Recharge Amount",
|
||||||
|
"rechargeAmountDesc": "Balance amount user receives after recharge",
|
||||||
|
"rechargeDuration": "Recharge Duration",
|
||||||
|
"durationUnit": "Duration Unit",
|
||||||
|
"saveBtn": "Save Changes",
|
||||||
|
"passwordHint": "Leave empty to keep current password"
|
||||||
},
|
},
|
||||||
"columns": {
|
"columns": {
|
||||||
"cardKey": "Card Key",
|
"cardKey": "Card Key",
|
||||||
@@ -1872,9 +1929,9 @@
|
|||||||
"permissionTitle": "Write Permission",
|
"permissionTitle": "Write Permission",
|
||||||
"permissionDesc": "Set who can modify this variable's value",
|
"permissionDesc": "Set who can modify this variable's value",
|
||||||
"writePermission": "Write Permission",
|
"writePermission": "Write Permission",
|
||||||
"developerOnly": "Developer Only",
|
"adminOnly": "Admin Only",
|
||||||
"developerOnlyDesc": "Only developers can modify this variable's value via API or dashboard",
|
"adminOnlyDesc": "Only admins can modify this variable's value via API or dashboard",
|
||||||
"developerOnlyAppDesc": "Only developers can modify this global variable's value via API or dashboard",
|
"adminOnlyAppDesc": "Only admins can modify this global variable's value via API or dashboard",
|
||||||
"userWritable": "User Writable",
|
"userWritable": "User Writable",
|
||||||
"userWritableDesc": "Users can modify their own variable values via client API",
|
"userWritableDesc": "Users can modify their own variable values via client API",
|
||||||
"userWritableAppDesc": "Users can modify this global variable value via client API, changes affect all users",
|
"userWritableAppDesc": "Users can modify this global variable value via client API, changes affect all users",
|
||||||
@@ -1921,6 +1978,9 @@
|
|||||||
"batchUpdateSuccess": "Batch update successful",
|
"batchUpdateSuccess": "Batch update successful",
|
||||||
"batchUpdateFailed": "Batch update failed",
|
"batchUpdateFailed": "Batch update failed",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
|
"edit": "Edit",
|
||||||
|
"editSuccess": "Updated successfully",
|
||||||
|
"editFailed": "Failed to fetch cloud function",
|
||||||
"columns": {
|
"columns": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"application": "Application",
|
"application": "Application",
|
||||||
@@ -1951,6 +2011,7 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"submitBtn": "Submit",
|
"submitBtn": "Submit",
|
||||||
|
"saveBtn": "Save",
|
||||||
"cancelBtn": "Cancel",
|
"cancelBtn": "Cancel",
|
||||||
"submitSuccess": "Created successfully",
|
"submitSuccess": "Created successfully",
|
||||||
"submitFailed": "Failed to create"
|
"submitFailed": "Failed to create"
|
||||||
@@ -2248,9 +2309,8 @@
|
|||||||
"smsSettings": "SMS Service",
|
"smsSettings": "SMS Service",
|
||||||
"paymentSettings": "Payment Settings",
|
"paymentSettings": "Payment Settings",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"developerDashboard": "Admin Dashboard",
|
|
||||||
"agentDashboard": "Agent Dashboard",
|
|
||||||
"adminDashboard": "Admin Dashboard",
|
"adminDashboard": "Admin Dashboard",
|
||||||
|
"agentDashboard": "Agent Dashboard",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"register": "Register",
|
"register": "Register",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"ban": "封禁",
|
"ban": "封禁",
|
||||||
"unban": "解封",
|
"unban": "解封",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
|
"confirm": "确认",
|
||||||
"refresh": "刷新",
|
"refresh": "刷新",
|
||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
"totalRecords": "共 {count} 条记录",
|
"totalRecords": "共 {count} 条记录",
|
||||||
@@ -194,7 +195,7 @@
|
|||||||
"activeRate": "活跃率",
|
"activeRate": "活跃率",
|
||||||
"searchPlaceholder": "搜索用户名、邮箱、设备指纹...",
|
"searchPlaceholder": "搜索用户名、邮箱、设备指纹...",
|
||||||
"allRoles": "全部角色",
|
"allRoles": "全部角色",
|
||||||
"developer": "管理员",
|
"admin": "管理员",
|
||||||
"agent": "代理商",
|
"agent": "代理商",
|
||||||
"allStatus": "全部状态",
|
"allStatus": "全部状态",
|
||||||
"active": "正常",
|
"active": "正常",
|
||||||
@@ -219,6 +220,15 @@
|
|||||||
"password": "密码",
|
"password": "密码",
|
||||||
"passwordPlaceholder": "留空则不修改",
|
"passwordPlaceholder": "留空则不修改",
|
||||||
"passwordHint": "留空则不修改密码",
|
"passwordHint": "留空则不修改密码",
|
||||||
|
"preview": "预览",
|
||||||
|
"app": "应用",
|
||||||
|
"usernameLabel": "用户名",
|
||||||
|
"emailLabel": "邮箱",
|
||||||
|
"saveBtn": "保存修改",
|
||||||
|
"cancel": "取消",
|
||||||
|
"usernameRequired": "请输入用户名",
|
||||||
|
"emailRequired": "请输入邮箱",
|
||||||
|
"applicationRequired": "请选择应用",
|
||||||
"deviceId": "设备指纹",
|
"deviceId": "设备指纹",
|
||||||
"deviceIdPlaceholder": "设备指纹(可选)",
|
"deviceIdPlaceholder": "设备指纹(可选)",
|
||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
@@ -259,6 +269,35 @@
|
|||||||
"balanceUpdateFailed": "更新失败",
|
"balanceUpdateFailed": "更新失败",
|
||||||
"invalidAmount": "请输入有效的数值"
|
"invalidAmount": "请输入有效的数值"
|
||||||
},
|
},
|
||||||
|
"create": {
|
||||||
|
"title": "创建用户",
|
||||||
|
"description": "为应用创建新的用户账号,设置基本信息和应用关联",
|
||||||
|
"basicInfo": "基本信息",
|
||||||
|
"basicInfoDesc": "填写用户的基本信息",
|
||||||
|
"application": "所属应用",
|
||||||
|
"selectApplication": "请选择应用",
|
||||||
|
"username": "用户名",
|
||||||
|
"usernamePlaceholder": "请输入用户名",
|
||||||
|
"email": "邮箱",
|
||||||
|
"emailPlaceholder": "请输入邮箱",
|
||||||
|
"password": "密码",
|
||||||
|
"passwordPlaceholder": "请输入密码",
|
||||||
|
"preview": "预览",
|
||||||
|
"app": "应用",
|
||||||
|
"usernameLabel": "用户名",
|
||||||
|
"emailLabel": "邮箱",
|
||||||
|
"submit": "创建用户",
|
||||||
|
"cancel": "取消",
|
||||||
|
"usernameRequired": "请输入用户名",
|
||||||
|
"emailRequired": "请输入邮箱",
|
||||||
|
"passwordRequired": "请输入密码",
|
||||||
|
"applicationRequired": "请选择应用",
|
||||||
|
"success": "创建成功",
|
||||||
|
"failed": "创建失败",
|
||||||
|
"saveBtn": "保存修改"
|
||||||
|
},
|
||||||
|
"editFailed": "获取用户信息失败",
|
||||||
|
"editSuccess": "更新用户成功",
|
||||||
"resetPassword": "重置密码",
|
"resetPassword": "重置密码",
|
||||||
"resetPasswordDesc": "为用户「{username}」设置新密码",
|
"resetPasswordDesc": "为用户「{username}」设置新密码",
|
||||||
"newPassword": "新密码",
|
"newPassword": "新密码",
|
||||||
@@ -562,7 +601,7 @@
|
|||||||
"totalOrders": "总订单",
|
"totalOrders": "总订单",
|
||||||
"totalRevenue": "总收入",
|
"totalRevenue": "总收入",
|
||||||
"onlineUsers": "在线用户",
|
"onlineUsers": "在线用户",
|
||||||
"totalDevelopers": "管理员",
|
"totalAdmins": "管理员",
|
||||||
"totalApps": "应用",
|
"totalApps": "应用",
|
||||||
"totalVisitors": "访客",
|
"totalVisitors": "访客",
|
||||||
"totalKeys": "卡密",
|
"totalKeys": "卡密",
|
||||||
@@ -901,7 +940,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"welcomeBack": "欢迎回来",
|
"welcomeBack": "欢迎回来",
|
||||||
"developer": "管理员",
|
"adminRole": "管理员",
|
||||||
"loading": "加载中",
|
"loading": "加载中",
|
||||||
"totalApplications": "应用总数",
|
"totalApplications": "应用总数",
|
||||||
"totalCards": "卡密总数",
|
"totalCards": "卡密总数",
|
||||||
@@ -1355,8 +1394,11 @@
|
|||||||
"batchUpdateSuccess": "批量更新成功",
|
"batchUpdateSuccess": "批量更新成功",
|
||||||
"batchUpdateFailed": "批量更新失败",
|
"batchUpdateFailed": "批量更新失败",
|
||||||
"select": "选择",
|
"select": "选择",
|
||||||
|
"editFailed": "获取卡类信息失败",
|
||||||
|
"editSuccess": "更新卡类成功",
|
||||||
"create": {
|
"create": {
|
||||||
"title": "创建卡类",
|
"title": "创建卡类",
|
||||||
|
"description": "创建新的卡密类型,设置基本信息和充值配置",
|
||||||
"basicInfo": "基本信息",
|
"basicInfo": "基本信息",
|
||||||
"basicInfoDesc": "填写卡类的基本信息",
|
"basicInfoDesc": "填写卡类的基本信息",
|
||||||
"billingConfig": "充值配置",
|
"billingConfig": "充值配置",
|
||||||
@@ -1390,7 +1432,22 @@
|
|||||||
"createFailed": "创建失败",
|
"createFailed": "创建失败",
|
||||||
"saveSuccess": "保存成功",
|
"saveSuccess": "保存成功",
|
||||||
"saveFailed": "保存失败",
|
"saveFailed": "保存失败",
|
||||||
"editTitle": "编辑卡类"
|
"editTitle": "编辑卡类",
|
||||||
|
"rechargeConfig": "充值配置",
|
||||||
|
"rechargeConfigDesc": "设置卡密的充值类型和金额",
|
||||||
|
"rechargeType": "充值类型",
|
||||||
|
"balanceRecharge": "余额充值",
|
||||||
|
"balanceRechargeDesc": "充值账户余额,适用于计时/计次模式",
|
||||||
|
"subscriptionRecharge": "订阅充值",
|
||||||
|
"subscriptionRechargeDesc": "充值会员时长,适用于订阅模式",
|
||||||
|
"permanentLabel": "永久会员",
|
||||||
|
"permanentLabelDesc": "开启后,使用此卡密的用户将成为永久会员",
|
||||||
|
"rechargeAmount": "充值金额",
|
||||||
|
"rechargeAmountDesc": "用户充值后获得的余额数量",
|
||||||
|
"rechargeDuration": "充值时长",
|
||||||
|
"durationUnit": "时长单位",
|
||||||
|
"saveBtn": "保存修改",
|
||||||
|
"passwordHint": "留空则不修改密码"
|
||||||
},
|
},
|
||||||
"columns": {
|
"columns": {
|
||||||
"name": "类型名称",
|
"name": "类型名称",
|
||||||
@@ -1859,9 +1916,9 @@
|
|||||||
"permissionTitle": "写入权限",
|
"permissionTitle": "写入权限",
|
||||||
"permissionDesc": "设置谁可以修改此变量的值",
|
"permissionDesc": "设置谁可以修改此变量的值",
|
||||||
"writePermission": "写入权限",
|
"writePermission": "写入权限",
|
||||||
"developerOnly": "仅管理员",
|
"adminOnly": "仅管理员",
|
||||||
"developerOnlyDesc": "只有管理员可以通过API或后台修改此变量的值",
|
"adminOnlyDesc": "只有管理员可以通过API或后台修改此变量的值",
|
||||||
"developerOnlyAppDesc": "只有管理员可以通过API或后台修改此全局变量的值",
|
"adminOnlyAppDesc": "只有管理员可以通过API或后台修改此全局变量的值",
|
||||||
"userWritable": "用户可写",
|
"userWritable": "用户可写",
|
||||||
"userWritableDesc": "用户可以通过客户端API修改自己的变量值",
|
"userWritableDesc": "用户可以通过客户端API修改自己的变量值",
|
||||||
"userWritableAppDesc": "用户可以通过客户端API修改此全局变量的值,修改后对所有用户生效",
|
"userWritableAppDesc": "用户可以通过客户端API修改此全局变量的值,修改后对所有用户生效",
|
||||||
@@ -1908,6 +1965,9 @@
|
|||||||
"batchUpdateSuccess": "批量更新成功",
|
"batchUpdateSuccess": "批量更新成功",
|
||||||
"batchUpdateFailed": "批量更新失败",
|
"batchUpdateFailed": "批量更新失败",
|
||||||
"select": "选择",
|
"select": "选择",
|
||||||
|
"edit": "编辑",
|
||||||
|
"editSuccess": "更新成功",
|
||||||
|
"editFailed": "获取云端函数失败",
|
||||||
"columns": {
|
"columns": {
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
"application": "应用",
|
"application": "应用",
|
||||||
@@ -1938,6 +1998,7 @@
|
|||||||
"status": "状态",
|
"status": "状态",
|
||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
"submitBtn": "提交",
|
"submitBtn": "提交",
|
||||||
|
"saveBtn": "保存",
|
||||||
"cancelBtn": "取消",
|
"cancelBtn": "取消",
|
||||||
"submitSuccess": "创建成功",
|
"submitSuccess": "创建成功",
|
||||||
"submitFailed": "创建失败"
|
"submitFailed": "创建失败"
|
||||||
@@ -2235,9 +2296,8 @@
|
|||||||
"smsSettings": "短信服务",
|
"smsSettings": "短信服务",
|
||||||
"paymentSettings": "支付配置",
|
"paymentSettings": "支付配置",
|
||||||
"profile": "个人中心",
|
"profile": "个人中心",
|
||||||
"developerDashboard": "管理后台",
|
|
||||||
"agentDashboard": "代理后台",
|
|
||||||
"adminDashboard": "管理后台",
|
"adminDashboard": "管理后台",
|
||||||
|
"agentDashboard": "代理后台",
|
||||||
"logout": "退出登录",
|
"logout": "退出登录",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
"register": "注册",
|
"register": "注册",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin',
|
path: '/admin',
|
||||||
component: () => import('@/layouts/developer.vue'),
|
component: () => import('@/layouts/admin.vue'),
|
||||||
meta: { auth: true, role: 'admin' },
|
meta: { auth: true, role: 'admin' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user