修复bug
This commit is contained in:
@@ -11,11 +11,14 @@ RUN apk add --no-cache \
|
||||
COPY package*.json ./
|
||||
|
||||
# 安装依赖
|
||||
RUN npm ci
|
||||
RUN npm install
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建项目
|
||||
RUN npm run build
|
||||
|
||||
# 创建非root用户
|
||||
RUN addgroup -g 1001 -S nodejs
|
||||
RUN adduser -S nodejs -u 1001
|
||||
@@ -30,6 +33,5 @@ EXPOSE 3000
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:3000 || exit 1
|
||||
|
||||
# 启动命令 - 支持环境变量
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
# 启动命令
|
||||
CMD ["npm", "run", "start"]
|
||||
@@ -4,8 +4,9 @@
|
||||
"description": "Pandora 前端应用",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite preview --host 0.0.0.0 --port 3000",
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
@@ -13,17 +14,19 @@
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.3.8",
|
||||
"vue-router": "^4.2.5",
|
||||
"pinia": "^2.1.7",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"vee-validate": "^4.10.5",
|
||||
"vue-toastification": "^2.0.0-rc.5",
|
||||
"axios": "^1.5.0",
|
||||
"@headlessui/vue": "^1.7.16",
|
||||
"@heroicons/vue": "^2.0.18",
|
||||
"@vue/runtime-core": "^3.3.4",
|
||||
"@vue/runtime-dom": "^3.3.4",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"axios": "^1.5.0",
|
||||
"clsx": "^2.0.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
"pinia": "^2.1.7",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"vee-validate": "^4.10.5",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue-toastification": "^2.0.0-rc.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.6.3",
|
||||
@@ -38,13 +41,13 @@
|
||||
"postcss": "^8.4.29",
|
||||
"prettier": "^3.0.3",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "~5.2.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^4.4.11",
|
||||
"vitest": "^0.34.4",
|
||||
"vue-tsc": "^1.8.15"
|
||||
"vue-tsc": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ export interface WebsiteConfig {
|
||||
const getWebsiteUrls = () => {
|
||||
// 检查是否在浏览器环境中
|
||||
if (typeof window !== 'undefined') {
|
||||
// 在客户端,使用Vite定义的全局变量
|
||||
// 在客户端,使用Vite注入的环境变量
|
||||
return {
|
||||
claude: (window as any).__CLAUDE_URL__ || 'https://chat.micar9.com:8443',
|
||||
chatgpt: (window as any).__CHATGPT_URL__ || 'https://chat.openai.com',
|
||||
grok: (window as any).__GROK_URL__ || 'https://grok-mirror.micar9.com:8443'
|
||||
claude: import.meta.env.CLAUDE_TARGET_URL || 'https://chat.micar9.com:8443',
|
||||
chatgpt: import.meta.env.CHATGPT_TARGET_URL || 'https://chat.openai.com',
|
||||
grok: import.meta.env.GROK_TARGET_URL || 'https://grok-mirror.micar9.com:8443'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import type { RouteRecordRaw, NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useAdminStore } from '@/stores/admin'
|
||||
import { adminAuth } from '@/utils/auth'
|
||||
import { useToast } from 'vue-toastification'
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
@@ -74,17 +72,18 @@ const router = createRouter({
|
||||
})
|
||||
|
||||
// 路由守卫
|
||||
router.beforeEach((to: any, from: any, next: any) => {
|
||||
router.beforeEach(async (
|
||||
to: RouteLocationNormalized,
|
||||
_from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) => {
|
||||
// 设置页面标题
|
||||
document.title = `${to.meta.title} - Pandora`
|
||||
|
||||
// 获取认证状态
|
||||
const authStore = useAuthStore()
|
||||
const adminStore = useAdminStore()
|
||||
const toast = useToast()
|
||||
const title = to.meta.title as string
|
||||
document.title = `${title} - Pandora`
|
||||
|
||||
// 检查是否需要用户认证
|
||||
if (to.meta.requiresAuth) {
|
||||
const authStore = useAuthStore()
|
||||
if (!authStore.isLoggedIn) {
|
||||
next('/')
|
||||
return
|
||||
|
||||
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { adminAPI } from '@/utils/api'
|
||||
import { adminAuth } from '@/utils/auth'
|
||||
import type { User, PaginatedResponse } from '@/types'
|
||||
import type { User } from '@/types'
|
||||
|
||||
export const useAdminStore = defineStore('admin', () => {
|
||||
// 状态
|
||||
|
||||
@@ -138,45 +138,6 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置TOTP
|
||||
const setupTOTP = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await authAPI.setupTOTP()
|
||||
return response
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.message || '设置二步验证失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 验证TOTP
|
||||
const verifyTOTP = async (totpToken: string) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await authAPI.verifyTOTP(totpToken)
|
||||
|
||||
// 更新用户信息
|
||||
if (user.value && token.value) {
|
||||
user.value.totpEnabled = true
|
||||
userAuth.setLogin(token.value, user.value)
|
||||
}
|
||||
|
||||
return response
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.message || '验证失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
const getProfile = async () => {
|
||||
loading.value = true
|
||||
@@ -238,8 +199,6 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
resendVerification,
|
||||
forgotPassword,
|
||||
resetPassword,
|
||||
setupTOTP,
|
||||
verifyTOTP,
|
||||
getProfile,
|
||||
logout,
|
||||
clearError
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
export interface User {
|
||||
id: string
|
||||
username: string
|
||||
email: string
|
||||
role: string
|
||||
isActive: boolean
|
||||
emailVerified?: boolean
|
||||
totpEnabled: boolean
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
accounts?: string[]
|
||||
}
|
||||
|
||||
// 网站账号相关类型
|
||||
@@ -47,15 +48,10 @@ export interface Session {
|
||||
// 审计日志相关类型
|
||||
export interface AuditLog {
|
||||
id: string
|
||||
userId: string
|
||||
action: string
|
||||
resource: string
|
||||
resourceId?: string
|
||||
details?: any
|
||||
ipAddress?: string
|
||||
userAgent?: string
|
||||
createdAt: string
|
||||
user: User
|
||||
description: string
|
||||
time: string
|
||||
ipAddress: string
|
||||
}
|
||||
|
||||
// API响应类型
|
||||
@@ -116,4 +112,37 @@ export interface Notification {
|
||||
message: string
|
||||
duration?: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
id: string
|
||||
username: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface SystemSettings {
|
||||
allowRegistration: boolean
|
||||
sessionTimeout: number
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface AdminMenu {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
icon: string
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface AdminUser {
|
||||
id: string
|
||||
username: string
|
||||
role: string
|
||||
}
|
||||
|
||||
export interface SystemStats {
|
||||
totalUsers: number
|
||||
totalAccounts: number
|
||||
todayVisits: number
|
||||
alerts: number
|
||||
}
|
||||
@@ -113,17 +113,6 @@ export const authAPI = {
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 设置TOTP
|
||||
async setupTOTP() {
|
||||
const response = await api.post('/auth/setup-totp')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 验证TOTP
|
||||
async verifyTOTP(token: string) {
|
||||
const response = await api.post('/auth/verify-totp', { token })
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
async getProfile() {
|
||||
@@ -147,26 +136,7 @@ export const authAPI = {
|
||||
const response = await api.post('/auth/logout')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取TOTP二维码
|
||||
async getTOTPQRCode() {
|
||||
const response = await api.get('/auth/totp/qr-code')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 启用TOTP
|
||||
async enableTOTP(token: string) {
|
||||
const response = await api.post('/auth/totp/enable', { token })
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 禁用TOTP
|
||||
async disableTOTP(password: string) {
|
||||
const response = await api.post('/auth/totp/disable', { password })
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
// 账号管理API
|
||||
export const accountAPI = {
|
||||
// 获取用户可用账号
|
||||
|
||||
@@ -301,70 +301,94 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAdminStore } from '@/stores/admin'
|
||||
import type { AdminMenu, AdminUser, SystemStats } from '@/types'
|
||||
import { adminAuth } from '@/utils/auth'
|
||||
import { useToast } from 'vue-toastification'
|
||||
|
||||
const router = useRouter()
|
||||
const adminStore = useAdminStore()
|
||||
const toast = useToast()
|
||||
|
||||
// 管理菜单
|
||||
const adminMenus = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '用户管理',
|
||||
description: '管理用户账号和权限',
|
||||
icon: 'UserIcon',
|
||||
route: '/admin/users'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '账号管理',
|
||||
description: '管理网站账号和token',
|
||||
icon: 'KeyIcon',
|
||||
route: '/admin/accounts'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '权限管理',
|
||||
description: '配置用户访问权限',
|
||||
icon: 'ShieldIcon',
|
||||
route: '/admin/permissions'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '系统监控',
|
||||
description: '查看系统运行状态',
|
||||
icon: 'ChartIcon',
|
||||
route: '/admin/monitor'
|
||||
}
|
||||
])
|
||||
|
||||
// 统计数据
|
||||
const stats = ref({
|
||||
const adminUser = ref<AdminUser | null>(null)
|
||||
const stats = ref<SystemStats>({
|
||||
totalUsers: 0,
|
||||
totalAccounts: 0,
|
||||
todayVisits: 0,
|
||||
alerts: 0
|
||||
})
|
||||
|
||||
// 最近活动
|
||||
const recentActivities = ref([])
|
||||
const adminMenus: AdminMenu[] = [
|
||||
{
|
||||
id: 'users',
|
||||
name: '用户管理',
|
||||
description: '管理系统用户',
|
||||
icon: 'UserIcon',
|
||||
path: '/admin/users'
|
||||
},
|
||||
{
|
||||
id: 'accounts',
|
||||
name: '账号管理',
|
||||
description: '管理网站账号',
|
||||
icon: 'KeyIcon',
|
||||
path: '/admin/accounts'
|
||||
},
|
||||
{
|
||||
id: 'permissions',
|
||||
name: '权限管理',
|
||||
description: '配置访问权限',
|
||||
icon: 'ShieldIcon',
|
||||
path: '/admin/permissions'
|
||||
},
|
||||
{
|
||||
id: 'monitor',
|
||||
name: '系统监控',
|
||||
description: '监控系统状态',
|
||||
icon: 'ChartIcon',
|
||||
path: '/admin/monitor'
|
||||
}
|
||||
]
|
||||
|
||||
// 导航到菜单
|
||||
const navigateToMenu = (menu: any) => {
|
||||
router.push(menu.route)
|
||||
const navigateToMenu = (menu: AdminMenu) => {
|
||||
router.push(menu.path)
|
||||
}
|
||||
|
||||
// 管理员用户信息
|
||||
const adminUser = ref<any>(null)
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
adminAuth.logout()
|
||||
router.push('/admin/login')
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await adminStore.logout()
|
||||
router.push('/admin/login')
|
||||
} catch (error) {
|
||||
console.error('退出登录失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载管理员信息
|
||||
const loadAdminInfo = async () => {
|
||||
try {
|
||||
// 从adminAuth获取管理员信息
|
||||
const adminInfo = adminAuth.getAdminInfo()
|
||||
if (adminInfo) {
|
||||
adminUser.value = adminInfo
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载管理员信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载系统统计数据
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
const response = await adminStore.getStats()
|
||||
if (response && response.stats) {
|
||||
stats.value = response.stats
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载系统统计失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 最近活动
|
||||
const recentActivities = ref<any[]>([])
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string) => {
|
||||
const date = new Date(time)
|
||||
@@ -382,40 +406,6 @@ const formatTime = (time: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载统计数据
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
console.log('开始加载统计数据...')
|
||||
const response = await adminStore.getStats()
|
||||
console.log('统计数据响应:', response)
|
||||
|
||||
if (response.success && response.data) {
|
||||
stats.value = response.data
|
||||
console.log('统计数据加载成功:', stats.value)
|
||||
} else {
|
||||
console.error('统计数据格式错误:', response)
|
||||
toast.error('统计数据格式错误')
|
||||
// 设置默认值
|
||||
stats.value = {
|
||||
totalUsers: 0,
|
||||
totalAccounts: 0,
|
||||
todayVisits: 0,
|
||||
alerts: 0
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error)
|
||||
toast.error('加载统计数据失败')
|
||||
// 设置默认值
|
||||
stats.value = {
|
||||
totalUsers: 0,
|
||||
totalAccounts: 0,
|
||||
todayVisits: 0,
|
||||
alerts: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载最近活动
|
||||
const loadRecentActivities = async () => {
|
||||
try {
|
||||
@@ -423,50 +413,22 @@ const loadRecentActivities = async () => {
|
||||
const response = await adminStore.getRecentActivities()
|
||||
console.log('最近活动响应:', response)
|
||||
|
||||
if (response.success && response.data) {
|
||||
recentActivities.value = response.data.activities || []
|
||||
if (response && response.activities) {
|
||||
recentActivities.value = response.activities
|
||||
console.log('最近活动加载成功:', recentActivities.value)
|
||||
} else {
|
||||
console.error('最近活动数据格式错误:', response)
|
||||
toast.error('最近活动数据格式错误')
|
||||
recentActivities.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载最近活动失败:', error)
|
||||
toast.error('加载最近活动失败')
|
||||
recentActivities.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(async () => {
|
||||
|
||||
// 检查管理员登录状态
|
||||
if (!adminAuth.isLoggedIn()) {
|
||||
router.push('/admin/login')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取管理员用户信息
|
||||
const adminInfo = adminAuth.getAdminInfo()
|
||||
|
||||
if (!adminInfo) {
|
||||
// 如果没有管理员信息,清除登录状态并跳转到登录页
|
||||
adminAuth.logout()
|
||||
router.push('/admin/login')
|
||||
return
|
||||
}
|
||||
|
||||
adminUser.value = adminInfo
|
||||
|
||||
// 加载数据
|
||||
try {
|
||||
await Promise.all([
|
||||
loadStats(),
|
||||
loadRecentActivities()
|
||||
])
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error)
|
||||
}
|
||||
await loadAdminInfo()
|
||||
await loadStats()
|
||||
await loadRecentActivities()
|
||||
})
|
||||
</script>
|
||||
@@ -164,23 +164,6 @@
|
||||
<div>
|
||||
<h4 class="text-md font-medium text-gray-900 dark:text-white mb-4">安全设置</h4>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">二步验证</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">是否启用TOTP二步验证</p>
|
||||
</div>
|
||||
<button
|
||||
@click="toggleSystemSetting('enableTOTP')"
|
||||
class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2"
|
||||
:class="systemSettings.enableTOTP ? 'bg-primary-600' : 'bg-gray-200 dark:bg-gray-700'"
|
||||
>
|
||||
<span
|
||||
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
|
||||
:class="systemSettings.enableTOTP ? 'translate-x-5' : 'translate-x-0'"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">会话超时</p>
|
||||
@@ -241,85 +224,62 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useAdminStore } from '@/stores/admin'
|
||||
import { useToast } from 'vue-toastification'
|
||||
import type { User, Account, SystemSettings } from '@/types'
|
||||
|
||||
const adminStore = useAdminStore()
|
||||
const toast = useToast()
|
||||
|
||||
// 搜索和筛选
|
||||
const users = ref<User[]>([])
|
||||
const availableAccounts = ref<Account[]>([])
|
||||
const searchQuery = ref('')
|
||||
const accountFilter = ref('')
|
||||
|
||||
// 用户列表
|
||||
const users = ref([])
|
||||
const filteredUsers = computed(() => {
|
||||
let filtered = users.value
|
||||
|
||||
if (searchQuery.value) {
|
||||
filtered = filtered.filter((user: any) =>
|
||||
user.username.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
user.email.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
if (accountFilter.value) {
|
||||
filtered = filtered.filter((user: any) =>
|
||||
user.accounts?.includes(accountFilter.value)
|
||||
)
|
||||
}
|
||||
|
||||
return filtered
|
||||
})
|
||||
|
||||
// 可用账号
|
||||
const availableAccounts = ref([])
|
||||
|
||||
// 加载可用账号
|
||||
const loadAvailableAccounts = async () => {
|
||||
try {
|
||||
const response = await adminStore.getAccounts()
|
||||
availableAccounts.value = response.accounts || []
|
||||
} catch (error) {
|
||||
console.error('加载可用账号失败:', error)
|
||||
// 如果加载失败,使用默认数据
|
||||
availableAccounts.value = [
|
||||
{ id: 'account1', username: '账号1', description: '第一个登录账号' },
|
||||
{ id: 'account2', username: '账号2', description: '第二个登录账号' },
|
||||
{ id: 'account3', username: '账号3', description: '第三个登录账号' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 系统设置
|
||||
const systemSettings = ref({
|
||||
allowRegistration: true,
|
||||
enableTOTP: false,
|
||||
const showAccountModal = ref(false)
|
||||
const selectedUser = ref<User | null>(null)
|
||||
const selectedUserAccounts = ref<string[]>([])
|
||||
const systemSettings = ref<SystemSettings>({
|
||||
allowRegistration: false,
|
||||
sessionTimeout: 24
|
||||
})
|
||||
|
||||
// 模态框状态
|
||||
const showAccountModal = ref(false)
|
||||
const selectedUser = ref(null)
|
||||
const selectedUserAccounts = ref([])
|
||||
// 过滤用户列表
|
||||
const filteredUsers = computed(() => {
|
||||
return users.value.filter(user => {
|
||||
const matchesSearch = !searchQuery.value ||
|
||||
user.username.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
user.email.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
|
||||
const matchesAccount = !accountFilter.value ||
|
||||
(user.accounts && user.accounts.includes(accountFilter.value))
|
||||
|
||||
return matchesSearch && matchesAccount
|
||||
})
|
||||
})
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 这里可以添加实际的搜索逻辑
|
||||
console.log('搜索:', searchQuery.value, '账号筛选:', accountFilter.value)
|
||||
}
|
||||
|
||||
// 根据账户ID获取账户名称
|
||||
const getAccountName = (accountId: string) => {
|
||||
// 获取账号名称
|
||||
const getAccountName = (accountId: string): string => {
|
||||
const account = availableAccounts.value.find(acc => acc.id === accountId)
|
||||
return account ? account.username : accountId
|
||||
}
|
||||
|
||||
// 编辑用户账号权限
|
||||
const editUserAccounts = (user: any) => {
|
||||
selectedUser.value = { ...user }
|
||||
selectedUserAccounts.value = [...(user.accounts || [])]
|
||||
const editUserAccounts = (user: User) => {
|
||||
selectedUser.value = user
|
||||
selectedUserAccounts.value = user.accounts || []
|
||||
showAccountModal.value = true
|
||||
}
|
||||
|
||||
// 切换系统设置
|
||||
const toggleSystemSetting = (setting: keyof SystemSettings) => {
|
||||
if (typeof systemSettings.value[setting] === 'boolean') {
|
||||
systemSettings.value[setting] = !systemSettings.value[setting]
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 实现搜索逻辑
|
||||
}
|
||||
|
||||
// 切换用户账号权限
|
||||
const toggleUserAccount = (accountId: string) => {
|
||||
const index = selectedUserAccounts.value.indexOf(accountId)
|
||||
@@ -333,22 +293,25 @@ const toggleUserAccount = (accountId: string) => {
|
||||
// 保存用户账号权限
|
||||
const saveUserAccounts = async () => {
|
||||
try {
|
||||
if (!selectedUser.value?.id) {
|
||||
toast.error('用户ID不存在')
|
||||
return
|
||||
}
|
||||
|
||||
// 调用API保存用户账号权限到后端
|
||||
await adminStore.updateUserAccounts(selectedUser.value.id, selectedUserAccounts.value)
|
||||
console.log('保存用户账号权限:', selectedUser.value?.id, selectedUserAccounts.value)
|
||||
console.log('保存用户账号权限:', selectedUser.value.id, selectedUserAccounts.value)
|
||||
|
||||
// 直接更新本地用户数据
|
||||
if (selectedUser.value) {
|
||||
const userIndex = users.value.findIndex((user: any) => user.id === selectedUser.value.id)
|
||||
console.log('找到用户索引:', userIndex, '用户ID:', selectedUser.value.id)
|
||||
if (userIndex !== -1) {
|
||||
// 使用Vue的响应式更新机制
|
||||
users.value[userIndex] = {
|
||||
...users.value[userIndex],
|
||||
accounts: [...selectedUserAccounts.value]
|
||||
}
|
||||
console.log('更新后的用户数据:', users.value[userIndex])
|
||||
const userIndex = users.value.findIndex((user: any) => user.id === selectedUser.value!.id)
|
||||
console.log('找到用户索引:', userIndex, '用户ID:', selectedUser.value.id)
|
||||
if (userIndex !== -1) {
|
||||
// 使用Vue的响应式更新机制
|
||||
users.value[userIndex] = {
|
||||
...users.value[userIndex],
|
||||
accounts: [...selectedUserAccounts.value]
|
||||
}
|
||||
console.log('更新后的用户数据:', users.value[userIndex])
|
||||
}
|
||||
|
||||
toast.success('权限保存成功')
|
||||
@@ -359,12 +322,6 @@ const saveUserAccounts = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 切换系统设置
|
||||
const toggleSystemSetting = (setting: string) => {
|
||||
systemSettings.value[setting] = !systemSettings.value[setting]
|
||||
console.log('切换系统设置:', setting, systemSettings.value[setting])
|
||||
}
|
||||
|
||||
// 加载用户列表
|
||||
const loadUsers = async () => {
|
||||
try {
|
||||
@@ -380,6 +337,22 @@ const loadUsers = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载可用账号
|
||||
const loadAvailableAccounts = async () => {
|
||||
try {
|
||||
const response = await adminStore.getAccounts()
|
||||
availableAccounts.value = response.accounts || []
|
||||
} catch (error) {
|
||||
console.error('加载可用账号失败:', error)
|
||||
// 如果加载失败,使用默认数据
|
||||
availableAccounts.value = [
|
||||
{ id: 'account1', username: '账号1', description: '第一个登录账号' },
|
||||
{ id: 'account2', username: '账号2', description: '第二个登录账号' },
|
||||
{ id: 'account3', username: '账号3', description: '第三个登录账号' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(async () => {
|
||||
await loadUsers()
|
||||
|
||||
@@ -103,9 +103,6 @@
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ user.username }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ user.firstName || user.lastName ? `${user.firstName || ''} ${user.lastName || ''}`.trim() : '未设置姓名' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -269,7 +266,7 @@ const showCreateModal = ref(false)
|
||||
const createForm = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
role: 'user'
|
||||
role: 'user' as string
|
||||
})
|
||||
|
||||
// 编辑用户模态框
|
||||
@@ -277,7 +274,7 @@ const showEditModal = ref(false)
|
||||
const editForm = reactive({
|
||||
id: '',
|
||||
username: '',
|
||||
role: 'user',
|
||||
role: 'user' as string,
|
||||
isActive: true,
|
||||
password: ''
|
||||
})
|
||||
@@ -324,7 +321,11 @@ const changePage = async (page: number) => {
|
||||
// 创建用户
|
||||
const handleCreateUser = async () => {
|
||||
try {
|
||||
await adminStore.createUser(createForm)
|
||||
await adminStore.createUser({
|
||||
username: createForm.username,
|
||||
password: createForm.password,
|
||||
role: createForm.role
|
||||
})
|
||||
toast.success('用户创建成功')
|
||||
showCreateModal.value = false
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ onMounted(async () => {
|
||||
|
||||
// 加载用户账号
|
||||
await loadUserAccounts()
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('Dashboard初始化失败:', error)
|
||||
|
||||
// 如果是认证错误,重定向到登录页面
|
||||
|
||||
@@ -10,20 +10,12 @@ export default defineConfig({
|
||||
'@': resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
// 定义环境变量,使其在客户端可用
|
||||
VITE_API_URL: JSON.stringify(process.env.VITE_API_URL || 'http://localhost:3001'),
|
||||
VITE_APP_NAME: JSON.stringify(process.env.VITE_APP_NAME || 'Pandora'),
|
||||
VITE_CLAUDE_TARGET_URL: JSON.stringify(process.env.CLAUDE_TARGET_URL || 'https://claude.ai'),
|
||||
VITE_CHATGPT_TARGET_URL: JSON.stringify(process.env.CHATGPT_TARGET_URL || 'https://chat.openai.com'),
|
||||
VITE_GROK_TARGET_URL: JSON.stringify(process.env.GROK_TARGET_URL || 'https://grok.x.ai'),
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
host: '0.0.0.0',
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3001',
|
||||
target: 'http://backend:3001',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user