1065 lines
39 KiB
HTML
1065 lines
39 KiB
HTML
<resource-share type="style" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"></resource-share>
|
||
<style>
|
||
/* 全局样式 */
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
body {
|
||
background-color: #fff;
|
||
font-family: "Microsoft YaHei", sans-serif;
|
||
overflow: hidden;
|
||
height: 100vh;
|
||
}
|
||
/* 顶部操作栏 */
|
||
.top-actions {
|
||
padding: 10px 20px;
|
||
background-color: #f8f9fa;
|
||
border-bottom: 1px solid #e6e6e6;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
}
|
||
/* 重载按钮样式 */
|
||
.reload-btn {
|
||
padding: 6px 12px;
|
||
background-color: #1E9FFF;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.reload-btn:hover {
|
||
background-color: #0088ff;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 2px 6px rgba(30, 159, 255, 0.3);
|
||
}
|
||
.reload-btn i {
|
||
font-size: 12px;
|
||
}
|
||
.reload-btn.loading {
|
||
background-color: #999;
|
||
cursor: not-allowed;
|
||
}
|
||
.reload-btn.loading:hover {
|
||
background-color: #999;
|
||
transform: none;
|
||
box-shadow: none;
|
||
}
|
||
/* 背包容器 */
|
||
.backpack-container {
|
||
display: flex;
|
||
height: calc(100vh - 57px); /* 减去顶部操作栏高度 */
|
||
padding: 20px;
|
||
gap: 20px;
|
||
}
|
||
/* 左侧背包区域 */
|
||
.backpack-left {
|
||
flex: 7;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
height: 100%;
|
||
}
|
||
/* 分类标签栏 */
|
||
.category-tabs {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
padding: 10px;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
}
|
||
.category-tab {
|
||
padding: 8px 16px;
|
||
background-color: #fff;
|
||
border: 1px solid #e6e6e6;
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-size: 14px;
|
||
}
|
||
.category-tab.active {
|
||
background-color: #009688;
|
||
color: #fff;
|
||
border-color: #009688;
|
||
box-shadow: 0 2px 8px rgba(0,150,136,0.3);
|
||
}
|
||
.category-tab:hover:not(.active) {
|
||
border-color: #009688;
|
||
color: #009688;
|
||
transform: translateY(-2px);
|
||
}
|
||
/* 物品列表容器 */
|
||
.items-container {
|
||
flex: 1;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
overflow-y: auto;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||
}
|
||
/* 物品网格 */
|
||
.items-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
/* 物品卡片 */
|
||
.item-card {
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
padding: 12px;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
border: 2px solid transparent;
|
||
height: auto; /* 确保height不是100% */
|
||
}
|
||
.item-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||
border-color: #009688;
|
||
}
|
||
.item-card.active {
|
||
border-color: #009688;
|
||
box-shadow: 0 8px 16px rgba(0,150,136,0.2);
|
||
}
|
||
/* 物品图标容器 */
|
||
.item-icon {
|
||
width: 80px;
|
||
height: 80px;
|
||
margin: 0 auto 10px;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
.item-icon::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0,0,0,0.02);
|
||
z-index: 1;
|
||
}
|
||
.item-icon img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: contain;
|
||
z-index: 2;
|
||
transition: transform 0.3s ease;
|
||
}
|
||
.item-card:hover .item-icon img {
|
||
transform: scale(1.1);
|
||
}
|
||
/* 物品名称 */
|
||
.item-name {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
margin-bottom: 5px;
|
||
color: #333;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
/* 物品数量 */
|
||
.item-count {
|
||
font-size: 12px;
|
||
text-align: center;
|
||
color: #666;
|
||
background-color: #f0f0f0;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
display: inline-block;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
/* 右侧详情区域 */
|
||
.backpack-right {
|
||
flex: 3;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||
height: 100%;
|
||
}
|
||
/* 详情头部 */
|
||
.detail-header {
|
||
text-align: center;
|
||
position: relative; /* 新增:为高级链接定位做准备 */
|
||
padding-right: 60px; /* 新增:给高级链接留出空间 */
|
||
}
|
||
/* 新增:高级链接样式 */
|
||
.advanced-link {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
color: #009688;
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
padding: 5px 10px;
|
||
border-radius: 4px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.advanced-link:hover {
|
||
color: #fff;
|
||
background-color: #009688;
|
||
text-decoration: none;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 2px 6px rgba(0,150,136,0.3);
|
||
}
|
||
.advanced-link i {
|
||
margin-right: 5px;
|
||
font-size: 12px;
|
||
}
|
||
.detail-icon {
|
||
width: 250px;
|
||
height: 250px;
|
||
margin: 0 auto 15px;
|
||
border-radius: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: #fff;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||
overflow: hidden;
|
||
}
|
||
.detail-icon img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
.detail-name {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #333;
|
||
margin-bottom: 5px;
|
||
}
|
||
/* 详情描述 */
|
||
.detail-desc {
|
||
background-color: #fff;
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
font-size: 14px;
|
||
color: #555;
|
||
line-height: 1.6;
|
||
}
|
||
/* 操作按钮区域 */
|
||
.detail-actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
/* 数量选择器 */
|
||
.count-selector {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
background-color: #fff;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
}
|
||
.count-label {
|
||
font-size: 14px;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
.count-input {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.count-btn {
|
||
width: 30px;
|
||
height: 30px;
|
||
border: 1px solid #e6e6e6;
|
||
background-color: #fff;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.2s ease;
|
||
}
|
||
.count-btn:hover {
|
||
background-color: #009688;
|
||
color: #fff;
|
||
border-color: #009688;
|
||
}
|
||
.count-num {
|
||
width: 60px;
|
||
height: 30px;
|
||
text-align: center;
|
||
border: 1px solid #e6e6e6;
|
||
border-radius: 4px;
|
||
margin: 0 5px;
|
||
}
|
||
/* 功能按钮 */
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
.action-btn {
|
||
flex: 1;
|
||
padding: 10px;
|
||
border-radius: 6px;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.btn-view {
|
||
background-color: #1E9FFF;
|
||
color: #fff;
|
||
}
|
||
.btn-use {
|
||
background-color: #009688;
|
||
color: #fff;
|
||
}
|
||
.btn-discard {
|
||
background-color: #FF5722;
|
||
color: #fff;
|
||
}
|
||
.action-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||
}
|
||
/* 加载动画 */
|
||
.loading {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
.loading-spinner {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 4px solid #f3f3f3;
|
||
border-top: 4px solid #009688;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
/* 空数据样式 */
|
||
.empty-data {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
color: #999;
|
||
font-size: 16px;
|
||
}
|
||
.empty-data i {
|
||
font-size: 48px;
|
||
color: #ddd;
|
||
}
|
||
/* 高级API弹窗样式 */
|
||
.advanced-api-content {
|
||
padding: 15px;
|
||
max-height: 60vh;
|
||
overflow-y: auto;
|
||
}
|
||
.api-item {
|
||
margin-bottom: 10px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #e6e6e6;
|
||
}
|
||
.api-key {
|
||
font-weight: 600;
|
||
color: #009688;
|
||
margin-bottom: 5px;
|
||
}
|
||
.api-value {
|
||
font-family: "Consolas", monospace;
|
||
font-size: 13px;
|
||
color: #333;
|
||
background-color: #f8f9fa;
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
word-break: break-all;
|
||
}
|
||
/* 滚动条美化 */
|
||
::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
::-webkit-scrollbar-track {
|
||
background: #f1f1f1;
|
||
border-radius: 3px;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
background: #ccc;
|
||
border-radius: 3px;
|
||
}
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: #009688;
|
||
}
|
||
</style>
|
||
<!-- 新增顶部操作栏 -->
|
||
<div class="top-actions">
|
||
<button class="reload-btn" id="reloadBackpackBtn">
|
||
<i class="layui-icon layui-icon-refresh"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="backpack-container">
|
||
<!-- 左侧背包区域 -->
|
||
<div class="backpack-left">
|
||
<!-- 分类标签 -->
|
||
<div class="category-tabs" id="categoryTabs">
|
||
<div class="category-tab active" data-tag="all">全部</div>
|
||
</div>
|
||
<!-- 物品列表 -->
|
||
<div class="items-container" id="itemsContainer">
|
||
<!-- 加载中 -->
|
||
<div class="loading">
|
||
<div class="loading-spinner"></div>
|
||
<div>加载背包数据中...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧详情区域 -->
|
||
<div class="backpack-right" id="detailPanel">
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-component"></i>
|
||
<div>请选择一个物品查看详情</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
layui.use(['layer', 'form'], function() {
|
||
var layer = layui.layer;
|
||
var form = layui.form;
|
||
|
||
// 全局变量
|
||
var backpackItems = []; // 所有背包物品
|
||
var allTags = new Set(); // 所有分类标签
|
||
var selectedItem = null; // 当前选中的物品
|
||
var selectedCount = 1; // 当前选择的数量
|
||
var isLoading = false; // 加载状态标识
|
||
// 新增:图片缓存Map - key: 原始URL, value: Blob URL
|
||
var imageCache = new Map();
|
||
// 新增:默认占位图URL
|
||
const defaultIconUrl = 'https://placeholder.pics/svg/80x80/FFFFFF-E6E6E6/暂无图标';
|
||
|
||
// 初始化函数
|
||
function init() {
|
||
// 获取背包数据
|
||
getBackpackItems();
|
||
|
||
// 绑定重载按钮事件
|
||
bindReloadButtonEvent();
|
||
|
||
// 新增:页面卸载时清理Blob URL,避免内存泄漏
|
||
window.addEventListener('beforeunload', cleanImageCache);
|
||
}
|
||
|
||
// 新增:清理图片缓存(释放Blob URL)
|
||
function cleanImageCache() {
|
||
imageCache.forEach(blobUrl => {
|
||
try {
|
||
URL.revokeObjectURL(blobUrl);
|
||
} catch (e) {
|
||
console.warn('释放Blob URL失败:', e);
|
||
}
|
||
});
|
||
imageCache.clear();
|
||
}
|
||
|
||
// 新增:加载图片并转换为Blob URL(带缓存)
|
||
function loadImageToBlob(url) {
|
||
// 处理默认占位图
|
||
if (!url || url === defaultIconUrl) {
|
||
return Promise.resolve(defaultIconUrl);
|
||
}
|
||
|
||
// 检查缓存,存在则直接返回
|
||
if (imageCache.has(url)) {
|
||
return Promise.resolve(imageCache.get(url));
|
||
}
|
||
|
||
// 不存在则加载图片并转换为Blob
|
||
return fetch(url)
|
||
.then(response => {
|
||
// 检查响应是否成功
|
||
if (!response.ok) {
|
||
throw new Error(`图片加载失败: ${response.status}`);
|
||
}
|
||
// 转换为Blob
|
||
return response.blob();
|
||
})
|
||
.then(blob => {
|
||
// 创建Blob URL并缓存
|
||
const blobUrl = URL.createObjectURL(blob);
|
||
imageCache.set(url, blobUrl);
|
||
return blobUrl;
|
||
})
|
||
.catch(error => {
|
||
console.error(`加载图片失败(${url}):`, error);
|
||
// 失败时返回默认占位图
|
||
return defaultIconUrl;
|
||
});
|
||
}
|
||
|
||
// 绑定重载按钮事件
|
||
function bindReloadButtonEvent() {
|
||
const reloadBtn = document.getElementById('reloadBackpackBtn');
|
||
reloadBtn.addEventListener('click', function() {
|
||
if (isLoading) return; // 如果正在加载,不重复请求
|
||
|
||
// 禁用按钮并显示加载状态
|
||
this.classList.add('loading');
|
||
const btnHtml = this.innerHTML;
|
||
this.innerHTML = '<i class="layui-icon layui-icon-loading"></i>';
|
||
|
||
// 清空选中状态
|
||
selectedItem = null;
|
||
selectedCount = 1;
|
||
|
||
// 重置详情面板为加载状态
|
||
document.getElementById('detailPanel').innerHTML = `
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-component"></i>
|
||
<div>请选择一个物品查看详情</div>
|
||
</div>
|
||
`;
|
||
|
||
// 重新加载背包数据
|
||
getBackpackItems()
|
||
.finally(() => {
|
||
// 恢复按钮状态
|
||
this.classList.remove('loading');
|
||
this.innerHTML = btnHtml;
|
||
});
|
||
});
|
||
}
|
||
|
||
// 获取背包物品数据(修改为返回Promise)
|
||
function getBackpackItems() {
|
||
isLoading = true;
|
||
|
||
// 显示加载状态
|
||
document.getElementById('itemsContainer').innerHTML = `
|
||
<div class="loading">
|
||
<div class="loading-spinner"></div>
|
||
<div>加载背包数据中...</div>
|
||
</div>
|
||
`;
|
||
|
||
return fetch('/API/Backpack/GetBackpackItems', {
|
||
method: 'GET',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
isLoading = false;
|
||
|
||
if (data.code === 0) {
|
||
backpackItems = data.data || [];
|
||
// 处理分类标签
|
||
processItemTags();
|
||
// 渲染分类标签
|
||
renderCategoryTabs();
|
||
// 渲染物品列表
|
||
renderItemsList('all');
|
||
|
||
// 提示加载成功
|
||
layer.msg('背包数据已更新', {icon: 1, time: 1500});
|
||
} else {
|
||
// 显示错误信息
|
||
document.getElementById('itemsContainer').innerHTML = `
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-face-cry"></i>
|
||
<div>${data.msg || '加载失败,请重试'}</div>
|
||
</div>
|
||
`;
|
||
layer.msg(data.msg || '加载失败,请重试', {icon: 2});
|
||
}
|
||
})
|
||
.catch(error => {
|
||
isLoading = false;
|
||
console.error('获取背包数据失败:', error);
|
||
document.getElementById('itemsContainer').innerHTML = `
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-face-cry"></i>
|
||
<div>网络错误,无法加载背包数据</div>
|
||
</div>
|
||
`;
|
||
layer.msg('网络错误,无法加载背包数据', {icon: 2});
|
||
});
|
||
}
|
||
|
||
// 处理物品标签(兼容null)
|
||
function processItemTags() {
|
||
allTags.clear();
|
||
backpackItems.forEach(item => {
|
||
// 处理ItemTags可能为null的情况
|
||
const tags = item.ItemTags && Array.isArray(item.ItemTags) ? item.ItemTags : [];
|
||
tags.forEach(tag => {
|
||
if (tag && tag.trim()) {
|
||
allTags.add(tag.trim());
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// 渲染分类标签
|
||
function renderCategoryTabs() {
|
||
const tabsContainer = document.getElementById('categoryTabs');
|
||
// 保留全部标签,清空其他
|
||
tabsContainer.innerHTML = '<div class="category-tab active" data-tag="all">全部</div>';
|
||
|
||
// 添加其他标签
|
||
Array.from(allTags).forEach(tag => {
|
||
const tab = document.createElement('div');
|
||
tab.className = 'category-tab';
|
||
tab.dataset.tag = tag;
|
||
tab.textContent = tag;
|
||
tabsContainer.appendChild(tab);
|
||
|
||
// 绑定点击事件
|
||
tab.addEventListener('click', function() {
|
||
// 切换激活状态
|
||
document.querySelectorAll('.category-tab').forEach(t => {
|
||
t.classList.remove('active');
|
||
});
|
||
this.classList.add('active');
|
||
// 渲染对应物品
|
||
renderItemsList(this.dataset.tag);
|
||
});
|
||
});
|
||
|
||
// 绑定全部标签点击事件
|
||
document.querySelector('.category-tab[data-tag="all"]').addEventListener('click', function() {
|
||
document.querySelectorAll('.category-tab').forEach(t => {
|
||
t.classList.remove('active');
|
||
});
|
||
this.classList.add('active');
|
||
renderItemsList('all');
|
||
});
|
||
}
|
||
|
||
// 渲染物品列表(修改:使用图片缓存)
|
||
function renderItemsList(filterTag) {
|
||
const container = document.getElementById('itemsContainer');
|
||
|
||
// 过滤物品
|
||
let filteredItems = backpackItems;
|
||
if (filterTag !== 'all') {
|
||
filteredItems = backpackItems.filter(item => {
|
||
const tags = item.ItemTags && Array.isArray(item.ItemTags) ? item.ItemTags : [];
|
||
return tags.includes(filterTag);
|
||
});
|
||
}
|
||
|
||
// 渲染物品
|
||
if (filteredItems.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-component"></i>
|
||
<div>暂无该分类的物品</div>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
// 生成物品网格
|
||
let itemsHtml = '<div class="items-grid">';
|
||
filteredItems.forEach(item => {
|
||
// 处理元数据默认值
|
||
const metaData = item.ItemMetaData || {};
|
||
const allowUsed = metaData.AllowUsed !== undefined ? metaData.AllowUsed : false;
|
||
const disposable = metaData.Disposable !== undefined ? metaData.Disposable : false;
|
||
|
||
// 生成唯一ID用于图片加载
|
||
const itemId = `item-${item.BackpackItemUUID}`;
|
||
|
||
itemsHtml += `
|
||
<div class="item-card animate__animated animate__fadeIn" data-uuid="${item.BackpackItemUUID}">
|
||
<div class="item-icon">
|
||
<img id="${itemId}" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||
alt="${item.ItemName || '未知物品'}"
|
||
data-original="${item.ItemIcon || defaultIconUrl}">
|
||
</div>
|
||
<div class="item-name">${item.ItemName || '未知物品'}</div>
|
||
<div class="item-count">数量: ${item.ItemLength || 0}</div>
|
||
</div>
|
||
`;
|
||
});
|
||
itemsHtml += '</div>';
|
||
|
||
container.innerHTML = itemsHtml;
|
||
|
||
// 批量加载图片(使用缓存)
|
||
filteredItems.forEach(item => {
|
||
const itemId = `item-${item.BackpackItemUUID}`;
|
||
const imgElement = document.getElementById(itemId);
|
||
const originalUrl = imgElement.dataset.original;
|
||
|
||
// 加载图片并替换src
|
||
loadImageToBlob(originalUrl).then(blobUrl => {
|
||
if (imgElement) {
|
||
imgElement.src = blobUrl;
|
||
}
|
||
});
|
||
});
|
||
|
||
// 绑定物品卡片点击事件
|
||
document.querySelectorAll('.item-card').forEach(card => {
|
||
card.addEventListener('click', function() {
|
||
// 切换选中状态
|
||
document.querySelectorAll('.item-card').forEach(c => {
|
||
c.classList.remove('active');
|
||
});
|
||
this.classList.add('active');
|
||
|
||
// 获取选中物品
|
||
const uuid = this.dataset.uuid;
|
||
selectedItem = backpackItems.find(item => item.BackpackItemUUID === uuid);
|
||
selectedCount = 1; // 重置选择数量
|
||
|
||
// 渲染详情面板
|
||
renderDetailPanel();
|
||
});
|
||
});
|
||
}
|
||
|
||
// 渲染详情面板(修改:使用图片缓存)
|
||
function renderDetailPanel() {
|
||
if (!selectedItem) {
|
||
document.getElementById('detailPanel').innerHTML = `
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-component"></i>
|
||
<div>请选择一个物品查看详情</div>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
// 处理元数据默认值
|
||
const metaData = selectedItem.ItemMetaData || {};
|
||
const allowUsed = metaData.AllowUsed !== undefined ? metaData.AllowUsed : false;
|
||
const disposable = metaData.Disposable !== undefined ? metaData.Disposable : false;
|
||
const usedRunJs = metaData.UsedRunJs || '';
|
||
const viewRunJs = metaData.ViewRunJs || '';
|
||
const itemCount = selectedItem.ItemLength || 0;
|
||
|
||
// 生成详情图片ID
|
||
const detailImgId = `detail-${selectedItem.BackpackItemUUID}`;
|
||
|
||
// 构建详情HTML
|
||
let detailHtml = `
|
||
<div class="detail-header animate__animated animate__fadeIn">
|
||
<!-- 新增:高级链接 -->
|
||
<a href="javascript:;" class="advanced-link" id="advancedLink">
|
||
<i class="layui-icon layui-icon-set"></i>高级
|
||
</a>
|
||
<div class="detail-icon">
|
||
<img id="${detailImgId}" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||
alt="${selectedItem.ItemName || '未知物品'}"
|
||
data-original="${selectedItem.ItemIcon || defaultIconUrl}">
|
||
</div>
|
||
<div class="detail-name">${selectedItem.ItemName || '未知物品'}</div>
|
||
</div>
|
||
|
||
<div class="detail-desc animate__animated animate__fadeIn animate__delay-100ms">
|
||
${selectedItem.ItemDescription || '<div style="text-align:center; color:#999">暂无物品介绍</div>'}
|
||
</div>
|
||
|
||
<div class="detail-actions animate__animated animate__fadeIn animate__delay-200ms">
|
||
<div class="count-selector">
|
||
<div class="count-label">选择数量:</div>
|
||
<div class="count-input">
|
||
<button class="count-btn minus">-</button>
|
||
<input type="number" class="count-num" value="${selectedCount}" min="1" max="${itemCount}">
|
||
<button class="count-btn plus">+</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="action-buttons">
|
||
<button class="action-btn btn-view">查看</button>
|
||
${allowUsed ? `<button class="action-btn btn-use">使用</button>` : ''}
|
||
<button class="action-btn btn-discard">丢弃</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('detailPanel').innerHTML = detailHtml;
|
||
|
||
// 加载详情图片(使用缓存)
|
||
const detailImgElement = document.getElementById(detailImgId);
|
||
const originalUrl = detailImgElement.dataset.original;
|
||
loadImageToBlob(originalUrl).then(blobUrl => {
|
||
if (detailImgElement) {
|
||
detailImgElement.src = blobUrl;
|
||
}
|
||
});
|
||
|
||
// 绑定高级链接点击事件(替换原来的高级按钮事件)
|
||
document.getElementById('advancedLink').addEventListener('click', function() {
|
||
showAdvancedApiInfo();
|
||
});
|
||
|
||
// 绑定数量选择器事件
|
||
const minusBtn = document.querySelector('.count-btn.minus');
|
||
const plusBtn = document.querySelector('.count-btn.plus');
|
||
const countInput = document.querySelector('.count-num');
|
||
|
||
minusBtn.addEventListener('click', function() {
|
||
if (selectedCount > 1) {
|
||
selectedCount--;
|
||
countInput.value = selectedCount;
|
||
}
|
||
});
|
||
|
||
plusBtn.addEventListener('click', function() {
|
||
if (selectedCount < itemCount) {
|
||
selectedCount++;
|
||
countInput.value = selectedCount;
|
||
}
|
||
});
|
||
|
||
countInput.addEventListener('change', function() {
|
||
let val = parseInt(this.value);
|
||
if (isNaN(val)) val = 1;
|
||
if (val < 1) val = 1;
|
||
if (val > itemCount) val = itemCount;
|
||
selectedCount = val;
|
||
this.value = val;
|
||
});
|
||
|
||
// 绑定查看按钮事件 - 新增ViewRunJs执行逻辑
|
||
document.querySelector('.btn-view').addEventListener('click', function() {
|
||
// 执行查看JS代码
|
||
if (viewRunJs) {
|
||
try {
|
||
eval(viewRunJs);
|
||
} catch (e) {
|
||
console.error('执行物品查看JS失败:', e);
|
||
layer.msg('物品查看脚本执行失败', {icon: 2});
|
||
}
|
||
}
|
||
// 默认提示
|
||
else { layer.msg(`查看 ${selectedItem.ItemName} x ${selectedCount}`, {icon: 1}); }
|
||
});
|
||
|
||
// 绑定使用按钮事件(如果存在)
|
||
const useBtn = document.querySelector('.btn-use');
|
||
if (useBtn) {
|
||
useBtn.addEventListener('click', function() {
|
||
layer.confirm(`确定使用 ${selectedItem.ItemName} x ${selectedCount} 吗?`, {
|
||
title: '使用物品',
|
||
btn: ['确定', '取消']
|
||
}, function(index) {
|
||
layer.close(index);
|
||
// 执行使用JS代码
|
||
if (usedRunJs) {
|
||
try {
|
||
eval(usedRunJs);
|
||
} catch (e) {
|
||
console.error('执行物品使用JS失败:', e);
|
||
layer.msg('物品使用脚本执行失败', {icon: 2});
|
||
}
|
||
}
|
||
layer.msg(`已使用 ${selectedItem.ItemName} x ${selectedCount}`, {icon: 1});
|
||
// 可添加更新物品数量的逻辑
|
||
});
|
||
});
|
||
}
|
||
|
||
// 绑定丢弃按钮事件 - 修改为POST提交表单数据
|
||
document.querySelector('.btn-discard').addEventListener('click', function() {
|
||
layer.confirm(`确定丢弃 ${selectedItem.ItemName} x ${selectedCount} 吗?该操作不可恢复!`, {
|
||
title: '丢弃物品',
|
||
btn: ['确定', '取消'],
|
||
icon: 3
|
||
}, function(index) {
|
||
layer.close(index);
|
||
|
||
// 显示加载层
|
||
const loadIndex = layer.load(2, {
|
||
shade: [0.3, '#000'],
|
||
time: 10*1000
|
||
});
|
||
|
||
// 构建表单数据
|
||
const formData = new FormData();
|
||
formData.append('BackpackItemUUID', selectedItem.BackpackItemUUID);
|
||
formData.append('Count', selectedCount);
|
||
|
||
// POST提交到丢弃API
|
||
fetch('/API/Backpack/DiscardItem', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
// 忽略状态码,尝试解析JSON
|
||
return response.text().then(text => {
|
||
try {
|
||
return JSON.parse(text);
|
||
} catch (e) {
|
||
console.error('解析返回数据失败:', e, text);
|
||
return { code: 1, msg: '返回数据格式错误' };
|
||
}
|
||
});
|
||
})
|
||
.then(data => {
|
||
layer.close(loadIndex);
|
||
|
||
// 处理返回结果
|
||
if (data.code === 0) {
|
||
layer.msg(`成功丢弃 ${selectedItem.ItemName} x ${selectedCount}`, {icon: 1});
|
||
|
||
// 更新本地数据并重新渲染
|
||
// 1. 找到物品索引
|
||
const itemIndex = backpackItems.findIndex(item => item.BackpackItemUUID === selectedItem.BackpackItemUUID);
|
||
if (itemIndex !== -1) {
|
||
// 2. 更新物品数量
|
||
backpackItems[itemIndex].ItemLength = Math.max(0, backpackItems[itemIndex].ItemLength - selectedCount);
|
||
|
||
// 3. 如果数量为0,移除该物品
|
||
if (backpackItems[itemIndex].ItemLength === 0) {
|
||
backpackItems.splice(itemIndex, 1);
|
||
// 清空选中状态
|
||
selectedItem = null;
|
||
selectedCount = 1;
|
||
// 重置详情面板
|
||
document.getElementById('detailPanel').innerHTML = `
|
||
<div class="empty-data">
|
||
<i class="layui-icon layui-icon-component"></i>
|
||
<div>请选择一个物品查看详情</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
// 4. 重新渲染物品列表
|
||
const activeTag = document.querySelector('.category-tab.active').dataset.tag;
|
||
renderItemsList(activeTag);
|
||
} else {
|
||
layer.msg(data.msg || '丢弃物品失败', {icon: 2});
|
||
}
|
||
})
|
||
.catch(error => {
|
||
layer.close(loadIndex);
|
||
console.error('丢弃物品请求失败:', error);
|
||
layer.msg('网络错误,丢弃物品失败', {icon: 2});
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
// 显示高级API信息弹窗
|
||
function showAdvancedApiInfo() {
|
||
if (!selectedItem) return;
|
||
function escapeHtml(str) {
|
||
if (str === undefined || str === null) return '无';
|
||
const strType = typeof str;
|
||
if (strType !== 'string') {
|
||
return strType === 'object' ? JSON.stringify(str) : String(str);
|
||
}
|
||
return str
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''')
|
||
.replace(/`/g, '`');
|
||
}
|
||
|
||
// 处理元数据默认值
|
||
const metaData = selectedItem.ItemMetaData || {};
|
||
const allowUsed = metaData.AllowUsed !== undefined ? metaData.AllowUsed : false;
|
||
const disposable = metaData.Disposable !== undefined ? metaData.Disposable : false;
|
||
const usedRunJs = metaData.UsedRunJs || '';
|
||
const viewRunJs = metaData.ViewRunJs || '';
|
||
|
||
// 构建API信息HTML(所有动态内容都经过转义)
|
||
let apiHtml = '<div class="advanced-api-content">';
|
||
|
||
// 基础信息(全量转义)
|
||
apiHtml += `
|
||
<div class="api-item">
|
||
<div class="api-key">基础信息</div>
|
||
<div class="api-value">
|
||
BackpackItemUUID: ${escapeHtml(selectedItem.BackpackItemUUID)}<br>
|
||
ItemName: ${escapeHtml(selectedItem.ItemName)}<br>
|
||
ItemLength: ${escapeHtml(selectedItem.ItemLength || 0)}<br>
|
||
ItemIcon: ${escapeHtml(selectedItem.ItemIcon)}<br>
|
||
ItemIconColor: #${escapeHtml(selectedItem.ItemIconColor)}<br>
|
||
ItemTags: ${escapeHtml(
|
||
selectedItem.ItemTags && Array.isArray(selectedItem.ItemTags)
|
||
? selectedItem.ItemTags.join(', ')
|
||
: '无'
|
||
)}<br>
|
||
ItemDescription: ${escapeHtml(selectedItem.ItemDescription)}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 元数据信息(全量转义)
|
||
apiHtml += `
|
||
<div class="api-item">
|
||
<div class="api-key">元数据 (ItemMetaData)</div>
|
||
<div class="api-value">
|
||
AllowUsed: ${escapeHtml(allowUsed)}<br>
|
||
Disposable: ${escapeHtml(disposable)}<br>
|
||
UsedRunJs: ${escapeHtml(usedRunJs)}<br>
|
||
ViewRunJs: ${escapeHtml(viewRunJs)}<br>
|
||
原始数据: <pre>${escapeHtml(JSON.stringify(metaData, null, 2))}</pre>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
apiHtml += '</div>';
|
||
|
||
// 弹出层
|
||
layer.open({
|
||
type: 1,
|
||
title: `[高级] ${escapeHtml(selectedItem.ItemName)} API信息`, // 标题也需要转义
|
||
area: ['80%', '70%'],
|
||
content: apiHtml,
|
||
shade: 0.5,
|
||
anim: 1,
|
||
maxmin: true
|
||
});
|
||
}
|
||
|
||
// 初始化
|
||
init();
|
||
});
|
||
</script> |