index.js 11 KB

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