Files
think-plugs-recorder/view/recorder/components/data_readers.html
2025-08-16 11:10:37 +08:00

251 lines
11 KiB
HTML

<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-size: 14px; line-height: 1.5; color: #262626;">
<div class="recorder-readers {$options.css_class|default=''}" style="background: #ffffff; border: 1px solid #e6e6e6; border-radius: 6px; padding: 15px;">
{if isset($readers) && !empty($readers)}
<div style="margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #f0f0f0;">
<h5 id="readersToggle" style="margin: 0; color: #333; font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; transition: all 0.3s ease;"
onmouseover="this.style.color='#1890ff';" onmouseout="this.style.color='#333';">
<i id="readersToggleIcon" class="layui-icon layui-icon-down" style="color: #1890ff; font-size: 12px; transition: transform 0.3s ease;"></i>
<i class="layui-icon layui-icon-username" style="color: #1890ff; font-size: 16px;"></i>
读取用户 ({$readers|count})
</h5>
</div>
<div id="readersContent" style="display: none; flex-direction: column; gap: 12px; overflow: hidden; transition: all 0.3s ease;">
{volist name="readers" id="reader"}
<div class="recorder-reader recorder-slide-in" data-user-id="{$reader.user_id}"
style="display: flex; align-items: flex-start; padding: 12px; background: #fafafa; border-radius: 6px; border-left: 3px solid #1890ff; transition: all 0.3s ease; cursor: pointer;"
onmouseover="this.style.background='#f0f9ff'; this.style.borderLeftColor='#40a9ff'; this.style.transform='translateX(2px)';"
onmouseout="this.style.background='#fafafa'; this.style.borderLeftColor='#1890ff'; this.style.transform='translateX(0)';">
<div style="width: 36px; height: 36px; background: linear-gradient(135deg, #1890ff, #40a9ff); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 12px; color: #ffffff; flex-shrink: 0; box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);">
<i class="layui-icon layui-icon-username" style="font-size: 16px;"></i>
</div>
<div style="flex: 1; min-width: 0;">
<div style="font-size: 14px; font-weight: 600; color: #333; margin-bottom: 6px;">{$reader.user_nickname}</div>
<div style="display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: #666; margin-bottom: 4px;">
<span style="display: flex; align-items: center; color: #1890ff; font-weight: 500;">
<i class="layui-icon layui-icon-read" style="font-size: 12px; margin-right: 2px;"></i>
读取 {$reader.read_count} 次
</span>
{if $reader.first_read_at != $reader.last_read_at}
<span style="display: flex; align-items: center;">
<i class="layui-icon layui-icon-time" style="font-size: 12px; margin-right: 2px;"></i>
最后: {$reader.last_read_at_relative}
</span>
{else/}
<span style="display: flex; align-items: center;">
<i class="layui-icon layui-icon-time" style="font-size: 12px; margin-right: 2px;"></i>
{$reader.first_read_at_relative}
</span>
{/if}
</div>
<div style="color: #999; font-size: 11px; line-height: 1.4;">
<small>
首次: {$reader.first_read_at_formatted}
{if $reader.first_read_at != $reader.last_read_at}
| 最后: {$reader.last_read_at_formatted}
{/if}
</small>
</div>
</div>
</div>
{/volist}
</div>
{else}
<div style="text-align: center; padding: 50px 20px; color: #999;">
<i class="layui-icon layui-icon-face-cry" style="font-size: 48px; margin-bottom: 12px; color: #ddd; display: block;"></i>
<p style="margin: 0; font-size: 14px;">暂无用户读取过此数据</p>
</div>
{/if}
</div>
</div>
<style>
@keyframes recorderSlideIn {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
.recorder-slide-in {
animation: recorderSlideIn 0.3s ease-in-out;
}
/* 折叠相关样式 */
.readers-collapsed #readersToggleIcon {
transform: rotate(-90deg);
}
.readers-expanded #readersToggleIcon {
transform: rotate(0deg);
}
.readers-content-hidden {
max-height: 0 !important;
opacity: 0;
margin-top: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.readers-content-visible {
max-height: 2000px;
opacity: 1;
}
@media (max-width: 768px) {
.recorder-readers {
padding: 12px !important;
}
.recorder-reader {
padding: 10px !important;
flex-direction: column !important;
text-align: center !important;
}
.recorder-reader > div:first-child {
margin-right: 0 !important;
margin-bottom: 8px !important;
}
.recorder-reader > div:last-child > div:nth-child(2) {
flex-direction: column !important;
gap: 4px !important;
}
}
</style>
<script>
(function() {
'use strict';
// 折叠/展开功能
function initCollapse() {
var toggle = document.getElementById('readersToggle');
var content = document.getElementById('readersContent');
var icon = document.getElementById('readersToggleIcon');
var container = document.querySelector('.recorder-readers');
if (!toggle || !content || !icon || !container) return;
// 设置默认折叠状态
container.classList.add('readers-collapsed');
content.classList.add('readers-content-hidden');
// 点击折叠/展开
toggle.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
var isCollapsed = container.classList.contains('readers-collapsed');
if (isCollapsed) {
// 展开
container.classList.remove('readers-collapsed');
container.classList.add('readers-expanded');
content.classList.remove('readers-content-hidden');
content.classList.add('readers-content-visible');
content.style.display = 'flex';
} else {
// 折叠
container.classList.remove('readers-expanded');
container.classList.add('readers-collapsed');
content.classList.remove('readers-content-visible');
content.classList.add('readers-content-hidden');
// 延迟隐藏以保证动画效果
setTimeout(function() {
if (container.classList.contains('readers-collapsed')) {
content.style.display = 'none';
}
}, 300);
}
});
}
// 读取用户点击处理
document.addEventListener('click', function(e) {
var reader = e.target.closest('.recorder-reader');
if (reader) {
// 移除其他选中状态
document.querySelectorAll('.recorder-reader.selected').forEach(function(el) {
el.classList.remove('selected');
el.style.background = '#fafafa';
el.style.borderLeftColor = '#1890ff';
});
// 添加选中状态
reader.classList.add('selected');
reader.style.background = '#e6f7ff';
reader.style.borderLeftColor = '#40a9ff';
// 触发自定义事件
var event = new CustomEvent('recorderReaderSelect', {
detail: {
userId: reader.dataset.userId,
element: reader
}
});
document.dispatchEvent(event);
}
});
// 为新添加的元素添加动画
function addAnimation() {
var readers = document.querySelectorAll('.recorder-reader:not(.animated)');
readers.forEach(function(reader, index) {
reader.style.animationDelay = (index * 100) + 'ms';
reader.classList.add('recorder-slide-in', 'animated');
});
}
// 初始化
setTimeout(function() {
initCollapse();
addAnimation();
}, 100);
// 响应式处理
function handleResize() {
var isMobile = window.innerWidth <= 768;
var readers = document.querySelectorAll('.recorder-reader');
readers.forEach(function(reader) {
if (isMobile) {
reader.style.padding = '10px';
reader.style.flexDirection = 'column';
reader.style.textAlign = 'center';
var avatar = reader.querySelector('div:first-child');
if (avatar) {
avatar.style.marginRight = '0';
avatar.style.marginBottom = '8px';
}
var details = reader.querySelector('div:last-child > div:nth-child(2)');
if (details) {
details.style.flexDirection = 'column';
details.style.gap = '4px';
}
} else {
reader.style.padding = '12px';
reader.style.flexDirection = 'row';
reader.style.textAlign = 'left';
var avatar = reader.querySelector('div:first-child');
if (avatar) {
avatar.style.marginRight = '12px';
avatar.style.marginBottom = '0';
}
var details = reader.querySelector('div:last-child > div:nth-child(2)');
if (details) {
details.style.flexDirection = 'row';
details.style.gap = '12px';
}
}
});
}
window.addEventListener('resize', handleResize);
handleResize();
})();
</script>