登录注册/数据分析

This commit is contained in:
comlibmb
2026-01-22 21:15:02 +08:00
parent 75fad97d5d
commit fdbee0fa32
41 changed files with 5812 additions and 2102 deletions

View File

@@ -0,0 +1,529 @@
<!-- Supabase 连接测试页面 -->
<template>
<view class="test-container">
<view class="header">
<text class="title">Supabase 连接测试</text>
</view>
<view class="config-section">
<text class="section-title">当前配置</text>
<view class="config-item">
<text class="config-label">Supabase URL:</text>
<text class="config-value">{{ configUrl }}</text>
</view>
<view class="config-item">
<text class="config-label">API Key:</text>
<text class="config-value">{{ configKey.substring(0, 20) }}...</text>
</view>
<view class="config-item">
<text class="config-label">WebSocket URL:</text>
<text class="config-value">{{ configWs }}</text>
</view>
</view>
<view class="test-section">
<button class="test-btn" @click="testConnection" :disabled="isTesting">
{{ isTesting ? '测试中...' : '测试连接' }}
</button>
</view>
<view class="result-section" v-if="testResult">
<text class="section-title">测试结果</text>
<view class="result-item" :class="{ success: testResult.success, error: !testResult.success }">
<text class="result-icon">{{ testResult.success ? '✅' : '❌' }}</text>
<text class="result-text">{{ testResult.message }}</text>
</view>
<view v-if="testResult.details" class="result-details">
<text class="details-title">详细信息:</text>
<text class="details-text">{{ testResult.details }}</text>
</view>
<view v-if="testResult.data" class="result-data">
<text class="data-title">返回数据:</text>
<text class="data-text">{{ JSON.stringify(testResult.data, null, 2) }}</text>
</view>
</view>
<view class="test-list">
<text class="section-title">测试项目</text>
<view class="test-item" v-for="(test, index) in testList" :key="index">
<view class="test-info">
<text class="test-name">{{ test.name }}</text>
<text class="test-status" :class="test.status">{{ getStatusText(test.status) }}</text>
</view>
<button class="test-item-btn" @click="runTest(test)" :disabled="isTesting">执行</button>
</view>
</view>
</view>
</template>
<script lang="uts">
import supa from '@/components/supadb/aksupainstance.uts'
import { SUPA_URL, SUPA_KEY, WS_URL } from '@/ak/config.uts'
type TestResultType = {
success: boolean
message: string
details?: string
data?: any
}
type TestItemType = {
name: string
status: string
func: () => Promise<TestResultType>
}
export default {
data() {
return {
configUrl: SUPA_URL,
configKey: SUPA_KEY,
configWs: WS_URL,
isTesting: false,
testResult: null as TestResultType | null,
testList: [
{
name: '1. 基础连接测试',
status: 'pending',
func: this.testBasicConnection
} as TestItemType,
{
name: '2. 查询测试(查询用户表)',
status: 'pending',
func: this.testQuery
} as TestItemType,
{
name: '3. 认证测试',
status: 'pending',
func: this.testAuth
} as TestItemType,
{
name: '4. 实时连接测试',
status: 'pending',
func: this.testRealtime
} as TestItemType
] as Array<TestItemType>
}
},
methods: {
// 综合连接测试
async testConnection() {
this.isTesting = true
this.testResult = null
try {
// 测试1: 基础连接
const basicResult = await this.testBasicConnection()
this.updateTestStatus(0, basicResult.success ? 'success' : 'error')
if (!basicResult.success) {
this.testResult = basicResult
this.isTesting = false
return
}
// 测试2: 查询测试
const queryResult = await this.testQuery()
this.updateTestStatus(1, queryResult.success ? 'success' : 'error')
// 测试3: 认证测试
const authResult = await this.testAuth()
this.updateTestStatus(2, authResult.success ? 'success' : 'error')
// 汇总结果
const allSuccess = basicResult.success && queryResult.success && authResult.success
this.testResult = {
success: allSuccess,
message: allSuccess
? '所有测试通过Supabase 连接正常。'
: '部分测试失败,请查看详细信息。',
details: `基础连接: ${basicResult.success ? '✓' : '✗'}, 查询: ${queryResult.success ? '✓' : '✗'}, 认证: ${authResult.success ? '✓' : '✗'}`,
data: {
basic: basicResult,
query: queryResult,
auth: authResult
}
}
} catch (err) {
this.testResult = {
success: false,
message: '测试过程中发生错误',
details: err?.toString() || '未知错误'
}
} finally {
this.isTesting = false
}
},
// 测试1: 基础连接
async testBasicConnection(): Promise<TestResultType> {
try {
// 尝试访问 Supabase REST API
const response = await uni.request({
url: `${SUPA_URL}/rest/v1/`,
method: 'GET',
header: {
'apikey': SUPA_KEY,
'Authorization': `Bearer ${SUPA_KEY}`
},
timeout: 5000
})
if (response.statusCode === 200 || response.statusCode === 404) {
// 404 也是正常的,说明服务器响应了
return {
success: true,
message: '基础连接成功',
details: `HTTP 状态码: ${response.statusCode}`,
data: response.data
}
} else {
return {
success: false,
message: '连接失败',
details: `HTTP 状态码: ${response.statusCode}`
}
}
} catch (err) {
return {
success: false,
message: '无法连接到 Supabase',
details: err?.toString() || '网络错误或服务器不可达'
}
}
},
// 测试2: 查询测试
async testQuery(): Promise<TestResultType> {
try {
// 尝试查询 users 表(如果存在)
const { data, error } = await supa
.from('users')
.select('id, phone, nickname')
.limit(5)
if (error !== null) {
// 如果表不存在,尝试查询其他表
if (error.message?.includes('relation') || error.message?.includes('does not exist')) {
// 尝试查询 orders 表
const { data: orderData, error: orderError } = await supa
.from('orders')
.select('id')
.limit(1)
if (orderError !== null) {
return {
success: false,
message: '查询失败',
details: `错误: ${orderError.message || orderError.toString()}`
}
}
return {
success: true,
message: '查询成功(使用 orders 表)',
details: 'users 表不存在,但 orders 表可访问',
data: orderData
}
}
return {
success: false,
message: '查询失败',
details: `错误: ${error.message || error.toString()}`
}
}
return {
success: true,
message: '查询成功',
details: `返回 ${data?.length || 0} 条记录`,
data: data
}
} catch (err) {
return {
success: false,
message: '查询测试失败',
details: err?.toString() || '未知错误'
}
}
},
// 测试3: 认证测试
async testAuth(): Promise<TestResultType> {
try {
// 检查是否已登录
const { data: sessionData, error: sessionError } = await supa.auth.getSession()
if (sessionError !== null) {
return {
success: false,
message: '获取会话失败',
details: sessionError.message || sessionError.toString()
}
}
if (sessionData?.session !== null) {
return {
success: true,
message: '认证成功',
details: `用户已登录: ${sessionData.session.user.email || sessionData.session.user.phone || '未知'}`,
data: {
user: sessionData.session.user,
expires_at: sessionData.session.expires_at
}
}
} else {
return {
success: false,
message: '未登录',
details: '需要先登录才能测试认证功能'
}
}
} catch (err) {
return {
success: false,
message: '认证测试失败',
details: err?.toString() || '未知错误'
}
}
},
// 测试4: 实时连接测试
async testRealtime(): Promise<TestResultType> {
try {
// WebSocket 连接测试比较复杂,这里只做 URL 验证
if (WS_URL.startsWith('ws://') || WS_URL.startsWith('wss://')) {
return {
success: true,
message: 'WebSocket URL 格式正确',
details: `URL: ${WS_URL}`
}
} else {
return {
success: false,
message: 'WebSocket URL 格式错误',
details: `URL 应以 ws:// 或 wss:// 开头`
}
}
} catch (err) {
return {
success: false,
message: '实时连接测试失败',
details: err?.toString() || '未知错误'
}
}
},
// 运行单个测试
async runTest(test: TestItemType) {
this.isTesting = true
test.status = 'testing'
try {
const result = await test.func()
test.status = result.success ? 'success' : 'error'
this.testResult = result
} catch (err) {
test.status = 'error'
this.testResult = {
success: false,
message: '测试执行失败',
details: err?.toString() || '未知错误'
}
} finally {
this.isTesting = false
}
},
// 更新测试状态
updateTestStatus(index: number, status: string) {
if (this.testList[index]) {
this.testList[index].status = status
}
},
// 获取状态文本
getStatusText(status: string): string {
const statusMap: Record<string, string> = {
'pending': '待测试',
'testing': '测试中...',
'success': '✓ 通过',
'error': '✗ 失败'
}
return statusMap[status] || '未知'
}
}
}
</script>
<style>
.test-container {
padding: 40rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.header {
margin-bottom: 40rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.config-section, .test-section, .result-section, .test-list {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
}
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.config-item {
display: flex;
flex-direction: column;
margin-bottom: 20rpx;
}
.config-label {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.config-value {
font-size: 22rpx;
color: #333;
word-break: break-all;
}
.test-btn {
width: 100%;
height: 80rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border-radius: 12rpx;
font-size: 28rpx;
border: none;
}
.test-btn:disabled {
background: #ccc;
}
.result-item {
display: flex;
align-items: center;
padding: 20rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.result-item.success {
background-color: #e8f5e8;
}
.result-item.error {
background-color: #ffebee;
}
.result-icon {
font-size: 32rpx;
margin-right: 15rpx;
}
.result-text {
font-size: 26rpx;
color: #333;
flex: 1;
}
.result-details, .result-data {
margin-top: 20rpx;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
}
.details-title, .data-title {
font-size: 24rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.details-text, .data-text {
font-size: 22rpx;
color: #666;
word-break: break-all;
white-space: pre-wrap;
}
.test-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.test-item:last-child {
border-bottom: none;
}
.test-info {
flex: 1;
}
.test-name {
font-size: 26rpx;
color: #333;
margin-bottom: 8rpx;
}
.test-status {
font-size: 22rpx;
}
.test-status.pending {
color: #999;
}
.test-status.testing {
color: #2196f3;
}
.test-status.success {
color: #4caf50;
}
.test-status.error {
color: #f44336;
}
.test-item-btn {
padding: 12rpx 24rpx;
background-color: #667eea;
color: #fff;
border-radius: 8rpx;
font-size: 24rpx;
border: none;
}
.test-item-btn:disabled {
background-color: #ccc;
}
</style>