fix: auth.ts API路径添加/api前缀

- /auth/login -> /api/auth/login
- /auth/logout -> /api/auth/logout
- /auth/me -> /api/auth/me
- /auth/refresh -> /api/auth/refresh
This commit is contained in:
chiguyong 2026-03-23 09:52:13 +08:00
parent ea1eabafb0
commit f111f4a8d5
7 changed files with 373 additions and 262 deletions

View File

@ -2,17 +2,17 @@ import request from '@/utils/request'
import type { LoginRequest, LoginResponse } from '@/types'
export const login = (data: LoginRequest) => {
return request.post<LoginResponse>('/auth/login', data)
return request.post<LoginResponse>('/api/auth/login', data)
}
export const logout = () => {
return request.post('/auth/logout')
return request.post('/api/auth/logout')
}
export const getCurrentUser = () => {
return request.get('/auth/me')
return request.get('/api/auth/me')
}
export const refreshToken = () => {
return request.post('/auth/refresh')
return request.post('/api/auth/refresh')
}

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
interface Props {
current?: number
@ -34,16 +35,18 @@ const handleChange = (page: number, size: number) => {
<template>
<div class="table-pagination">
<a-pagination
v-model:current="current"
:total="total"
:page-size="pageSize"
:show-size-changer="true"
:show-quick-jumper="true"
:page-size-options="pageSizes.map(String)"
:show-total="(total: number) => `共 ${total} 条`"
@change="handleChange"
/>
<a-config-provider :locale="zhCN">
<a-pagination
v-model:current="current"
:total="total"
:page-size="pageSize"
:show-size-changer="true"
:show-quick-jumper="true"
:page-size-options="pageSizes.map(String)"
:show-total="(total: number) => `共 ${total} 条`"
@change="handleChange"
/>
</a-config-provider>
</div>
</template>

View File

@ -19,7 +19,15 @@ interface Props {
deleteDescription?: string
}
defineProps<Props>()
const props = withDefaults(defineProps<Props>(), {
actions: () => [],
showEdit: true,
showDelete: true,
editText: '编辑',
deleteText: '删除',
deleteTitle: '确认删除',
deleteDescription: '删除后不可恢复,是否继续?'
})
const emit = defineEmits<{
(e: 'edit'): void
@ -87,6 +95,10 @@ const handleAction = (key: string) => {
.table-actions {
display: flex;
align-items: center;
gap: 4px;
gap: 0;
}
.table-actions :deep(.ant-btn) {
padding: 0 4px;
}
</style>

View File

@ -1,24 +1,32 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from 'vue'
import { Table, Tag, message } from 'ant-design-vue'
import { ref, onMounted, reactive, computed } from 'vue'
import { Button, Drawer, Form, Space, message } from 'ant-design-vue'
import { PlusOutlined, SearchOutlined } from '@ant-design/icons-vue'
import type { Permission } from '@/types'
import { getPermissions, createPermission, updatePermission, deletePermission } from '@/api/permission'
import {
TableToolbar,
TableActions,
Pagination
} from '@/components'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const columns: any[] = [
{ title: '权限编码', dataIndex: 'code', key: 'code', width: 150 },
{ title: '权限名称', dataIndex: 'name', key: 'name', width: 150 },
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
{ title: '资源', dataIndex: 'resource', key: 'resource', width: 180 },
{ title: '方法', dataIndex: 'method', key: 'method', width: 80 },
//
const columns = [
{ title: '权限编码', dataIndex: 'code', key: 'code', width: 140 },
{ title: '权限名称', dataIndex: 'name', key: 'name', width: 120 },
{ title: '类型', dataIndex: 'type', key: 'type', width: 80 },
{ title: '资源', dataIndex: 'resource', key: 'resource', width: 160, ellipsis: true },
{ title: '方法', dataIndex: 'method', key: 'method', width: 70 },
{ title: '描述', dataIndex: 'description', key: 'description', ellipsis: true },
{ title: '操作', key: 'action', width: 150, fixed: 'right' }
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
]
const permissions = ref<Permission[]>([])
const loading = ref(false)
const modalVisible = ref(false)
const modalTitle = ref('新建权限')
const drawerVisible = ref(false)
const drawerTitle = ref('')
const formRef = ref()
const submitting = ref(false)
const editingId = ref<string | null>(null)
//
@ -28,9 +36,14 @@ const searchKeyword = ref('')
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total: number) => `${total}`
total: 0
})
//
const paginatedData = computed(() => {
const start = (pagination.current - 1) * pagination.pageSize
const end = start + pagination.pageSize
return permissions.value.slice(start, end)
})
//
@ -43,12 +56,6 @@ const formState = reactive({
description: ''
})
const formRules = {
code: [{ required: true, message: '请输入权限代码' }],
name: [{ required: true, message: '请输入权限名称' }],
type: [{ required: true, message: '请选择类型' }]
}
const typeOptions = [
{ value: 'MENU', label: '菜单' },
{ value: 'BUTTON', label: '按钮' },
@ -103,13 +110,13 @@ const resetForm = () => {
editingId.value = null
}
const openCreateModal = () => {
const handleAdd = () => {
resetForm()
modalTitle.value = '新建权限'
modalVisible.value = true
drawerTitle.value = '新建权限'
drawerVisible.value = true
}
const openEditModal = (record: Permission) => {
const handleEdit = (record: Permission) => {
editingId.value = record.id
formState.code = record.code
formState.name = record.name
@ -117,24 +124,8 @@ const openEditModal = (record: Permission) => {
formState.resource = record.resource || ''
formState.method = record.method || 'GET'
formState.description = record.description || ''
modalTitle.value = '编辑权限'
modalVisible.value = true
}
const handleSubmit = async () => {
try {
if (editingId.value) {
await updatePermission(editingId.value, formState)
message.success('更新成功')
} else {
await createPermission(formState)
message.success('创建成功')
}
modalVisible.value = false
fetchPermissions()
} catch {
message.error(editingId.value ? '更新失败' : '创建失败')
}
drawerTitle.value = '编辑权限'
drawerVisible.value = true
}
const handleDelete = async (id: string) => {
@ -147,9 +138,37 @@ const handleDelete = async (id: string) => {
}
}
const handleTableChange = (pag: any) => {
pagination.current = pag.current
pagination.pageSize = pag.pageSize
const handleSubmit = async () => {
try {
await formRef.value.validate()
submitting.value = true
if (editingId.value) {
await updatePermission(editingId.value, formState)
message.success('更新成功')
} else {
await createPermission(formState)
message.success('创建成功')
}
drawerVisible.value = false
fetchPermissions()
} catch (error: any) {
if (error.errorFields) return
message.error('操作失败')
} finally {
submitting.value = false
}
}
const handleClose = () => {
formRef.value?.resetFields()
drawerVisible.value = false
}
const handlePageChange = (page: number, pageSize: number) => {
pagination.current = page
pagination.pageSize = pageSize
fetchPermissions()
}
@ -158,6 +177,12 @@ const handleSearch = () => {
fetchPermissions()
}
const handleReset = () => {
searchKeyword.value = ''
pagination.current = 1
fetchPermissions()
}
onMounted(fetchPermissions)
</script>
@ -166,123 +191,102 @@ onMounted(fetchPermissions)
<!-- 页面标题 -->
<div class="page-header">
<h2 class="page-title">权限管理</h2>
<div class="page-header-actions">
<Button type="primary" @click="handleAdd">
<PlusOutlined /> 新建权限
</Button>
</div>
</div>
<!-- 表格 -->
<div class="table-card">
<div class="table-toolbar">
<!-- 筛选区 -->
<div class="filter-bar">
<a-space>
<a-input
v-model:value="searchKeyword"
placeholder="搜索权限编码或名称"
style="width: 240px"
allow-clear
@press-enter="handleSearch"
>
<template #prefix>
<SearchOutlined />
</template>
</a-input>
</div>
<Table
:columns="columns"
:data-source="permissions"
:loading="loading"
:row-key="(record: Permission) => record.id"
:pagination="pagination"
:scroll="{ x: 1000 }"
@change="handleTableChange"
>
<template #title>
<div class="table-title">
<span>权限列表</span>
<a-button type="primary" @click="openCreateModal">
<template #icon><PlusOutlined /></template>
新建
</a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<Tag :color="getTypeColor(record.type)">
{{ getTypeLabel(record.type) }}
</Tag>
</template>
<template v-else-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="openEditModal(record as any)">
编辑
</a-button>
<a-popconfirm
title="确定删除该权限吗?"
ok-text="确定"
cancel-text="取消"
@confirm="handleDelete(record.id)"
>
<a-button type="link" size="small" danger>
删除
</a-button>
</a-popconfirm>
</a-space>
</template>
</template>
</Table>
/>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</div>
<!-- 新建/编辑模态框 -->
<a-modal
v-model:open="modalVisible"
:title="modalTitle"
:width="500"
@ok="handleSubmit"
@cancel="modalVisible = false"
>
<a-form
:model="formState"
:rules="formRules"
layout="vertical"
class="permission-form"
<!-- 表格 -->
<div class="table-card">
<TableToolbar @refresh="fetchPermissions" />
<a-table
:columns="columns"
:data-source="paginatedData"
:loading="loading"
:row-key="(record: Permission) => record.id"
:pagination="false"
>
<a-form-item label="权限代码" name="code">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<a-tag :color="getTypeColor(record.type)">
{{ getTypeLabel(record.type) }}
</a-tag>
</template>
<template v-else-if="column.key === 'action'">
<TableActions @edit="handleEdit(record)" @delete="handleDelete(record.id)" />
</template>
</template>
</a-table>
<Pagination
v-model:current="pagination.current"
v-model:pageSize="pagination.pageSize"
:total="pagination.total"
@change="handlePageChange"
/>
</div>
<!-- 抽屉 -->
<Drawer
v-model:open="drawerVisible"
:title="drawerTitle"
width="480px"
:footer-style="{ textAlign: 'right' }"
@close="handleClose"
>
<Form
ref="formRef"
:model="formState"
layout="vertical"
:rules="{
code: [{ required: true, message: '请输入权限代码' }],
name: [{ required: true, message: '请输入权限名称' }],
type: [{ required: true, message: '请选择类型' }]
}"
>
<Form.Item label="权限代码" name="code">
<a-input v-model:value="formState.code" placeholder="如: system:user:view" />
</a-form-item>
<a-form-item label="权限名称" name="name">
</Form.Item>
<Form.Item label="权限名称" name="name">
<a-input v-model:value="formState.name" placeholder="如: 查看用户" />
</a-form-item>
<a-form-item label="类型" name="type">
</Form.Item>
<Form.Item label="类型" name="type">
<a-select v-model:value="formState.type" :options="typeOptions" />
</a-form-item>
<a-form-item label="资源路径" name="resource">
</Form.Item>
<Form.Item label="资源路径" name="resource">
<a-input v-model:value="formState.resource" placeholder="如: /api/users" />
</a-form-item>
<a-form-item label="请求方法" name="method">
</Form.Item>
<Form.Item label="请求方法" name="method">
<a-select v-model:value="formState.method" :options="methodOptions" />
</a-form-item>
<a-form-item label="描述" name="description">
</Form.Item>
<Form.Item label="描述" name="description">
<a-textarea v-model:value="formState.description" :rows="3" placeholder="权限描述" />
</a-form-item>
</a-form>
</a-modal>
</Form.Item>
</Form>
<template #footer>
<Space>
<Button @click="handleClose">取消</Button>
<Button type="primary" :loading="submitting" @click="handleSubmit">确定</Button>
</Space>
</template>
</Drawer>
</div>
</template>
<style scoped>
.table-toolbar {
margin-bottom: 16px;
}
.table-title {
display: flex;
justify-content: space-between;
align-items: center;
}
.permission-form {
padding-top: 8px;
}
</style>

View File

@ -1,18 +1,24 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { Table, Button, Drawer, Input, Select, Form, Space, Popconfirm, message } from 'ant-design-vue'
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
import { ref, onMounted, reactive, computed } from 'vue'
import { Button, Drawer, Input, Select, Form, Space, message } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import { getRoles, createRole, updateRole, deleteRole } from '@/api/role'
import type { Role } from '@/types'
import {
TableToolbar,
TableActions,
Pagination,
StatusTag
} from '@/components'
const columns = [
{ title: '角色编码', dataIndex: 'code', key: 'code', width: 120 },
{ title: '角色名称', dataIndex: 'name', key: 'name', width: 150 },
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
{ title: '角色编码', dataIndex: 'code', key: 'code', width: 100 },
{ title: '角色名称', dataIndex: 'name', key: 'name', width: 100 },
{ title: '类型', dataIndex: 'type', key: 'type', width: 90 },
{ title: '数据权限', dataIndex: 'dataScope', key: 'dataScope', width: 100 },
{ title: '描述', dataIndex: 'description', key: 'description', ellipsis: true },
{ title: '状态', dataIndex: 'status', key: 'status', width: 80 },
{ title: '操作', key: 'action', width: 120, fixed: 'right' }
{ title: '状态', dataIndex: 'status', key: 'status', width: 70 },
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
]
const roles = ref<Role[]>([])
@ -22,6 +28,24 @@ const drawerTitle = ref('')
const formRef = ref()
const submitting = ref(false)
//
const searchKeyword = ref('')
const searchStatus = ref('')
//
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0
})
//
const paginatedData = computed(() => {
const start = (pagination.current - 1) * pagination.pageSize
const end = start + pagination.pageSize
return roles.value.slice(start, end)
})
const formState = ref({
id: '',
code: '',
@ -32,11 +56,6 @@ const formState = ref({
status: 'ACTIVE'
})
const statusOptions = [
{ value: 'ACTIVE', label: '正常', color: 'success' },
{ value: 'DISABLED', label: '禁用', color: 'error' }
]
const typeOptions = [
{ value: 'SYSTEM', label: '系统角色' },
{ value: 'PROJECT', label: '项目角色' },
@ -44,9 +63,9 @@ const typeOptions = [
]
const dataScopeOptions = [
{ value: 'ALL', label: '全部数据', color: 'red' },
{ value: 'PROJECT', label: '本项目数据', color: 'blue' },
{ value: 'SELF', label: '本人数据', color: 'green' }
{ value: 'ALL', label: '全部数据' },
{ value: 'PROJECT', label: '本项目数据' },
{ value: 'SELF', label: '本人数据' }
]
const fetchRoles = async () => {
@ -54,6 +73,7 @@ const fetchRoles = async () => {
try {
const res = await getRoles()
roles.value = res.data.data || []
pagination.total = roles.value.length
} catch {
message.error('获取角色列表失败')
} finally {
@ -61,6 +81,24 @@ const fetchRoles = async () => {
}
}
const handleSearch = () => {
pagination.current = 1
fetchRoles()
}
const handleReset = () => {
searchKeyword.value = ''
searchStatus.value = ''
pagination.current = 1
fetchRoles()
}
const handlePageChange = (page: number, pageSize: number) => {
pagination.current = page
pagination.pageSize = pageSize
fetchRoles()
}
const handleAdd = () => {
drawerTitle.value = '新增角色'
formState.value = {
@ -126,49 +164,24 @@ const handleClose = () => {
drawerVisible.value = false
}
const getStatusColor = (status: string) => {
const map: Record<string, string> = {
ACTIVE: 'success',
DISABLED: 'error'
}
return map[status] || 'default'
}
const getStatusLabel = (status: string) => {
const map: Record<string, string> = {
ACTIVE: '正常',
DISABLED: '禁用'
}
return map[status] || status
}
const getTypeLabel = (type: string) => {
const map: Record<string, string> = {
SYSTEM: 'SYSTEM',
PROJECT: 'PROJECT',
DEPARTMENT: 'DEPARTMENT'
SYSTEM: '系统角色',
PROJECT: '项目角色',
DEPARTMENT: '部门级'
}
return map[type] || type
}
const getDataScopeLabel = (dataScope: string) => {
const map: Record<string, string> = {
ALL: 'ALL',
PROJECT: 'PROJECT',
SELF: 'SELF'
ALL: '全部数据',
PROJECT: '本项目数据',
SELF: '本人数据'
}
return map[dataScope] || dataScope
}
const getDataScopeColor = (dataScope: string) => {
const map: Record<string, string> = {
ALL: 'red',
PROJECT: 'blue',
SELF: 'green'
}
return map[dataScope] || 'default'
}
onMounted(fetchRoles)
</script>
@ -184,49 +197,54 @@ onMounted(fetchRoles)
</div>
</div>
<!-- 筛选区 -->
<div class="filter-bar">
<a-space>
<a-input
v-model:value="searchKeyword"
placeholder="搜索角色编码或名称"
style="width: 240px"
allow-clear
@press-enter="handleSearch"
/>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</div>
<!-- 表格 -->
<div class="table-card">
<Table
<TableToolbar @refresh="fetchRoles" />
<a-table
:columns="columns"
:data-source="roles"
:data-source="paginatedData"
:loading="loading"
:row-key="(record: Role) => record.id"
:pagination="{ pageSize: 10, showSizeChanger: true, showTotal: (total: number) => `共 ${total} 条` }"
:pagination="false"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<a-tag>{{ getTypeLabel(record.type) }}</a-tag>
</template>
<template v-else-if="column.key === 'dataScope'">
<a-tag :color="getDataScopeColor(record.dataScope)">
{{ getDataScopeLabel(record.dataScope) }}
</a-tag>
<a-tag>{{ getDataScopeLabel(record.dataScope) }}</a-tag>
</template>
<template v-else-if="column.key === 'status'">
<a-tag :color="getStatusColor(record.status)">
{{ getStatusLabel(record.status) }}
</a-tag>
<StatusTag :status="record.status" />
</template>
<template v-else-if="column.key === 'action'">
<Space>
<Button type="link" size="small" @click="handleEdit(record)">
<EditOutlined /> 编辑
</Button>
<Popconfirm
title="确认删除"
description="删除后不可恢复,是否继续?"
ok-text="确认"
cancel-text="取消"
@confirm="handleDelete(record.id)"
>
<Button type="link" danger size="small">
<DeleteOutlined /> 删除
</Button>
</Popconfirm>
</Space>
<TableActions @edit="handleEdit(record)" @delete="handleDelete(record.id)" />
</template>
</template>
</Table>
</a-table>
<Pagination
v-model:current="pagination.current"
v-model:pageSize="pagination.pageSize"
:total="pagination.total"
@change="handlePageChange"
/>
</div>
<!-- 抽屉 -->
@ -262,7 +280,7 @@ onMounted(fetchRoles)
<Select v-model:value="formState.dataScope" :options="dataScopeOptions" placeholder="请选择数据权限" />
</Form.Item>
<Form.Item label="状态" name="status">
<Select v-model:value="formState.status" :options="statusOptions" />
<Select v-model:value="formState.status" :options="[{ value: 'ACTIVE', label: '正常' }, { value: 'DISABLED', label: '禁用' }]" />
</Form.Item>
</Form>
<template #footer>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from 'vue'
import { ref, onMounted, reactive, computed } from 'vue'
import { Button, Drawer, Form, Space, message } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import { getUsers, createUser, updateUser, deleteUser } from '@/api/user'
@ -19,12 +19,12 @@ import {
//
const columns = [
{ title: '用户名', dataIndex: 'username', key: 'username', width: 120 },
{ title: '姓名', dataIndex: 'realName', key: 'realName', width: 100 },
{ title: '手机', dataIndex: 'phone', key: 'phone', width: 120 },
{ title: '邮箱', dataIndex: 'email', key: 'email', ellipsis: true },
{ title: '状态', dataIndex: 'status', key: 'status', width: 80 },
{ title: '操作', key: 'action', width: 120, fixed: 'right' as const }
{ title: '用户名', dataIndex: 'username', key: 'username', width: 100 },
{ title: '姓名', dataIndex: 'realName', key: 'realName', width: 80 },
{ title: '手机', dataIndex: 'phone', key: 'phone', width: 110 },
{ title: '邮箱', dataIndex: 'email', key: 'email', width: 180, ellipsis: true },
{ title: '状态', dataIndex: 'status', key: 'status', width: 70 },
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
]
//
@ -46,6 +46,13 @@ const pagination = reactive({
total: 0
})
//
const paginatedData = computed(() => {
const start = (pagination.current - 1) * pagination.pageSize
const end = start + pagination.pageSize
return users.value.slice(start, end)
})
//
const formState = ref({
id: '',
@ -172,33 +179,38 @@ onMounted(fetchUsers)
<template>
<div class="page-container">
<!-- 页面标题 -->
<PageHeader title="用户管理">
<template #extra>
<div class="page-header">
<h2 class="page-title">用户管理</h2>
<div class="page-header-actions">
<Button type="primary" @click="handleAdd">
<PlusOutlined /> 新增用户
</Button>
</template>
</PageHeader>
</div>
</div>
<!-- 筛选区 -->
<FilterBar @search="handleSearch" @reset="handleReset">
<a-input
v-model:value="searchKeyword"
placeholder="搜索用户名/姓名/手机"
style="width: 240px"
allow-clear
@pressEnter="handleSearch"
/>
<StatusSelect v-model="searchStatus" placeholder="全部状态" />
</FilterBar>
<div class="filter-bar">
<a-space>
<a-input
v-model:value="searchKeyword"
placeholder="搜索用户名/姓名/手机"
style="width: 240px"
allow-clear
@pressEnter="handleSearch"
/>
<StatusSelect v-model="searchStatus" placeholder="全部状态" />
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</div>
<!-- 表格 -->
<TableCard>
<div class="table-card">
<TableToolbar @refresh="fetchUsers" />
<a-table
:columns="columns"
:data-source="users"
:data-source="paginatedData"
:loading="loading"
:row-key="(record: User) => record.id"
:pagination="false"
@ -219,7 +231,7 @@ onMounted(fetchUsers)
:total="pagination.total"
@change="handlePageChange"
/>
</TableCard>
</div>
<!-- 抽屉 -->
<Drawer

62
test-login.cjs Normal file
View File

@ -0,0 +1,62 @@
const puppeteer = require('puppeteer');
(async () => {
console.log('启动浏览器...');
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
page.on('console', msg => console.log('Browser console:', msg.text()));
page.on('pageerror', error => console.log('Browser error:', error.message));
console.log('导航到登录页...');
await page.goto('http://127.0.0.1:5175/login', { waitUntil: 'networkidle0' });
await new Promise(r => setTimeout(r, 2000));
const title = await page.title();
console.log('页面标题:', title);
await page.screenshot({ path: '/tmp/login-page.png' });
console.log('已截图: /tmp/login-page.png');
const usernameInput = await page.$('input');
console.log('找到输入框:', usernameInput ? '是' : '否');
if (usernameInput) {
const inputs = await page.$$('input');
console.log('输入框数量:', inputs.length);
if (inputs.length >= 2) {
await inputs[0].type('admin');
await inputs[1].type('Admin@123');
await page.screenshot({ path: '/tmp/before-login.png' });
const buttons = await page.$$('button');
console.log('按钮数量:', buttons.length);
for (let i = 0; i < buttons.length; i++) {
const text = await buttons[i].textContent();
console.log(`按钮 ${i}:`, text.trim());
}
if (buttons.length > 0) {
await buttons[0].click();
await new Promise(r => setTimeout(r, 3000));
const url = page.url();
console.log('登录后URL:', url);
await page.screenshot({ path: '/tmp/after-login.png' });
console.log('已截图: /tmp/after-login.png');
}
}
}
await browser.close();
console.log('测试完成');
})();