product-pricing.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // 商品定价管理模块
  2. class ProductPricingManager {
  3. constructor(options = {}) {
  4. this.apiBaseUrl = options.apiBaseUrl || 'http://localhost:3000';
  5. // 默认商品配置
  6. this.products = [
  7. { id: 'vip-matting', name: 'VIP抠图', desc: '使用VIP服务进行图片抠图', price: 0 },
  8. { id: 'ai-generate', name: 'AI生图', desc: '使用AI生成图片', price: 0 }
  9. ];
  10. this.init();
  11. }
  12. init() {
  13. this.bindEvents();
  14. this.loadSettings();
  15. }
  16. bindEvents() {
  17. const productList = document.getElementById('productList');
  18. if (productList) {
  19. productList.addEventListener('input', (e) => {
  20. if (e.target.classList.contains('price-input')) {
  21. const productId = e.target.dataset.productId;
  22. const confirmBtn = e.target.closest('.product-item').querySelector('.btn-confirm');
  23. if (confirmBtn) {
  24. confirmBtn.classList.add('show');
  25. }
  26. }
  27. });
  28. productList.addEventListener('click', (e) => {
  29. if (e.target.closest('.btn-confirm')) {
  30. const button = e.target.closest('.btn-confirm');
  31. const productId = button.dataset.productId;
  32. this.saveProduct(productId);
  33. }
  34. });
  35. }
  36. }
  37. async loadSettings() {
  38. try {
  39. const response = await fetch(`${this.apiBaseUrl}/api/admin/product-pricing/settings`);
  40. if (response.ok) {
  41. const result = await response.json();
  42. if (result.success && result.products) {
  43. // 更新产品价格,保持默认产品结构
  44. result.products.forEach(serverProduct => {
  45. const localProduct = this.products.find(p => p.id === serverProduct.id);
  46. if (localProduct) {
  47. localProduct.price = serverProduct.price || 0;
  48. }
  49. });
  50. }
  51. }
  52. } catch (error) {
  53. console.log('[ProductPricingManager] 使用默认设置');
  54. }
  55. this.render();
  56. }
  57. render() {
  58. const list = document.getElementById('productList');
  59. if (!list) return;
  60. list.innerHTML = this.products.map(product => `
  61. <div class="product-item" data-product-id="${product.id}">
  62. <div class="product-info">
  63. <div class="product-name">${product.name}</div>
  64. <div class="product-desc">${product.desc}</div>
  65. </div>
  66. <div class="product-price">
  67. <span class="price-label">价格</span>
  68. <div class="price-input-wrapper">
  69. <input type="text" class="price-input" value="${product.price}"
  70. data-product-id="${product.id}"
  71. data-original="${product.price}">
  72. <span class="price-unit">Ani币</span>
  73. </div>
  74. <button class="btn-confirm" data-product-id="${product.id}" title="确认修改">✓</button>
  75. </div>
  76. </div>
  77. `).join('');
  78. }
  79. async saveProduct(productId) {
  80. const product = this.products.find(p => p.id === productId);
  81. if (!product) return;
  82. const input = document.querySelector(`.price-input[data-product-id="${productId}"]`);
  83. if (!input) return;
  84. const newPrice = parseFloat(input.value);
  85. if (isNaN(newPrice) || newPrice < 0) {
  86. this.showMessage('价格必须是大于等于0的数字', 'error');
  87. return;
  88. }
  89. const originalPrice = parseFloat(input.dataset.original);
  90. if (newPrice === originalPrice) {
  91. const confirmBtn = input.closest('.product-item').querySelector('.btn-confirm');
  92. if (confirmBtn) {
  93. confirmBtn.classList.remove('show');
  94. }
  95. return;
  96. }
  97. try {
  98. const response = await fetch(`${this.apiBaseUrl}/api/admin/product-pricing/settings`, {
  99. method: 'POST',
  100. headers: {
  101. 'Content-Type': 'application/json'
  102. },
  103. body: JSON.stringify({
  104. productId: productId,
  105. price: newPrice
  106. })
  107. });
  108. const result = await response.json();
  109. if (result.success) {
  110. product.price = newPrice;
  111. input.dataset.original = newPrice.toString();
  112. const confirmBtn = input.closest('.product-item').querySelector('.btn-confirm');
  113. if (confirmBtn) {
  114. confirmBtn.classList.remove('show');
  115. }
  116. this.showMessage('价格更新成功', 'success');
  117. } else {
  118. this.showMessage(result.message || '保存失败', 'error');
  119. }
  120. } catch (error) {
  121. console.error('[ProductPricingManager] 保存失败:', error);
  122. this.showMessage('保存失败,请稍后重试', 'error');
  123. }
  124. }
  125. showMessage(message, type) {
  126. const msgBox = document.getElementById('msgBox');
  127. if (msgBox) {
  128. msgBox.textContent = message;
  129. msgBox.className = `msg ${type}`;
  130. setTimeout(() => {
  131. msgBox.className = 'msg';
  132. }, 3000);
  133. }
  134. }
  135. }