// 商店页面主逻辑
// 负责资源加载、搜索、分类筛选等功能
(function () {
'use strict';
class StoreView {
constructor() {
this.resources = [];
this.filteredResources = [];
this.currentCategory = '';
this.searchQuery = '';
this.itemTemplate = null;
this.currentFps = 8;
this.frameUrls = [];
this.currentFrame = 0;
this.itemAnimations = new Map(); // 存储每个 item 的动画数据
this.isUserLoggedIn = false; // 本地登录状态缓存
this.init();
}
async init() {
// 加载 item 模板
await this.loadItemTemplate();
// 绑定事件
this.bindEvents();
// 加载分类
await this.loadCategories();
// 加载资源
await this.loadResources();
}
async loadItemTemplate() {
try {
// 使用相对于当前页面的路径
// store.html 位于 page/store/store.html,item.html 位于同一目录
const response = await fetch('./item.html');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const html = await response.text();
this.itemTemplate = html;
} catch (error) {
console.error('[StoreView] 加载模板失败:', error);
// 如果相对路径失败,尝试绝对路径
try {
const response = await fetch('/page/store/item.html');
if (response.ok) {
const html = await response.text();
this.itemTemplate = html;
} else {
throw new Error('绝对路径也失败');
}
} catch (fallbackError) {
console.error('[StoreView] 备用路径也失败:', fallbackError);
this.itemTemplate = '
加载失败
';
}
}
}
async loadCategories() {
try {
const response = await fetch('http://localhost:3000/api/store/categories');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success && data.categories) {
const categoryBar = document.getElementById('categoryBar');
if (categoryBar) {
// 保留"全部"按钮,添加其他分类
const allButton = categoryBar.querySelector('.category-item[data-category=""]');
categoryBar.innerHTML = '';
if (allButton) {
categoryBar.appendChild(allButton);
}
// 添加动态分类按钮
data.categories.forEach(category => {
const button = document.createElement('button');
button.className = 'category-item';
button.dataset.category = category.name; // 使用文件夹名称作为分类名称
button.textContent = category.name;
categoryBar.appendChild(button);
});
}
}
} catch (error) {
console.error('[StoreView] 加载分类失败:', error);
// 如果加载失败,保持默认的"全部"按钮
}
}
bindEvents() {
// 监听登录状态变化
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'login-success' && event.data.user) {
this.isUserLoggedIn = true;
} else if (event.data && event.data.type === 'logout') {
this.isUserLoggedIn = false;
}
});
// 搜索栏
const searchInput = document.getElementById('searchInput');
const searchButton = document.getElementById('searchButton');
if (searchInput) {
searchInput.addEventListener('input', (e) => {
this.searchQuery = e.target.value.trim();
this.filterResources();
});
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.filterResources();
}
});
}
if (searchButton) {
searchButton.addEventListener('click', () => {
this.filterResources();
});
}
// 分类栏
const categoryItems = document.querySelectorAll('.category-item');
categoryItems.forEach(item => {
item.addEventListener('click', () => {
// 移除所有 active 类
categoryItems.forEach(i => i.classList.remove('active'));
// 添加 active 类到当前项
item.classList.add('active');
// 更新当前分类
this.currentCategory = item.dataset.category || '';
// 重新加载资源
this.loadResources();
});
});
// 预览弹窗关闭
const previewClose = document.getElementById('previewClose');
const previewModal = document.getElementById('previewModal');
if (previewClose) {
previewClose.addEventListener('click', () => {
this.stopAnimation();
if (previewModal) {
previewModal.style.display = 'none';
}
});
}
if (previewModal) {
previewModal.addEventListener('click', (e) => {
if (e.target === previewModal) {
this.stopAnimation();
previewModal.style.display = 'none';
}
});
}
// 帧率滑块
const fpsSlider = document.getElementById('fpsSlider');
const fpsDisplay = document.getElementById('fpsDisplay');
if (fpsSlider && fpsDisplay) {
fpsSlider.addEventListener('input', (e) => {
const fps = parseInt(e.target.value);
this.currentFps = fps;
fpsDisplay.textContent = `${fps} FPS`;
// 如果动画正在播放,重新启动以应用新帧率
if (this.animationInterval && this.frameUrls.length > 0) {
const previewImage = document.getElementById('previewAnimationImage');
if (previewImage) {
this.stopAnimation();
this.startAnimation(previewImage, this.frameUrls);
}
}
});
}
// 使用事件委托处理动态添加的按钮
const resourcesGrid = document.getElementById('resourcesGrid');
if (resourcesGrid) {
// 购买按钮点击
resourcesGrid.addEventListener('click', (e) => {
console.log('[StoreView] 点击事件触发,目标:', e.target);
if (e.target.closest('.item-buy-button')) {
const button = e.target.closest('.item-buy-button');
const path = button.dataset.resourcePath;
console.log('[StoreView] 点击购买按钮,路径:', path);
// 检查是否已登录
if (!this.isLoggedIn()) {
console.log('[StoreView] 未登录,跳转登录页');
// 显示登录界面
if (window.parent !== window) {
window.parent.postMessage({
type: 'navigation',
page: 'login'
}, '*');
}
return;
}
console.log('[StoreView] 已登录,调用 handleBuy');
this.handleBuy(path);
}
});
}
}
async loadResources() {
this.showLoading(true);
this.hideEmpty();
try {
const params = new URLSearchParams();
if (this.currentCategory) {
params.append('category', this.currentCategory);
}
if (this.searchQuery) {
params.append('search', this.searchQuery);
}
const response = await fetch(`http://localhost:3000/api/store/resources?${params.toString()}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success) {
this.resources = data.resources || [];
// 使用服务器返回的价格,如果没有则默认为0
this.resources.forEach(resource => {
if (resource.points === undefined || resource.points === null) {
resource.points = 0;
}
});
this.filteredResources = this.resources;
this.renderResources();
} else {
throw new Error(data.error || '加载资源失败');
}
} catch (error) {
console.error('[StoreView] 加载资源失败:', error);
this.showGlobalAlert('加载资源失败: ' + error.message);
this.resources = [];
this.filteredResources = [];
this.renderResources();
} finally {
this.showLoading(false);
}
}
filterResources() {
// 重新加载资源(服务器端筛选)
this.loadResources();
}
renderResources() {
const grid = document.getElementById('resourcesGrid');
if (!grid) return;
if (this.filteredResources.length === 0) {
grid.innerHTML = '';
this.showEmpty();
return;
}
this.hideEmpty();
// 计算列数
const columnWidth = 200;
const gap = 16;
const containerWidth = grid.offsetWidth || grid.clientWidth || window.innerWidth;
const columnCount = Math.max(1, Math.floor((containerWidth + gap) / (columnWidth + gap)));
// 创建列容器
grid.innerHTML = '';
const masonryContainer = document.createElement('div');
masonryContainer.className = 'resources-grid-masonry';
const columns = [];
const columnHeights = [];
for (let i = 0; i < columnCount; i++) {
const column = document.createElement('div');
column.className = 'masonry-column';
masonryContainer.appendChild(column);
columns.push(column);
columnHeights.push(0);
}
grid.appendChild(masonryContainer);
// 创建临时容器来测量项目高度
const tempContainer = document.createElement('div');
tempContainer.style.position = 'absolute';
tempContainer.style.visibility = 'hidden';
tempContainer.style.width = `${columnWidth}px`;
tempContainer.style.top = '-9999px';
tempContainer.style.left = '-9999px';
document.body.appendChild(tempContainer);
// 渲染所有项目到临时容器并测量
const itemsData = this.filteredResources.map(resource => {
const itemHtml = this.renderItem(resource);
tempContainer.innerHTML = itemHtml;
const itemElement = tempContainer.querySelector('.store-item');
const height = itemElement ? itemElement.offsetHeight : 0;
return { html: itemHtml, height };
});
// 将项目分配到最短的列
itemsData.forEach(({ html, height }) => {
// 找到最短的列
const shortestColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));
// 添加到最短的列
columns[shortestColumnIndex].insertAdjacentHTML('beforeend', html);
// 更新列高度
columnHeights[shortestColumnIndex] += height + gap;
});
// 清理临时容器
document.body.removeChild(tempContainer);
// 为每个 item 绑定鼠标事件和 FPS 控制
const allItems = masonryContainer.querySelectorAll('.store-item');
allItems.forEach(item => {
const category = item.dataset.category;
const folder = item.dataset.folder;
const previewImage = item.querySelector('.item-preview-image');
const fpsSlider = item.querySelector('.item-fps-slider');
const fpsDisplay = item.querySelector('.item-fps-display');
if (category && folder && previewImage) {
// 存储当前 FPS(每个 item 独立)
let currentFps = 8;
// FPS 滑块事件
if (fpsSlider && fpsDisplay) {
fpsSlider.addEventListener('input', (e) => {
const fps = parseInt(e.target.value);
currentFps = fps;
fpsDisplay.textContent = `${fps} FPS`;
// 如果动画正在播放,重新启动以应用新帧率
if (previewImage._animationInterval) {
this.stopItemAnimation(previewImage);
this.startItemAnimation(previewImage, category, folder, fps);
}
});
// 阻止事件冒泡,避免触发 item 的 mouseleave
fpsSlider.addEventListener('mousedown', (e) => {
e.stopPropagation();
});
}
// 鼠标进入时播放动画
item.addEventListener('mouseenter', () => {
this.startItemAnimation(previewImage, category, folder, currentFps);
});
// 鼠标离开时停止动画
item.addEventListener('mouseleave', () => {
this.stopItemAnimation(previewImage);
});
}
});
// 使用 resize observer 监听窗口大小变化
if (!this.resizeObserver) {
this.resizeObserver = new ResizeObserver(() => {
this.renderResources();
});
this.resizeObserver.observe(grid);
}
// 检查每个资源是否已存在(如果已登录),只更新按钮状态,不重新渲染
if (this.isLoggedIn()) {
this.checkResourcesOwnership(this.filteredResources).then(() => {
// 只更新按钮状态,不重新渲染整个列表
this.updateButtonStates();
});
}
}
// 更新单个资源的按钮状态
updateSingleButtonState(resourcePath) {
const item = document.querySelector(`[data-resource-path="${CSS.escape(resourcePath)}"]`);
if (!item) {
console.warn('[StoreView] 未找到资源项:', resourcePath);
return;
}
const resource = this.resources.find(r => r.path === resourcePath);
if (!resource) {
console.warn('[StoreView] 未找到资源对象:', resourcePath);
return;
}
const button = item.querySelector('.item-buy-button');
const priceEl = item.querySelector('.item-price');
if (button) {
const isFree = resource.points === 0;
const isOwned = resource.isOwned || false;
if (isFree) {
// 免费资源:显示"免费",按钮"添加"
button.textContent = '添加';
button.classList.remove('item-button-added');
if (priceEl) priceEl.textContent = '免费';
} else if (isOwned) {
// 付费且已购买:显示价格,按钮"添加"(绿色)
button.textContent = '添加';
button.classList.add('item-button-added');
if (priceEl) priceEl.textContent = `${resource.points} Ani币`;
} else {
// 付费且未购买:显示价格,按钮"购买"
button.textContent = '购买';
button.classList.remove('item-button-added');
if (priceEl) priceEl.textContent = `${resource.points} Ani币`;
}
console.log('[StoreView] 单个按钮状态已更新:', resourcePath, 'isOwned:', isOwned);
}
}
// 更新按钮状态(不重新渲染整个列表)
updateButtonStates() {
console.log('[StoreView] updateButtonStates 开始,资源列表:', this.resources.map(r => ({path: r.path, isOwned: r.isOwned, points: r.points})));
const allItems = document.querySelectorAll('.store-item');
console.log('[StoreView] 找到', allItems.length, '个 store-item 元素');
allItems.forEach(item => {
const path = item.dataset.resourcePath;
const resource = this.resources.find(r => r.path === path);
if (resource) {
const button = item.querySelector('.item-buy-button');
const priceEl = item.querySelector('.item-price');
if (button) {
const isFree = resource.points === 0;
const isOwned = resource.isOwned || false;
if (isFree) {
// 免费资源:显示"免费",按钮"添加"
button.textContent = '添加';
button.classList.remove('item-button-added');
if (priceEl) priceEl.textContent = '免费';
} else if (isOwned) {
// 付费且已购买:显示价格,按钮"添加"(绿色)
button.textContent = '添加';
button.classList.add('item-button-added');
if (priceEl) priceEl.textContent = `${resource.points} Ani币`;
} else {
// 付费且未购买:显示价格,按钮"购买"
button.textContent = '购买';
button.classList.remove('item-button-added');
if (priceEl) priceEl.textContent = `${resource.points} Ani币`;
}
}
}
});
}
renderItem(resource) {
if (!this.itemTemplate) {
return '模板未加载
';
}
// 使用已保存的点数(在加载资源时已生成)
const points = resource.points !== undefined ? resource.points : 0;
// 根据点数和资源状态设置按钮文字和类
const isFree = points === 0;
const isOwned = resource.isOwned || false;
let buttonText = '添加';
let buttonClass = '';
let priceText = points === 0 ? '免费' : `${points} Ani币`;
if (isFree) {
// 免费资源:显示"免费",按钮"添加"
buttonText = '添加';
buttonClass = '';
} else if (isOwned) {
// 付费且已购买:显示价格,按钮"添加"(绿色)
buttonText = '添加';
buttonClass = 'item-button-added';
} else {
// 付费且未购买:显示价格,按钮"购买"
buttonText = '购买';
buttonClass = '';
}
// 先替换data-points中的{{points}}为原始points值
let html = this.itemTemplate
.replace(/data-points="\{\{points\}\}"/g, `data-points="${points}"`)
.replace(/\{\{name\}\}/g, this.escapeHtml(resource.name))
.replace(/\{\{category\}\}/g, this.escapeHtml(resource.category))
.replace(/\{\{categoryDir\}\}/g, this.escapeHtml(resource.categoryDir))
.replace(/\{\{previewUrl\}\}/g, resource.previewUrl || '')
.replace(/\{\{frameCount\}\}/g, resource.frameCount || 0)
.replace(/\{\{path\}\}/g, this.escapeHtml(resource.path))
.replace(/\{\{buttonText\}\}/g, buttonText)
.replace(/\{\{buttonClass\}\}/g, buttonClass);
// 最后替换价格显示中的{{points}}为priceText
html = html.replace(/\{\{points\}\}/g, priceText);
return html;
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
async playAnimation(category, folder) {
const previewModal = document.getElementById('previewModal');
const previewImage = document.getElementById('previewAnimationImage');
const previewTitle = document.getElementById('previewTitle');
if (!previewModal || !previewImage) return;
// 显示弹窗
previewModal.style.display = 'flex';
if (previewTitle) {
previewTitle.textContent = `动画预览: ${folder}`;
}
// 加载帧列表
try {
const response = await fetch(
`http://localhost:3000/api/store/frames?category=${encodeURIComponent(category)}&folder=${encodeURIComponent(folder)}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success && data.frameUrls && data.frameUrls.length > 0) {
// 保存帧URLs
this.frameUrls = data.frameUrls;
// 开始播放动画
this.startAnimation(previewImage, data.frameUrls);
} else {
throw new Error('没有可用的帧');
}
} catch (error) {
console.error('[StoreView] 播放动画失败:', error);
this.showGlobalAlert('播放动画失败: ' + error.message);
previewModal.style.display = 'none';
}
}
startAnimation(imgElement, frameUrls) {
// 停止之前的动画
this.stopAnimation();
this.currentFrame = 0;
const fps = this.currentFps;
const interval = 1000 / fps;
// 预加载所有帧
const images = [];
let loadedCount = 0;
frameUrls.forEach((url, index) => {
const img = new Image();
img.onload = () => {
loadedCount++;
if (loadedCount === frameUrls.length) {
// 所有帧加载完成,开始播放
this.animationInterval = setInterval(() => {
this.currentFrame = (this.currentFrame + 1) % frameUrls.length;
imgElement.src = frameUrls[this.currentFrame];
}, interval);
}
};
img.onerror = () => {
loadedCount++;
if (loadedCount === frameUrls.length) {
this.animationInterval = setInterval(() => {
this.currentFrame = (this.currentFrame + 1) % frameUrls.length;
imgElement.src = frameUrls[this.currentFrame];
}, interval);
}
};
img.src = url;
images.push(img);
});
// 立即显示第一帧
if (frameUrls.length > 0) {
this.currentFrame = 0;
imgElement.src = frameUrls[0];
}
}
stopAnimation() {
if (this.animationInterval) {
clearInterval(this.animationInterval);
this.animationInterval = null;
}
}
async startItemAnimation(imgElement, category, folder, fps = 8) {
// 如果已经有动画在播放,先停止
this.stopItemAnimation(imgElement);
// 检查是否已缓存帧数据
const cacheKey = `${category}/${folder}`;
let frameUrls = this.itemAnimations.get(cacheKey);
if (!frameUrls) {
// 加载帧列表
try {
const response = await fetch(
`http://localhost:3000/api/store/frames?category=${encodeURIComponent(category)}&folder=${encodeURIComponent(folder)}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success && data.frameUrls && data.frameUrls.length > 0) {
frameUrls = data.frameUrls;
this.itemAnimations.set(cacheKey, frameUrls);
} else {
return; // 没有可用的帧
}
} catch (error) {
console.error('[StoreView] 加载帧失败:', error);
return;
}
}
// 开始播放动画
let currentFrame = 0;
const interval = 1000 / fps;
// 立即显示第一帧
if (frameUrls.length > 0) {
imgElement.src = frameUrls[0];
}
// 存储动画 interval
const animationInterval = setInterval(() => {
currentFrame = (currentFrame + 1) % frameUrls.length;
imgElement.src = frameUrls[currentFrame];
}, interval);
// 将 interval 和 fps 存储到 imgElement 上
imgElement._animationInterval = animationInterval;
imgElement._currentFps = fps;
}
stopItemAnimation(imgElement) {
if (imgElement._animationInterval) {
clearInterval(imgElement._animationInterval);
imgElement._animationInterval = null;
}
}
isLoggedIn() {
// 首先检查本地缓存
if (this.isUserLoggedIn) {
return true;
}
// 如果本地缓存为 false,尝试从导航栏检查(作为备用方案)
try {
const navigationFrame = window.parent.document.getElementById('navigationFrame');
if (navigationFrame && navigationFrame.contentWindow) {
const navDoc = navigationFrame.contentDocument || navigationFrame.contentWindow.document;
const userAvatarContainer = navDoc.getElementById('userAvatarContainer');
// 如果用户头像容器存在且显示,说明已登录
if (userAvatarContainer) {
const computedStyle = navDoc.defaultView.getComputedStyle(userAvatarContainer);
const isLoggedIn = computedStyle.display !== 'none';
// 更新本地缓存
this.isUserLoggedIn = isLoggedIn;
return isLoggedIn;
}
}
} catch (error) {
// 跨域或无法访问时,使用本地缓存
console.warn('[StoreView] 无法检查登录状态,使用本地缓存:', error);
}
return this.isUserLoggedIn;
}
// 检查资源是否已存在
async checkResourcesOwnership(resources) {
if (!this.isLoggedIn()) {
return;
}
const username = this.getCurrentUsername();
if (!username) {
return;
}
// 批量检查资源
const checkPromises = resources.map(async (resource) => {
try {
const response = await fetch(`/api/pay/check-resource?username=${encodeURIComponent(username)}&resourcePath=${encodeURIComponent(resource.path)}`);
const result = await response.json();
if (result.success) {
resource.isOwned = result.exists;
}
} catch (error) {
console.error(`[StoreView] 检查资源 ${resource.path} 失败:`, error);
}
});
await Promise.all(checkPromises);
}
getCurrentUsername() {
try {
const loginDataStr = localStorage.getItem('loginData');
if (!loginDataStr) {
return null;
}
const loginData = JSON.parse(loginDataStr);
const now = Date.now();
// 检查是否过期
if (now >= loginData.expireTime) {
localStorage.removeItem('loginData');
return null;
}
return loginData.user ? loginData.user.username : null;
} catch (error) {
console.error('[StoreView] 获取用户名失败:', error);
return null;
}
}
async handleBuy(path) {
console.log('[StoreView] handleBuy 被调用,path:', path);
// 获取资源信息
const resource = this.resources.find(r => r.path === path);
console.log('[StoreView] 找到资源:', resource);
if (!resource) {
this.showGlobalAlert('资源不存在');
return;
}
// 获取点数(从渲染的 item 中获取,或使用资源的点数)
const itemElement = document.querySelector(`[data-resource-path="${path}"]`);
let points = resource.points;
if (itemElement) {
const pointsEl = itemElement.querySelector('.item-price');
if (pointsEl) {
const pointsText = pointsEl.textContent.replace('Ani币', '').trim();
points = parseInt(pointsText) || points;
}
const dataPoints = itemElement.querySelector('.item-buy-button')?.dataset.points;
if (dataPoints) {
points = parseInt(dataPoints) || points;
}
}
// 检查是否登录
const username = this.getCurrentUsername();
if (!username) {
if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'navigation',
page: 'login'
}, '*');
}
return;
}
// 如果是0点,直接添加
if (points === 0) {
try {
const response = await fetch('/api/pay/purchase', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
resourcePath: resource.path,
categoryDir: resource.categoryDir,
itemName: resource.name,
points: 0
})
});
const result = await response.json();
if (result.success) {
// 标记为已拥有
resource.isOwned = true;
// 立即更新单个按钮状态
this.updateSingleButtonState(resource.path);
// 同时更新所有按钮状态(确保一致性)
this.updateButtonStates();
if (window.parent && window.parent.HintView) {
window.parent.HintView.success('添加成功!文件已添加到网盘', 3000);
}
// 通知父窗口刷新网盘
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'refresh-disk' }, '*');
}
} else {
if (window.parent && window.parent.HintView) {
window.parent.HintView.error(result.message || '添加失败', 3000);
}
}
} catch (error) {
console.error('[StoreView] 添加资源失败:', error);
if (window.parent && window.parent.HintView) {
window.parent.HintView.error('添加失败,请稍后重试', 3000);
}
}
return;
}
// 检查用户点数
console.log('[StoreView] 检查用户点数,用户名:', username, '资源价格:', points);
try {
const pointsResponse = await fetch(`/api/user/points?username=${encodeURIComponent(username)}`);
console.log('[StoreView] 点数请求响应:', pointsResponse.status);
if (!pointsResponse.ok) {
throw new Error(`HTTP error! status: ${pointsResponse.status}`);
}
const pointsResult = await pointsResponse.json();
console.log('[StoreView] 点数结果:', pointsResult);
if (!pointsResult.success) {
throw new Error(pointsResult.message || '获取点数失败');
}
const userPoints = pointsResult.points || 0;
console.log('[StoreView] 用户点数:', userPoints, '需要点数:', points);
if (userPoints >= points) {
// 点数充足,弹出确认对话框
console.log('[StoreView] 点数充足,准备弹出确认对话框');
console.log('[StoreView] window.parent.GlobalConfirm 存在:', !!(window.parent && window.parent.GlobalConfirm));
let confirmed = false;
if (window.parent && window.parent.GlobalConfirm) {
// GlobalConfirm.show 返回 Promise
confirmed = await window.parent.GlobalConfirm.show(
`确定要花费 ${points} Ani币购买 ${resource.name} 吗?`
);
console.log('[StoreView] 用户选择:', confirmed);
} else {
// 降级使用原生 confirm
console.log('[StoreView] GlobalConfirm 不可用,使用原生 confirm');
confirmed = confirm(`确定要花费 ${points} Ani币购买 ${resource.name} 吗?`);
}
if (confirmed) {
// 确认购买
try {
console.log('[StoreView] 发送购买请求...');
const response = await fetch('/api/pay/purchase', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
resourcePath: resource.path,
categoryDir: resource.categoryDir,
itemName: resource.name,
points: points
})
});
const result = await response.json();
console.log('[StoreView] 购买结果:', result);
if (result.success) {
// 标记为已拥有
resource.isOwned = true;
console.log('[StoreView] 购买成功,资源已标记为已拥有:', resource.path, 'isOwned:', resource.isOwned);
// 立即更新单个按钮状态
this.updateSingleButtonState(resource.path);
// 同时更新所有按钮状态(确保一致性)
this.updateButtonStates();
if (window.parent && window.parent.HintView) {
window.parent.HintView.success(`购买成功!已扣除 ${points} Ani币,文件已添加到网盘`, 3000);
}
// 通知父窗口刷新点数和网盘
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'refresh-points' }, '*');
window.parent.postMessage({ type: 'refresh-disk' }, '*');
}
} else {
if (window.parent && window.parent.HintView) {
window.parent.HintView.error(result.message || '购买失败', 3000);
}
}
} catch (error) {
console.error('[StoreView] 购买失败:', error);
if (window.parent && window.parent.HintView) {
window.parent.HintView.error('购买失败,请稍后重试', 3000);
}
}
}
} else {
// 点数不足,弹出充值窗口
if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'open-recharge-view',
needPoints: points,
currentPoints: userPoints
}, '*');
}
}
} catch (error) {
console.error('[StoreView] 检查点数失败:', error);
if (window.parent && window.parent.HintView) {
window.parent.HintView.error('检查点数失败,请稍后重试', 3000);
}
}
}
showLoading(show) {
const loadingState = document.getElementById('loadingState');
if (loadingState) {
loadingState.style.display = show ? 'flex' : 'none';
}
}
showEmpty() {
const emptyState = document.getElementById('emptyState');
if (emptyState) {
emptyState.style.display = 'flex';
}
}
hideEmpty() {
const emptyState = document.getElementById('emptyState');
if (emptyState) {
emptyState.style.display = 'none';
}
}
showGlobalAlert(message) {
if (window.parent && window.parent.postMessage) {
window.parent.postMessage({
type: 'global-alert',
text: message,
duration: 3000
}, '*');
} else {
alert(message);
}
}
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
new StoreView();
});
} else {
new StoreView();
}
})();