851 lines
29 KiB
HTML
851 lines
29 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Supabase 消息系统演示</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.container {
|
||
background: white;
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||
padding: 40px;
|
||
max-width: 900px;
|
||
width: 90%;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.header h1 {
|
||
color: #333;
|
||
margin-bottom: 10px;
|
||
font-size: 2.5rem;
|
||
}
|
||
|
||
.header p {
|
||
color: #666;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.user-info {
|
||
background: #f8f9fa;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
margin-bottom: 30px;
|
||
border-left: 4px solid #007bff;
|
||
}
|
||
|
||
.user-info h3 {
|
||
color: #333;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.user-info .info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.info-label {
|
||
font-weight: 700;
|
||
color: #555;
|
||
}
|
||
|
||
.info-value {
|
||
color: #333;
|
||
background: white;
|
||
padding: 5px 10px;
|
||
border-radius: 5px;
|
||
font-family: monospace;
|
||
}
|
||
|
||
.role-badge {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
font-size: 0.8rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.role-admin { background: #dc3545; color: white; }
|
||
.role-teacher { background: #28a745; color: white; }
|
||
.role-student { background: #007bff; color: white; }
|
||
|
||
.section {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.section h3 {
|
||
color: #333;
|
||
margin-bottom: 15px;
|
||
font-size: 1.3rem;
|
||
border-bottom: 2px solid #eee;
|
||
padding-bottom: 10px;
|
||
}
|
||
|
||
.button-group {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.btn {
|
||
background: #007bff;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 20px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 0.9rem;
|
||
font-weight: 400;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn:hover {
|
||
background: #0056b3;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn:disabled {
|
||
background: #ccc;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
.btn-success { background: #28a745; }
|
||
.btn-success:hover { background: #1e7e34; }
|
||
|
||
.btn-warning { background: #ffc107; color: #333; }
|
||
.btn-warning:hover { background: #e0a800; }
|
||
|
||
.btn-danger { background: #dc3545; }
|
||
.btn-danger:hover { background: #c82333; }
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: 700;
|
||
color: #333;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group textarea,
|
||
.form-group select {
|
||
width: 100%;
|
||
padding: 12px;
|
||
border: 2px solid #e0e0e0;
|
||
border-radius: 8px;
|
||
font-size: 1rem;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group textarea:focus,
|
||
.form-group select:focus {
|
||
outline: none;
|
||
border-color: #007bff;
|
||
}
|
||
|
||
.results {
|
||
background: #f8f9fa;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
margin-top: 20px;
|
||
border: 1px solid #e0e0e0;
|
||
}
|
||
|
||
.results h4 {
|
||
color: #333;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.results pre {
|
||
background: #2d3748;
|
||
color: #e2e8f0;
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
font-size: 0.9rem;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.message-list {
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.message-item {
|
||
background: white;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
margin-bottom: 10px;
|
||
border-left: 4px solid #007bff;
|
||
}
|
||
|
||
.message-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.message-title {
|
||
font-weight: 700;
|
||
color: #333;
|
||
}
|
||
|
||
.message-time {
|
||
color: #666;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.message-content {
|
||
color: #555;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.hidden {
|
||
display: none;
|
||
}
|
||
|
||
.loading {
|
||
display: inline-block;
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 3px solid #f3f3f3;
|
||
border-top: 3px solid #3498db;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
.alert {
|
||
padding: 15px;
|
||
margin-bottom: 20px;
|
||
border-radius: 8px;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.alert-success {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
border: 1px solid #c3e6cb;
|
||
}
|
||
|
||
.alert-error {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
border: 1px solid #f5c6cb;
|
||
}
|
||
|
||
.alert-info {
|
||
background: #d1ecf1;
|
||
color: #0c5460;
|
||
border: 1px solid #bee5eb;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1> Supabase 消息系统</h1>
|
||
<p>完整的角色管理与权限控制演示</p>
|
||
</div>
|
||
|
||
<!-- 登录表单 -->
|
||
<div id="login-section" class="section">
|
||
<h3> 用户登录</h3>
|
||
<div class="form-group">
|
||
<label>邮箱地址</label>
|
||
<input type="email" id="email" placeholder="请输入邮箱" value="teacher@example.com">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>密码</label>
|
||
<input type="password" id="password" placeholder="请输入密码" value="password123">
|
||
</div>
|
||
<div class="button-group">
|
||
<button class="btn" onclick="signIn()">
|
||
<span></span> 登录
|
||
</button>
|
||
<button class="btn btn-success" onclick="signUp()">
|
||
<span></span> 注册
|
||
</button>
|
||
</div>
|
||
<div class="alert alert-info">
|
||
<strong>测试账户:</strong><br>
|
||
教师: teacher@example.com / password123<br>
|
||
学生: student@example.com / password123
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户信息 -->
|
||
<div id="user-section" class="section hidden">
|
||
<div class="user-info">
|
||
<h3> 当前用户信息</h3>
|
||
<div class="info-grid">
|
||
<div class="info-item">
|
||
<span class="info-label">邮箱:</span>
|
||
<span class="info-value" id="user-email">-</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">角色:</span>
|
||
<span class="role-badge" id="user-role">-</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">用户ID:</span>
|
||
<span class="info-value" id="user-id">-</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">部门:</span>
|
||
<span class="info-value" id="user-department">-</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="btn btn-danger" onclick="signOut()">
|
||
<span></span> 退出登录
|
||
</button>
|
||
<button class="btn btn-warning" onclick="testPermissions()">
|
||
<span></span> 测试权限
|
||
</button>
|
||
<button class="btn" onclick="refreshUserInfo()">
|
||
<span></span> 刷新信息
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 消息功能 -->
|
||
<div id="message-section" class="section hidden">
|
||
<h3> 消息功能</h3>
|
||
|
||
<!-- 发送消息 -->
|
||
<div class="form-group">
|
||
<label>消息标题</label>
|
||
<input type="text" id="message-title" placeholder="请输入消息标题">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>消息内容</label>
|
||
<textarea id="message-content" rows="3" placeholder="请输入消息内容"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>接收者类型</label>
|
||
<select id="receiver-type">
|
||
<option value="user">指定用户</option>
|
||
<option value="broadcast">广播消息</option>
|
||
<option value="group">群组消息</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>接收者ID(用户ID或群组ID)</label>
|
||
<input type="text" id="receiver-id" placeholder="请输入接收者ID">
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="btn" onclick="sendMessage()">
|
||
<span></span> 发送消息
|
||
</button>
|
||
<button class="btn btn-success" onclick="loadMessages()">
|
||
<span></span> 刷新消息
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 消息列表 -->
|
||
<div class="message-list" id="message-list">
|
||
<h4> 消息列表</h4>
|
||
<div id="messages-container">
|
||
<p>点击"刷新消息"加载消息列表</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 管理员功能 -->
|
||
<div id="admin-section" class="section hidden">
|
||
<h3>⚙️ 管理员功能</h3>
|
||
|
||
<div class="form-group">
|
||
<label>目标用户ID</label>
|
||
<input type="text" id="target-user-id" placeholder="请输入要管理的用户ID">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>新角色</label>
|
||
<select id="new-role">
|
||
<option value="student">学生</option>
|
||
<option value="teacher">教师</option>
|
||
<option value="admin">管理员</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="btn" onclick="updateUserRole()">
|
||
<span></span> 更新用户角色
|
||
</button>
|
||
<button class="btn btn-success" onclick="getAllUsers()">
|
||
<span></span> 查看所有用户
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 结果显示 -->
|
||
<div id="results" class="results hidden">
|
||
<h4> 操作结果</h4>
|
||
<pre id="results-content"></pre>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Supabase 配置 - 请替换为你的实际配置
|
||
const SUPABASE_URL = 'YOUR_SUPABASE_URL'
|
||
const SUPABASE_ANON_KEY = 'YOUR_SUPABASE_ANON_KEY'
|
||
|
||
// 如果没有配置,使用演示模式
|
||
if (SUPABASE_URL === 'YOUR_SUPABASE_URL') {
|
||
document.querySelector('.container').innerHTML = `
|
||
<div class="header">
|
||
<h1>⚠️ 配置需要</h1>
|
||
<p>请在代码中配置你的 Supabase URL 和 API Key</p>
|
||
</div>
|
||
<div class="alert alert-error">
|
||
<strong>配置步骤:</strong><br>
|
||
1. 替换 SUPABASE_URL 为你的项目URL<br>
|
||
2. 替换 SUPABASE_ANON_KEY 为你的匿名密钥<br>
|
||
3. 执行 deploy_complete_system.sql 部署数据库<br>
|
||
4. 创建测试用户账户
|
||
</div>
|
||
`
|
||
return
|
||
}
|
||
|
||
const supabase = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
|
||
let currentUser = null
|
||
let currentRole = null
|
||
|
||
// 初始化
|
||
document.addEventListener('DOMContentLoaded', async () => {
|
||
console.log(' 初始化消息系统演示')
|
||
|
||
// 检查当前用户
|
||
const { data: { user } } = await supabase.auth.getUser()
|
||
if (user) {
|
||
await handleUserSignedIn(user)
|
||
}
|
||
|
||
// 监听认证状态变化
|
||
supabase.auth.onAuthStateChange(async (event, session) => {
|
||
console.log(' 认证状态变化:', event)
|
||
|
||
if (event === 'SIGNED_IN' && session?.user) {
|
||
await handleUserSignedIn(session.user)
|
||
} else if (event === 'SIGNED_OUT') {
|
||
handleUserSignedOut()
|
||
}
|
||
})
|
||
})
|
||
|
||
// 用户登录处理
|
||
async function handleUserSignedIn(user) {
|
||
currentUser = user
|
||
currentRole = await getUserRole(user.id)
|
||
|
||
// 显示/隐藏相应区域
|
||
document.getElementById('login-section').classList.add('hidden')
|
||
document.getElementById('user-section').classList.remove('hidden')
|
||
document.getElementById('message-section').classList.remove('hidden')
|
||
|
||
if (currentRole === 'admin') {
|
||
document.getElementById('admin-section').classList.remove('hidden')
|
||
}
|
||
|
||
// 更新用户信息显示
|
||
updateUserInfoDisplay()
|
||
|
||
showAlert('登录成功!', 'success')
|
||
}
|
||
|
||
// 用户退出处理
|
||
function handleUserSignedOut() {
|
||
currentUser = null
|
||
currentRole = null
|
||
|
||
// 显示/隐藏相应区域
|
||
document.getElementById('login-section').classList.remove('hidden')
|
||
document.getElementById('user-section').classList.add('hidden')
|
||
document.getElementById('message-section').classList.add('hidden')
|
||
document.getElementById('admin-section').classList.add('hidden')
|
||
document.getElementById('results').classList.add('hidden')
|
||
|
||
showAlert('已退出登录', 'info')
|
||
}
|
||
|
||
// 更新用户信息显示
|
||
function updateUserInfoDisplay() {
|
||
if (!currentUser) return
|
||
|
||
document.getElementById('user-email').textContent = currentUser.email
|
||
document.getElementById('user-id').textContent = currentUser.id.substring(0, 8) + '...'
|
||
|
||
const roleElement = document.getElementById('user-role')
|
||
roleElement.textContent = currentRole || 'unknown'
|
||
roleElement.className = `role-badge role-${currentRole || 'unknown'}`
|
||
|
||
// 从元数据获取部门信息
|
||
const department = currentUser.user_metadata?.department ||
|
||
currentUser.raw_user_meta_data?.department ||
|
||
'未设置'
|
||
document.getElementById('user-department').textContent = department
|
||
}
|
||
|
||
// 获取用户角色
|
||
async function getUserRole(userId) {
|
||
try {
|
||
const { data, error } = await supabase
|
||
.rpc('get_user_role', { target_user_id: userId })
|
||
|
||
if (error) throw error
|
||
return data || 'student'
|
||
} catch (error) {
|
||
console.error('获取用户角色失败:', error)
|
||
return 'student'
|
||
}
|
||
}
|
||
|
||
// 登录
|
||
async function signIn() {
|
||
const email = document.getElementById('email').value
|
||
const password = document.getElementById('password').value
|
||
|
||
if (!email || !password) {
|
||
showAlert('请输入邮箱和密码', 'error')
|
||
return
|
||
}
|
||
|
||
try {
|
||
const { data, error } = await supabase.auth.signInWithPassword({
|
||
email,
|
||
password
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
// 用户登录成功会触发 onAuthStateChange
|
||
} catch (error) {
|
||
console.error('登录失败:', error)
|
||
showAlert(`登录失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 注册
|
||
async function signUp() {
|
||
const email = document.getElementById('email').value
|
||
const password = document.getElementById('password').value
|
||
|
||
if (!email || !password) {
|
||
showAlert('请输入邮箱和密码', 'error')
|
||
return
|
||
}
|
||
|
||
try {
|
||
const { data, error } = await supabase.auth.signUp({
|
||
email,
|
||
password,
|
||
options: {
|
||
data: {
|
||
department: 'Demo Department'
|
||
}
|
||
}
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
showAlert('注册成功!请检查邮箱验证链接', 'success')
|
||
} catch (error) {
|
||
console.error('注册失败:', error)
|
||
showAlert(`注册失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 退出登录
|
||
async function signOut() {
|
||
try {
|
||
const { error } = await supabase.auth.signOut()
|
||
if (error) throw error
|
||
|
||
// 退出成功会触发 onAuthStateChange
|
||
} catch (error) {
|
||
console.error('退出失败:', error)
|
||
showAlert(`退出失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 测试权限
|
||
async function testPermissions() {
|
||
if (!currentUser) return
|
||
|
||
try {
|
||
const { data, error } = await supabase
|
||
.rpc('test_message_permissions', { test_user_id: currentUser.id })
|
||
|
||
if (error) throw error
|
||
|
||
showResults('权限测试结果', data)
|
||
} catch (error) {
|
||
console.error('权限测试失败:', error)
|
||
showAlert(`权限测试失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 刷新用户信息
|
||
async function refreshUserInfo() {
|
||
if (!currentUser) return
|
||
|
||
try {
|
||
const { data: { user } } = await supabase.auth.getUser()
|
||
if (user) {
|
||
currentUser = user
|
||
currentRole = await getUserRole(user.id)
|
||
updateUserInfoDisplay()
|
||
showAlert('用户信息已刷新', 'success')
|
||
}
|
||
} catch (error) {
|
||
console.error('刷新用户信息失败:', error)
|
||
showAlert(`刷新失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 发送消息
|
||
async function sendMessage() {
|
||
const title = document.getElementById('message-title').value
|
||
const content = document.getElementById('message-content').value
|
||
const receiverType = document.getElementById('receiver-type').value
|
||
const receiverId = document.getElementById('receiver-id').value
|
||
|
||
if (!title || !content) {
|
||
showAlert('请输入消息标题和内容', 'error')
|
||
return
|
||
}
|
||
|
||
if (receiverType !== 'broadcast' && !receiverId) {
|
||
showAlert('请输入接收者ID', 'error')
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 获取消息类型ID
|
||
const { data: messageTypes } = await supabase
|
||
.from('ak_message_types')
|
||
.select('id')
|
||
.eq('type_name', 'notification')
|
||
.limit(1)
|
||
|
||
const messageTypeId = messageTypes?.[0]?.id
|
||
|
||
if (!messageTypeId) {
|
||
throw new Error('未找到消息类型')
|
||
}
|
||
|
||
const { data, error } = await supabase
|
||
.rpc('send_secure_message', {
|
||
message_type_id: messageTypeId,
|
||
receiver_type: receiverType,
|
||
receiver_id: receiverType === 'broadcast' ? null : receiverId,
|
||
title,
|
||
content,
|
||
metadata_json: { source: 'web_demo' }
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
showAlert('消息发送成功!', 'success')
|
||
|
||
// 清空表单
|
||
document.getElementById('message-title').value = ''
|
||
document.getElementById('message-content').value = ''
|
||
document.getElementById('receiver-id').value = ''
|
||
|
||
// 刷新消息列表
|
||
await loadMessages()
|
||
|
||
} catch (error) {
|
||
console.error('发送消息失败:', error)
|
||
showAlert(`发送消息失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 加载消息列表
|
||
async function loadMessages() {
|
||
try {
|
||
const { data, error } = await supabase
|
||
.from('ak_messages')
|
||
.select(`
|
||
*,
|
||
ak_message_types(type_name, display_name)
|
||
`)
|
||
.order('created_at', { ascending: false })
|
||
.limit(10)
|
||
|
||
if (error) throw error
|
||
|
||
displayMessages(data)
|
||
showAlert('消息列表已刷新', 'success')
|
||
|
||
} catch (error) {
|
||
console.error('加载消息失败:', error)
|
||
showAlert(`加载消息失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 显示消息列表
|
||
function displayMessages(messages) {
|
||
const container = document.getElementById('messages-container')
|
||
|
||
if (!messages || messages.length === 0) {
|
||
container.innerHTML = '<p>暂无消息</p>'
|
||
return
|
||
}
|
||
|
||
const messagesHtml = messages.map(msg => `
|
||
<div class="message-item">
|
||
<div class="message-header">
|
||
<span class="message-title">${msg.title}</span>
|
||
<span class="message-time">${new Date(msg.created_at).toLocaleString()}</span>
|
||
</div>
|
||
<div class="message-content">${msg.content}</div>
|
||
<div style="margin-top: 10px; font-size: 0.8rem; color: #888;">
|
||
ID: ${msg.id.substring(0, 8)}... |
|
||
类型: ${msg.ak_message_types?.display_name || '未知'} |
|
||
接收者: ${msg.receiver_type}
|
||
</div>
|
||
</div>
|
||
`).join('')
|
||
|
||
container.innerHTML = messagesHtml
|
||
}
|
||
|
||
// 更新用户角色(管理员功能)
|
||
async function updateUserRole() {
|
||
const targetUserId = document.getElementById('target-user-id').value
|
||
const newRole = document.getElementById('new-role').value
|
||
|
||
if (!targetUserId) {
|
||
showAlert('请输入目标用户ID', 'error')
|
||
return
|
||
}
|
||
|
||
try {
|
||
const { data, error } = await supabase
|
||
.rpc('update_user_role', {
|
||
target_user_id: targetUserId,
|
||
new_role: newRole,
|
||
additional_data: { department: 'Updated via Demo' }
|
||
})
|
||
|
||
if (error) throw error
|
||
|
||
showAlert('用户角色更新成功!', 'success')
|
||
document.getElementById('target-user-id').value = ''
|
||
|
||
} catch (error) {
|
||
console.error('更新用户角色失败:', error)
|
||
showAlert(`更新失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 获取所有用户(管理员功能)
|
||
async function getAllUsers() {
|
||
try {
|
||
const { data, error } = await supabase
|
||
.from('user_roles_detailed')
|
||
.select('*')
|
||
.order('created_at', { ascending: false })
|
||
|
||
if (error) throw error
|
||
|
||
showResults('所有用户列表', data)
|
||
|
||
} catch (error) {
|
||
console.error('获取用户列表失败:', error)
|
||
showAlert(`获取用户列表失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 显示结果
|
||
function showResults(title, data) {
|
||
const resultsSection = document.getElementById('results')
|
||
const resultsContent = document.getElementById('results-content')
|
||
|
||
resultsSection.classList.remove('hidden')
|
||
resultsSection.querySelector('h4').textContent = ` ${title}`
|
||
resultsContent.textContent = JSON.stringify(data, null, 2)
|
||
|
||
// 滚动到结果区域
|
||
resultsSection.scrollIntoView({ behavior: 'smooth' })
|
||
}
|
||
|
||
// 显示提示信息
|
||
function showAlert(message, type = 'info') {
|
||
// 移除现有的alert
|
||
const existingAlert = document.querySelector('.alert')
|
||
if (existingAlert && !existingAlert.classList.contains('alert-info')) {
|
||
existingAlert.remove()
|
||
}
|
||
|
||
const alertDiv = document.createElement('div')
|
||
alertDiv.className = `alert alert-${type}`
|
||
alertDiv.textContent = message
|
||
|
||
// 插入到container的开始
|
||
const container = document.querySelector('.container')
|
||
container.insertBefore(alertDiv, container.firstChild)
|
||
|
||
// 3秒后自动移除
|
||
setTimeout(() => {
|
||
if (alertDiv.parentNode) {
|
||
alertDiv.remove()
|
||
}
|
||
}, 3000)
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|