script.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // 供应商交货计划分析工具
  2. // 用于分析Excel文件中的字段信息,包括数据类型、空值统计等
  3. // 确保DOM加载完成后执行代码
  4. document.addEventListener('DOMContentLoaded', function() {
  5. // 获取DOM元素
  6. const fileUpload = document.getElementById('file-upload');
  7. const fileName = document.getElementById('file-name');
  8. const fieldAnalysis = document.getElementById('field-analysis');
  9. const dataPreview = document.getElementById('data-preview');
  10. const analyzeBtn = document.getElementById('analyze-btn');
  11. // 存储当前选择的文件
  12. let selectedFile = null;
  13. // 监听文件上传事件
  14. fileUpload.addEventListener('change', function(e) {
  15. const file = e.target.files[0];
  16. if (file) {
  17. fileName.textContent = file.name;
  18. selectedFile = file;
  19. analyzeBtn.disabled = false; // 启用分析按钮
  20. }
  21. });
  22. // 监听分析按钮点击事件
  23. analyzeBtn.addEventListener('click', function() {
  24. if (selectedFile) {
  25. // 显示加载状态
  26. fieldAnalysis.innerHTML = '<p class="placeholder">正在分析Excel文件,请稍候...</p>';
  27. dataPreview.innerHTML = '';
  28. parseExcelFile(selectedFile);
  29. }
  30. });
  31. // 解析Excel文件
  32. function parseExcelFile(file) {
  33. const reader = new FileReader();
  34. reader.onload = function(e) {
  35. const data = new Uint8Array(e.target.result);
  36. const workbook = XLSX.read(data, { type: 'array' });
  37. // 获取第一个工作表
  38. const firstSheetName = workbook.SheetNames[0];
  39. const worksheet = workbook.Sheets[firstSheetName];
  40. // 转换为JSON格式
  41. const jsonData = XLSX.utils.sheet_to_json(worksheet);
  42. // 分析字段
  43. analyzeFields(jsonData);
  44. // 显示数据预览
  45. showDataPreview(jsonData);
  46. };
  47. reader.readAsArrayBuffer(file);
  48. }
  49. // 分析Excel中的每个字段
  50. function analyzeFields(data) {
  51. if (!data || data.length === 0) {
  52. fieldAnalysis.innerHTML = '<p class="placeholder">Excel文件中没有数据</p>';
  53. return;
  54. }
  55. fieldAnalysis.innerHTML = '';
  56. // 获取所有字段名
  57. const fields = Object.keys(data[0]);
  58. fields.forEach(field => {
  59. const fieldStats = analyzeField(data, field);
  60. const fieldCard = createFieldCard(field, fieldStats);
  61. fieldAnalysis.appendChild(fieldCard);
  62. });
  63. }
  64. // 分析单个字段的统计信息
  65. function analyzeField(data, fieldName) {
  66. const values = data.map(row => row[fieldName]).filter(val => val !== undefined && val !== null && val !== '');
  67. const stats = {
  68. fieldName,
  69. totalCount: data.length,
  70. nonEmptyCount: values.length,
  71. emptyCount: data.length - values.length,
  72. dataType: values.length > 0 ? getDataType(values[0], fieldName) : 'unknown',
  73. uniqueValues: [...new Set(values)].length,
  74. sampleValues: values.slice(0, 5) // 显示前5个样本值
  75. };
  76. // 如果是数值类型,计算额外的统计信息
  77. if (stats.dataType === 'number' || stats.dataType === 'date') {
  78. const numericValues = values.map(val => {
  79. if (stats.dataType === 'date') {
  80. // 处理日期类型
  81. return new Date(val).getTime();
  82. }
  83. return parseFloat(val);
  84. }).filter(val => !isNaN(val));
  85. if (numericValues.length > 0) {
  86. stats.min = Math.min(...numericValues);
  87. stats.max = Math.max(...numericValues);
  88. stats.average = numericValues.reduce((sum, val) => sum + val, 0) / numericValues.length;
  89. // 如果是日期类型,格式化最小和最大值
  90. if (stats.dataType === 'date') {
  91. stats.min = new Date(stats.min).toLocaleDateString();
  92. stats.max = new Date(stats.max).toLocaleDateString();
  93. }
  94. }
  95. }
  96. return stats;
  97. }
  98. // 特定字段名称列表(确保这些字段被识别为日期类型)
  99. const DATE_FIELDS = ['交货日期', '交期回复'];
  100. // 获取数据类型
  101. function getDataType(value, fieldName = '') {
  102. if (value === null || value === undefined) return 'unknown';
  103. // 对于特定的日期字段,强制识别为日期类型
  104. if (DATE_FIELDS.includes(fieldName)) {
  105. // 尝试各种日期格式解析
  106. const dateFormats = [
  107. value, // 原值
  108. value.replace(/\//g, '-'), // 将/替换为-(例如2023/05/20 -> 2023-05-20)
  109. value.replace(/\./g, '-') // 将.替换为-(例如2023.05.20 -> 2023-05-20)
  110. ];
  111. for (const format of dateFormats) {
  112. const date = new Date(format);
  113. if (!isNaN(date.getTime()) && date.toISOString() !== '0001-01-01T00:00:00.000Z' && date.getFullYear() > 1900) {
  114. return 'date';
  115. }
  116. }
  117. }
  118. // 尝试检查是否为日期
  119. if (typeof value === 'string') {
  120. const date = new Date(value);
  121. if (!isNaN(date.getTime()) && date.toISOString() !== '0001-01-01T00:00:00.000Z' && date.getFullYear() > 1900) {
  122. return 'date';
  123. }
  124. }
  125. // 检查是否为数字
  126. if (typeof value === 'number' || !isNaN(parseFloat(value)) && isFinite(value)) {
  127. return 'number';
  128. }
  129. // 检查是否为布尔值
  130. if (typeof value === 'boolean' || value === 'true' || value === 'false') {
  131. return 'boolean';
  132. }
  133. return 'string';
  134. }
  135. // 创建字段分析卡片
  136. function createFieldCard(fieldName, stats) {
  137. const card = document.createElement('div');
  138. card.className = 'field-card';
  139. let content = `<h3>${fieldName}</h3><div class="field-details">`;
  140. content += `<div class="field-detail">数据类型: ${stats.dataType}</div>`;
  141. content += `<div class="field-detail">总记录数: ${stats.totalCount}</div>`;
  142. content += `<div class="field-detail">非空值数: ${stats.nonEmptyCount} (${((stats.nonEmptyCount / stats.totalCount) * 100).toFixed(2)}%)</div>`;
  143. content += `<div class="field-detail">空值数: ${stats.emptyCount} (${((stats.emptyCount / stats.totalCount) * 100).toFixed(2)}%)</div>`;
  144. content += `<div class="field-detail">唯一值数量: ${stats.uniqueValues}</div>`;
  145. // 添加数值类型的统计信息
  146. if (stats.min !== undefined) {
  147. content += `<div class="field-detail">最小值: ${stats.min}</div>`;
  148. }
  149. if (stats.max !== undefined) {
  150. content += `<div class="field-detail">最大值: ${stats.max}</div>`;
  151. }
  152. if (stats.average !== undefined) {
  153. content += `<div class="field-detail">平均值: ${stats.average.toFixed(2)}</div>`;
  154. }
  155. // 添加样本值
  156. if (stats.sampleValues && stats.sampleValues.length > 0) {
  157. content += `<div class="field-detail">样本值: ${stats.sampleValues.join(', ')}</div>`;
  158. }
  159. content += '</div>';
  160. card.innerHTML = content;
  161. return card;
  162. }
  163. // 显示数据预览
  164. function showDataPreview(data) {
  165. if (!data || data.length === 0) {
  166. dataPreview.innerHTML = '<p class="placeholder">Excel文件中没有数据</p>';
  167. return;
  168. }
  169. // 获取所有字段名
  170. const fields = Object.keys(data[0]);
  171. // 创建表格
  172. const table = document.createElement('table');
  173. table.className = 'data-table';
  174. // 创建表头
  175. const thead = document.createElement('thead');
  176. const headerRow = document.createElement('tr');
  177. fields.forEach(field => {
  178. const th = document.createElement('th');
  179. th.textContent = field;
  180. headerRow.appendChild(th);
  181. });
  182. thead.appendChild(headerRow);
  183. table.appendChild(thead);
  184. // 创建表体
  185. const tbody = document.createElement('tbody');
  186. // 只显示前10行数据
  187. const previewData = data.slice(0, 10);
  188. previewData.forEach(row => {
  189. const tr = document.createElement('tr');
  190. fields.forEach(field => {
  191. const td = document.createElement('td');
  192. td.textContent = row[field] !== undefined && row[field] !== null ? row[field] : '';
  193. tr.appendChild(td);
  194. });
  195. tbody.appendChild(tr);
  196. });
  197. table.appendChild(tbody);
  198. // 清空并添加表格
  199. dataPreview.innerHTML = '';
  200. dataPreview.appendChild(table);
  201. // 如果有更多数据,显示提示
  202. if (data.length > 10) {
  203. const moreInfo = document.createElement('p');
  204. moreInfo.className = 'placeholder';
  205. moreInfo.textContent = `仅显示前10行数据,共 ${data.length} 行`;
  206. dataPreview.appendChild(moreInfo);
  207. }
  208. }
  209. });