统一角色体系为admin/agent/user,修复代理查询角色错误,添加语言切换和明暗切换

This commit is contained in:
Admin
2026-04-29 17:33:54 +08:00
parent 5c63882e71
commit 2a8f32cbb9
21 changed files with 78 additions and 196 deletions
+3 -3
View File
@@ -308,7 +308,7 @@ func initData() {
Username: "dev", Username: "dev",
Email: &devEmail, Email: &devEmail,
Password: string(hashedPassword), Password: string(hashedPassword),
Role: "developer", Role: "admin",
Status: "active", Status: "active",
} }
DB.Create(&devUser) DB.Create(&devUser)
@@ -324,7 +324,7 @@ func initData() {
Username: "agent", Username: "agent",
Email: &agentEmail, Email: &agentEmail,
Password: string(hashedPassword), Password: string(hashedPassword),
Role: "developer", Role: "agent",
Status: "active", Status: "active",
} }
DB.Create(&agentUser) DB.Create(&agentUser)
@@ -678,7 +678,7 @@ func initTicketData() {
Username: "dev", Username: "dev",
Email: &devEmail, Email: &devEmail,
Password: string(hashedPassword), Password: string(hashedPassword),
Role: "developer", Role: "admin",
Status: "active", Status: "active",
} }
DB.Create(&devUser) DB.Create(&devUser)
+3 -3
View File
@@ -114,7 +114,7 @@ func AdminAuth() gin.HandlerFunc {
} }
} }
// DeveloperAuth 开发者权限中间件 // DeveloperAuth 开发者权限中间件(管理员即开发者)
func DeveloperAuth() gin.HandlerFunc { func DeveloperAuth() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
role, exists := c.Get("role") role, exists := c.Get("role")
@@ -124,8 +124,8 @@ func DeveloperAuth() gin.HandlerFunc {
return return
} }
if role != "developer" && role != "admin" { if role != "admin" {
response.Error(c, http.StatusForbidden, "Developer access required") response.Error(c, http.StatusForbidden, "Admin access required")
c.Abort() c.Abort()
return return
} }
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
// User 平台用户模型(开发者、管理员) // User 平台用户模型(管理员、代理商
type User struct { type User struct {
ID uint `gorm:"primaryKey" json:"id"` ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"uniqueIndex;size:50" json:"username"` Username string `gorm:"uniqueIndex;size:50" json:"username"`
@@ -362,8 +362,8 @@ func handleInviteAgent(c *gin.Context) {
response.Error(c, 404, "开发者不存在") response.Error(c, 404, "开发者不存在")
return return
} }
if agent.Role != "developer" { if agent.Role != "agent" {
response.Error(c, 400, "该用户不是开发者") response.Error(c, 400, "该用户不是代理商")
return return
} }
@@ -421,8 +421,8 @@ func handleRequestAuthorization(c *gin.Context) {
response.Error(c, 404, "开发者不存在") response.Error(c, 404, "开发者不存在")
return return
} }
if developer.Role != "developer" { if developer.Role != "admin" {
response.Error(c, 400, "该用户不是开发者") response.Error(c, 400, "该用户不是管理员")
return return
} }
+10 -10
View File
@@ -47,7 +47,7 @@ type AgentTreeNode struct {
func handleGetAgents(c *gin.Context) { func handleGetAgents(c *gin.Context) {
var users []model.User var users []model.User
if err := database.DB.Where("role = ?", "admin"). if err := database.DB.Where("role = ?", "agent").
Preload("ParentAgent"). Preload("ParentAgent").
Order("created_at DESC"). Order("created_at DESC").
Find(&users).Error; err != nil { Find(&users).Error; err != nil {
@@ -123,7 +123,7 @@ func handleGetAgents(c *gin.Context) {
func handleGetAgentsTree(c *gin.Context) { func handleGetAgentsTree(c *gin.Context) {
var users []model.User var users []model.User
if err := database.DB.Where("role = ?", "admin"). if err := database.DB.Where("role = ?", "agent").
Preload("ParentAgent"). Preload("ParentAgent").
Order("created_at DESC"). Order("created_at DESC").
Find(&users).Error; err != nil { Find(&users).Error; err != nil {
@@ -239,7 +239,7 @@ func handleCreateAgent(c *gin.Context) {
if req.ParentAgentID != nil { if req.ParentAgentID != nil {
var parentAgent model.User var parentAgent model.User
if err := database.DB.Where("id = ? AND role = ?", *req.ParentAgentID, "admin").First(&parentAgent).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", *req.ParentAgentID, "agent").First(&parentAgent).Error; err != nil {
response.Error(c, 404, "上级代理不存在") response.Error(c, 404, "上级代理不存在")
return return
} }
@@ -283,7 +283,7 @@ func handleGetAgentDetail(c *gin.Context) {
agentID := c.Param("id") agentID := c.Param("id")
var user model.User var user model.User
if err := database.DB.Where("id = ? AND role = ?", agentID, "admin"). if err := database.DB.Where("id = ? AND role = ?", agentID, "agent").
Preload("ParentAgent"). Preload("ParentAgent").
First(&user).Error; err != nil { First(&user).Error; err != nil {
response.Error(c, 404, "代理不存在") response.Error(c, 404, "代理不存在")
@@ -378,7 +378,7 @@ func handleUpdateAgent(c *gin.Context) {
agentID := c.Param("id") agentID := c.Param("id")
var user model.User var user model.User
if err := database.DB.Where("id = ? AND role = ?", agentID, "admin").First(&user).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", agentID, "agent").First(&user).Error; err != nil {
response.Error(c, 404, "代理不存在") response.Error(c, 404, "代理不存在")
return return
} }
@@ -402,7 +402,7 @@ func handleUpdateAgent(c *gin.Context) {
return return
} }
var parentAgent model.User var parentAgent model.User
if err := database.DB.Where("id = ? AND role = ?", *req.ParentAgentID, "admin").First(&parentAgent).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", *req.ParentAgentID, "agent").First(&parentAgent).Error; err != nil {
response.Error(c, 404, "上级代理不存在") response.Error(c, 404, "上级代理不存在")
return return
} }
@@ -446,7 +446,7 @@ func handleUpdateAgentStatus(c *gin.Context) {
agentID := c.Param("id") agentID := c.Param("id")
var user model.User var user model.User
if err := database.DB.Where("id = ? AND role = ?", agentID, "admin").First(&user).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", agentID, "agent").First(&user).Error; err != nil {
response.Error(c, 404, "代理不存在") response.Error(c, 404, "代理不存在")
return return
} }
@@ -477,7 +477,7 @@ func handleDeleteAgent(c *gin.Context) {
agentID := c.Param("id") agentID := c.Param("id")
var user model.User var user model.User
if err := database.DB.Where("id = ? AND role = ?", agentID, "developer").First(&user).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", agentID, "agent").First(&user).Error; err != nil {
response.Error(c, 404, "代理不存在") response.Error(c, 404, "代理不存在")
return return
} }
@@ -500,7 +500,7 @@ func handleGetAgentCards(c *gin.Context) {
agentID := c.Param("id") agentID := c.Param("id")
var user model.User var user model.User
if err := database.DB.Where("id = ? AND role = ?", agentID, "developer").First(&user).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", agentID, "agent").First(&user).Error; err != nil {
response.Error(c, 404, "代理不存在") response.Error(c, 404, "代理不存在")
return return
} }
@@ -557,7 +557,7 @@ func handleUpdateAgentCards(c *gin.Context) {
agentID := c.Param("id") agentID := c.Param("id")
var user model.User var user model.User
if err := database.DB.Where("id = ? AND role = ?", agentID, "developer").First(&user).Error; err != nil { if err := database.DB.Where("id = ? AND role = ?", agentID, "agent").First(&user).Error; err != nil {
response.Error(c, 404, "代理不存在") response.Error(c, 404, "代理不存在")
return return
} }
+5 -5
View File
@@ -419,8 +419,8 @@ func handleCreateCloudVariable(c *gin.Context) {
req.Scope = "app" req.Scope = "app"
} }
if req.WritePermission != "developer" && req.WritePermission != "user" && req.WritePermission != "app_user" { if req.WritePermission != "admin" && req.WritePermission != "user" && req.WritePermission != "app_user" {
req.WritePermission = "developer" req.WritePermission = "admin"
} }
if req.Status != "active" && req.Status != "inactive" { if req.Status != "active" && req.Status != "inactive" {
@@ -489,7 +489,7 @@ func handleUpdateCloudVariable(c *gin.Context) {
variable.DataType = req.DataType variable.DataType = req.DataType
} }
variable.MaxRecords = req.MaxRecords variable.MaxRecords = req.MaxRecords
if req.WritePermission == "developer" || req.WritePermission == "user" || req.WritePermission == "app_user" { if req.WritePermission == "admin" || req.WritePermission == "user" || req.WritePermission == "app_user" {
variable.WritePermission = req.WritePermission variable.WritePermission = req.WritePermission
} }
variable.Description = req.Description variable.Description = req.Description
@@ -571,8 +571,8 @@ func handleUploadCloudVariable(c *gin.Context) {
scope = "app" scope = "app"
} }
if writePermission != "developer" && writePermission != "user" { if writePermission != "admin" && writePermission != "user" {
writePermission = "developer" writePermission = "admin"
} }
var user model.User var user model.User
+2 -2
View File
@@ -130,7 +130,7 @@ func handleRegister(c *gin.Context) {
req.Username, req.Email, req.Phone, req.Role, req.Type) req.Username, req.Email, req.Phone, req.Role, req.Type)
if req.Role == "" { if req.Role == "" {
req.Role = "developer" req.Role = "admin"
} }
username := req.Username username := req.Username
@@ -794,7 +794,7 @@ func handleGetPublicStats(c *gin.Context) {
var totalApps, totalUsers, totalVerifications int64 var totalApps, totalUsers, totalVerifications int64
database.DB.Model(&model.Application{}).Count(&totalApps) database.DB.Model(&model.Application{}).Count(&totalApps)
database.DB.Model(&model.User{}).Where("role = ?", "developer").Count(&totalUsers) database.DB.Model(&model.User{}).Where("role = ?", "admin").Count(&totalUsers)
database.DB.Model(&model.Card{}).Where("status = ?", "used").Count(&totalVerifications) database.DB.Model(&model.Card{}).Where("status = ?", "used").Count(&totalVerifications)
response.Success(c, gin.H{ response.Success(c, gin.H{
+2 -2
View File
@@ -70,7 +70,7 @@ func (s *AuthService) Login(username, password, agentPath string) (map[string]in
// Register 用户注册 // Register 用户注册
func (s *AuthService) Register(username, email, password string) error { func (s *AuthService) Register(username, email, password string) error {
return s.RegisterWithRole(username, email, "", password, "developer") return s.RegisterWithRole(username, email, "", password, "admin")
} }
// RegisterWithRole 用户注册(带角色) // RegisterWithRole 用户注册(带角色)
@@ -115,7 +115,7 @@ func (s *AuthService) RegisterWithRole(username, email, phone, password, role st
} }
if role == "" { if role == "" {
role = "developer" role = "admin"
} }
user := model.User{ user := model.User{
+1 -1
View File
@@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS users (
signature VARCHAR(255) DEFAULT NULL COMMENT '个性签名', signature VARCHAR(255) DEFAULT NULL COMMENT '个性签名',
points INT DEFAULT 0 COMMENT '积分', points INT DEFAULT 0 COMMENT '积分',
level INT DEFAULT 1 COMMENT '等级', level INT DEFAULT 1 COMMENT '等级',
role ENUM('user', 'developer', 'agent', 'admin') DEFAULT 'user' COMMENT '角色', role ENUM('user', 'agent', 'admin') DEFAULT 'user' COMMENT '角色',
status ENUM('active', 'inactive', 'banned') DEFAULT 'active' COMMENT '状态', status ENUM('active', 'inactive', 'banned') DEFAULT 'active' COMMENT '状态',
agent_id BIGINT UNSIGNED DEFAULT NULL COMMENT '所属代理ID', agent_id BIGINT UNSIGNED DEFAULT NULL COMMENT '所属代理ID',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -7,10 +7,10 @@ import TeamSwitcher from '@/components/app-sidebar/team-switcher.vue'
import NavFooter from '@/components/developer-sidebar/nav-footer.vue' import NavFooter from '@/components/developer-sidebar/nav-footer.vue'
const user = reactive({ const user = reactive({
name: '开发者', name: '管理员',
email: 'developer@example.com', email: 'admin@example.com',
avatar: '/avatars/developer.jpg', avatar: '/avatars/admin.jpg',
role: 'developer', role: 'admin',
}) })
onMounted(() => { onMounted(() => {
@@ -228,7 +228,7 @@ watch(route, () => {
{{ t('nav.pricing') }} {{ t('nav.pricing') }}
</router-link> </router-link>
<template v-if="isLoggedIn && currentUser?.role === 'developer'"> <template v-if="isLoggedIn && currentUser?.role === 'admin'">
<div class="w-px h-4 bg-border mx-1" /> <div class="w-px h-4 bg-border mx-1" />
<UiDropdownMenu> <UiDropdownMenu>
@@ -300,79 +300,6 @@ watch(route, () => {
</UiDropdownMenuContent> </UiDropdownMenuContent>
</UiDropdownMenu> </UiDropdownMenu>
</template> </template>
<template v-if="isLoggedIn && currentUser?.role === 'admin'">
<div class="w-px h-4 bg-border mx-1" />
<UiDropdownMenu>
<UiDropdownMenuTrigger as-child>
<button
class="transition-colors hover:text-foreground/80 px-3 py-2 rounded-lg flex items-center gap-1"
:class="isMenuActive(adminMenus.main.items) ? 'text-foreground bg-accent' : 'text-foreground/60'"
>
{{ adminMenus.main.title }}
<Icon icon="lucide:chevron-down" class="h-4 w-4" />
</button>
</UiDropdownMenuTrigger>
<UiDropdownMenuContent align="start" class="w-40">
<UiDropdownMenuItem
v-for="item in adminMenus.main.items"
:key="item.url"
:class="route.path === item.url ? 'bg-accent' : ''"
@click="router.push(item.url)"
>
<Icon :icon="item.icon" class="mr-2 h-4 w-4" />
{{ item.title }}
</UiDropdownMenuItem>
</UiDropdownMenuContent>
</UiDropdownMenu>
<UiDropdownMenu>
<UiDropdownMenuTrigger as-child>
<button
class="transition-colors hover:text-foreground/80 px-3 py-2 rounded-lg flex items-center gap-1"
:class="isMenuActive(adminMenus.content.items) ? 'text-foreground bg-accent' : 'text-foreground/60'"
>
{{ adminMenus.content.title }}
<Icon icon="lucide:chevron-down" class="h-4 w-4" />
</button>
</UiDropdownMenuTrigger>
<UiDropdownMenuContent align="start" class="w-40">
<UiDropdownMenuItem
v-for="item in adminMenus.content.items"
:key="item.url"
:class="route.path === item.url ? 'bg-accent' : ''"
@click="router.push(item.url)"
>
<Icon :icon="item.icon" class="mr-2 h-4 w-4" />
{{ item.title }}
</UiDropdownMenuItem>
</UiDropdownMenuContent>
</UiDropdownMenu>
<UiDropdownMenu>
<UiDropdownMenuTrigger as-child>
<button
class="transition-colors hover:text-foreground/80 px-3 py-2 rounded-lg flex items-center gap-1"
:class="isMenuActive(adminMenus.system.items) ? 'text-foreground bg-accent' : 'text-foreground/60'"
>
{{ adminMenus.system.title }}
<Icon icon="lucide:chevron-down" class="h-4 w-4" />
</button>
</UiDropdownMenuTrigger>
<UiDropdownMenuContent align="start" class="w-40">
<UiDropdownMenuItem
v-for="item in adminMenus.system.items"
:key="item.url"
:class="route.path === item.url ? 'bg-accent' : ''"
@click="router.push(item.url)"
>
<Icon :icon="item.icon" class="mr-2 h-4 w-4" />
{{ item.title }}
</UiDropdownMenuItem>
</UiDropdownMenuContent>
</UiDropdownMenu>
</template>
</nav> </nav>
</div> </div>
@@ -438,18 +365,14 @@ watch(route, () => {
<Icon icon="lucide:user" class="mr-2 h-4 w-4" /> <Icon icon="lucide:user" class="mr-2 h-4 w-4" />
<span>{{ t('nav.profile') }}</span> <span>{{ t('nav.profile') }}</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
<UiDropdownMenuItem v-if="currentUser?.role === 'developer'" @click="router.push('/developer')"> <UiDropdownMenuItem v-if="currentUser?.role === 'admin'" @click="router.push('/developer')">
<Icon icon="lucide:layout-dashboard" class="mr-2 h-4 w-4" /> <Icon icon="lucide:layout-dashboard" class="mr-2 h-4 w-4" />
<span>{{ t('nav.developerDashboard') }}</span> <span>{{ t('nav.developerDashboard') }}</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
<UiDropdownMenuItem v-if="currentUser?.role === 'agent'" @click="router.push('/agent/dashboard')"> <UiDropdownMenuItem v-if="currentUser?.role === 'agent'" @click="router.push('/agent')">
<Icon icon="lucide:layout-dashboard" class="mr-2 h-4 w-4" /> <Icon icon="lucide:layout-dashboard" class="mr-2 h-4 w-4" />
<span>{{ t('nav.agentDashboard') }}</span> <span>{{ t('nav.agentDashboard') }}</span>
</UiDropdownMenuItem> </UiDropdownMenuItem>
<UiDropdownMenuItem v-if="currentUser?.role === 'admin'" @click="router.push('/admin')">
<Icon icon="lucide:shield" class="mr-2 h-4 w-4" />
<span>{{ t('nav.adminDashboard') }}</span>
</UiDropdownMenuItem>
<UiDropdownMenuSeparator /> <UiDropdownMenuSeparator />
<UiDropdownMenuItem @click="handleLogout"> <UiDropdownMenuItem @click="handleLogout">
<Icon icon="lucide:log-out" class="mr-2 h-4 w-4" /> <Icon icon="lucide:log-out" class="mr-2 h-4 w-4" />
@@ -502,7 +425,7 @@ watch(route, () => {
</button> </button>
</div> </div>
<template v-if="isLoggedIn && currentUser?.role === 'developer'"> <template v-if="isLoggedIn && currentUser?.role === 'admin'">
<div class="space-y-1"> <div class="space-y-1">
<p class="text-xs font-medium text-muted-foreground px-3 mb-2"> <p class="text-xs font-medium text-muted-foreground px-3 mb-2">
{{ developerMenus.main.title }} {{ developerMenus.main.title }}
@@ -551,56 +474,6 @@ watch(route, () => {
</button> </button>
</div> </div>
</template> </template>
<template v-if="isLoggedIn && currentUser?.role === 'admin'">
<div class="space-y-1">
<p class="text-xs font-medium text-muted-foreground px-3 mb-2">
{{ adminMenus.main.title }}
</p>
<button
v-for="item in adminMenus.main.items"
:key="item.url"
class="w-full flex items-center gap-3 px-3 py-2 text-sm rounded-lg transition-colors"
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
@click="navigateTo(item.url)"
>
<Icon :icon="item.icon" class="size-4" />
{{ item.title }}
</button>
</div>
<div class="space-y-1">
<p class="text-xs font-medium text-muted-foreground px-3 mb-2">
{{ adminMenus.content.title }}
</p>
<button
v-for="item in adminMenus.content.items"
:key="item.url"
class="w-full flex items-center gap-3 px-3 py-2 text-sm rounded-lg transition-colors"
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
@click="navigateTo(item.url)"
>
<Icon :icon="item.icon" class="size-4" />
{{ item.title }}
</button>
</div>
<div class="space-y-1">
<p class="text-xs font-medium text-muted-foreground px-3 mb-2">
{{ adminMenus.system.title }}
</p>
<button
v-for="item in adminMenus.system.items"
:key="item.url"
class="w-full flex items-center gap-3 px-3 py-2 text-sm rounded-lg transition-colors"
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
@click="navigateTo(item.url)"
>
<Icon :icon="item.icon" class="size-4" />
{{ item.title }}
</button>
</div>
</template>
</nav> </nav>
<div v-if="!isLoggedIn" class="pt-4 border-t mt-4 space-y-2"> <div v-if="!isLoggedIn" class="pt-4 border-t mt-4 space-y-2">
+5 -1
View File
@@ -6,7 +6,9 @@ import { useRouter } from 'vue-router'
import NavTeam from '@/components/app-sidebar/nav-team.vue' import NavTeam from '@/components/app-sidebar/nav-team.vue'
import TeamSwitcher from '@/components/app-sidebar/team-switcher.vue' import TeamSwitcher from '@/components/app-sidebar/team-switcher.vue'
import LanguageChange from '@/components/language-change.vue'
import ThemePopover from '@/components/custom-theme/theme-popover.vue' import ThemePopover from '@/components/custom-theme/theme-popover.vue'
import ToggleTheme from '@/components/toggle-theme.vue'
const router = useRouter() const router = useRouter()
@@ -218,7 +220,9 @@ function handleLogout() {
</template> </template>
</UiBreadcrumbList> </UiBreadcrumbList>
</UiBreadcrumb> </UiBreadcrumb>
<div class="ml-auto"> <div class="ml-auto flex items-center gap-2">
<LanguageChange />
<ToggleTheme />
<ThemePopover /> <ThemePopover />
</div> </div>
</header> </header>
+4
View File
@@ -3,7 +3,9 @@ import { computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import DeveloperSidebar from '@/components/developer-sidebar/index.vue' import DeveloperSidebar from '@/components/developer-sidebar/index.vue'
import LanguageChange from '@/components/language-change.vue'
import ThemePopover from '@/components/custom-theme/theme-popover.vue' import ThemePopover from '@/components/custom-theme/theme-popover.vue'
import ToggleTheme from '@/components/toggle-theme.vue'
const router = useRouter() const router = useRouter()
@@ -91,6 +93,8 @@ const breadcrumbs = computed(() => {
</UiBreadcrumb> </UiBreadcrumb>
<div class="ml-auto flex items-center gap-2"> <div class="ml-auto flex items-center gap-2">
<LanguageChange />
<ToggleTheme />
<ThemePopover /> <ThemePopover />
</div> </div>
</header> </header>
@@ -30,7 +30,7 @@ const form = ref({
data_type: 'single', data_type: 'single',
max_records: 0, max_records: 0,
scope: 'app', scope: 'app',
write_permission: 'developer', write_permission: 'admin',
description: '', description: '',
status: 'active', status: 'active',
}) })
@@ -61,7 +61,7 @@ async function fetchVariable() {
data_type: variable.data_type || 'single', data_type: variable.data_type || 'single',
max_records: variable.max_records || 0, max_records: variable.max_records || 0,
scope: variable.scope || 'app', scope: variable.scope || 'app',
write_permission: variable.write_permission || 'developer', write_permission: variable.write_permission || 'admin',
description: variable.description || '', description: variable.description || '',
status: variable.status || 'active', status: variable.status || 'active',
} }
@@ -297,10 +297,10 @@ onMounted(async () => {
<UiRadioGroup v-model="form.write_permission" class="grid grid-cols-1 gap-4"> <UiRadioGroup v-model="form.write_permission" class="grid grid-cols-1 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"
:class="form.write_permission === 'developer' ? 'border-primary bg-primary/5' : 'hover:bg-accent'" :class="form.write_permission === 'admin' ? 'border-primary bg-primary/5' : 'hover:bg-accent'"
@click="form.write_permission = 'developer'" @click="form.write_permission = 'admin'"
> >
<UiRadioGroupItem value="developer" class="mt-0.5" /> <UiRadioGroupItem value="admin" class="mt-0.5" />
<div> <div>
<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" />
@@ -398,7 +398,7 @@ onMounted(async () => {
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span> <span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span>
<UiBadge variant="secondary"> <UiBadge variant="secondary">
{{ form.write_permission === 'developer' ? t('developer.cloudVariables.create.developerOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('developer.cloudVariables.create.userWritable') }} {{ form.write_permission === 'admin' ? t('developer.cloudVariables.create.developerOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('developer.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">
@@ -30,7 +30,7 @@ const form = ref({
data_type: 'single', data_type: 'single',
max_records: 0, max_records: 0,
scope: 'app', scope: 'app',
write_permission: 'developer', write_permission: 'admin',
description: '', description: '',
status: 'active', status: 'active',
}) })
@@ -391,10 +391,10 @@ onMounted(() => {
<UiRadioGroup v-model="form.write_permission" class="grid grid-cols-1 gap-4"> <UiRadioGroup v-model="form.write_permission" class="grid grid-cols-1 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"
:class="form.write_permission === 'developer' ? 'border-primary bg-primary/5' : 'hover:bg-accent'" :class="form.write_permission === 'admin' ? 'border-primary bg-primary/5' : 'hover:bg-accent'"
@click="form.write_permission = 'developer'" @click="form.write_permission = 'admin'"
> >
<UiRadioGroupItem value="developer" class="mt-0.5" /> <UiRadioGroupItem value="admin" class="mt-0.5" />
<div> <div>
<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" />
@@ -492,7 +492,7 @@ onMounted(() => {
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span> <span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span>
<UiBadge variant="secondary"> <UiBadge variant="secondary">
{{ form.write_permission === 'developer' ? t('developer.cloudVariables.create.developerOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('developer.cloudVariables.create.userWritable') }} {{ form.write_permission === 'admin' ? t('developer.cloudVariables.create.developerOnly') : form.write_permission === 'app_user' ? '应用用户可写' : t('developer.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">
+1 -3
View File
@@ -38,9 +38,7 @@ async function handleLogin() {
toast.success('登录成功') toast.success('登录成功')
const isAgent = data.user.role === 'agent' || data.user.parent_agent_id if (data.user.role === 'agent' || data.user.parent_agent_id) {
if (isAgent) {
router.push('/agent') router.push('/agent')
} }
else { else {
@@ -28,7 +28,7 @@ const form = ref({
default_value: '', default_value: '',
var_type: 'string', var_type: 'string',
scope: 'app', scope: 'app',
write_permission: 'developer', write_permission: 'admin',
description: '', description: '',
status: 'active', status: 'active',
}) })
@@ -57,7 +57,7 @@ async function fetchVariable() {
default_value: variable.default_value || '', default_value: variable.default_value || '',
var_type: variable.var_type || 'string', var_type: variable.var_type || 'string',
scope: variable.scope || 'app', scope: variable.scope || 'app',
write_permission: variable.write_permission || 'developer', write_permission: variable.write_permission || 'admin',
description: variable.description || '', description: variable.description || '',
status: variable.status || 'active', status: variable.status || 'active',
} }
@@ -239,10 +239,10 @@ onMounted(async () => {
<UiRadioGroup v-model="form.write_permission" class="grid grid-cols-1 md:grid-cols-2 gap-4"> <UiRadioGroup v-model="form.write_permission" 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"
:class="form.write_permission === 'developer' ? 'border-primary bg-primary/5' : 'hover:bg-accent'" :class="form.write_permission === 'admin' ? 'border-primary bg-primary/5' : 'hover:bg-accent'"
@click="form.write_permission = 'developer'" @click="form.write_permission = 'admin'"
> >
<UiRadioGroupItem value="developer" class="mt-0.5" /> <UiRadioGroupItem value="admin" class="mt-0.5" />
<div> <div>
<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" />
@@ -310,7 +310,7 @@ onMounted(async () => {
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span> <span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span>
<UiBadge variant="secondary"> <UiBadge variant="secondary">
{{ form.write_permission === 'developer' ? t('developer.cloudVariables.create.developerOnly') : t('developer.cloudVariables.create.userWritable') }} {{ form.write_permission === 'admin' ? t('developer.cloudVariables.create.developerOnly') : t('developer.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">
@@ -30,7 +30,7 @@ const form = ref({
data_type: 'single', data_type: 'single',
max_records: 0, max_records: 0,
scope: 'app', scope: 'app',
write_permission: 'developer', write_permission: 'admin',
description: '', description: '',
status: 'active', status: 'active',
}) })
@@ -391,10 +391,10 @@ onMounted(() => {
<UiRadioGroup v-model="form.write_permission" class="grid grid-cols-1 md:grid-cols-2 gap-4"> <UiRadioGroup v-model="form.write_permission" 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"
:class="form.write_permission === 'developer' ? 'border-primary bg-primary/5' : 'hover:bg-accent'" :class="form.write_permission === 'admin' ? 'border-primary bg-primary/5' : 'hover:bg-accent'"
@click="form.write_permission = 'developer'" @click="form.write_permission = 'admin'"
> >
<UiRadioGroupItem value="developer" class="mt-0.5" /> <UiRadioGroupItem value="admin" class="mt-0.5" />
<div> <div>
<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" />
@@ -462,7 +462,7 @@ onMounted(() => {
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span> <span class="text-muted-foreground">{{ t('developer.cloudVariables.create.writePermission') }}</span>
<UiBadge variant="secondary"> <UiBadge variant="secondary">
{{ form.write_permission === 'developer' ? t('developer.cloudVariables.create.developerOnly') : t('developer.cloudVariables.create.userWritable') }} {{ form.write_permission === 'admin' ? t('developer.cloudVariables.create.developerOnly') : t('developer.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">
+1 -2
View File
@@ -273,9 +273,8 @@ function formatNumber(num: number) {
function getRoleName(role: string) { function getRoleName(role: string) {
const roles: Record<string, string> = { const roles: Record<string, string> = {
developer: '开发者',
agent: '代理商',
admin: '管理员', admin: '管理员',
agent: '代理商',
} }
return roles[role] || role return roles[role] || role
} }
+5 -1
View File
@@ -19,10 +19,14 @@ export function createRouterGuard(router: Router) {
const isAuthPage = to.path === '/login' || to.path === '/register' const isAuthPage = to.path === '/login' || to.path === '/register'
if (requiresAuth && !token) { if (requiresAuth && !token) {
return '/login' return { path: '/login', query: { redirect: to.fullPath } }
} }
if (isAuthPage && token && user) { if (isAuthPage && token && user) {
const redirect = (to.query.redirect as string) || ''
if (redirect) {
return redirect
}
if (user.role === 'agent' || user.parent_agent_id) { if (user.role === 'agent' || user.parent_agent_id) {
return '/agent' return '/agent'
} }
+1 -1
View File
@@ -26,7 +26,7 @@ const routes: RouteRecordRaw[] = [
{ {
path: '/developer', path: '/developer',
component: () => import('@/layouts/developer.vue'), component: () => import('@/layouts/developer.vue'),
meta: { auth: true, role: 'developer' }, meta: { auth: true, role: 'admin' },
children: [ children: [
{ {
path: '', path: '',