Files
SMANAGE/backup/Olds/3/members.html
T

352 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<style>
/* 仅保留必要的自定义样式(Tailwind 无法覆盖的部分) */
/* 1. 卡片右上角三角形裁剪与伪元素 */
.user-dark-card {
clip-path: polygon(0 0, calc(100% - 30px) 0, 100% 30px, 100% 100%, 0 100%);
}
.user-dark-card::before {
content: "";
position: absolute;
top: 0;
right: 0;
width: 0;
height: 0;
border-top: 30px solid white;
border-left: 30px solid transparent;
box-shadow: -3px 3px 5px rgba(0, 0, 0, 0.3);
margin: -1px -1px 0 0;
}
/* 2. 音频进度条拖动交互(Tailwind 无法覆盖的动态样式) */
.user-dark-progress {
transition: width 0.2s ease;
}
.user-dark-handle {
transition: left 0.2s ease;
}
</style>
<!-- 顶部头部区域 -->
<div class="relative overflow-hidden bg-gradient-to-r from-blue-600 to-indigo-700 text-white">
<!-- 背景装饰方块(原 squares 效果) -->
<div class="absolute inset-0">
<div class="absolute top-10 left-10 w-20 h-20 bg-white/10 rounded"></div>
<div class="absolute top-20 right-20 w-32 h-32 bg-white/10 rounded"></div>
<div class="absolute bottom-10 left-1/4 w-24 h-24 bg-white/10 rounded"></div>
<div class="absolute bottom-20 right-1/3 w-16 h-16 bg-white/10 rounded"></div>
</div>
<!-- 头部内容容器:所有文本初始隐藏(透明+下移) -->
<div class="container mx-auto px-4 py-32 relative z-10 text-center">
<!-- 1. 主标题 GVSDS• :第1个显示 -->
<h1 class="text-4xl md:text-5xl font-bold mb-2" id="title-main" style="opacity: 0; transform: translateY(10px); transition: all 0.8s ease-out;">GVSDS•</h1>
<!-- 2. 英文名称 :第2个显示 -->
<h3 class="text-xl md:text-2xl mb-1" id="title-en" style="opacity: 0; transform: translateY(10px); transition: all 0.8s ease-out;">Galaxy Vantone Software Development Studio</h3>
<!-- 3. 中文名称 :第3个显示 -->
<h3 class="text-xl md:text-2xl mb-4" id="title-cn" style="opacity: 0; transform: translateY(10px); transition: all 0.8s ease-out;">银河锋锐软件开发工作室</h3>
<!-- 4. 描述文本 :第4个显示 -->
<p class="text-lg text-white/90" id="title-desc" style="opacity: 0; transform: translateY(10px); transition: all 0.8s ease-out;">本工作室的成员们</p>
</div>
</div>
<script>
// 纯JS实现依次显示逻辑:页面加载完成后执行
window.addEventListener('DOMContentLoaded', function() {
// 1. 定义需要依次显示的元素(ID + 显示延迟时间,单位:毫秒)
const textElements = [
{ id: 'title-main', delay: 100 }, // 第1个:延迟100ms显示(主标题)
{ id: 'title-en', delay: 800 }, // 第2个:延迟800ms显示(英文名称)
{ id: 'title-cn', delay: 1500 }, // 第3个:延迟1500ms显示(中文名称)
{ id: 'title-desc', delay: 2200 } // 第4个:延迟2200ms显示(描述文本)
];
// 2. 定义「显示元素」的函数:修改样式为可见状态
function showElement(elementId) {
const element = document.getElementById(elementId);
if (element) { // 避免元素不存在时报错
element.style.opacity = '1'; // 从不透明变为完全可见
element.style.transform = 'translateY(0)'; // 从下移10px归位
}
}
// 3. 按顺序触发每个元素的显示(定时器控制时序)
textElements.forEach(item => {
setTimeout(() => {
showElement(item.id);
}, item.delay);
});
});
</script>
<!-- 加载界面(自定义,替代 layer) -->
<div id="loading" class="fixed inset-0 bg-black/80 flex flex-col items-center justify-center z-50">
<div class="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mb-4"></div>
<p class="text-gray-700 text-lg">加载成员数据中...</p>
</div>
<!-- 成员卡片容器 -->
<div id="members" class="container mx-auto px-4 py-8 flex flex-col items-center gap-8 mb-10"></div>
<script>
// 1. 解析URL参数(替代原 eval 方式,更安全)
const URLARGS = new URLSearchParams(window.location.search);
const Type = URLARGS.get('Type') || ''; // 默认空值,避免Type未传时报错
console.log('请求类型 Type:', Type);
// 2. 页面加载完成后请求数据
window.addEventListener('load', async function () {
try {
// 发起 fetch 请求(替代原 $.get)
const response = await fetch(`/api/gerp/site-users/?Type=${Type}`);
if (!response.ok) {
throw new Error(`请求失败: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log('成员数据:', data);
if (!data.data || !Array.isArray(data.data)) {
throw new Error('成员数据格式错误');
}
// 渲染成员卡片
renderMemberCards(data.data);
} catch (error) {
console.error('加载成员数据失败:', error);
// 加载失败提示
document.getElementById('loading').innerHTML = `
<div class="text-red-500 mb-4">
<i class="layui-icon layui-icon-error text-4xl"></i>
</div>
<p class="text-gray-700 text-lg">加载失败: ${error.message}</p>
<button onclick="window.location.reload()" class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
重试
</button>
`;
} finally {
// 隐藏加载界面(成功/失败都执行)
setTimeout(() => {
document.getElementById('loading').classList.add('opacity-0', 'pointer-events-none');
setTimeout(() => {
document.getElementById('loading').classList.add('hidden');
}, 300);
}, 500);
}
});
// 3. 渲染成员卡片
function renderMemberCards(members) {
const container = document.getElementById('members');
members.forEach(member => {
// 生成联系方式HTML(微信/QQ/邮箱)
const contactHtml = generateContactHtml(member);
// 生成音频播放器HTML(如有音频路径)
const audioHtml = member.UserSoundPath ? generateAudioHtml(member.UserSoundPath) : '';
// 生成标签HTML(成员职责)
const tagsHtml = generateTagsHtml(member.UserWork || []);
// 卡片HTML结构(Tailwind 样式)
const cardHtml = `
<div class="user-dark-card w-full md:w-10/12 lg:w-8/12 bg-gray-900 text-white rounded-lg shadow-lg overflow-hidden relative p-5 flex flex-col md:flex-row">
<!-- 左侧图片区域 -->
<div class="w-full md:w-48 md:h-48 mb-4 md:mb-0 md:mr-6 flex-shrink-0">
<img src="${member.UserPhotoPath || 'https://picsum.photos/200/200'}"
alt="${member.UserName || '成员头像'}"
class="w-full h-full object-cover rounded-lg">
</div>
<!-- 右侧内容区域 -->
<div class="flex-1 flex flex-col">
<!-- 标题与标签 -->
<div class="mb-3">
<h2 class="text-2xl font-bold flex flex-wrap items-center">
${member.UserName || '未命名成员'}
${tagsHtml}
</h2>
</div>
<!-- 成员简介 -->
<p class="text-gray-300 text-base leading-relaxed mb-4 flex-1">
${member.UserBrInt || '暂无成员简介'}
</p>
<!-- 音频播放器(如有) -->
${audioHtml}
<!-- 联系方式 -->
${contactHtml ? `
<div class="mt-4 flex flex-wrap gap-2">
${contactHtml}
</div>
` : ''}
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', cardHtml);
});
// 初始化所有音频播放器
initAllAudioPlayers();
}
// 4. 生成联系方式HTML
function generateContactHtml(member) {
let html = '';
// 微信
if (member.UserWX) {
html += `
<a href="javascript:;" class="px-3 py-1.5 bg-blue-500 text-white rounded text-sm flex items-center gap-1.5 hover:bg-blue-600 transition-colors">
<i class="layui-icon layui-icon-wechat"></i> ${member.UserWXName || '微信'} | ${member.UserWX}
</a>
`;
}
// QQ
if (member.UserQQ) {
html += `
<a href="javascript:;" class="px-3 py-1.5 bg-blue-400 text-white rounded text-sm flex items-center gap-1.5 hover:bg-blue-500 transition-colors">
<i class="layui-icon layui-icon-qq"></i> ${member.UserQQName || 'QQ'} | ${member.UserQQ}
</a>
`;
}
// 邮箱
if (member.UserEmail) {
html += `
<a href="mailto:${member.UserEmail}" class="px-3 py-1.5 bg-blue-600 text-white rounded text-sm flex items-center gap-1.5 hover:bg-blue-700 transition-colors">
<i class="layui-icon layui-icon-mail"></i> Email | ${member.UserEmail}
</a>
`;
}
return html;
}
// 5. 生成音频播放器HTML
function generateAudioHtml(audioSrc) {
return `
<div class="user-dark-audio-player flex items-center mt-3">
<!-- 播放/暂停按钮 -->
<button class="user-dark-play-pause-btn px-3 py-2 bg-blue-500 text-white rounded mr-3 hover:bg-blue-600 transition-colors">
<i class="layui-icon layui-icon-play"></i>
</button>
<!-- 进度条与时间 -->
<div class="user-dark-progress-container flex items-center flex-1 gap-2">
<span class="user-dark-current-time text-xs text-gray-400">0:00</span>
<!-- 进度条容器 -->
<div class="user-dark-progress-bar flex-1 h-2.5 bg-gray-700 rounded relative cursor-pointer">
<div class="user-dark-progress h-full bg-blue-500 rounded w-0"></div>
<div class="user-dark-handle absolute top-1/2 -translate-y-1/2 w-5 h-5 bg-white rounded-full -ml-2.5 left-0"></div>
</div>
<span class="user-dark-total-time text-xs text-gray-400">0:00</span>
</div>
<!-- 音频元素(隐藏) -->
<audio src="${audioSrc}" preload="metadata"></audio>
</div>
`;
}
// 6. 生成成员标签HTML
function generateTagsHtml(tags) {
if (!tags.length) return '';
return tags.map(tag => `
<span class="ml-2 px-2 py-0.5 bg-blue-500 text-white rounded text-xs">${tag}</span>
`).join('');
}
// 7. 初始化所有音频播放器
function initAllAudioPlayers() {
const cards = document.querySelectorAll('.user-dark-card');
cards.forEach(card => {
const audio = card.querySelector('audio');
if (!audio) return;
// 绑定元素
const playPauseBtn = card.querySelector('.user-dark-play-pause-btn');
const playIcon = playPauseBtn.querySelector('i');
const progressBar = card.querySelector('.user-dark-progress-bar');
const progress = card.querySelector('.user-dark-progress');
const handle = card.querySelector('.user-dark-handle');
const currentTimeEl = card.querySelector('.user-dark-current-time');
const totalTimeEl = card.querySelector('.user-dark-total-time');
// 播放/暂停切换
playPauseBtn.addEventListener('click', () => {
if (audio.paused) {
audio.play().catch(err => {
console.error('音频播放失败:', err);
alert('音频播放失败,请检查文件路径或浏览器权限');
});
playIcon.classList.replace('layui-icon-play', 'layui-icon-pause');
} else {
audio.pause();
playIcon.classList.replace('layui-icon-pause', 'layui-icon-play');
}
});
// 音频元数据加载完成(获取总时长)
audio.addEventListener('loadedmetadata', () => {
totalTimeEl.textContent = formatTime(audio.duration);
});
// 音频播放进度更新
audio.addEventListener('timeupdate', () => {
const percent = (audio.currentTime / audio.duration) * 100;
progress.style.width = `${percent}%`;
handle.style.left = `calc(${percent}% - 10px)`;
currentTimeEl.textContent = formatTime(audio.currentTime);
});
// 音频播放结束重置
audio.addEventListener('ended', () => {
playIcon.classList.replace('layui-icon-pause', 'layui-icon-play');
progress.style.width = '0%';
handle.style.left = '0';
currentTimeEl.textContent = '0:00';
audio.currentTime = 0;
});
// 进度条点击跳转
progressBar.addEventListener('click', (e) => {
const rect = progressBar.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const percent = (clickX / progressBar.offsetWidth) * 100;
updateProgress(percent);
});
// 进度条拖动(鼠标事件)
let isDragging = false;
handle.addEventListener('mousedown', () => isDragging = true);
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const rect = progressBar.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const percent = (clickX / progressBar.offsetWidth) * 100;
updateProgress(Math.max(0, Math.min(100, percent))); // 限制在0-100%
}
});
document.addEventListener('mouseup', () => isDragging = false);
// 更新进度条与音频时间的工具函数
function updateProgress(percent) {
progress.style.width = `${percent}%`;
handle.style.left = `calc(${percent}% - 10px)`;
audio.currentTime = (percent / 100) * audio.duration;
currentTimeEl.textContent = formatTime(audio.currentTime);
}
});
}
// 8. 时间格式化工具函数(秒转 分:秒)
function formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
}
</script>