Html +css+js 写的一个小商城系统(POS系统)

图:

代码:

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;
            font-family: "Microsoft Yahei", sans-serif;
        }

        body {
            background-color: #f5f5f5;
            padding: 2px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 0 10px;
        }

        header {
            text-align: center;
            margin-bottom: 6px;
            padding: 20px;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            position: relative;
        }

        /* 购物车样式 */
        .cart-icon {
            position: absolute;
            top: 20px;
            right: 20px;
            font-size: 24px;
            cursor: pointer;
            color: #007bff;
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .cart-badge {
            background-color: #e63946;
            color: white;
            font-size: 12px;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        /* 购物车弹窗 */
        .cart-modal {
            position: fixed;
            top: 0;
            right: 0;
            width: 380px;
            height: 100vh;
            background-color: white;
            box-shadow: -2px 0 10px rgba(0,0,0,0.1);
            z-index: 1000;
            transform: translateX(100%);
            transition: transform 0.3s ease;
            display: flex;
            flex-direction: column;
        }

        .cart-modal.show {
            transform: translateX(0);
        }

        .cart-header {
            padding: 15px;
            border-bottom: 1px solid #eee;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .cart-close {
            font-size: 20px;
            cursor: pointer;
            color: #666;
        }

        .cart-body {
            flex: 1;
            padding: 15px;
            overflow-y: auto;
        }

        .cart-empty {
            text-align: center;
            padding: 40px 0;
            color: #999;
        }

        .cart-item {
            display: flex;
            padding: 10px 0;
            border-bottom: 1px solid #eee;
            gap: 10px;
        }

        .cart-item-img {
            width: 60px;
            height: 60px;
            object-fit: contain;
            background-color: #f9f9f9;
            border-radius: 4px;
        }

        .cart-item-info {
            flex: 1;
        }

        .cart-item-name {
            font-size: 14px;
            margin-bottom: 5px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .cart-item-price {
            font-size: 14px;
            color: #e63946;
            font-weight: bold;
            margin-bottom: 8px;
        }

        .cart-item-quantity {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .quantity-btn {
            width: 24px;
            height: 24px;
            border: 1px solid #ddd;
            background-color: #f5f5f5;
            border-radius: 4px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
        }

        .quantity-input {
            width: 30px;
            height: 24px;
            text-align: center;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 12px;
        }

        .cart-item-delete {
            color: #999;
            font-size: 12px;
            cursor: pointer;
            margin-top: 5px;
        }

        .cart-item-delete:hover {
            color: #e63946;
        }

        .cart-footer {
            padding: 15px;
            border-top: 1px solid #eee;
        }

        .cart-total {
            display: flex;
            justify-content: space-between;
            font-size: 16px;
            margin-bottom: 15px;
        }

        .cart-total-price {
            color: #e63946;
            font-weight: bold;
        }

        .cart-actions {
            display: flex;
            gap: 10px;
        }

        .cart-btn {
            flex: 1;
            padding: 10px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }

        .cart-clear {
            background-color: #f5f5f5;
            color: #666;
        }

        .cart-export {
            background-color: #28a745;
            color: white;
        }

        .cart-checkout {
            background-color: #007bff;
            color: white;
        }

        /* 遮罩层 */
        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.3);
            z-index: 999;
            display: none;
        }

        .overlay.show {
            display: block;
        }

        /* 密码验证弹窗样式 */
        .password-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            z-index: 2000;
            display: none;
            align-items: center;
            justify-content: center;
        }

        .password-modal.show {
            display: flex;
        }

        .password-content {
            background-color: white;
            padding: 25px;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
            width: 90%;
            max-width: 350px;
        }

        .password-content h3 {
            margin-bottom: 10px;
            color: #333;
            text-align: center;
        }

        .password-content p {
            color: #666;
            margin-bottom: 20px;
            text-align: center;
            font-size: 14px;
        }

        .password-content input {
            width: 100%;
            padding: 10px 15px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 16px;
            margin-bottom: 20px;
            box-sizing: border-box;
        }

        .password-content input:focus {
            outline: none;
            border-color: #007bff;
        }

        .password-actions {
            display: flex;
            gap: 10px;
        }

        .password-btn {
            flex: 1;
            padding: 10px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s;
        }

        .password-btn:hover {
            opacity: 0.9;
        }

        #passwordCancel {
            background-color: #f5f5f5;
            color: #666;
        }

        .password-confirm {
            background-color: #28a745;
            color: white;
        }

        /* 打印容器 - 重要修改:改为正常隐藏 */
        .print-container {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: auto;
            min-height: 100%;
            background: white;
            color: black;
            padding: 15px;
            box-sizing: border-box;
            font-size: 12px;
            z-index: 99999;
            overflow: visible;
        }

        /* 打印预览容器 - 用于正常显示 */
        .print-preview-container {
            width: 100%;
            max-width: 210mm; /* A4宽度 */
            margin: 0 auto;
            padding: 15mm;
            box-sizing: border-box;
        }

        .print-header {
            text-align: center;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid #333;
        }

        .print-header h2 {
            font-size: 18px;
            margin-bottom: 5px;
        }

        .print-header p {
            font-size: 12px;
            color: #666;
        }

        .print-table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 15px;
            font-size: 11px;
            table-layout: fixed;
        }

        .print-table th, .print-table td {
            border: 1px solid #333;
            padding: 6px 4px;
            text-align: center;
            word-break: break-word;
            vertical-align: middle;
        }

        .print-table th {
            background-color: #f0f0f0;
            font-weight: bold;
        }

        .print-table td:nth-child(2) {
            text-align: left;
            padding-left: 8px;
        }

        .print-total {
            text-align: right;
            font-size: 14px;
            font-weight: bold;
            margin-top: 10px;
            padding-right: 20px;
        }

        .print-footer {
            text-align: center;
            margin-top: 20px;
            font-size: 10px;
            color: #666;
            border-top: 1px solid #ddd;
            padding-top: 10px;
        }

        /* 打印样式 - 核心修复:简化打印逻辑 */
        @media print {
            /* 隐藏所有非打印元素 */
            body * {
                display: none !important;
                visibility: hidden !important;
                opacity: 0 !important;
            }
            
            /* 只显示打印容器及其内容 */
            .print-container,
            .print-container * {
                display: block !important;
                visibility: visible !important;
                opacity: 1 !important;
                position: static !important;
                top: auto !important;
                left: auto !important;
                width: 100% !important;
                height: auto !important;
                max-width: none !important;
                min-height: auto !important;
                margin: 0 !important;
                padding: 0 !important;
                box-shadow: none !important;
                background: white !important;
                color: black !important;
                float: none !important;
                overflow: visible !important;
                page-break-inside: avoid !important;
            }
            
            .print-container {
                display: block !important;
                position: absolute !important;
                top: 0 !important;
                left: 0 !important;
                width: 100% !important;
                height: auto !important;
                padding: 0 !important;
                margin: 0 !important;
            }
            
            .print-preview-container {
                width: 100% !important;
                max-width: 100% !important;
                padding: 10mm !important;
                margin: 0 !important;
            }
            
            /* A5纸张设置 */
            @page {
                size: A5 portrait;
                margin: 10mm;
            }
            
            .print-table {
                width: 100% !important;
                border-collapse: collapse !important;
                font-size: 10px !important;
                page-break-inside: avoid !important;
            }
            
            .print-table th, .print-table td {
                border: 1px solid #000 !important;
                padding: 4px 3px !important;
                page-break-inside: avoid !important;
                page-break-before: avoid !important;
                page-break-after: avoid !important;
            }
            
            .print-table th {
                background-color: #f0f0f0 !important;
                -webkit-print-color-adjust: exact !important;
                print-color-adjust: exact !important;
            }
            
            .print-header {
                margin-bottom: 10px !important;
                padding-bottom: 5px !important;
                border-bottom: 2px solid #333 !important;
                page-break-after: avoid !important;
            }
            
            .print-header h2 {
                font-size: 16px !important;
                margin-bottom: 3px !important;
            }
            
            .print-total {
                text-align: right !important;
                font-size: 12px !important;
                font-weight: bold !important;
                margin-top: 8px !important;
                padding-right: 10px !important;
                page-break-before: avoid !important;
            }
            
            .print-footer {
                text-align: center !important;
                margin-top: 10px !important;
                font-size: 9px !important;
                color: #666 !important;
                page-break-before: avoid !important;
            }
            
            /* 避免表格跨页断裂 */
            tr {
                page-break-inside: avoid !important;
                page-break-after: auto !important;
            }
        }

        h1 {
            color: #333;
            margin-bottom: 10px;
        }

        .filter-section {
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            margin-bottom: 6px;
        }

        .category-filter {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 15px;
            align-items: center;
        }

        .category-btn {
            padding: 8px 16px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background-color: #fff;
            cursor: pointer;
            transition: all 0.3s;
        }

        .category-btn:hover {
            background-color: #007bff;
            color: #fff;
            border-color: #007bff;
        }

        .category-btn.active {
            background-color: #007bff;
            color: #fff;
            border-color: #007bff;
        }

        /* 新增:导出全部商品按钮样式 */
        .export-all-btn {
            padding: 8px 16px;
            border: 1px solid #28a745;
            border-radius: 4px;
            background-color: #28a745;
            color: white;
            cursor: pointer;
            transition: all 0.3s;
            margin-left: auto;
        }

        .export-all-btn:hover {
            background-color: #218838;
            border-color: #1e7e34;
        }

        .search-box {
            display: flex;
            gap: 10px;
        }

        #search-input {
            flex: 1;
            padding: 8px 16px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }

        #search-btn {
            padding: 8px 16px;
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        #search-btn:hover {
            background-color: #0056b3;
        }

        /* 核心修改:调整网格布局为一行5列 */
        .product-list {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
            gap: 15px;
        }

        .product-card {
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            overflow: hidden;
            transition: transform 0.3s;
        }

        .product-card:hover {
            transform: translateY(-5px);
        }

        .product-img {
            width: 100%;
            height: 160px;
            object-fit: contain;
            padding: 15px;
            background-color: #f9f9f9;
        }

        .product-info {
            padding: 12px;
        }

        .product-name {
            font-size: 14px;
            color: #333;
            margin-bottom: 8px;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }

        .product-price {
            font-size: 16px;
            color: #e63946;
            font-weight: bold;
            margin-bottom: 8px;
        }

        .product-stock {
            font-size: 12px;
            color: #666;
            margin-bottom: 8px;
        }

        .product-stock.out-of-stock {
            color: #e63946;
        }

        .product-category {
            font-size: 11px;
            color: #999;
            background-color: #f0f0f0;
            padding: 2px 6px;
            border-radius: 4px;
            display: inline-block;
            margin-bottom: 8px;
        }

        /* 添加购物车按钮 */
        .add-to-cart {
            width: 100%;
            padding: 6px 0;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 12px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        .add-to-cart:hover {
            background-color: #0056b3;
        }

        .add-to-cart:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }

        .no-result {
            grid-column: 1 / -1;
            text-align: center;
            padding: 50px;
            color: #666;
            font-size: 18px;
        }

        /* 强制宽屏时固定5列 */
        @media (min-width: 1400px) {
            .product-list {
                grid-template-columns: repeat(5, 1fr) !important;
            }
        }

        /* 移动端适配 */
        @media (max-width: 768px) {
            .cart-modal {
                width: 100%;
            }
            .cart-actions {
                flex-direction: column;
            }
            .category-filter {
                flex-direction: column;
                align-items: stretch;
            }
            .export-all-btn {
                margin-left: 0;
                margin-top: 10px;
                width: 100%;
            }
            
            .product-list {
                grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
                gap: 10px;
            }
            
            .product-img {
                height: 140px;
                padding: 10px;
            }
            
            .product-info {
                padding: 10px;
            }
            
            .product-name {
                font-size: 13px;
            }
            
            .product-price {
                font-size: 14px;
            }
        }

        @media (max-width: 480px) {
            .product-list {
                grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            }
            
            .product-img {
                height: 120px;
            }
            
            .product-name {
                font-size: 12px;
            }
            
            .product-price {
                font-size: 13px;
            }
        }
    </style>
    <!-- 引入SheetJS库用于导出Excel -->
    <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
</head>
<body>
    <div class="container">
        <header>
            <h1>电脑配件商品库</h1>
            <p>各类电脑配件、外设、网络产品等价格参考,不是现价!</p>
            <!-- 购物车图标 -->
            <div class="cart-icon" id="cartIcon">
                🛒 <span class="cart-badge" id="cartBadge">0</span>
            </div>
        </header>

        <section class="filter-section">
            <div class="category-filter">
                <button class="category-btn active" data-category="all">全部商品</button>
                <!-- 分类按钮会通过JS动态生成 -->
                <!-- 新增:导出全部商品按钮 -->
                <button class="export-all-btn" id="exportAllBtn">导出全部商品Excel</button>
            </div>
            <div class="search-box">
                <input type="text" id="search-input" placeholder="输入商品名称搜索...">
                <button id="search-btn">搜索</button>
            </div>
        </section>

        <section class="product-list" id="product-list">
            <!-- 商品卡片会通过JS动态生成 -->
        </section>
    </div>

    <!-- 购物车弹窗 -->
    <div class="cart-modal" id="cartModal">
        <div class="cart-header">
            <h3>我的购物车</h3>
            <span class="cart-close" id="cartClose">×</span>
        </div>
        <div class="cart-body" id="cartBody">
            <div class="cart-empty">购物车空空如也~</div>
        </div>
        <div class="cart-footer">
            <div class="cart-total">
                <span>总计:</span>
                <span class="cart-total-price" id="cartTotalPrice">¥0.00</span>
            </div>
            <div class="cart-actions">
                <button class="cart-btn cart-clear" id="cartClear">清空购物车</button>
                <button class="cart-btn cart-export" id="cartExport">导出Excel</button>
                <button class="cart-btn cart-checkout" id="cartCheckout">结算/打印</button>
            </div>
        </div>
    </div>

    <!-- 密码验证弹窗 -->
    <div class="password-modal" id="passwordModal">
        <div class="password-content">
            <h3>验证密码</h3>
            <p>请输入密码以导出全部商品</p>
            <input type="password" id="passwordInput" placeholder="请输入密码" autocomplete="off">
            <div class="password-actions">
                <button class="password-btn" id="passwordCancel">取消</button>
                <button class="password-btn password-confirm" id="passwordConfirm">确认</button>
            </div>
        </div>
    </div>

    <!-- 打印容器 -->
    <div class="print-container" id="printContainer">
        <div class="print-preview-container">
            <div class="print-header">
                <h2>电脑配件采购清单</h2>
                <p id="printDate"></p>
            </div>
            <table class="print-table">
                <thead>
                    <tr>
                        <th width="10%">序号</th>
                        <th width="40%">商品名称</th>
                        <th width="15%">单价(¥)</th>
                        <th width="15%">数量</th>
                        <th width="20%">小计(¥)</th>
                    </tr>
                </thead>
                <tbody id="printTableBody">
                    <!-- 打印内容会动态生成 -->
                </tbody>
            </table>
            <div class="print-total" id="printTotal">
                合计金额:¥0.00
            </div>
            <div class="print-footer">
                打印时间:<span id="printTime"></span> | 备注:价格仅为参考,非实际交易价格
            </div>
        </div>
    </div>

    <!-- 遮罩层 -->
    <div class="overlay" id="overlay"></div>

    <script>
        // 商品数据
        const productData = [
            {"id":1,"name":"大正色带LQ630K_LQ635k","price":10,"stock":5,"category":"耗材","image_path":"images\\51_20251005_192131.jpg"},
            // ... 其他商品数据保持不变 ...
            {"id":138,"name":"TP BE6400 6线路由器","price":245,"stock":1,"category":"网络产品","image_path":"images\\product_20251107140555_BE6400.jpg"}
        ];

        // 获取DOM元素
        const categoryFilter = document.querySelector('.category-filter');
        const productList = document.getElementById('product-list');
        const searchInput = document.getElementById('search-input');
        const searchBtn = document.getElementById('search-btn');
        const exportAllBtn = document.getElementById('exportAllBtn');
        
        // 购物车相关DOM
        const cartIcon = document.getElementById('cartIcon');
        const cartModal = document.getElementById('cartModal');
        const cartClose = document.getElementById('cartClose');
        const cartBody = document.getElementById('cartBody');
        const cartBadge = document.getElementById('cartBadge');
        const cartTotalPrice = document.getElementById('cartTotalPrice');
        const cartClear = document.getElementById('cartClear');
        const cartExport = document.getElementById('cartExport');
        const cartCheckout = document.getElementById('cartCheckout');
        const overlay = document.getElementById('overlay');
        
        // 密码验证相关DOM
        const passwordModal = document.getElementById('passwordModal');
        const passwordInput = document.getElementById('passwordInput');
        const passwordConfirm = document.getElementById('passwordConfirm');
        const passwordCancel = document.getElementById('passwordCancel');
        
        // 打印相关DOM
        const printContainer = document.getElementById('printContainer');
        const printTableBody = document.getElementById('printTableBody');
        const printTotal = document.getElementById('printTotal');
        const printDate = document.getElementById('printDate');
        const printTime = document.getElementById('printTime');

        // 导出全部商品的密码
        const EXPORT_PASSWORD = '123';

        // 购物车数据
        let cart = JSON.parse(localStorage.getItem('productCart')) || [];

        // 生成分类按钮
        function renderCategoryButtons() {
            const categories = [...new Set(productData.map(item => item.category))];
            categories.forEach(category => {
                const btn = document.createElement('button');
                btn.className = 'category-btn';
                btn.dataset.category = category;
                btn.textContent = category;
                categoryFilter.appendChild(btn);
            });

            // 分类按钮点击事件
            const categoryBtns = document.querySelectorAll('.category-btn');
            categoryBtns.forEach(btn => {
                btn.addEventListener('click', () => {
                    // 移除所有active类
                    categoryBtns.forEach(b => b.classList.remove('active'));
                    // 添加当前active类
                    btn.classList.add('active');
                    // 筛选商品
                    const category = btn.dataset.category;
                    filterProducts(category);
                });
            });
        }

        // 渲染商品列表
        function renderProducts(products) {
            productList.innerHTML = '';
            
            if (products.length === 0) {
                const noResult = document.createElement('div');
                noResult.className = 'no-result';
                noResult.textContent = '暂无匹配的商品';
                productList.appendChild(noResult);
                return;
            }

            products.forEach(product => {
                const card = document.createElement('div');
                card.className = 'product-card';
                
                // 处理图片路径(替换反斜杠)
                const imgPath = product.image_path.replace(/\\/g, '/');
                
                // 库存状态类名
                const stockClass = product.stock === 0 ? 'out-of-stock' : '';
                // 库存文本
                const stockText = product.stock === 0 ? '缺货' : `库存:${product.stock}件`;
                // 禁用添加购物车按钮(缺货时)
                const isDisabled = product.stock === 0 ? 'disabled' : '';

                card.innerHTML = `
                    <img src="${imgPath}" alt="${product.name}" class="product-img">
                    <div class="product-info">
                        <h3 class="product-name">${product.name}</h3>
                        <div class="product-price">¥${product.price.toFixed(2)}</div>
                        <div class="product-stock ${stockClass}">${stockText}</div>
                        <div class="product-category">${product.category}</div>
                        <button class="add-to-cart" ${isDisabled} data-id="${product.id}">
                            ${product.stock === 0 ? '缺货' : '加入购物车'}
                        </button>
                    </div>
                `;
                
                productList.appendChild(card);
            });

            // 绑定加入购物车事件
            bindAddToCartEvents();
        }

        // 筛选商品
        function filterProducts(category = 'all', keyword = '') {
            let filteredProducts = productData;
            
            // 按分类筛选
            if (category !== 'all') {
                filteredProducts = filteredProducts.filter(item => item.category === category);
            }
            
            // 按关键词筛选
            if (keyword) {
                filteredProducts = filteredProducts.filter(item => 
                    item.name.toLowerCase().includes(keyword.toLowerCase())
                );
            }
            
            // 渲染筛选后的商品
            renderProducts(filteredProducts);
        }

        // 绑定加入购物车事件
        function bindAddToCartEvents() {
            const addToCartBtns = document.querySelectorAll('.add-to-cart');
            addToCartBtns.forEach(btn => {
                btn.addEventListener('click', () => {
                    const productId = parseInt(btn.dataset.id);
                    const product = productData.find(item => item.id === productId);
                    
                    if (product) {
                        addToCart(product);
                        // 简单的添加成功提示
                        btn.textContent = '已加入';
                        setTimeout(() => {
                            btn.textContent = '加入购物车';
                        }, 1000);
                    }
                });
            });
        }

        // 添加商品到购物车
        function addToCart(product) {
            // 检查商品是否已在购物车
            const existingItem = cart.find(item => item.id === product.id);
            
            if (existingItem) {
                // 已存在,增加数量(不超过库存)
                existingItem.quantity = Math.min(existingItem.quantity + 1, product.stock);
            } else {
                // 不存在,添加新商品
                cart.push({
                    id: product.id,
                    name: product.name,
                    price: product.price,
                    quantity: 1,
                    stock: product.stock,
                    image_path: product.image_path
                });
            }
            
            // 保存到本地存储
            saveCart();
            // 更新购物车UI
            updateCartUI();
        }

        // 更新购物车数量
        function updateCartItemQuantity(productId, newQuantity) {
            const itemIndex = cart.findIndex(item => item.id === productId);
            
            if (itemIndex !== -1) {
                const product = productData.find(item => item.id === productId);
                // 限制数量在1-库存之间
                newQuantity = Math.max(1, Math.min(newQuantity, product.stock));
                cart[itemIndex].quantity = newQuantity;
                
                // 保存并更新UI
                saveCart();
                updateCartUI();
            }
        }

        // 从购物车删除商品
        function removeFromCart(productId) {
            cart = cart.filter(item => item.id !== productId);
            saveCart();
            updateCartUI();
        }

        // 清空购物车
        function clearCart() {
            cart = [];
            saveCart();
            updateCartUI();
        }

        // 保存购物车到本地存储
        function saveCart() {
            localStorage.setItem('productCart', JSON.stringify(cart));
        }

        // 更新购物车UI
        function updateCartUI() {
            // 更新购物车数量徽章
            const totalItems = cart.reduce((total, item) => total + item.quantity, 0);
            cartBadge.textContent = totalItems;
            
            // 更新购物车总价
            const totalPrice = cart.reduce((total, item) => total + (item.price * item.quantity), 0);
            cartTotalPrice.textContent = `¥${totalPrice.toFixed(2)}`;
            
            // 渲染购物车列表
            renderCartItems();
        }

        // 渲染购物车商品列表
        function renderCartItems() {
            if (cart.length === 0) {
                cartBody.innerHTML = '<div class="cart-empty">购物车空空如也~</div>';
                return;
            }
            
            cartBody.innerHTML = '';
            
            cart.forEach(item => {
                const imgPath = item.image_path.replace(/\\/g, '/');
                const cartItem = document.createElement('div');
                cartItem.className = 'cart-item';
                
                cartItem.innerHTML = `
                    <img src="${imgPath}" alt="${item.name}" class="cart-item-img">
                    <div class="cart-item-info">
                        <div class="cart-item-name">${item.name}</div>
                        <div class="cart-item-price">¥${item.price.toFixed(2)}</div>
                        <div class="cart-item-quantity">
                            <span class="quantity-btn minus" data-id="${item.id}">-</span>
                            <input type="text" class="quantity-input" value="${item.quantity}" data-id="${item.id}">
                            <span class="quantity-btn plus" data-id="${item.id}">+</span>
                        </div>
                        <div class="cart-item-delete" data-id="${item.id}">删除</div>
                    </div>
                `;
                
                cartBody.appendChild(cartItem);
            });
            
            // 绑定购物车操作事件
            bindCartItemEvents();
        }

        // 绑定购物车商品操作事件
        function bindCartItemEvents() {
            // 数量减
            const minusBtns = document.querySelectorAll('.quantity-btn.minus');
            minusBtns.forEach(btn => {
                btn.addEventListener('click', () => {
                    const productId = parseInt(btn.dataset.id);
                    const item = cart.find(item => item.id === productId);
                    if (item) {
                        updateCartItemQuantity(productId, item.quantity - 1);
                    }
                });
            });
            
            // 数量加
            const plusBtns = document.querySelectorAll('.quantity-btn.plus');
            plusBtns.forEach(btn => {
                btn.addEventListener('click', () => {
                    const productId = parseInt(btn.dataset.id);
                    const item = cart.find(item => item.id === productId);
                    if (item) {
                        updateCartItemQuantity(productId, item.quantity + 1);
                    }
                });
            });
            
            // 输入框修改数量
            const quantityInputs = document.querySelectorAll('.quantity-input');
            quantityInputs.forEach(input => {
                input.addEventListener('change', () => {
                    const productId = parseInt(input.dataset.id);
                    const newQuantity = parseInt(input.value) || 1;
                    updateCartItemQuantity(productId, newQuantity);
                });
            });
            
            // 删除商品
            const deleteBtns = document.querySelectorAll('.cart-item-delete');
            deleteBtns.forEach(btn => {
                btn.addEventListener('click', () => {
                    const productId = parseInt(btn.dataset.id);
                    removeFromCart(productId);
                });
            });
        }

        // 绑定购物车弹窗事件
        function bindCartModalEvents() {
            // 打开购物车
            cartIcon.addEventListener('click', () => {
                cartModal.classList.add('show');
                overlay.classList.add('show');
            });
            
            // 关闭购物车
            cartClose.addEventListener('click', () => {
                cartModal.classList.remove('show');
                overlay.classList.remove('show');
            });
            
            // 点击遮罩层关闭
            overlay.addEventListener('click', () => {
                cartModal.classList.remove('show');
                overlay.classList.remove('show');
            });
            
            // 清空购物车
            cartClear.addEventListener('click', clearCart);
            
            // 导出Excel
            cartExport.addEventListener('click', exportCartToExcel);
            
            // 结算/打印
            cartCheckout.addEventListener('click', handleCheckout);
        }

        // 导出购物车为Excel
        function exportCartToExcel() {
            if (cart.length === 0) {
                alert('购物车为空,无法导出!');
                return;
            }
            
            // 准备Excel数据
            const excelData = [
                ['序号', '商品名称', '单价(¥)', '数量', '小计(¥)']
            ];
            
            let total = 0;
            cart.forEach((item, index) => {
                const subtotal = item.price * item.quantity;
                total += subtotal;
                excelData.push([
                    index + 1,
                    item.name,
                    item.price.toFixed(2),
                    item.quantity,
                    subtotal.toFixed(2)
                ]);
            });
            
            // 添加合计行
            excelData.push(['', '', '', '合计', total.toFixed(2)]);
            
            // 创建工作簿和工作表
            const workbook = XLSX.utils.book_new();
            const worksheet = XLSX.utils.aoa_to_sheet(excelData);
            
            // 设置列宽
            const wscols = [
                {wch: 6},  // 序号
                {wch: 30}, // 商品名称
                {wch: 10}, // 单价
                {wch: 6},  // 数量
                {wch: 10}  // 小计
            ];
            worksheet['!cols'] = wscols;
            
            // 将工作表添加到工作簿
            XLSX.utils.book_append_sheet(workbook, worksheet, '采购清单');
            
            // 生成并下载Excel文件
            const date = new Date();
            const fileName = `电脑配件采购清单_${date.getFullYear()}${(date.getMonth()+1).toString().padStart(2,0)}${date.getDate().toString().padStart(2,0)}.xlsx`;
            XLSX.writeFile(workbook, fileName);
        }

        // 导出全部商品为Excel
        function exportAllProductsToExcel() {
            if (productData.length === 0) {
                alert('商品库为空,无法导出!');
                return;
            }
            
            // 准备Excel数据
            const excelData = [
                ['商品ID', '商品名称', '单价(¥)', '库存数量', '商品分类', '图片链接']
            ];
            
            productData.forEach((item, index) => {
                excelData.push([
                    item.id,
                    item.name,
                    item.price.toFixed(2),
                    item.stock,
                    item.category,
                    item.image_path
                ]);
            });
            
            // 创建工作簿和工作表
            const workbook = XLSX.utils.book_new();
            const worksheet = XLSX.utils.aoa_to_sheet(excelData);
            
            // 设置列宽
            const wscols = [
                {wch: 8},   // 商品ID
                {wch: 40},  // 商品名称
                {wch: 10},  // 单价
                {wch: 10},  // 库存数量
                {wch: 12},  // 商品分类
                {wch: 30}   // 图片链接
            ];
            worksheet['!cols'] = wscols;
            
            // 将工作表添加到工作簿
            XLSX.utils.book_append_sheet(workbook, worksheet, '全部商品库');
            
            // 生成并下载Excel文件
            const date = new Date();
            const fileName = `电脑配件商品库_${date.getFullYear()}${(date.getMonth()+1).toString().padStart(2,0)}${date.getDate().toString().padStart(2,0)}.xlsx`;
            XLSX.writeFile(workbook, fileName);
        }

        // 处理结算/打印 - 关键修复:使用iframe打印
        function handleCheckout() {
            if (cart.length === 0) {
                alert('购物车为空,无法结算!');
                return;
            }
            
            // 创建打印专用的iframe
            createPrintIframe();
        }

        // 创建打印专用的iframe - 核心修复
        function createPrintIframe() {
            // 填充打印内容
            fillPrintContent();
            
            // 将打印容器显示出来(仅用于获取内容)
            printContainer.style.display = 'block';
            
            // 获取打印内容HTML
            const printContent = printContainer.innerHTML;
            
            // 隐藏打印容器
            printContainer.style.display = 'none';
            
            // 创建iframe
            const iframe = document.createElement('iframe');
            iframe.style.position = 'fixed';
            iframe.style.right = '0';
            iframe.style.bottom = '0';
            iframe.style.width = '0';
            iframe.style.height = '0';
            iframe.style.border = 'none';
            iframe.style.visibility = 'hidden';
            document.body.appendChild(iframe);
            
            // 写入内容到iframe
            const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
            iframeDoc.open();
            iframeDoc.write(`
                <!DOCTYPE html>
                <html>
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>打印采购清单</title>
                    <style>
                        body {
                            margin: 0;
                            padding: 10mm;
                            font-family: "Microsoft Yahei", Arial, sans-serif;
                            font-size: 12px;
                            color: black;
                        }
                        
                        .print-header {
                            text-align: center;
                            margin-bottom: 15px;
                            padding-bottom: 10px;
                            border-bottom: 2px solid #333;
                        }
                        
                        .print-header h2 {
                            font-size: 18px;
                            margin-bottom: 5px;
                        }
                        
                        .print-header p {
                            font-size: 12px;
                            color: #666;
                        }
                        
                        .print-table {
                            width: 100%;
                            border-collapse: collapse;
                            margin-bottom: 15px;
                            font-size: 11px;
                            table-layout: fixed;
                        }
                        
                        .print-table th, .print-table td {
                            border: 1px solid #333;
                            padding: 6px 4px;
                            text-align: center;
                            word-break: break-word;
                            vertical-align: middle;
                        }
                        
                        .print-table th {
                            background-color: #f0f0f0;
                            font-weight: bold;
                        }
                        
                        .print-table td:nth-child(2) {
                            text-align: left;
                            padding-left: 8px;
                        }
                        
                        .print-total {
                            text-align: right;
                            font-size: 14px;
                            font-weight: bold;
                            margin-top: 10px;
                            padding-right: 20px;
                        }
                        
                        .print-footer {
                            text-align: center;
                            margin-top: 20px;
                            font-size: 10px;
                            color: #666;
                            border-top: 1px solid #ddd;
                            padding-top: 10px;
                        }
                        
                        @media print {
                            @page {
                                size: A5 portrait;
                                margin: 10mm;
                            }
                            
                            body {
                                padding: 0;
                                margin: 0;
                            }
                        }
                    </style>
                </head>
                <body>
                    ${printContent}
                </body>
                </html>
            `);
            iframeDoc.close();
            
            // 等待内容加载后打印
            setTimeout(() => {
                // 移动端和桌面端统一处理
                iframe.contentWindow.focus();
                iframe.contentWindow.print();
                
                // 打印后移除iframe
                setTimeout(() => {
                    document.body.removeChild(iframe);
                }, 1000);
            }, 300);
            
            // 关闭购物车弹窗
            cartModal.classList.remove('show');
            overlay.classList.remove('show');
        }

        // 填充打印内容
        function fillPrintContent() {
            // 清空之前的打印内容
            printTableBody.innerHTML = '';
            
            // 设置日期和时间
            const now = new Date();
            const dateStr = `${now.getFullYear()}年${now.getMonth()+1}月${now.getDate()}日`;
            const timeStr = `${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}:${now.getSeconds().toString().padStart(2,'0')}`;
            printDate.textContent = dateStr;
            printTime.textContent = timeStr;
            
            // 填充商品列表
            let total = 0;
            cart.forEach((item, index) => {
                const subtotal = item.price * item.quantity;
                total += subtotal;
                
                // 缩短过长的商品名称
                let displayName = item.name;
                if (displayName.length > 25) {
                    displayName = displayName.substring(0, 23) + '...';
                }
                
                const tr = document.createElement('tr');
                tr.innerHTML = `
                    <td>${index + 1}</td>
                    <td>${displayName}</td>
                    <td>${item.price.toFixed(2)}</td>
                    <td>${item.quantity}</td>
                    <td>${subtotal.toFixed(2)}</td>
                `;
                printTableBody.appendChild(tr);
            });
            
            // 添加合计行
            const totalTr = document.createElement('tr');
            totalTr.innerHTML = `
                <td colspan="3"></td>
                <td><strong>合计</strong></td>
                <td><strong>${total.toFixed(2)}</strong></td>
            `;
            printTableBody.appendChild(totalTr);
            
            // 设置总计显示
            printTotal.textContent = `合计金额:¥${total.toFixed(2)}`;
        }

        // 搜索按钮点击事件
        searchBtn.addEventListener('click', () => {
            const keyword = searchInput.value.trim();
            const activeCategory = document.querySelector('.category-btn.active').dataset.category;
            filterProducts(activeCategory, keyword);
        });

        // 回车搜索
        searchInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                searchBtn.click();
            }
        });

        // 绑定密码验证事件
        function bindPasswordEvents() {
            // 导出全部商品按钮点击事件
            exportAllBtn.addEventListener('click', () => {
                // 显示密码输入弹窗
                passwordInput.value = '';
                passwordModal.classList.add('show');
                overlay.classList.add('show');
                // 聚焦到密码输入框
                setTimeout(() => {
                    passwordInput.focus();
                }, 100);
            });

            // 密码确认按钮事件
            passwordConfirm.addEventListener('click', () => {
                const inputPassword = passwordInput.value.trim();
                
                if (inputPassword === EXPORT_PASSWORD) {
                    // 密码正确,执行导出
                    exportAllProductsToExcel();
                    // 关闭弹窗
                    passwordModal.classList.remove('show');
                    overlay.classList.remove('show');
                    // 清空输入框
                    passwordInput.value = '';
                } else {
                    // 密码错误提示
                    alert('密码错误!请输入正确的密码');
                    passwordInput.value = '';
                    passwordInput.focus();
                }
            });

            // 密码取消按钮事件
            passwordCancel.addEventListener('click', () => {
                passwordModal.classList.remove('show');
                overlay.classList.remove('show');
                passwordInput.value = '';
            });

            // 点击遮罩层关闭密码弹窗
            overlay.addEventListener('click', () => {
                passwordModal.classList.remove('show');
                passwordInput.value = '';
            });

            // 回车键确认密码
            passwordInput.addEventListener('keypress', (e) => {
                if (e.key === 'Enter') {
                    passwordConfirm.click();
                }
            });
        }

        // 初始化
        function init() {
            // 生成分类按钮
            renderCategoryButtons();
            // 渲染所有商品
            filterProducts('all');
            // 更新购物车UI
            updateCartUI();
            // 绑定购物车弹窗事件
            bindCartModalEvents();
            // 绑定密码验证事件
            bindPasswordEvents();
        }

        // 页面加载完成后初始化
        window.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html>

电脑配件商pos系统

一个功能完善的电脑配件商品库存管理与采购清单生成系统,支持商品浏览、购物车管理、Excel导出和打印功能。

🌟 功能特点

1. 商品管理

  • 138种电脑配件商品展示(CPU、内存、硬盘、外设、耗材等)

  • 按分类筛选(耗材、网络产品、电脑配件、线材转接等12个分类)

  • 关键词搜索功能

  • 实时库存显示(缺货/有货状态)

2. 购物车系统

  • 添加/删除商品

  • 数量调整(支持加减按钮和直接输入)

  • 实时计算总价

  • 本地存储,关闭页面后数据不丢失

  • 购物车徽章显示商品数量

3. 导出功能

  • 导出购物车:将购物车内容导出为Excel文件

  • 导出全部商品:密码保护,导出完整商品库到Excel

  • 打印采购清单:A5格式,适合移动端和电脑端打印

4. 移动端适配

  • 响应式设计,适配手机、平板、电脑

  • 移动端友好的触控操作

  • 优化的打印体验

🚀 快速开始

在线使用

直接将代码保存为HTML文件,在浏览器中打开即可使用。

本地部署

  1. 将代码保存为 index.html

  2. 确保 images 文件夹存在并包含所有商品图片

  3. 用浏览器打开 index.html

📁 文件结构

复制代码
电脑配件商品库/
├── index.html              # 主程序文件
├── images/                 # 商品图片目录(138张图片)
│   ├── 51_20251005_192131.jpg
│   ├── MW325R.png
│   └── ... (其他图片)
└── README.md               # 说明文档

🎯 使用指南

1. 浏览商品

  • 点击分类按钮筛选特定类别商品

  • 使用搜索框查找特定商品

  • 查看商品价格、库存和图片

2. 购物车操作

  • 点击"加入购物车"按钮添加商品

  • 点击右上角购物车图标打开购物车

  • 在购物车中调整商品数量或删除商品

  • 点击"清空购物车"移除所有商品

3. 导出和打印

  • 导出Excel:在购物车中点击"导出Excel"

  • 打印清单:在购物车中点击"结算/打印"

  • 导出全部商品:点击"导出全部商品Excel"(需密码)

4. 密码功能

  • 导出全部商品需要密码验证

  • 默认密码:123

  • 可在代码中修改 EXPORT_PASSWORD 常量

🔧 技术特性

前端技术

  • HTML5/CSS3:响应式布局,网格系统

  • JavaScript ES6+:原生JavaScript实现所有功能

  • LocalStorage:本地存储购物车数据

  • SheetJS (xlsx):Excel文件导出功能

打印功能

  • A5纸张格式:适合小票打印

  • iframe打印:解决移动端打印兼容性问题

  • CSS打印媒体查询:优化打印样式

  • 移动端适配:完美支持手机打印

响应式设计

  • 桌面端:一行5列商品

  • 平板:3-4列商品

  • 手机:2列商品

  • 打印:A5格式适配

⚙️ 自定义配置

修改密码

在JavaScript代码中修改:

复制代码
const EXPORT_PASSWORD = '123'; // 改为你的密码

添加商品

productData 数组中添加新商品:

复制代码
{
    "id": 139,
    "name": "商品名称",
    "price": 100,
    "stock": 10,
    "category": "分类名称",
    "image_path": "images/图片文件.jpg"
}

修改分类

程序会自动从商品数据中提取所有分类并生成按钮。

📱 移动端支持

触控优化

  • 大尺寸按钮和触摸区域

  • 滑动友好的购物车面板

  • 移动端打印预览优化

打印适配

  • 自动检测移动设备

  • 优化字体大小和边距

  • 表格自动换行和截断

📊 数据格式

商品数据结构

复制代码
{
    id: Number,          // 商品ID
    name: String,        // 商品名称
    price: Number,       // 单价
    stock: Number,       // 库存数量
    category: String,    // 分类
    image_path: String   // 图片路径
}

购物车数据结构

复制代码
{
    id: Number,          // 商品ID
    name: String,        // 商品名称
    price: Number,       // 单价
    quantity: Number,    // 数量
    stock: Number,       // 最大库存
    image_path: String   // 图片路径
}

🔒 安全特性

  1. 本地存储:所有数据保存在浏览器本地

  2. 密码保护:导出全部商品需要密码验证

  3. 输入验证:数量调整有限制(1-库存最大值)

  4. XSS防护:文本内容自动转义

🌐 浏览器兼容性

  • Chrome 60+ ✅

  • Firefox 55+ ✅

  • Safari 11+ ✅

  • Edge 79+ ✅

  • iOS Safari 11+ ✅

  • Android Chrome 60+ ✅

📄 许可证

本项目仅供学习和内部使用。

🐛 故障排除

常见问题

  1. 图片不显示

    • 检查 images 文件夹是否存在

    • 检查图片路径是否正确

    • 检查图片文件名是否匹配

  2. 打印问题

    • 确保浏览器允许弹窗

    • 移动端可能需要允许打印权限

    • 如遇空白页,尝试刷新页面

  3. Excel导出问题

    • 确保网络连接正常(需要加载SheetJS库)

    • 检查浏览器是否允许下载

  4. 数据丢失

    • 购物车数据保存在localStorage

    • 清除浏览器缓存会删除数据

    • 建议定期导出重要数据

调试方法

  1. 按F12打开开发者工具

  2. 查看Console是否有错误信息

  3. 检查Application > LocalStorage查看数据

🎨 界面预览

桌面端

  • 顶部:标题和购物车图标

  • 中部:分类筛选和搜索框

  • 底部:5列商品网格

移动端

  • 顶部:标题和购物车图标

  • 中部:分类筛选(垂直排列)

  • 底部:2列商品网格

  • 右侧滑出购物车面板

打印预览

  • A5纸张尺寸

  • 简洁的采购清单表格

  • 包含日期、时间和总价

相关推荐
顾安r2 小时前
1.1 脚本网页 战推棋
java·前端·游戏·html·virtualenv
一颗小青松2 小时前
vue 腾讯地图经纬度转高德地图经纬度
前端·javascript·vue.js
OranTech2 小时前
练习06-CSS设置样式
css
怕浪猫10 小时前
第一章 JSX 增强特性与函数组件入门
前端·javascript·react.js
幻影星空VR元宇宙11 小时前
飞行影院设备价格解析及性价比分析
css·百慕大冒险·幻影星空
前端小L11 小时前
贪心算法专题(十):维度权衡的艺术——「根据身高重建队列」
javascript·算法·贪心算法
Fortunate Chen12 小时前
类与对象(下)
java·javascript·jvm
低保和光头哪个先来14 小时前
场景6:对浏览器内核的理解
开发语言·前端·javascript·vue.js·前端框架