去除String中的html标签,并对<td><tr>进行替换

本文介绍了一个使用Java实现的简单HTML标签清洗器,该清洗器可以移除HTML标签,并将特定标签替换为指定字符,如将<table>、<tr>和<td>等标签分别替换为空格或换行符。
import java.util.regex.Matcher;
import java.util.regex.Pattern;

private String cleanHtmlTags(String htmlText)
    {
        if (StringUtils.isEmpty(htmlText))
        {
            return "";
        }
        htmlText = htmlText.replaceAll("&nbsp;", " "); // 过滤html标签
        String regEx_html = "<[^>]+>";
        String regEx_td = "<[td]+>";
        String regEx_tr = "<[tr]+>";
        Pattern p_html = Pattern.compile(regEx_td, Pattern.CASE_INSENSITIVE);
        Matcher m_html = p_html.matcher(htmlText);
        htmlText = m_html.replaceAll(" "); // td替换成空格
        
        p_html = Pattern.compile(regEx_tr, Pattern.CASE_INSENSITIVE);
        m_html = p_html.matcher(htmlText);
        htmlText = m_html.replaceAll("\n"); // tr替换成换行
        
        p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
        m_html = p_html.matcher(htmlText);
        htmlText = m_html.replaceAll(""); // 过滤html标签
        
        return htmlText;
    }

 

// 监听来自父窗口的消息 window.addEventListener(‘message’, function(event) { if (event.source === window.parent && event.data === ‘DataManagerReady’) { initializeDataManager(); } }); // 初始化数据管理器 function initializeDataManager() { const dataManager = window.parent.dataManager; if (dataManager) { // 注册数据刷新回调 dataManager.registerCallback('all', function(operation, entity, data) { updatePageData(dataManager); }); // 初始页面数据更新 updatePageData(dataManager); // 设置搜索功能 setupSearch(dataManager); // 设置排序功能 setupSorting(); } else { console.error('DataManager not available'); } } // 更新页面数据 function updatePageData(dataManager) { updateStats(dataManager); updateTable(dataManager); document.getElementById(‘lastUpdate’).textContent = new Date().toLocaleTimeString(); } // 更新统计卡片数据 function updateStats(dataManager) { const data = dataManager.data; document.getElementById('orderCount').textContent = data.dingdans?.length || 0; document.getElementById('productCount').textContent = data.chanpins?.length || 0; document.getElementById('materialCount').textContent = data.bancais?.length || 0; const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0; document.getElementById('totalStock').textContent = totalStock; } // 全局变量存储当前排序状态 let currentSort = { column: null, direction: ‘asc’ // ‘asc’ 或 ‘desc’ }; // 更新结果表格 function updateTable(dataManager) { const tableBody = document.getElementById(‘resultBody’); tableBody.innerHTML = ‘’; const data = dataManager.data; let results = []; // 改为let以便排序 let resultCount = 0; // 处理订单产品数据 if (data.dingdan_chanpins) { data.dingdan_chanpins.forEach(dc => { if (!dc.dingdan || !dc.chanpin) return; if (dc.chanpin.chanpin_zujians) { dc.chanpin.chanpin_zujians.forEach(cz => { if (!cz.zujian || !cz.bancai) return; const bancai = cz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = bancai.kucun?.shuliang || 0; results.push({ orderNumber: dc.dingdan.number, productInfo: dc.chanpin.bianhao, productQuantity: dc.shuliang, component: cz.zujian.name, material: materialInfo, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, // 添加原始数据用于排序 raw: { orderNumber: dc.dingdan.number, productQuantity: dc.shuliang, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } }); } // 处理直接订单组件数据 if (data.dingdan_chanpin_zujians) { data.dingdan_chanpin_zujians.forEach(dcz => { if (!dcz.dingdan || !dcz.chanpin_zujian || !dcz.chanpin_zujian.zujian || !dcz.bancai) return; const bancai = dcz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = bancai.kucun?.shuliang || 0; results.push({ orderNumber: dcz.dingdan.number, productInfo: dcz.chanpin_zujian.chanpin?.bianhao || '独立组件', productQuantity: dcz.shuliang, component: dcz.chanpin_zujian.zujian.name, material: materialInfo, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, // 添加原始数据用于排序 raw: { orderNumber: dcz.dingdan.number, productQuantity: dcz.shuliang, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } // 应用排序 if (currentSort.column !== null) { results = sortResults(results, currentSort.column, currentSort.direction); } // 填充表格 if (resultCount > 0) { document.getElementById('noResults').style.display = 'none'; results.forEach(row => { const tr = document.createElement('tr'); tr.innerHTML = ` <td>${row.orderNumber}</td> <td>${row.productInfo}</td> <td>${row.productQuantity}</td> <td>${row.component}</td> <td>${row.material}</td> <td>${row.materialPerComponent}</td> <td>${row.materialOrderQuantity}</td> <td>${row.stockQuantity}</td> <td>${row.operation}</td> `; tableBody.appendChild(tr); }); } else { document.getElementById('noResults').style.display = 'flex'; } document.getElementById('resultCount').textContent = resultCount; } // 排序函数 function sortResults(results, columnIndex, direction) { return results.sort((a, b) => { // 根据列索引确定排序字段 let field; switch(columnIndex) { case 0: // 订单号 field = ‘orderNumber’; break; case 1: // 产品信息 field = ‘productInfo’; break; case 2: // 产品数量 field = ‘productQuantity’; break; case 3: // 组件 field = ‘component’; break; case 4: // 板材 // 特殊处理:按厚度排序 return sortByThickness(a, b, direction); case 5: // 单件用量 field = ‘materialPerComponent’; break; case 6: // 订单用量 field = ‘materialOrderQuantity’; break; case 7: // 库存数量 field = ‘stockQuantity’; break; default: return 0; } // 数值排序 if (['productQuantity', 'materialPerComponent', 'materialOrderQuantity', 'stockQuantity'].includes(field)) { return sortNumeric(a.raw[field], b.raw[field], direction); } // 字符串排序 return sortString(a[field], b[field], direction); }); } // 按厚度排序 function sortByThickness(a, b, direction) { return sortNumeric(a.raw.thickness, b.raw.thickness, direction); } // 数值排序 function sortNumeric(aVal, bVal, direction) { const aNum = parseFloat(aVal) || 0; const bNum = parseFloat(bVal) || 0; if (direction === 'asc') { return aNum - bNum; } else { return bNum - aNum; } } // 字符串排序 function sortString(aVal, bVal, direction) { const aStr = String(aVal).toLowerCase(); const bStr = String(bVal).toLowerCase(); if (aStr < bStr) { return direction === 'asc' ? -1 : 1; } else if (aStr > bStr) { return direction === 'asc' ? 1 : -1; } return 0; } // 获取板材详细信息函数(添加油状态) function getMaterialInfo(bancai) { if (!bancai) return ‘未知板材’; // 获取材质名称 const caizhiName = bancai.caizhi?.name || '未知材质'; // 获取木皮信息(添加油状态) const formatMupi = (mupi) => { if (!mupi) return null; return `${mupi.name}${mupi.you ? '(油)' : ''}`; }; const mupi1Str = formatMupi(bancai.mupi1) || '无'; const mupi2Str = formatMupi(bancai.mupi2) || '无'; const mupiInfo = mupi1Str + (mupi2Str !== '无' ? `/${mupi2Str}` : ''); // 厚度转换为字符串 const thickness = bancai.houdu ? `${bancai.houdu.toFixed(1)}mm` : '未知厚度'; return `${caizhiName} - ${mupiInfo} (${thickness})`; } // 设置搜索功能(添加油状态搜索) function setupSearch(dataManager) { // 订单搜索 ParseError: KaTeX parse error: Expected 'EOF', got '#' at position 3: ('#̲orderSearch').o…(this).val().toLowerCase(), 0); }); // 产品搜索 $('#productSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 1); }); // 板材材质搜索 $('#materialSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 木皮搜索(添加油状态搜索) $('#woodSearch').on('input', function() { const searchTerm = $(this).val().toLowerCase(); // 支持中文和数字搜索 const searchKeywords = { '有油': '有油', '无油': '无油', 'you': '有油', 'wu': '无油', '1': '有油', '0': '无油', '(油)': '(油)' }; const actualTerm = searchKeywords[searchTerm] || searchTerm; filterTable(actualTerm, 4); }); // 厚度搜索(修复正则表达式) $('#thicknessSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 库存状态搜索 $('#stockStatusBtn').click(function() { const minStock = parseInt($('#minStock').val()) || 0; const maxStock = parseInt($('#maxStock').val()) || Number.MAX_SAFE_INTEGER; filterByStock(minStock, maxStock); }); } // 设置排序功能 function setupSorting() { // 获取所有表头 const headers = document.querySelectorAll(‘#resultsTable th[data-sortable]’); headers.forEach((header, index) => { header.addEventListener('click', () => { // 更新排序状态 if (currentSort.column === index) { // 同一列:切换方向 currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc'; } else { // 新列:默认升序 currentSort.column = index; currentSort.direction = 'asc'; } // 更新UI updateSortIndicators(); // 重新渲染表格 const dataManager = window.parent.dataManager; if (dataManager) { updateTable(dataManager); } }); }); } // 更新排序指示器 function updateSortIndicators() { // 清除所有指示器 const headers = document.querySelectorAll(‘#resultsTable th[data-sortable]’); headers.forEach(header => { header.querySelector(‘.sort-indicator’).textContent = ‘’; }); // 为当前排序列添加指示器 if (currentSort.column !== null) { const currentHeader = headers[currentSort.column]; const indicator = currentHeader.querySelector('.sort-indicator'); indicator.textContent = currentSort.direction === 'asc' ? '↑' : '↓'; } } // 表格过滤函数(修复厚度搜索) function filterTable(searchTerm, columnIndex) { const rows = $(‘#resultBody tr’); let visibleCount = 0; rows.each(function() { const cellText = $(this).find(`td:eq(${columnIndex})`).text().toLowerCase(); let isMatch = false; // 特殊处理油状态搜索 if (searchTerm === '有油' || searchTerm === '无油' || searchTerm === '(油)') { isMatch = cellText.includes(searchTerm); } // 处理厚度字符串匹配(修复正则表达式) else if (columnIndex === 4 && !isNaN(parseFloat(searchTerm))) { // 提取厚度数字部分进行匹配 const thicknessMatch = cellText.match(/$(\d+\.?\d*)mm$/); if (thicknessMatch) { const thicknessValue = parseFloat(thicknessMatch[1]); const searchValue = parseFloat(searchTerm); // 允许小数点误差 isMatch = Math.abs(thicknessValue - searchValue) < 0.1; } } // 常规文本匹配 else { isMatch = cellText.includes(searchTerm); } $(this).toggle(isMatch); if (isMatch && searchTerm) { $(this).addClass('highlight'); } else { $(this).removeClass('highlight'); } if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 按库存量过滤 function filterByStock(minStock, maxStock) { const rows = $(‘#resultBody tr’); let visibleCount = 0; rows.each(function() { const stockCell = $(this).find('td:eq(7)').text(); const stockValue = parseInt(stockCell) || 0; const isMatch = stockValue >= minStock && stockValue <= maxStock; $(this).toggle(isMatch); if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 显示板材详情(添加油状态显示) function showMaterialDetail(bancaiId) { const dataManager = window.parent.dataManager; if (!dataManager) return; const bancais = dataManager.data.bancais || []; const bancai = bancais.find(b => b.id === bancaiId); if (!bancai) { alert('未找到板材信息'); return; } // 格式化木皮信息(添加油状态) const formatMupiDetail = (mupi) => { if (!mupi) return '无'; return `${mupi.name} (${mupi.you ? '有油' : '无油'})`; }; // 构建详情信息 const detailHtml = ` <div class="material-detail"> <h4>板材详情 (ID: ${bancai.id})</h4> <p><strong>材质:</strong> ${bancai.caizhi?.name || '未知'}</p> <p><strong>厚度:</strong> ${bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知'}</p> <p><strong>木皮1:</strong> ${formatMupiDetail(bancai.mupi1)}</p> <p><strong>木皮2:</strong> ${formatMupiDetail(bancai.mupi2)}</p> <p><strong>库存数量:</strong> ${bancai.kucun?.shuliang || 0}</p> <div class="related-orders"> <h5>相关订单:</h5> <ul> ${getRelatedOrders(bancai).map(order => `<li>订单 ${order.number} (ID: ${order.id})</li>` ).join('') || '<li>无相关订单</li>'} </ul> </div> </div> `; // 显示详情(实际项目中可用模态框) const detailWindow = window.open('', '_blank', 'width=600,height=400'); detailWindow.document.write(` <!DOCTYPE html> <html> <head> <title>板材详情</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } .material-detail { max-width: 500px; } h4 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; } p { margin: 10px 0; } .related-orders { margin-top: 20px; } ul { list-style-type: none; padding: 0; } li { padding: 5px 0; border-bottom: 1px solid #eee; } </style> </head> <body> ${detailHtml} </body> </html> `); detailWindow.document.close(); } // 获取板材相关订单 function getRelatedOrders(bancai) { const dataManager = window.parent.dataManager; if (!dataManager) return []; const orders = []; const dingdan_chanpin_zujians = dataManager.data.dingdan_chanpin_zujians || []; // 查找直接订单组件关联 dingdan_chanpin_zujians.forEach(dcz => { if (dcz.bancai?.id === bancai.id && dcz.dingdan && !orders.some(o => o.id === dcz.dingdan.id)) { orders.push(dcz.dingdan); } }); // 查找产品组件关联 const chanpin_zujians = dataManager.data.chanpin_zujians || []; chanpin_zujians.forEach(cz => { if (cz.bancai?.id === bancai.id) { const dingdan_chanpins = dataManager.data.dingdan_chanpins || []; dingdan_chanpins.forEach(dc => { if (dc.chanpin?.id === cz.chanpin?.id && dc.dingdan && !orders.some(o => o.id === dc.dingdan.id)) { orders.push(dc.dingdan); } }); } }); return orders; } // 页面加载时尝试初始化 if (window.parent) { window.parent.postMessage(‘RequestDataManager’, ‘*’); } // 添加全局函数供按钮调用 window.showMaterialDetail = showMaterialDetail; <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>板材库存查询系统</title> <!-- 引入外部CSS --> <link rel="stylesheet" href="../css/dingdan.css"> <!-- 引入jQuery --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- 引入Bootstrap --> <link href="../js/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css" rel="stylesheet"> <script src="../js/bootstrap-5.3.0-alpha1-dist/umd/popper.min.js"></script> <script src="../js/bootstrap-5.3.0-alpha1-dist/js/bootstrap.min.js"></script> <!-- 引入Bootstrap Icons --> <link rel="stylesheet" href="../js/bootstrap-icons-1.8.1/bootstrap-icons.css"> <!-- 引入主JS文件 --> <script src="../js/main.js"></script> </head> <body> <div class="container py-4"> <!-- 标题部分 --> <div class="text-center mb-4"> <h1 class="text-primary"><i class="bi bi-boxes"></i> 板材库存管理系统</h1> <p class="text-muted">查询订单、产品、板材及库存信息</p> </div> <!-- 统计卡片 --> <div class="row mb-4"> <div class="col-md-3"> <div class="card stats-card border-primary"> <div class="card-body"> <h5 class="card-title">订单总数</h5> <p class="card-text fs-3 text-primary" id="orderCount">0</p> </div> </div> </div> <div class="col-md-3"> <div class="card stats-card border-info"> <div class="card-body"> <h5 class="card-title">产品种类</h5> <p class="card-text fs-3 text-info" id="productCount">0</p> </div> </div> </div> <div class="col-md-3"> <div class="card stats-card border-success"> <div class="card-body"> <h5 class="card-title">板材库存</h5> <p class="card-text fs-3 text-success" id="materialCount">0</p> </div> </div> </div> <div class="col-md-3"> <div class="card stats-card border-warning"> <div class="card-body"> <h5 class="card-title">库存总量</h5> <p class="card-text fs-3 text-warning" id="totalStock">0</p> </div> </div> </div> </div> <!-- 搜索区域 --> <div class="card search-section mb-4"> <div class="card-header"> <h5 class="mb-0"><i class="bi bi-search me-2"></i>高级搜索</h5> </div> <div class="card-body"> <div class="row g-3"> <!-- 订单搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-clipboard-search search-icon"></i> <input type="text" class="form-control with-icon" id="orderSearch" placeholder="搜索订单号..." aria-label="订单号搜索"> </div> </div> <!-- 产品搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-grid search-icon"></i> <input type="text" class="form-control with-icon" id="productSearch" placeholder="搜索产品编号..." aria-label="产品编号搜索"> </div> </div> <!-- 板材搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-box search-icon"></i> <input type="text" class="form-control with-icon" id="materialSearch" placeholder="搜索板材ID或材质..." aria-label="板材搜索"> </div> </div> <!-- 木皮搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-tree search-icon"></i> <input type="text" class="form-control with-icon" id="woodSearch" placeholder="搜索木皮名称..." aria-label="木皮搜索"> </div> </div> <!-- 厚度搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-arrows-vertical search-icon"></i> <input type="number" class="form-control with-icon" id="thicknessSearch" placeholder="厚度(mm)" min="0" step="0.1"> </div> </div> <!-- 库存范围搜索 --> <div class="col-md-4"> <div class="input-group"> <span class="input-group-text"><i class="bi bi-box"></i></span> <input type="number" class="form-control" id="minStock" placeholder="最小库存" min="0"> <input type="number" class="form-control" id="maxStock" placeholder="最大库存" min="0"> <button class="btn btn-primary" type="button" id="stockStatusBtn"> <i class="bi bi-search"></i> </button> </div> </div> </div> </div> </div> <!-- 结果区域 --> <div class="card"> <div class="card-header d-flex justify-content-between align-items-center"> <h5 class="mb-0"><i class="bi bi-table me-2"></i>查询结果</h5> <div class="text-secondary"> <span id="resultCount">0</span> 条记录 <span class="ms-2"><i class="bi bi-info-circle"></i> <small>实时数据更新时间: <span id="lastUpdate">--:--:--</span></small></span> </div> </div> <div class="card-body result-section"> <div class="table-responsive"> <table class="table table-hover" id="resultsTable"> <thead class="table-light sticky-top"> <tr> <th data-sortable>订单号 <span class="sort-indicator"></span></th> <th data-sortable>产品信息 <span class="sort-indicator"></span></th> <th data-sortable>产品数量 <span class="sort-indicator"></span></th> <th data-sortable>组件 <span class="sort-indicator"></span></th> <th data-sortable>板材 <span class="sort-indicator"></span></th> <th data-sortable>单件用量 <span class="sort-indicator"></span></th> <th data-sortable>订单用量 <span class="sort-indicator"></span></th> <th data-sortable>库存数量 <span class="sort-indicator"></span></th> <th>操作</th> </tr> </thead> <tbody id="resultBody"> <!-- 数据加载中 --> <tr id="loadingRow"> <td colspan="9" class="text-center py-5"> <div class="d-flex align-items-center justify-content-center"> <div class="spinner-border text-primary" role="status"> <span class="visually-hidden">加载中...</span> </div> <div class="ms-3">正在加载数据,请稍候...</div> </div> </td> </tr> </tbody> </table> </div> <!-- 空结果提示 --> <div id="noResults" class="no-results text-center py-5" style="display: none;"> <div> <i class="bi bi-inboxes text-muted" style="font-size: 3rem;"></i> <h4 class="mt-3 text-muted">没有找到匹配的记录</h4> <p class="text-muted">请尝试调整您的搜索条件</p> </div> </div> </div> </div> </div> <!-- 引入订单JS --> <script src="../js/dingdan.js"></script> </body> </html> 库存列 数据错了, kucun要跟着板材走, 但库存.id!=板材.id 每个库存有一个板材对象,板材没有库存对象
06-21
<template> <div class="table-container"> <table class="dynamic-table"> <thead> <tr> <th rowspan="2">Factory</th> <th rowspan="2">Year</th> <template v-for="year in yearList" :key="year"> <th colspan="4" class="year-subheader">{{ year }}</th> </template> </tr> <tr> <template v-for="year in yearList" :key="'subheader-' + year"> <th>Category</th> <th>双数%</th> <th>总双数</th> <th>ASP</th> </template> </tr> </thead> <tbody> <template v-for="(factory, factoryIndex) in pageList" :key="factory.fact" > <!-- 按固定顺序分组渲染 --> <template v-for="yearType in fixedYearOrder" :key="yearType"> <!-- 获取当前年份类型的所有行 --> <template v-if="getRowsByYearType(factory.fcList, yearType).length > 0" > <tr v-for="(fc, fcIndex) in getRowsByYearType( factory.fcList, yearType )" :key="fc" > <!-- 工厂名称列(只在工厂的第一行显示) --> <td v-if="isFirstRowOfFactory(factoryIndex, yearType, fcIndex)" :rowspan="getFactoryRowspan(factory.fcList)" class="factory-cell" > {{ factory.fact }} </td> <!-- Year列(只在年份类型组的第一行显示) --> <td v-if="fcIndex == 0" :rowspan="getRowspan(factory.fcList, yearType)" class="yy-cell" > {{ yearType }} </td> <!-- 动态年份数据 --> <template v-for="year in yearList" :key="year"> <td>{{ fc }}</td> <!-- 双数%列 --> <td>{{ getPercentage(factory, fc, year) }}</td> <td>{{ getTotalQty(factory.infoList, fc, year) }}</td> <td>{{ getFob(factory.infoList, fc, year) }}</td> </template> </tr> </template> </template> <!-- 总计行 - 修复工厂和年份列合问题 --> <tr class="summary-row"> <!-- Year列显示"总计" - 关键修复:确保不重复显示 --> <td class="summary-cell">总计</td> <!-- 动态年份数据 --> <template v-for="year in yearList" :key="year"> <td></td> <!-- Category为空 --> <td class="percentage-cell"> {{ getTotalPercentage(factory, year) }} </td> <!-- 双数%总和 --> <td>{{ getFactoryYearTotal(factory, year) }}</td> <!-- 总双数计总 --> <td>{{ getWeightedASP(factory, year) }}</td> <!-- 加权平均ASP --> </template> </tr> </template> </tbody> </table> </div> </template> <script setup> import { ref, onMounted, computed } from "vue"; import { financeClassifyByYearList } from "@/api/planning/page"; const pageList = ref([]); const yearList = ref([]); const fixedYearOrder = ["JD", "NSW", "Golf"]; // 固定顺序 // 计算每个工厂每个年份的总双数之和 const factoryYearSums = computed(() => { const sums = {}; pageList.value.forEach((factory) => { const factoryName = factory.fact; sums[factoryName] = {}; // 初始化每个年份的总和为0 yearList.value.forEach((year) => { sums[factoryName][year] = 0; }); // 累加该工厂每个年份的总双数 if (factory.infoList) { factory.infoList.forEach((item) => { const year = String(item.year); if (yearList.value.includes(year) && item.totalQty) { sums[factoryName][year] += parseFloat(item.totalQty) || 0; } }); } }); return sums; }); // 计算每个工厂每个年份的总金额(用于加权平均ASP) const factoryYearAmounts = computed(() => { const amounts = {}; pageList.value.forEach((factory) => { const factoryName = factory.fact; amounts[factoryName] = {}; // 初始化每个年份的总金额为0 yearList.value.forEach((year) => { amounts[factoryName][year] = 0; }); // 累加该工厂每个年份的总金额 if (factory.infoList) { factory.infoList.forEach((item) => { const year = String(item.year); if (yearList.value.includes(year) && item.totalQty && item.fob) { const qty = parseFloat(item.totalQty) || 0; const fob = parseFloat(item.fob) || 0; amounts[factoryName][year] += qty * fob; } }); } }); return amounts; }); // 获取YY分类 function getYYCategory(fc) { const jdCategories = [ "JD 聯名款", "JD 成人鞋", "JD大童", "JD 中童", "JD小童", ]; const nswCategories = [ "NSW 聯名款", "NSW联名款", "NSW 联名款", "NSW 普通款", "NSW FUEL 聯名款", "NSW普通款", "SLT/SP/GEL/FUEL 快速訂單", ]; const golfCategories = ["GOLF", "JD GOLF 高尔夫", "GOLF "]; if (jdCategories.includes(fc)) return "JD"; if (nswCategories.includes(fc)) return "NSW"; if (golfCategories.includes(fc)) return "Golf"; return "其他"; } // 获取属于特定年份类型的行 function getRowsByYearType(fcList, yearType) { if (!fcList) return []; return fcList.filter((fc) => getYYCategory(fc) == yearType); } // 获取特定年份类型的行数(用于rowspan) function getRowspan(fcList, yearType) { return getRowsByYearType(fcList, yearType).length; } // 获取工厂的总行数(用于合工厂列) function getFactoryRowspan(fcList) { let totalRows = 0; fixedYearOrder.forEach((yearType) => { totalRows += getRowsByYearType(fcList, yearType).length; }); // 加上总计行 return totalRows + 1; } // 判断是否是工厂的第一行 function isFirstRowOfFactory(factoryIndex, yearType, fcIndex) { // 当前工厂的第一个年份类型的第一行 return yearType == fixedYearOrder[0] && fcIndex == 0; } // 关键修复:判断是否应该显示Year列 function shouldShowYearCell(factoryIndex) { // 只在工厂的第一个年份类型的第一行显示 return factoryIndex == 0; } // 获取总双数 function getTotalQty(infoList, fc, year) { if (!infoList) return ""; const item = infoList.find( (item) => item.financeClassify == fc && String(item.year) == year ); return item ? item.totalQty : ""; } // 获取双数百分比 function getPercentage(factory, fc, year) { const factoryName = factory.fact; const infoList = factory.infoList; if (!infoList) return "0.00%"; // 获取当前行的总双数 const item = infoList.find( (item) => item.financeClassify == fc && String(item.year) == year ); if (!item || !item.totalQty) return "0.00%"; // 获取当前工厂当前年份的总双数之和 const factoryYearTotal = factoryYearSums.value[factoryName]?.[year] || 0; // 防止除以0 if (factoryYearTotal == 0) return "0.00%"; // 计算百分比 const percentage = (parseFloat(item.totalQty) / factoryYearTotal) * 100; // 格式化为百分比字符串(保留两位小数) return percentage.toFixed(2) + "%"; } // 获取所有类别双数%的总和 function getTotalPercentage(factory, year) { const factoryName = factory.fact; const infoList = factory.infoList; if (!infoList) return "0.00%"; let totalPercentage = 0; // 累加所有类别的百分比 infoList.forEach((item) => { if (String(item.year) === year && item.totalQty) { const factoryYearTotal = factoryYearSums.value[factoryName]?.[year] || 0; if (factoryYearTotal > 0) { const percentage = (parseFloat(item.totalQty) / factoryYearTotal) * 100; totalPercentage += percentage; } } }); // 格式化为百分比字符串(保留两位小数) return totalPercentage.toFixed(2) + "%"; } // 获取FOB值 function getFob(infoList, fc, year) { if (!infoList) return ""; const item = infoList.find( (item) => item.financeClassify == fc && String(item.year) == year ); return item ? item.fob : ""; } // 获取工厂年份总双数 function getFactoryYearTotal(factory, year) { const factoryName = factory.fact; return factoryYearSums.value[factoryName]?.[year] || 0; } // 获取加权平均ASP function getWeightedASP(factory, year) { const factoryName = factory.fact; const totalQty = factoryYearSums.value[factoryName]?.[year] || 0; const totalAmount = factoryYearAmounts.value[factoryName]?.[year] || 0; if (totalQty == 0) return "0.00"; // 计算加权平均ASP:总金额 / 总双数 const asp = totalAmount / totalQty; return asp.toFixed(2); } // 获取数据 function getList() { financeClassifyByYearList().then((response) => { if (response.code == 200) { pageList.value = response.data; if (pageList.value.length > 0) { yearList.value = pageList.value[0].fyList || []; } } }); } onMounted(() => { getList(); }); </script> <style scoped> .table-container { overflow-x: auto; margin: 20px 0; } .dynamic-table { width: 100%; border-collapse: collapse; font-size: 14px; } .dynamic-table th, .dynamic-table td { border: 1px solid #e0e0e0; padding: 8px 12px; text-align: center; min-width: 80px; } .dynamic-table thead th { background-color: #f5f7fa; font-weight: 600; } .year-header { background-color: #e1f0fa; font-size: 16px; font-weight: bold; } .year-subheader { background-color: #d1e7f7; font-weight: bold; } .factory-cell { background-color: #f0f9eb; font-weight: bold; } .yy-cell { background-color: #fdf6ec; font-weight: bold; } .summary-row { background-color: #e8f4ff; font-weight: bold; } .summary-cell { background-color: #d1e7f7; font-weight: bold; } /* 确保合单元格的边框样式一致 */ .dynamic-table tr:first-child .factory-cell { border-top: 1px solid #e0e0e0; } </style> 分析以上代码,目前我需要在该列表的后面追加一块Factory为SZG的列表,用于展示三个工厂列表的汇总,首先该列表的Factory列值为SZG,样式与其他工厂一样,Year列的值也和其他工厂一样为JD,NSW,Golf;动态列的数据源于三个工厂对象的infoList集合中的每一条数据,比如当前动态列位于2025年,那么就取每个工厂对象的infoList集合中year为2025的所有数据,根据financeClassify分组统计totalQty,然后再根据Year列和Category列的对应关系,把总totalQty填充到对应行的总双数上,关于financeClassify分组的说明,NSW 聯名款",“NSW联名款”, "NSW 联名款“都表示NSW联名款这一分组即可,"NSW普通款"和"NSW 普通款"表示为NSW普通款这一分组即可
最新发布
08-27
<template> <!-- 入库表单 --> <el-dialog v-model="showRK"> <div> <p>装备ID:<el-input v-model="selectDatainfor.id" disabled></el-input></p> <p>二维码/条形码:<el-input v-model="selectDatainfor.qrcode" disabled></el-input></p> <p>装备名称<el-input v-model="selectDatainfor.name" disabled></el-input></p> <p>品牌<el-input v-model="selectDatainfor.brand" disabled></el-input></p> <p>规格型号<el-input v-model="selectDatainfor.model" disabled></el-input></p> <p>装备识别码<el-input v-model="selectDatainfor.shibiema" disabled></el-input></p> <p>所属单位<el-input v-model="selectDatainfor.unit" disabled></el-input></p> <p>库存数量<el-input v-model="selectDatainfor.kcnum" disabled></el-input></p> <p>入库数量<el-input-number v-model="selectDatainfor.rukunum" :min="1" :max="10000" ></el-input-number></p> <p>登记时间<el-input v-model="selectDatainfor.datetime" disabled></el-input></p> <p>登记账号<el-input v-model="selectDatainfor.zhanghao" disabled></el-input></p> <p>备注 <el-input v-model="selectDatainfor.beizhu" disabled></el-input></p> <p>经办人<el-input v-model="selectDatainfor.jingbanren" placeholder="请输入经办人" ></el-input></p> <el-button type="primary" @click="xinzeng">新增成功</el-button> </div> </el-dialog> <!--出库表单 --> <el-dialog v-model="showCK"> <div> <p>装备ID:<el-input v-model="selectDatainfor.id" disabled></el-input></p> <p>二维码/条形码:<el-input v-model="selectDatainfor.qrcode" disabled></el-input></p> <p>装备名称<el-input v-model="selectDatainfor.name" disabled></el-input></p> <p>品牌<el-input v-model="selectDatainfor.brand" disabled></el-input></p> <p>规格型号<el-input v-model="selectDatainfor.model" disabled></el-input></p> <p>装备识别码<el-input v-model="selectDatainfor.shibiema" disabled></el-input></p> <p>所属单位<el-input v-model="selectDatainfor.unit" disabled></el-input></p> <p>库存数量<el-input v-model="selectDatainfor.kcnum" disabled></el-input></p> <p>出库数量<el-input-number v-model="selectDatainfor.cuku" :min="1" :max="10000" ></el-input-number></p> <p>登记时间<el-input v-model="selectDatainfor.datetime" disabled></el-input></p> <p>登记账号<el-input v-model="selectDatainfor.zhanghao" disabled></el-input></p> <p>备注 <el-input v-model="selectDatainfor.beizhu" disabled></el-input></p> <p>经办人<el-input v-model="selectDatainfor.jingbanren" placeholder="请输入经办人" ></el-input></p> <el-button type="primary" @click="chuku">出库成功</el-button> </div> </el-dialog> <el-dialog v-model="histortshowData" title="装备出库历史信息"> <el-timeline style="max-with: 600px"> <el-timeline-item v-for="item in datahistory" :key="item.datetime" :timestamp="item.datetime"> {{ "账号:"+item.account+"于"+item.datetime+"入库了数量:"+item.num}} </el-timeline-item> </el-timeline> </el-dialog> <div class="maindata"> <p class="selectdata"> <el-input v-model="zbinfor.zbname" style="width: 240px" placeholder="请输入装备名称"></el-input> <el-button @click="selectDatas" color="rgb(26, 103, 236)">查询</el-button> <el-button round color="blue" @click="chuku">出库管理</el-button> </p> <el-table :data="zbdatainfor" style="width: 100%" stripe @row-click="getseIectrows" highlight-current-row > <el-table-column prop="id" label="装备ID" width="150" /> <el-table-column prop="qrcode" label="标签码" width="150" /> <el-table-column prop="name" label="装备名称" width="150" /> <el-table-column prop="brand" label="品牌" width="150" /> <el-table-column prop="model" label="规格型号" width="150" /> <el-table-column prop="shibiema" label="装备识别码" width="150" /> <el-table-column prop="unit" label="隶属单位" width="150" /> <el-table-column prop="kcnum" label="库存数量" width="150" /> <el-table-column prop="datetime" label="入库时间" width="150" /> <el-table-column prop="zhanghao" label="入库账号" width="150" /> <el-table-column prop="beizhu" label="备注" width="150" /> <el-table-column prop="ruku" label="操作选项" width="320" > <div> <el-button round color="green" @click="showRK=true">入库登记</el-button> <el-button round color="red" @click="showCK=true">出库申请</el-button> <el-button round @click="showCKSP">历史详情</el-button> </div> </el-table-column> </el-table> </div> </template> <script setup lang="ts"> import { ref,reactive} from 'vue'; import { historyCKData, historyRKData, selectData } from '../Util/apirequest/test.ts'; import { ElMessage } from 'element-plus'; import { NewRKData } from '../Util/apirequest/test.ts'; import { NewCKData } from '../Util/apirequest/test.ts'; const zbinfor = reactive({ zbname: '', }) interface zbItem { id: string qrcode:string name:string brand:string model:string shibiema:string unit:string kcnum:string cuku:string datetime:string uku:string zhanghao:string beizhu:string ruku:string } const zbdatainfor = ref<Array<zbItem>>([]); const showRK = ref(false); const showCK = ref(false); const histortshowData = ref(false); const getseIectrows=(row:any)=>{ selectDatainfor.id=row.id; selectDatainfor.qrcode=row.qrcode; selectDatainfor.name=row.name; selectDatainfor.brand=row.brand; selectDatainfor.model=row.model; selectDatainfor.shibiema=row.shibiema; selectDatainfor.unit=row.unit; selectDatainfor.kcnum=row.kcnum; //正则表达式 匹配单引号和双引号 selectDatainfor.cuku=row.cuku; selectDatainfor.datetime=row.datetime; selectDatainfor.zhanghao=row.zhanghao; selectDatainfor.beizhu=row.beizhu; selectDatainfor.ruku=row.ruku; selectDatainfor.rukunum=row.rukunum; selectDatainfor.jingbanren=row.jingbanren; selectDatainfor.jingbanrentuxiang=row.jingbanrentuxiang } const selectDatainfor = reactive({ id:'', qrcode:'', name:'', brand:'', model:'', shibiema:'', unit:'', kcnum:'', cuku:'1', datetime:'', zhanghao:'', beizhu:'', ruku:'', rukunum:'', jingbanren:'', jingbanrentuxiang:'', }) const datahistory = ref<Array<histotryColumdata>>([]); const datahistorys = ref<Array<histotryColumdata>>([]); interface histotryColumdata{ account: string, datetime:string, num:string } function selectDatas() { selectData(zbinfor.zbname).then((res) => { if (res == null) { ElMessage.error('未查询到相关数据') } else { console.log(res) zbdatainfor.value = res .replace(/['"]/g, '') .split('#') .filter((item) => item.trim() !== '') .map((d2: string) => { const [ id, qrcode, name, brand, model, shibiema, unit, kcnum, datetime, zhanghao, beizhu, ] = d2.split(',') return { id, qrcode, name, brand, model, shibiema, unit, kcnum, datetime, zhanghao, beizhu, } }) } }) } function showCKSP() { historyRKData(selectDatainfor.id).then((res:any) => { datahistory.value = res.replace(/['"]/g,'').split('*').filter(item =>item.trim() !=='').map((d2: string) => { const[account,datetime,num] =d2.split(','); return{account,datetime,num } }) getHistoryCKData(); }) } function getHistoryCKData() { historyCKData(selectDatainfor.id).then((res:any) => { datahistorys.value=res.split('*'); histortshowData.value=true; }) }; interface zbdata { id: string qrcode:string name:string brand:string model:string shibiema:string unit:string kcnum:string ruku:string datetime:string cuku:string zhanghao:string beizhu:string } const tableData:zbdata[] = [ ] function xinzeng (){ NewRKData(selectDatainfor).then((res:any) =>{ if (!selectDatainfor.jingbanren || selectDatainfor.jingbanren.trim() === "") { ElMessage.error("经办人不能为空,请输入姓名"); return; } else if(res == "true") { ElMessage.success("装备入库修改成功!"); // console.log(res); showRK.value=false; setTimeout(() => { window.location.reload(); }, 500); } else{ ElMessage.warning("入库未成功,请检查数据"); } }); } function chuku (){ NewCKData(selectDatainfor).then((res:any) =>{ if (!selectDatainfor.jingbanren || selectDatainfor.jingbanren.trim() === "") { ElMessage.error("经办人不能为空,请输入姓名"); return; } else if(res == "true") { ElMessage.success("装备出库修改成功!"); // console.log(res); showCK.value=false; setTimeout(() => { window.location.reload(); }, 500); } else{ ElMessage.warning("出库未成功,请检查数据"); } }); } // const captureImage =() => { // if (!canvasRef.value ||!videoRef.value) return; // const context = canvasRef.value.getContext('2d'); // context.drawImage(videoRef.value,0,0, videoWidth.value,videoHeight.value); // captureImage.value = canvasRef.value.toDataURL('image/png') // } </script> <style> .maindata{ background-color:#ffffff; position: fixed; overflow: auto; left:13%; top:15%; right:3%; bottom:5% } .maindata{ background-color:#ffffff; position: fixed; overflow: auto; left:13%; top:15%; right:3%; bottom:5% } .el-table__body tr.current-row>td{ background-color: #48f8de !important; color: #fff; } </style> 为什么搜索功能没有实现表格无法显示
08-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值