1081 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			1081 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			HTML
		
	
	
	
| <!DOCTYPE html>
 | |
| <html lang="zh-CN">
 | |
| <head>
 | |
|     <meta charset="UTF-8">
 | |
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | |
|     <title>积分商城 - 商品管理</title>
 | |
|     <style>
 | |
|         * {
 | |
|             margin: 0;
 | |
|             padding: 0;
 | |
|             box-sizing: border-box;
 | |
|         }
 | |
| 
 | |
|         body {
 | |
|             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 | |
|             background-color: #f5f5f5;
 | |
|             color: #333;
 | |
|             line-height: 1.6;
 | |
|         }
 | |
| 
 | |
|         .container {
 | |
|             max-width: 100%;
 | |
|             margin: 0 auto;
 | |
|             padding: 16px;
 | |
|         }
 | |
| 
 | |
|         .header {
 | |
|             background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | |
|             color: white;
 | |
|             padding: 20px 16px;
 | |
|             margin: -16px -16px 20px -16px;
 | |
|         }
 | |
| 
 | |
|         .header h1 {
 | |
|             font-size: 18px;
 | |
|             font-weight: 600;
 | |
|             text-align: center;
 | |
|         }
 | |
| 
 | |
|         .search-section {
 | |
|             background: white;
 | |
|             border-radius: 12px;
 | |
|             padding: 20px;
 | |
|             margin-bottom: 20px;
 | |
|             box-shadow: 0 2px 8px rgba(0,0,0,0.1);
 | |
|         }
 | |
| 
 | |
|         .search-grid {
 | |
|             display: grid;
 | |
|             gap: 15px;
 | |
|             margin-bottom: 20px;
 | |
|         }
 | |
| 
 | |
|         .input-group {
 | |
|             display: flex;
 | |
|             flex-direction: column;
 | |
|             gap: 8px;
 | |
|         }
 | |
| 
 | |
|         .input-group label {
 | |
|             font-size: 14px;
 | |
|             font-weight: 500;
 | |
|             color: #666;
 | |
|         }
 | |
| 
 | |
|         .input-group input {
 | |
|             padding: 12px 16px;
 | |
|             border: 1px solid #ddd;
 | |
|             border-radius: 8px;
 | |
|             font-size: 16px;
 | |
|             transition: all 0.3s ease;
 | |
|         }
 | |
| 
 | |
|         .input-group input:focus {
 | |
|             outline: none;
 | |
|             border-color: #667eea;
 | |
|             box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
 | |
|         }
 | |
| 
 | |
|         .search-btn {
 | |
|             background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | |
|             color: white;
 | |
|             border: none;
 | |
|             padding: 12px 24px;
 | |
|             border-radius: 8px;
 | |
|             font-size: 16px;
 | |
|             font-weight: 500;
 | |
|             cursor: pointer;
 | |
|             transition: all 0.3s ease;
 | |
|             width: 100%;
 | |
|         }
 | |
| 
 | |
|         .search-btn:hover {
 | |
|             transform: translateY(-2px);
 | |
|             box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
 | |
|         }
 | |
| 
 | |
|         .search-btn:active {
 | |
|             transform: translateY(0);
 | |
|         }
 | |
| 
 | |
|         .products-section {
 | |
|             background: white;
 | |
|             border-radius: 12px;
 | |
|             overflow: hidden;
 | |
|             box-shadow: 0 2px 8px rgba(0,0,0,0.1);
 | |
|             margin-bottom: 20px;
 | |
|         }
 | |
| 
 | |
|         .products-header {
 | |
|             background: #f8f9fa;
 | |
|             padding: 16px;
 | |
|             border-bottom: 1px solid #eee;
 | |
|         }
 | |
| 
 | |
|         .products-header h2 {
 | |
|             font-size: 16px;
 | |
|             font-weight: 600;
 | |
|             color: #333;
 | |
|         }
 | |
| 
 | |
|         .product-list {
 | |
|             padding: 0;
 | |
|         }
 | |
| 
 | |
|         .product-card {
 | |
|             padding: 16px;
 | |
|             border-bottom: 1px solid #f0f0f0;
 | |
|             transition: background-color 0.2s ease;
 | |
|         }
 | |
| 
 | |
|         .product-card:last-child {
 | |
|             border-bottom: none;
 | |
|         }
 | |
| 
 | |
|         .product-card:hover {
 | |
|             background-color: #f8f9fa;
 | |
|         }
 | |
| 
 | |
|         .product-info {
 | |
|             margin-bottom: 12px;
 | |
|         }
 | |
| 
 | |
|         .product-name {
 | |
|             font-size: 16px;
 | |
|             font-weight: 600;
 | |
|             color: #333;
 | |
|             margin-bottom: 8px;
 | |
|         }
 | |
| 
 | |
|         .product-details {
 | |
|             display: grid;
 | |
|             grid-template-columns: 1fr 1fr;
 | |
|             gap: 8px;
 | |
|             margin-bottom: 12px;
 | |
|         }
 | |
| 
 | |
|         .product-detail {
 | |
|             display: flex;
 | |
|             align-items: center;
 | |
|             gap: 4px;
 | |
|         }
 | |
| 
 | |
|         .detail-label {
 | |
|             font-size: 12px;
 | |
|             color: #666;
 | |
|             min-width: 40px;
 | |
|         }
 | |
| 
 | |
|         .detail-value {
 | |
|             font-size: 14px;
 | |
|             color: #333;
 | |
|             font-weight: 500;
 | |
|         }
 | |
| 
 | |
|         .product-price {
 | |
|             font-size: 18px;
 | |
|             font-weight: 700;
 | |
|             color: #e74c3c;
 | |
|             margin-bottom: 12px;
 | |
|         }
 | |
| 
 | |
|         .add-btn {
 | |
|             background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
 | |
|             color: white;
 | |
|             border: none;
 | |
|             padding: 10px 16px;
 | |
|             border-radius: 6px;
 | |
|             font-size: 14px;
 | |
|             font-weight: 500;
 | |
|             cursor: pointer;
 | |
|             transition: all 0.3s ease;
 | |
|             width: 100%;
 | |
|         }
 | |
| 
 | |
|         .add-btn:hover {
 | |
|             transform: translateY(-1px);
 | |
|             box-shadow: 0 3px 8px rgba(46, 204, 113, 0.3);
 | |
|         }
 | |
| 
 | |
|         .add-btn:active {
 | |
|             transform: translateY(0);
 | |
|         }
 | |
| 
 | |
|         .add-btn:disabled {
 | |
|             background: #95a5a6;
 | |
|             cursor: not-allowed;
 | |
|             transform: none;
 | |
|         }
 | |
| 
 | |
|         .add-btn.discount {
 | |
|             background: linear-gradient(135deg, #e67e22 0%, #f39c12 100%);
 | |
|         }
 | |
| 
 | |
|         .add-btn.discount:hover {
 | |
|             transform: translateY(-1px);
 | |
|             box-shadow: 0 3px 8px rgba(230, 126, 34, 0.3);
 | |
|         }
 | |
| 
 | |
|         .add-btn.discount:disabled {
 | |
|             background: #95a5a6;
 | |
|             cursor: not-allowed;
 | |
|             transform: none;
 | |
|         }
 | |
| 
 | |
|         .add-btn.discount:disabled:hover {
 | |
|             transform: none;
 | |
|             box-shadow: none;
 | |
|         }
 | |
| 
 | |
|         .pagination {
 | |
|             display: flex;
 | |
|             justify-content: center;
 | |
|             align-items: center;
 | |
|             gap: 8px;
 | |
|             background: white;
 | |
|             padding: 20px;
 | |
|             border-radius: 12px;
 | |
|             box-shadow: 0 2px 8px rgba(0,0,0,0.1);
 | |
|         }
 | |
| 
 | |
|         .pagination button {
 | |
|             padding: 8px 12px;
 | |
|             border: 1px solid #ddd;
 | |
|             background: white;
 | |
|             border-radius: 6px;
 | |
|             cursor: pointer;
 | |
|             font-size: 14px;
 | |
|             color: #666;
 | |
|             transition: all 0.2s ease;
 | |
|             min-width: 40px;
 | |
|         }
 | |
| 
 | |
|         .pagination button:hover {
 | |
|             background: #f8f9fa;
 | |
|             border-color: #667eea;
 | |
|         }
 | |
| 
 | |
|         .pagination button.active {
 | |
|             background: #667eea;
 | |
|             color: white;
 | |
|             border-color: #667eea;
 | |
|         }
 | |
| 
 | |
|         .pagination button:disabled {
 | |
|             opacity: 0.5;
 | |
|             cursor: not-allowed;
 | |
|         }
 | |
| 
 | |
|         .empty-state {
 | |
|             text-align: center;
 | |
|             padding: 40px 20px;
 | |
|             color: #999;
 | |
|         }
 | |
| 
 | |
|         .empty-state i {
 | |
|             font-size: 48px;
 | |
|             margin-bottom: 16px;
 | |
|             display: block;
 | |
|         }
 | |
| 
 | |
|         @media (min-width: 768px) {
 | |
|             .container {
 | |
|                 max-width: 768px;
 | |
|                 padding: 20px;
 | |
|             }
 | |
| 
 | |
|             .search-grid {
 | |
|                 grid-template-columns: 1fr 1fr;
 | |
|             }
 | |
| 
 | |
|             .product-details {
 | |
|                 grid-template-columns: 1fr 1fr 1fr 1fr;
 | |
|             }
 | |
| 
 | |
|             .add-btn {
 | |
|                 width: auto;
 | |
|                 min-width: 120px;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         .loading {
 | |
|             text-align: center;
 | |
|             padding: 20px;
 | |
|             color: #666;
 | |
|         }
 | |
| 
 | |
|         .toast {
 | |
|             position: fixed;
 | |
|             top: 20px;
 | |
|             left: 50%;
 | |
|             transform: translateX(-50%);
 | |
|             background: #333;
 | |
|             color: white;
 | |
|             padding: 12px 20px;
 | |
|             border-radius: 8px;
 | |
|             font-size: 14px;
 | |
|             z-index: 1000;
 | |
|             opacity: 0;
 | |
|             transition: opacity 0.3s ease;
 | |
|         }
 | |
| 
 | |
|         .toast.show {
 | |
|             opacity: 1;
 | |
|         }
 | |
| 
 | |
|         .toast.success {
 | |
|             background: #27ae60;
 | |
|         }
 | |
| 
 | |
|         .toast.error {
 | |
|             background: #e74c3c;
 | |
|         }
 | |
| 
 | |
|         .points-status {
 | |
|             display: inline-block;
 | |
|             padding: 4px 8px;
 | |
|             border-radius: 12px;
 | |
|             font-size: 12px;
 | |
|             font-weight: 500;
 | |
|             margin-left: 8px;
 | |
|         }
 | |
| 
 | |
|         .points-status.active {
 | |
|             background: #e8f5e8;
 | |
|             color: #27ae60;
 | |
|             border: 1px solid #27ae60;
 | |
|         }
 | |
| 
 | |
|         .points-status.discount {
 | |
|             background: #fff3cd;
 | |
|             color: #e67e22;
 | |
|             border: 1px solid #e67e22;
 | |
|         }
 | |
| 
 | |
|         .points-status.inactive {
 | |
|             background: #f8f8f8;
 | |
|             color: #666;
 | |
|             border: 1px solid #ddd;
 | |
|         }
 | |
| 
 | |
|         .modal-overlay {
 | |
|             position: fixed;
 | |
|             top: 0;
 | |
|             left: 0;
 | |
|             right: 0;
 | |
|             bottom: 0;
 | |
|             background: rgba(0, 0, 0, 0.5);
 | |
|             z-index: 1000;
 | |
|             display: none;
 | |
|             align-items: center;
 | |
|             justify-content: center;
 | |
|             backdrop-filter: blur(4px);
 | |
|         }
 | |
| 
 | |
|         .modal-overlay.show {
 | |
|             display: flex;
 | |
|         }
 | |
| 
 | |
|         .modal-content {
 | |
|             background: white;
 | |
|             border-radius: 16px;
 | |
|             max-width: 400px;
 | |
|             width: 90%;
 | |
|             max-height: 80vh;
 | |
|             overflow-y: auto;
 | |
|             box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
 | |
|             animation: modalSlideIn 0.3s ease-out;
 | |
|         }
 | |
| 
 | |
|         @keyframes modalSlideIn {
 | |
|             from {
 | |
|                 transform: scale(0.8) translateY(-20px);
 | |
|                 opacity: 0;
 | |
|             }
 | |
|             to {
 | |
|                 transform: scale(1) translateY(0);
 | |
|                 opacity: 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         .modal-header {
 | |
|             padding: 20px 24px;
 | |
|             border-bottom: 1px solid #eee;
 | |
|             position: relative;
 | |
|         }
 | |
| 
 | |
|         .modal-title {
 | |
|             font-size: 18px;
 | |
|             font-weight: 600;
 | |
|             color: #333;
 | |
|             margin: 0;
 | |
|         }
 | |
| 
 | |
|         .modal-close {
 | |
|             position: absolute;
 | |
|             top: 20px;
 | |
|             right: 24px;
 | |
|             background: none;
 | |
|             border: none;
 | |
|             font-size: 24px;
 | |
|             color: #999;
 | |
|             cursor: pointer;
 | |
|             line-height: 1;
 | |
|             transition: color 0.2s ease;
 | |
|         }
 | |
| 
 | |
|         .modal-close:hover {
 | |
|             color: #666;
 | |
|         }
 | |
| 
 | |
|         .modal-body {
 | |
|             padding: 24px;
 | |
|         }
 | |
| 
 | |
|         .exchange-info {
 | |
|             margin-bottom: 24px;
 | |
|         }
 | |
| 
 | |
|         .info-item {
 | |
|             display: flex;
 | |
|             justify-content: space-between;
 | |
|             align-items: center;
 | |
|             padding: 12px 0;
 | |
|             border-bottom: 1px solid #f5f5f5;
 | |
|         }
 | |
| 
 | |
|         .info-item:last-child {
 | |
|             border-bottom: none;
 | |
|         }
 | |
| 
 | |
|         .info-label {
 | |
|             font-size: 14px;
 | |
|             color: #666;
 | |
|             font-weight: 500;
 | |
|         }
 | |
| 
 | |
|         .info-value {
 | |
|             font-size: 16px;
 | |
|             color: #333;
 | |
|             font-weight: 600;
 | |
|         }
 | |
| 
 | |
|         .info-value.highlight {
 | |
|             color: #e74c3c;
 | |
|         }
 | |
| 
 | |
|         .modal-footer {
 | |
|             padding: 16px 24px;
 | |
|             border-top: 1px solid #eee;
 | |
|             display: flex;
 | |
|             gap: 12px;
 | |
|         }
 | |
| 
 | |
|         .modal-btn {
 | |
|             flex: 1;
 | |
|             padding: 12px 16px;
 | |
|             border: none;
 | |
|             border-radius: 8px;
 | |
|             font-size: 16px;
 | |
|             font-weight: 500;
 | |
|             cursor: pointer;
 | |
|             transition: all 0.3s ease;
 | |
|         }
 | |
| 
 | |
|         .modal-btn.cancel {
 | |
|             background: #f8f9fa;
 | |
|             color: #666;
 | |
|             border: 1px solid #ddd;
 | |
|         }
 | |
| 
 | |
|         .modal-btn.cancel:hover {
 | |
|             background: #e9ecef;
 | |
|             border-color: #adb5bd;
 | |
|         }
 | |
| 
 | |
|         .modal-btn.confirm {
 | |
|             background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
 | |
|             color: white;
 | |
|         }
 | |
| 
 | |
|         .modal-btn.confirm:hover {
 | |
|             transform: translateY(-1px);
 | |
|             box-shadow: 0 4px 12px rgba(39, 174, 96, 0.3);
 | |
|         }
 | |
| 
 | |
|         .info-input {
 | |
|             flex: 1;
 | |
|             padding: 8px 12px;
 | |
|             border: 1px solid #ddd;
 | |
|             border-radius: 6px;
 | |
|             font-size: 14px;
 | |
|             font-weight: 600;
 | |
|             color: #333;
 | |
|             transition: all 0.3s ease;
 | |
|             min-width: 120px;
 | |
|             text-align: right;
 | |
|         }
 | |
| 
 | |
|         .info-input:focus {
 | |
|             outline: none;
 | |
|             border-color: #667eea;
 | |
|             box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
 | |
|         }
 | |
| 
 | |
|         .info-input.highlight {
 | |
|             color: #e74c3c;
 | |
|             border-color: #e74c3c;
 | |
|         }
 | |
| 
 | |
|         .info-input.highlight:focus {
 | |
|             border-color: #e74c3c;
 | |
|             box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.1);
 | |
|         }
 | |
| 
 | |
|         .info-input::placeholder {
 | |
|             color: #999;
 | |
|             font-weight: normal;
 | |
|         }
 | |
| 
 | |
|         .info-input.error {
 | |
|             border-color: #e74c3c;
 | |
|             background-color: #ffeaea;
 | |
|         }
 | |
| 
 | |
|         .input-error-message {
 | |
|             color: #e74c3c;
 | |
|             font-size: 12px;
 | |
|             margin-top: 4px;
 | |
|             display: none;
 | |
|         }
 | |
|     </style>
 | |
| </head>
 | |
| <body>
 | |
|     <div class="container">
 | |
|         <div class="header">
 | |
|             <h1>积分商城 - 商品管理</h1>
 | |
|         </div>
 | |
| 
 | |
|         <div class="search-section">
 | |
|             <div class="search-grid">
 | |
|                 <div class="input-group">
 | |
|                     <label for="shopName">店铺</label>
 | |
|                     <input type="text" id="shopName" placeholder="请输入店铺名称">
 | |
|                 </div>
 | |
|                 <div class="input-group">
 | |
|                     <label for="productName">商品名称</label>
 | |
|                     <input type="text" id="productName" placeholder="请输入商品名称">
 | |
|                 </div>
 | |
|                 <div class="input-group">
 | |
|                     <label for="productId">商品ID</label>
 | |
|                     <input type="text" id="productId" placeholder="请输入商品ID">
 | |
|                 </div>
 | |
|             </div>
 | |
|             <button class="search-btn" onclick="searchProducts()">查询</button>
 | |
|         </div>
 | |
| 
 | |
|         <div class="products-section">
 | |
|             <div class="products-header">
 | |
|                 <h2>商品列表</h2>
 | |
|             </div>
 | |
|             <div class="product-list" id="productList">
 | |
|                 <div class="product-card">
 | |
|                     <div class="product-info">
 | |
|                         <div class="product-name">猪肉</div>
 | |
|                         <div class="product-details">
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">ID:</span>
 | |
|                                 <span class="detail-value">1212</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">店铺:</span>
 | |
|                                 <span class="detail-value">猪肉铺</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">规格:</span>
 | |
|                                 <span class="detail-value">大份</span>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                         <div class="product-price">¥2</div>
 | |
|                     </div>
 | |
|                     <button class="add-btn" onclick="addToPointsMall('1212', '猪肉', 'exchange')" style="margin-bottom: 8px;">添加为积分换购商品</button>
 | |
|                     <button class="add-btn discount" onclick="addToPointsMall('1212', '猪肉', 'discount')">添加为积分优惠购商品</button>
 | |
|                 </div>
 | |
| 
 | |
|                 <div class="product-card">
 | |
|                     <div class="product-info">
 | |
|                         <div class="product-name">牛肉</div>
 | |
|                         <div class="product-details">
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">ID:</span>
 | |
|                                 <span class="detail-value">1213</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">店铺:</span>
 | |
|                                 <span class="detail-value">牛肉铺</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">规格:</span>
 | |
|                                 <span class="detail-value">小份</span>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                         <div class="product-price">¥5</div>
 | |
|                     </div>
 | |
|                     <button class="add-btn" onclick="addToPointsMall('1213', '牛肉', 'exchange')" style="margin-bottom: 8px;">添加为积分换购商品</button>
 | |
|                     <button class="add-btn discount" onclick="addToPointsMall('1213', '牛肉', 'discount')">添加为积分优惠购商品</button>
 | |
|                 </div>
 | |
| 
 | |
|                 <div class="product-card">
 | |
|                     <div class="product-info">
 | |
|                         <div class="product-name">鸡肉</div>
 | |
|                         <div class="product-details">
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">ID:</span>
 | |
|                                 <span class="detail-value">1214</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">店铺:</span>
 | |
|                                 <span class="detail-value">鸡肉铺</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">规格:</span>
 | |
|                                 <span class="detail-value">无</span>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                         <div class="product-price">¥3</div>
 | |
|                     </div>
 | |
|                     <button class="add-btn" onclick="addToPointsMall('1214', '鸡肉', 'exchange')" style="margin-bottom: 8px;">添加为积分换购商品</button>
 | |
|                     <button class="add-btn discount" onclick="addToPointsMall('1214', '鸡肉', 'discount')">添加为积分优惠购商品</button>
 | |
|                 </div>
 | |
|             </div>
 | |
|         </div>
 | |
| 
 | |
|         <div class="pagination">
 | |
|             <button onclick="changePage('prev')" id="prevBtn">Previous</button>
 | |
|             <button class="active" onclick="changePage(1)">1</button>
 | |
|             <button onclick="changePage(2)">2</button>
 | |
|             <button onclick="changePage(3)">3</button>
 | |
|             <button onclick="changePage('next')" id="nextBtn">Next</button>
 | |
|         </div>
 | |
|     </div>
 | |
| 
 | |
|     <div class="toast" id="toast"></div>
 | |
| 
 | |
|     <!-- 积分兑换确认弹窗 -->
 | |
|     <div class="modal-overlay" id="exchangeModal">
 | |
|         <div class="modal-content">
 | |
|             <div class="modal-header">
 | |
|                 <h3 class="modal-title" id="modalTitle">添加积分商品确认</h3>
 | |
|                 <button class="modal-close" onclick="closeExchangeModal()">×</button>
 | |
|             </div>
 | |
|             <div class="modal-body">
 | |
|                 <div class="exchange-info">
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">商品ID</span>
 | |
|                         <span class="info-value" id="modalProductId">-</span>
 | |
|                     </div>
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">商品名称</span>
 | |
|                         <span class="info-value" id="modalProductName">-</span>
 | |
|                     </div>
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">归属摊位</span>
 | |
|                         <span class="info-value" id="modalProductShop">-</span>
 | |
|                     </div>
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">规格名称</span>
 | |
|                         <span class="info-value" id="modalProductSpec">-</span>
 | |
|                     </div>
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">SKU-ID</span>
 | |
|                         <span class="info-value" id="modalProductSkuId">-</span>
 | |
|                     </div>
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">商品原价</span>
 | |
|                         <span class="info-value" id="modalProductPrice">-</span>
 | |
|                     </div>
 | |
|                     <div class="info-item" id="pointsRequiredRow">
 | |
|                         <span class="info-label">兑换所需积分</span>
 | |
|                         <input type="number" class="info-input highlight" id="modalPointsRequired" min="1" placeholder="请输入积分">
 | |
|                     </div>
 | |
|                     <div class="info-item" id="discountPointsRow" style="display: none;">
 | |
|                         <span class="info-label">优惠购所需积分</span>
 | |
|                         <input type="number" class="info-input highlight" id="modalDiscountPoints" min="1" placeholder="请输入优惠购所需积分">
 | |
|                     </div>
 | |
|                     <div class="info-item" id="discountPriceRow" style="display: none;">
 | |
|                         <span class="info-label">优惠购商品价格</span>
 | |
|                         <input type="number" class="info-input" id="modalDiscountPrice" min="0" step="0.01" placeholder="请输入优惠购价格">
 | |
|                     </div>
 | |
|                     <div class="info-item">
 | |
|                         <span class="info-label">最大可兑换数量</span>
 | |
|                         <input type="number" class="info-input" id="modalMaxExchange" min="1" placeholder="请输入数量">
 | |
|                     </div>
 | |
|                     <div class="info-item" id="exchangeNoticeRow" style="display: none;">
 | |
|                         <span class="info-label" style="color: #999; font-size: 12px; font-weight: normal;">说明</span>
 | |
|                         <span class="info-value" style="color: #999; font-size: 12px; font-weight: normal;">固定支付0.01,商业支付要求</span>
 | |
|                     </div>
 | |
|                 </div>
 | |
|             </div>
 | |
|             <div class="modal-footer">
 | |
|                 <button class="modal-btn cancel" onclick="closeExchangeModal()">取消</button>
 | |
|                 <button class="modal-btn confirm" onclick="confirmAddToPointsMall()" id="confirmBtn">确认添加</button>
 | |
|             </div>
 | |
|         </div>
 | |
|     </div>
 | |
| 
 | |
|     <script>
 | |
|         let currentPage = 1;
 | |
|         const totalPages = 3;
 | |
| 
 | |
|         // 模拟商品数据
 | |
|         const allProducts = [
 | |
|             {id: '1212', name: '猪肉', shop: '猪肉铺', spec: '大份', price: 2, stock: 150, skuId: 'SKU1212001', exchangeType: 'none', pointsRequired: 200, maxExchangeCount: 50, discountPoints: 50},
 | |
|             {id: '1213', name: '牛肉', shop: '牛肉铺', spec: '小份', price: 5, stock: 80, skuId: 'SKU1213002', exchangeType: 'exchange', pointsRequired: 500, maxExchangeCount: 30, discountPoints: 100},
 | |
|             {id: '1214', name: '鸡肉', shop: '鸡肉铺', spec: '无', price: 3, stock: 200, skuId: 'SKU1214003', exchangeType: 'none', pointsRequired: 300, maxExchangeCount: 80, discountPoints: 80},
 | |
|             {id: '1215', name: '鱼肉', shop: '鱼肉铺', spec: '中份', price: 8, stock: 60, skuId: 'SKU1215004', exchangeType: 'discount', pointsRequired: 800, maxExchangeCount: 25, discountPoints: 200},
 | |
|             {id: '1216', name: '虾', shop: '海鲜铺', spec: '特大', price: 12, stock: 45, skuId: 'SKU1216005', exchangeType: 'none', pointsRequired: 1200, maxExchangeCount: 20, discountPoints: 300},
 | |
|             {id: '1217', name: '蟹', shop: '海鲜铺', spec: '精选', price: 25, stock: 30, skuId: 'SKU1217006', exchangeType: 'none', pointsRequired: 2500, maxExchangeCount: 15, discountPoints: 500},
 | |
|         ];
 | |
| 
 | |
|         let filteredProducts = [...allProducts];
 | |
|         let currentEditingProduct = null;
 | |
|         let currentExchangeType = null;
 | |
| 
 | |
|         function showToast(message, type = 'success') {
 | |
|             const toast = document.getElementById('toast');
 | |
|             toast.textContent = message;
 | |
|             toast.className = `toast ${type} show`;
 | |
|             
 | |
|             setTimeout(() => {
 | |
|                 toast.classList.remove('show');
 | |
|             }, 3000);
 | |
|         }
 | |
| 
 | |
|         function searchProducts() {
 | |
|             const shopName = document.getElementById('shopName').value.trim().toLowerCase();
 | |
|             const productName = document.getElementById('productName').value.trim().toLowerCase();
 | |
|             const productId = document.getElementById('productId').value.trim();
 | |
| 
 | |
|             filteredProducts = allProducts.filter(product => {
 | |
|                 const matchShop = !shopName || product.shop.toLowerCase().includes(shopName);
 | |
|                 const matchName = !productName || product.name.toLowerCase().includes(productName);
 | |
|                 const matchId = !productId || product.id.includes(productId);
 | |
|                 
 | |
|                 return matchShop && matchName && matchId;
 | |
|             });
 | |
| 
 | |
|             currentPage = 1;
 | |
|             renderProducts();
 | |
|             updatePagination();
 | |
| 
 | |
|             if (filteredProducts.length === 0) {
 | |
|                 showToast('未找到匹配的商品', 'error');
 | |
|             } else {
 | |
|                 showToast(`找到 ${filteredProducts.length} 个商品`);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         function renderProducts() {
 | |
|             const productList = document.getElementById('productList');
 | |
|             const startIndex = (currentPage - 1) * 3;
 | |
|             const endIndex = startIndex + 3;
 | |
|             const pageProducts = filteredProducts.slice(startIndex, endIndex);
 | |
| 
 | |
|             if (pageProducts.length === 0) {
 | |
|                 productList.innerHTML = `
 | |
|                     <div class="empty-state">
 | |
|                         <i>📦</i>
 | |
|                         <div>暂无商品数据</div>
 | |
|                     </div>
 | |
|                 `;
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             productList.innerHTML = pageProducts.map(product => {
 | |
|                 return `
 | |
|                 <div class="product-card">
 | |
|                     <div class="product-info">
 | |
|                         <div class="product-name">${product.name}</div>
 | |
|                         <div class="product-details">
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">ID:</span>
 | |
|                                 <span class="detail-value">${product.id}</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">店铺:</span>
 | |
|                                 <span class="detail-value">${product.shop}</span>
 | |
|                             </div>
 | |
|                             <div class="product-detail">
 | |
|                                 <span class="detail-label">规格:</span>
 | |
|                                 <span class="detail-value">${product.spec}</span>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                         <div class="product-price">¥${product.price}</div>
 | |
|                     </div>
 | |
|                     <button class="add-btn" onclick="addToPointsMall('${product.id}', '${product.name}', 'exchange')" style="margin-bottom: 8px;">添加为积分换购商品</button>
 | |
|                     <button class="add-btn discount" onclick="addToPointsMall('${product.id}', '${product.name}', 'discount')">添加为积分优惠购商品</button>
 | |
|                 </div>`;
 | |
|             }).join('');
 | |
|         }
 | |
| 
 | |
|         function addToPointsMall(productId, productName, exchangeType) {
 | |
|             const product = allProducts.find(p => p.id === productId);
 | |
|             if (!product) return;
 | |
| 
 | |
|             currentEditingProduct = product;
 | |
|             currentExchangeType = exchangeType;
 | |
|             showExchangeModal(product, exchangeType);
 | |
|         }
 | |
| 
 | |
|         function showExchangeModal(product, exchangeType) {
 | |
|             // 更新弹窗标题
 | |
|             const modalTitle = document.getElementById('modalTitle');
 | |
|             const pointsRequiredRow = document.getElementById('pointsRequiredRow');
 | |
|             const discountPointsRow = document.getElementById('discountPointsRow');
 | |
|             const discountPriceRow = document.getElementById('discountPriceRow');
 | |
|             const exchangeNoticeRow = document.getElementById('exchangeNoticeRow');
 | |
|             
 | |
|             if (exchangeType === 'exchange') {
 | |
|                 modalTitle.textContent = '添加积分换购商品确认';
 | |
|                 pointsRequiredRow.style.display = 'flex';
 | |
|                 discountPointsRow.style.display = 'none';
 | |
|                 discountPriceRow.style.display = 'none';
 | |
|                 exchangeNoticeRow.style.display = 'flex';
 | |
|             } else if (exchangeType === 'discount') {
 | |
|                 modalTitle.textContent = '添加积分优惠购商品确认';
 | |
|                 pointsRequiredRow.style.display = 'none';
 | |
|                 discountPointsRow.style.display = 'flex';
 | |
|                 discountPriceRow.style.display = 'flex';
 | |
|                 exchangeNoticeRow.style.display = 'none';
 | |
|             }
 | |
|             
 | |
|             document.getElementById('modalProductId').textContent = product.id;
 | |
|             document.getElementById('modalProductName').textContent = product.name;
 | |
|             document.getElementById('modalProductShop').textContent = product.shop;
 | |
|             document.getElementById('modalProductSpec').textContent = product.spec;
 | |
|             document.getElementById('modalProductSkuId').textContent = product.skuId || product.id;
 | |
|             document.getElementById('modalProductPrice').textContent = `¥${product.price}`;
 | |
|             
 | |
|             if (exchangeType === 'exchange') {
 | |
|                 document.getElementById('modalPointsRequired').value = product.pointsRequired;
 | |
|             } else if (exchangeType === 'discount') {
 | |
|                 document.getElementById('modalDiscountPoints').value = product.discountPoints;
 | |
|                 document.getElementById('modalDiscountPrice').value = product.discountPrice || product.price;
 | |
|             }
 | |
|             
 | |
|             document.getElementById('modalMaxExchange').value = product.maxExchangeCount;
 | |
|             
 | |
|             // 清除之前的错误状态
 | |
|             clearInputErrors();
 | |
|             
 | |
|             document.getElementById('exchangeModal').classList.add('show');
 | |
|             document.body.style.overflow = 'hidden'; // 防止背景滚动
 | |
|         }
 | |
| 
 | |
|         function closeExchangeModal() {
 | |
|             document.getElementById('exchangeModal').classList.remove('show');
 | |
|             document.body.style.overflow = 'auto';
 | |
|             currentEditingProduct = null;
 | |
|             currentExchangeType = null;
 | |
|         }
 | |
| 
 | |
|         function validateInputs() {
 | |
|             const pointsInput = document.getElementById('modalPointsRequired');
 | |
|             const discountPointsInput = document.getElementById('modalDiscountPoints');
 | |
|             const discountPriceInput = document.getElementById('modalDiscountPrice');
 | |
|             const maxExchangeInput = document.getElementById('modalMaxExchange');
 | |
|             
 | |
|             let isValid = true;
 | |
|             
 | |
|             // 验证积分输入(根据类型)
 | |
|             if (currentExchangeType === 'exchange') {
 | |
|                 const pointsValue = parseInt(pointsInput.value);
 | |
|                 if (!pointsValue || pointsValue < 1) {
 | |
|                     pointsInput.classList.add('error');
 | |
|                     showToast('兑换所需积分必须大于0', 'error');
 | |
|                     isValid = false;
 | |
|                 } else {
 | |
|                     pointsInput.classList.remove('error');
 | |
|                 }
 | |
|             } else if (currentExchangeType === 'discount') {
 | |
|                 const discountValue = parseInt(discountPointsInput.value);
 | |
|                 if (!discountValue || discountValue < 1) {
 | |
|                     discountPointsInput.classList.add('error');
 | |
|                     showToast('优惠购所需积分必须大于0', 'error');
 | |
|                     isValid = false;
 | |
|                 } else {
 | |
|                     discountPointsInput.classList.remove('error');
 | |
|                 }
 | |
|                 
 | |
|                 const discountPriceValue = parseFloat(discountPriceInput.value);
 | |
|                 if (isNaN(discountPriceValue) || discountPriceValue < 0) {
 | |
|                     discountPriceInput.classList.add('error');
 | |
|                     showToast('优惠购商品价格必须大于等于0', 'error');
 | |
|                     isValid = false;
 | |
|                 } else {
 | |
|                     discountPriceInput.classList.remove('error');
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // 验证数量输入
 | |
|             const maxExchangeValue = parseInt(maxExchangeInput.value);
 | |
|             if (!maxExchangeValue || maxExchangeValue < 1) {
 | |
|                 maxExchangeInput.classList.add('error');
 | |
|                 showToast('最大可兑换数量必须大于0', 'error');
 | |
|                 isValid = false;
 | |
|             } else if (maxExchangeValue > currentEditingProduct.stock) {
 | |
|                 maxExchangeInput.classList.add('error');
 | |
|                 showToast('最大可兑换数量不能超过库存数量', 'error');
 | |
|                 isValid = false;
 | |
|             } else {
 | |
|                 maxExchangeInput.classList.remove('error');
 | |
|             }
 | |
|             
 | |
|             return isValid;
 | |
|         }
 | |
|         
 | |
|         function clearInputErrors() {
 | |
|             document.getElementById('modalPointsRequired').classList.remove('error');
 | |
|             document.getElementById('modalDiscountPoints').classList.remove('error');
 | |
|             document.getElementById('modalDiscountPrice').classList.remove('error');
 | |
|             document.getElementById('modalMaxExchange').classList.remove('error');
 | |
|         }
 | |
| 
 | |
|         function confirmAddToPointsMall() {
 | |
|             if (!currentEditingProduct || !currentExchangeType) return;
 | |
|             
 | |
|             // 验证输入
 | |
|             if (!validateInputs()) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const confirmBtn = document.getElementById('confirmBtn');
 | |
|             const maxExchangeCount = parseInt(document.getElementById('modalMaxExchange').value);
 | |
|             
 | |
|             let pointsValue = 0;
 | |
|             let discountPriceValue = 0;
 | |
|             let successMessage = '';
 | |
|             
 | |
|             if (currentExchangeType === 'exchange') {
 | |
|                 pointsValue = parseInt(document.getElementById('modalPointsRequired').value);
 | |
|                 successMessage = `${currentEditingProduct.name} 已成功添加到积分换购商城!需要${pointsValue}积分,最多可兑换${maxExchangeCount}件`;
 | |
|             } else if (currentExchangeType === 'discount') {
 | |
|                 pointsValue = parseInt(document.getElementById('modalDiscountPoints').value);
 | |
|                 discountPriceValue = parseFloat(document.getElementById('modalDiscountPrice').value);
 | |
|                 successMessage = `${currentEditingProduct.name} 已成功添加到积分优惠购商城!需要${pointsValue}积分,优惠购价格¥${discountPriceValue},最多可购买${maxExchangeCount}件`;
 | |
|             }
 | |
|             
 | |
|             confirmBtn.disabled = true;
 | |
|             confirmBtn.textContent = '添加中...';
 | |
| 
 | |
|             setTimeout(() => {
 | |
|                 // 更新数据状态
 | |
|                 currentEditingProduct.exchangeType = currentExchangeType;
 | |
|                 currentEditingProduct.maxExchangeCount = maxExchangeCount;
 | |
|                 
 | |
|                 if (currentExchangeType === 'exchange') {
 | |
|                     currentEditingProduct.pointsRequired = pointsValue;
 | |
|                 } else if (currentExchangeType === 'discount') {
 | |
|                     currentEditingProduct.discountPoints = pointsValue;
 | |
|                     currentEditingProduct.discountPrice = discountPriceValue;
 | |
|                 }
 | |
|                 
 | |
|                 // 重新渲染当前页面
 | |
|                 renderProducts();
 | |
|                 
 | |
|                 // 关闭弹窗
 | |
|                 closeExchangeModal();
 | |
|                 
 | |
|                 // 恢复按钮状态
 | |
|                 confirmBtn.disabled = false;
 | |
|                 confirmBtn.textContent = '确认添加';
 | |
|                 
 | |
|                 showToast(successMessage);
 | |
|             }, 1000);
 | |
|         }
 | |
| 
 | |
|         function changePage(page) {
 | |
|             if (page === 'prev' && currentPage > 1) {
 | |
|                 currentPage--;
 | |
|             } else if (page === 'next' && currentPage < Math.ceil(filteredProducts.length / 3)) {
 | |
|                 currentPage++;
 | |
|             } else if (typeof page === 'number') {
 | |
|                 currentPage = page;
 | |
|             }
 | |
| 
 | |
|             renderProducts();
 | |
|             updatePagination();
 | |
|         }
 | |
| 
 | |
|         function updatePagination() {
 | |
|             const maxPages = Math.ceil(filteredProducts.length / 3);
 | |
|             const prevBtn = document.getElementById('prevBtn');
 | |
|             const nextBtn = document.getElementById('nextBtn');
 | |
| 
 | |
|             prevBtn.disabled = currentPage === 1;
 | |
|             nextBtn.disabled = currentPage === maxPages || maxPages === 0;
 | |
| 
 | |
|             // 更新页码按钮
 | |
|             const pageButtons = document.querySelectorAll('.pagination button:not(#prevBtn):not(#nextBtn)');
 | |
|             pageButtons.forEach((btn, index) => {
 | |
|                 const pageNum = index + 1;
 | |
|                 btn.style.display = pageNum <= maxPages ? 'block' : 'none';
 | |
|                 btn.classList.toggle('active', pageNum === currentPage);
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         // 初始化页面
 | |
|         document.addEventListener('DOMContentLoaded', function() {
 | |
|             renderProducts();
 | |
|             updatePagination();
 | |
| 
 | |
|             // 添加输入框回车搜索功能
 | |
|             document.addEventListener('keypress', function(e) {
 | |
|                 if (e.key === 'Enter') {
 | |
|                     searchProducts();
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|             // 点击弹窗背景关闭弹窗
 | |
|             document.getElementById('exchangeModal').addEventListener('click', function(e) {
 | |
|                 if (e.target === this) {
 | |
|                     closeExchangeModal();
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|             // ESC键关闭弹窗
 | |
|             document.addEventListener('keydown', function(e) {
 | |
|                 if (e.key === 'Escape') {
 | |
|                     closeExchangeModal();
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|             // 输入框实时验证
 | |
|             document.getElementById('modalPointsRequired').addEventListener('input', function() {
 | |
|                 if (this.classList.contains('error')) {
 | |
|                     this.classList.remove('error');
 | |
|                 }
 | |
|             });
 | |
|             
 | |
|             document.getElementById('modalDiscountPoints').addEventListener('input', function() {
 | |
|                 if (this.classList.contains('error')) {
 | |
|                     this.classList.remove('error');
 | |
|                 }
 | |
|             });
 | |
|             
 | |
|             document.getElementById('modalDiscountPrice').addEventListener('input', function() {
 | |
|                 if (this.classList.contains('error')) {
 | |
|                     this.classList.remove('error');
 | |
|                 }
 | |
|             });
 | |
|             
 | |
|             document.getElementById('modalMaxExchange').addEventListener('input', function() {
 | |
|                 if (this.classList.contains('error')) {
 | |
|                     this.classList.remove('error');
 | |
|                 }
 | |
|             });
 | |
|         });
 | |
|     </script>
 | |
| </body>
 | |
| </html> |