1
0

index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Excel/CSV 转 JSON 工具主逻辑
  2. // 作者:AI进化论-花生
  3. // 详细中文注释,便于初学者理解
  4. // 选择器
  5. const fileInput = document.getElementById('fileInput');
  6. const fileLink = document.querySelector('a.btn-file-input');
  7. const pasteInput = document.getElementById('pasteInput');
  8. const convertBtn = document.getElementById('convertBtn');
  9. const jsonOutput = document.getElementById('jsonOutput');
  10. const errorMsg = document.getElementById('errorMsg');
  11. // 清空错误提示
  12. function clearError() {
  13. errorMsg.textContent = '';
  14. }
  15. // 显示错误提示
  16. function showError(msg) {
  17. errorMsg.textContent = msg;
  18. }
  19. // 自动识别数字类型
  20. function parseValue(val) {
  21. if (/^-?\d+(\.\d+)?$/.test(val)) {
  22. return Number(val);
  23. }
  24. return val;
  25. }
  26. // 解析CSV文本为JSON
  27. function csvToJson(csv) {
  28. const lines = csv.split(/\r?\n/).filter(line => line.trim() !== '');
  29. if (lines.length < 2) return [];
  30. const headers = lines[0].split(',');
  31. return lines.slice(1).map(line => {
  32. const values = line.split(',');
  33. const obj = {};
  34. headers.forEach((h, i) => {
  35. obj[h.trim()] = parseValue((values[i] || '').trim());
  36. });
  37. return obj;
  38. });
  39. }
  40. // 解析TSV文本为JSON
  41. function tsvToJson(tsv) {
  42. const lines = tsv.split(/\r?\n/).filter(line => line.trim() !== '');
  43. if (lines.length < 2) return [];
  44. const headers = lines[0].split('\t');
  45. return lines.slice(1).map(line => {
  46. const values = line.split('\t');
  47. const obj = {};
  48. headers.forEach((h, i) => {
  49. obj[h.trim()] = parseValue((values[i] || '').trim());
  50. });
  51. return obj;
  52. });
  53. }
  54. // 处理文件上传
  55. fileInput.addEventListener('change', function (e) {
  56. clearError();
  57. const file = e.target.files[0];
  58. if (!file) return;
  59. const reader = new FileReader();
  60. const ext = file.name.split('.').pop().toLowerCase();
  61. if (["xlsx", "xls"].includes(ext)) {
  62. // 读取Excel文件
  63. reader.onload = function (evt) {
  64. try {
  65. const data = evt.target.result;
  66. const workbook = XLSX.read(data, { type: 'binary' });
  67. // 默认取第一个sheet
  68. const sheetName = workbook.SheetNames[0];
  69. const sheet = workbook.Sheets[sheetName];
  70. const json = XLSX.utils.sheet_to_json(sheet, { defval: '' });
  71. jsonOutput.value = JSON.stringify(json, null, 2);
  72. // 生成CSV文本并填充到输入框
  73. const csv = XLSX.utils.sheet_to_csv(sheet);
  74. pasteInput.value = csv;
  75. } catch (err) {
  76. showError('Excel文件解析失败,请确认文件格式!');
  77. }
  78. };
  79. reader.readAsBinaryString(file);
  80. } else if (ext === 'csv') {
  81. // 读取CSV文件
  82. reader.onload = function (evt) {
  83. try {
  84. const csv = evt.target.result;
  85. const json = csvToJson(csv);
  86. jsonOutput.value = JSON.stringify(json, null, 2);
  87. pasteInput.value = csv;
  88. } catch (err) {
  89. showError('CSV文件解析失败,请确认内容格式!');
  90. }
  91. };
  92. reader.readAsText(file);
  93. } else {
  94. showError('仅支持Excel(.xlsx/.xls)或CSV文件!');
  95. }
  96. });
  97. // 处理转换按钮点击
  98. convertBtn.addEventListener('click', function () {
  99. clearError();
  100. // 处理粘贴内容
  101. const text = pasteInput.value.trim();
  102. if (!text) {
  103. showError('请上传文件或粘贴表格数据!');
  104. return;
  105. }
  106. // 优先判断是否为TSV格式(多列Tab分隔)
  107. if (text.includes('\t') && text.includes('\n')) {
  108. try {
  109. const json = tsvToJson(text);
  110. jsonOutput.value = JSON.stringify(json, null, 2);
  111. } catch (err) {
  112. showError('粘贴内容(TSV)解析失败,请检查格式!');
  113. }
  114. } else if (text.includes(',') && text.includes('\n')) {
  115. // CSV格式
  116. try {
  117. const json = csvToJson(text);
  118. jsonOutput.value = JSON.stringify(json, null, 2);
  119. } catch (err) {
  120. showError('粘贴内容(CSV)解析失败,请检查格式!');
  121. }
  122. } else if (!text.includes(',') && !text.includes('\t') && text.includes('\n')) {
  123. // 处理单列多行的情况
  124. try {
  125. const lines = text.split(/\r?\n/).filter(line => line.trim() !== '');
  126. if (lines.length < 2) {
  127. showError('内容格式不正确,至少需要表头和一行数据!');
  128. return;
  129. }
  130. const header = lines[0].trim();
  131. const json = lines.slice(1).map(line => {
  132. const obj = {};
  133. obj[header] = parseValue(line.trim());
  134. return obj;
  135. });
  136. jsonOutput.value = JSON.stringify(json, null, 2);
  137. } catch (err) {
  138. showError('单列内容解析失败,请检查格式!');
  139. }
  140. } else {
  141. showError('仅支持CSV、TSV或单列表格的粘贴内容!');
  142. }
  143. });
  144. // 示例数据
  145. const EXAMPLES = {
  146. simple: `姓名,年龄,城市\n张三,18,北京\n李四,22,上海`,
  147. user: `ID,用户名,邮箱\n1,alice,[email protected]\n2,bob,[email protected]\n3,charlie,[email protected]`,
  148. score: `学号,姓名,数学,语文,英语\n1001,王小明,90,88,92\n1002,李小红,85,91,87\n1003,张大伟,78,80,85`
  149. };
  150. // 绑定"选择文件"a标签点击事件,触发文件选择
  151. const fileSelectLink = document.querySelector('.btn-file-input');
  152. if (fileSelectLink) {
  153. fileSelectLink.addEventListener('click', function(e) {
  154. e.preventDefault();
  155. fileInput.click();
  156. });
  157. }
  158. // 绑定示例按钮事件(只针对.link-btn)
  159. const exampleBtns = document.querySelectorAll('.link-btn');
  160. exampleBtns.forEach(btn => {
  161. btn.addEventListener('click', function(e) {
  162. e.preventDefault();
  163. const type = btn.getAttribute('data-example');
  164. if (EXAMPLES[type]) {
  165. pasteInput.value = EXAMPLES[type];
  166. clearError();
  167. jsonOutput.value = '';
  168. // 自动触发转换
  169. convertBtn.click();
  170. }
  171. });
  172. });
  173. // 复制按钮功能
  174. const copyBtn = document.getElementById('copyBtn');
  175. if (copyBtn) {
  176. copyBtn.addEventListener('click', function() {
  177. if (!jsonOutput.value) {
  178. showError('暂无内容可复制!');
  179. return;
  180. }
  181. jsonOutput.select();
  182. document.execCommand('copy');
  183. // 复制成功效果
  184. const originalText = copyBtn.innerHTML;
  185. copyBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
  186. copyBtn.style.background = '#27ae60';
  187. copyBtn.style.color = '#fff';
  188. copyBtn.style.borderColor = '#27ae60';
  189. setTimeout(() => {
  190. copyBtn.innerHTML = originalText;
  191. copyBtn.style.background = '';
  192. copyBtn.style.color = '';
  193. copyBtn.style.borderColor = '';
  194. }, 1500);
  195. clearError();
  196. });
  197. }
  198. // 打赏按钮
  199. const donateBtn = document.querySelector('.x-donate-link');
  200. if (donateBtn) {
  201. donateBtn.addEventListener('click', function(e) {
  202. e.preventDefault();
  203. e.stopPropagation();
  204. chrome.runtime.sendMessage({
  205. type: 'fh-dynamic-any-thing',
  206. thing: 'open-donate-modal',
  207. params: { toolName: 'excel2json' }
  208. });
  209. });
  210. }
  211. // 工具市场按钮
  212. const toolMarketBtn = document.querySelector('.x-other-tools');
  213. if (toolMarketBtn) {
  214. toolMarketBtn.addEventListener('click', function(e) {
  215. e.preventDefault();
  216. e.stopPropagation();
  217. chrome.runtime.openOptionsPage();
  218. });
  219. }
  220. // SQL Insert语句生成函数
  221. function jsonToSqlInsert(jsonArr, tableName = 'my_table') {
  222. if (!Array.isArray(jsonArr) || jsonArr.length === 0) return '';
  223. const keys = Object.keys(jsonArr[0]);
  224. // 多行合并为一条Insert
  225. const values = jsonArr.map(row =>
  226. '(' + keys.map(k => {
  227. const v = row[k];
  228. if (typeof v === 'number') {
  229. return v;
  230. } else {
  231. return `'${String(v).replace(/'/g, "''")}'`;
  232. }
  233. }).join(', ') + ')'
  234. ).join(',\n');
  235. return `INSERT INTO ${tableName} (${keys.join(', ')}) VALUES\n${values};`;
  236. }
  237. // 绑定SQL转换按钮
  238. const convertSqlBtn = document.getElementById('convertSqlBtn');
  239. if (convertSqlBtn) {
  240. convertSqlBtn.addEventListener('click', function () {
  241. clearError();
  242. const text = pasteInput.value.trim();
  243. if (!text) {
  244. showError('请上传文件或粘贴表格数据!');
  245. return;
  246. }
  247. let json = [];
  248. // 优先TSV
  249. if (text.includes('\t') && text.includes('\n')) {
  250. try {
  251. json = tsvToJson(text);
  252. } catch (err) {
  253. showError('粘贴内容(TSV)解析失败,请检查格式!');
  254. return;
  255. }
  256. } else if (text.includes(',') && text.includes('\n')) {
  257. try {
  258. json = csvToJson(text);
  259. } catch (err) {
  260. showError('粘贴内容(CSV)解析失败,请检查格式!');
  261. return;
  262. }
  263. } else if (!text.includes(',') && !text.includes('\t') && text.includes('\n')) {
  264. try {
  265. const lines = text.split(/\r?\n/).filter(line => line.trim() !== '');
  266. if (lines.length < 2) {
  267. showError('内容格式不正确,至少需要表头和一行数据!');
  268. return;
  269. }
  270. const header = lines[0].trim();
  271. json = lines.slice(1).map(line => {
  272. const obj = {};
  273. obj[header] = parseValue(line.trim());
  274. return obj;
  275. });
  276. } catch (err) {
  277. showError('单列内容解析失败,请检查格式!');
  278. return;
  279. }
  280. } else {
  281. showError('仅支持CSV、TSV或单列表格的粘贴内容!');
  282. return;
  283. }
  284. if (!json.length) {
  285. showError('没有可用数据生成SQL!');
  286. return;
  287. }
  288. // 默认表名my_table,可后续扩展让用户自定义
  289. const sql = jsonToSqlInsert(json, 'your_table_name');
  290. jsonOutput.value = sql;
  291. });
  292. }