2
0

nissaya_format_converter.blade.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>数据格式转换器</title>
  7. <style>
  8. * {
  9. margin: 0;
  10. padding: 0;
  11. box-sizing: border-box;
  12. }
  13. body {
  14. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  15. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  16. min-height: 100vh;
  17. color: #333;
  18. }
  19. .container {
  20. max-width: 1200px;
  21. margin: 0 auto;
  22. padding: 20px;
  23. }
  24. .header {
  25. text-align: center;
  26. margin-bottom: 30px;
  27. color: white;
  28. }
  29. .header h1 {
  30. font-size: 2.5rem;
  31. margin-bottom: 10px;
  32. text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
  33. }
  34. .header p {
  35. font-size: 1.1rem;
  36. opacity: 0.9;
  37. }
  38. .converter-card {
  39. background: white;
  40. border-radius: 20px;
  41. box-shadow: 0 20px 40px rgba(0,0,0,0.1);
  42. overflow: hidden;
  43. margin-bottom: 30px;
  44. backdrop-filter: blur(10px);
  45. }
  46. .input-section {
  47. padding: 30px;
  48. background: linear-gradient(135deg, #f8f9ff 0%, #e6f2ff 100%);
  49. border-bottom: 1px solid #e0e6ed;
  50. }
  51. .input-section h2 {
  52. color: #2d3748;
  53. margin-bottom: 15px;
  54. font-size: 1.3rem;
  55. }
  56. textarea {
  57. width: 100%;
  58. height: 250px;
  59. padding: 20px;
  60. border: 2px solid #e2e8f0;
  61. border-radius: 12px;
  62. font-size: 14px;
  63. line-height: 1.5;
  64. resize: vertical;
  65. transition: all 0.3s ease;
  66. background: white;
  67. font-family: 'Consolas', 'Monaco', monospace;
  68. }
  69. textarea:focus {
  70. outline: none;
  71. border-color: #667eea;
  72. box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
  73. }
  74. .controls {
  75. padding: 20px 30px;
  76. background: #f8fafc;
  77. display: flex;
  78. gap: 15px;
  79. flex-wrap: wrap;
  80. }
  81. .btn {
  82. padding: 12px 24px;
  83. border: none;
  84. border-radius: 10px;
  85. font-size: 14px;
  86. font-weight: 600;
  87. cursor: pointer;
  88. transition: all 0.3s ease;
  89. text-transform: uppercase;
  90. letter-spacing: 0.5px;
  91. }
  92. .btn-primary {
  93. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  94. color: white;
  95. }
  96. .btn-primary:hover {
  97. transform: translateY(-2px);
  98. box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
  99. }
  100. .btn-secondary {
  101. background: #e2e8f0;
  102. color: #4a5568;
  103. }
  104. .btn-secondary:hover {
  105. background: #cbd5e0;
  106. transform: translateY(-1px);
  107. }
  108. .btn-success {
  109. background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);
  110. color: white;
  111. }
  112. .btn-success:hover {
  113. transform: translateY(-2px);
  114. box-shadow: 0 10px 25px rgba(72, 187, 120, 0.3);
  115. }
  116. .output-section {
  117. padding: 30px;
  118. }
  119. .output-section h2 {
  120. color: #2d3748;
  121. margin-bottom: 20px;
  122. font-size: 1.3rem;
  123. }
  124. .table-container {
  125. background: #f8fafc;
  126. border-radius: 12px;
  127. padding: 20px;
  128. margin-bottom: 20px;
  129. overflow-x: auto;
  130. }
  131. table {
  132. width: 100%;
  133. border-collapse: collapse;
  134. background: white;
  135. border-radius: 8px;
  136. overflow: hidden;
  137. box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
  138. }
  139. th {
  140. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  141. color: white;
  142. padding: 15px 12px;
  143. text-align: left;
  144. font-weight: 600;
  145. font-size: 13px;
  146. text-transform: uppercase;
  147. letter-spacing: 0.5px;
  148. }
  149. td {
  150. padding: 12px;
  151. border-bottom: 1px solid #e2e8f0;
  152. vertical-align: top;
  153. font-size: 14px;
  154. line-height: 1.4;
  155. }
  156. tr:hover {
  157. background: #f7fafc;
  158. }
  159. .csv-output {
  160. background: #f8fafc;
  161. border-radius: 12px;
  162. padding: 20px;
  163. margin-top: 20px;
  164. }
  165. .csv-content {
  166. background: white;
  167. border: 1px solid #e2e8f0;
  168. border-radius: 8px;
  169. padding: 20px;
  170. font-family: 'Consolas', 'Monaco', monospace;
  171. font-size: 13px;
  172. line-height: 1.5;
  173. max-height: 300px;
  174. overflow-y: auto;
  175. white-space: pre-wrap;
  176. }
  177. .status-message {
  178. padding: 15px;
  179. border-radius: 8px;
  180. margin-bottom: 20px;
  181. font-weight: 500;
  182. }
  183. .status-success {
  184. background: #f0fff4;
  185. color: #38a169;
  186. border: 1px solid #9ae6b4;
  187. }
  188. .status-error {
  189. background: #fed7d7;
  190. color: #e53e3e;
  191. border: 1px solid #feb2b2;
  192. }
  193. .sample-data {
  194. background: #e6fffa;
  195. border: 1px solid #81e6d9;
  196. border-radius: 8px;
  197. padding: 15px;
  198. margin-top: 20px;
  199. }
  200. .sample-data h3 {
  201. color: #2d3748;
  202. margin-bottom: 10px;
  203. font-size: 1rem;
  204. }
  205. .sample-data code {
  206. background: #f7fafc;
  207. padding: 2px 6px;
  208. border-radius: 4px;
  209. font-family: 'Consolas', 'Monaco', monospace;
  210. font-size: 12px;
  211. }
  212. @media (max-width: 768px) {
  213. .container {
  214. padding: 10px;
  215. }
  216. .header h1 {
  217. font-size: 2rem;
  218. }
  219. .controls {
  220. flex-direction: column;
  221. }
  222. .btn {
  223. width: 100%;
  224. }
  225. }
  226. </style>
  227. </head>
  228. <body>
  229. <div class="container">
  230. <div class="header">
  231. <h1>📊 数据格式转换器</h1>
  232. <p>将结构化文本数据转换为CSV格式,支持实时预览</p>
  233. </div>
  234. <div class="converter-card">
  235. <div class="input-section">
  236. <h2>📝 输入数据</h2>
  237. <textarea id="inputData" placeholder="请粘贴您的数据...
  238. 示例格式:
  239. **No**
  240. **Myanmar Pāḷi**
  241. **Nissaya**
  242. **Roman Pāḷi**
  243. **Translation**
  244. **Remark**
  245. 1
  246. ဧဝံ မေ သုတန္တိအာဒိ၊
  247. ဧဝံမေသုတံ ဤသို့အစရှိသောစကားသည်။
  248. Evaṃ me sutantiādi,
  249. The word 'evam me sutam, etc.'
  250. 2
  251. ကန္ဒရကသုတ္တံ၊
  252. ကန္ဒရကသုတ်တည်း။
  253. kandarakasuttaṃ,
  254. is the Kandaraka sutta"></textarea>
  255. </div>
  256. <div class="controls">
  257. <button class="btn btn-primary" onclick="convertData()">🔄 转换数据</button>
  258. <button class="btn btn-secondary" onclick="clearData()">🗑️ 清空数据</button>
  259. <button class="btn btn-success" onclick="downloadCSV()" id="downloadBtn" disabled>📥 下载CSV</button>
  260. </div>
  261. <div class="output-section">
  262. <h2>📋 转换结果</h2>
  263. <div id="statusMessage"></div>
  264. <div id="tablePreview"></div>
  265. <div id="csvOutput"></div>
  266. </div>
  267. </div>
  268. <div class="sample-data">
  269. <h3>💡 使用说明</h3>
  270. <p>1. 请确保输入数据格式正确,表头用 <code>**标题**</code> 格式标记</p>
  271. <p>2. 数据行按顺序排列,每行一个字段</p>
  272. <p>3. 支持多语言文本,包括缅甸语、巴利语等</p>
  273. <p>4. 转换后可预览表格并下载CSV文件</p>
  274. </div>
  275. </div>
  276. <script>
  277. let csvData = '';
  278. let tableData = [];
  279. function showStatus(message, type = 'success') {
  280. const statusDiv = document.getElementById('statusMessage');
  281. statusDiv.innerHTML = `<div class="status-message status-${type}">${message}</div>`;
  282. setTimeout(() => {
  283. statusDiv.innerHTML = '';
  284. }, 5000);
  285. }
  286. function parseData(text) {
  287. const lines = text.trim().split('\n').filter(line => line.trim() !== '');
  288. // 提取表头
  289. const headers = [];
  290. let i = 0;
  291. while (i < lines.length) {
  292. const line = lines[i].trim();
  293. if (line.startsWith('**') && line.endsWith('**')) {
  294. headers.push(line.slice(2, -2));
  295. i++;
  296. } else {
  297. break;
  298. }
  299. }
  300. if (headers.length === 0) {
  301. throw new Error('未找到表头,请确保表头格式为 **标题**');
  302. }
  303. // 解析数据行
  304. const data = [];
  305. let currentRow = [];
  306. for (let j = i; j < lines.length; j++) {
  307. const line = lines[j].trim();
  308. // 检查是否是新行的开始(数字开头)
  309. if (/^\d+$/.test(line)) {
  310. // 如果当前行有数据,先保存
  311. if (currentRow.length > 0) {
  312. // 补齐缺失的列
  313. while (currentRow.length < headers.length) {
  314. currentRow.push('');
  315. }
  316. data.push([...currentRow]);
  317. }
  318. // 开始新行
  319. currentRow = [line];
  320. } else if (line !== '') {
  321. // 添加到当前行
  322. currentRow.push(line);
  323. }
  324. }
  325. // 添加最后一行
  326. if (currentRow.length > 0) {
  327. // 补齐缺失的列
  328. while (currentRow.length < headers.length) {
  329. currentRow.push('');
  330. }
  331. data.push(currentRow);
  332. }
  333. return { headers, data };
  334. }
  335. function generateCSV(headers, data) {
  336. const csvRows = [];
  337. // 添加表头
  338. csvRows.push(headers.map(header => `"${header.replace(/"/g, '""')}"`).join(','));
  339. // 添加数据行
  340. data.forEach(row => {
  341. const csvRow = row.map(cell => `"${cell.replace(/"/g, '""')}"`).join(',');
  342. csvRows.push(csvRow);
  343. });
  344. return csvRows.join('\n');
  345. }
  346. function createTable(headers, data) {
  347. if (data.length === 0) {
  348. return '<p>没有数据可显示</p>';
  349. }
  350. let html = '<div class="table-container"><table>';
  351. // 表头
  352. html += '<thead><tr>';
  353. headers.forEach(header => {
  354. html += `<th>${header}</th>`;
  355. });
  356. html += '</tr></thead>';
  357. // 数据行
  358. html += '<tbody>';
  359. data.forEach(row => {
  360. html += '<tr>';
  361. row.forEach(cell => {
  362. html += `<td>${cell}</td>`;
  363. });
  364. html += '</tr>';
  365. });
  366. html += '</tbody>';
  367. html += '</table></div>';
  368. return html;
  369. }
  370. function convertData() {
  371. const inputText = document.getElementById('inputData').value.trim();
  372. if (!inputText) {
  373. showStatus('请输入数据', 'error');
  374. return;
  375. }
  376. try {
  377. const { headers, data } = parseData(inputText);
  378. console.log('解析结果:', { headers, data }); // 调试信息
  379. if (data.length === 0) {
  380. showStatus('未找到有效数据行', 'error');
  381. return;
  382. }
  383. // 生成CSV
  384. csvData = generateCSV(headers, data);
  385. // 生成表格预览
  386. const tableHTML = createTable(headers, data);
  387. document.getElementById('tablePreview').innerHTML = tableHTML;
  388. // 显示CSV内容
  389. const csvHTML = `
  390. <div class="csv-output">
  391. <h3>📄 CSV格式预览</h3>
  392. <div class="csv-content">${csvData}</div>
  393. </div>
  394. `;
  395. document.getElementById('csvOutput').innerHTML = csvHTML;
  396. // 启用下载按钮
  397. document.getElementById('downloadBtn').disabled = false;
  398. showStatus(`转换成功!共转换 ${data.length} 行数据,${headers.length} 个字段`, 'success');
  399. } catch (error) {
  400. showStatus(`转换失败:${error.message}`, 'error');
  401. console.error('转换错误:', error);
  402. }
  403. }
  404. function clearData() {
  405. document.getElementById('inputData').value = '';
  406. document.getElementById('tablePreview').innerHTML = '';
  407. document.getElementById('csvOutput').innerHTML = '';
  408. document.getElementById('downloadBtn').disabled = true;
  409. csvData = '';
  410. showStatus('数据已清空', 'success');
  411. }
  412. function downloadCSV() {
  413. if (!csvData) {
  414. showStatus('没有可下载的数据', 'error');
  415. return;
  416. }
  417. // 添加BOM以支持Excel中的UTF-8显示
  418. const BOM = '\uFEFF';
  419. const blob = new Blob([BOM + csvData], { type: 'text/csv;charset=utf-8;' });
  420. const link = document.createElement('a');
  421. if (link.download !== undefined) {
  422. const url = URL.createObjectURL(blob);
  423. link.setAttribute('href', url);
  424. link.setAttribute('download', `converted_data_${new Date().toISOString().slice(0, 10)}.csv`);
  425. link.style.visibility = 'hidden';
  426. document.body.appendChild(link);
  427. link.click();
  428. document.body.removeChild(link);
  429. showStatus('CSV文件下载完成', 'success');
  430. }
  431. }
  432. // 自动加载示例数据
  433. document.addEventListener('DOMContentLoaded', function() {
  434. const sampleData = `**No**
  435. **Myanmar Pāḷi**
  436. **Nissaya**
  437. **Roman Pāḷi**
  438. **Translation**
  439. **Remark**
  440. 1
  441. ဧဝံ မေ သုတန္တိအာဒိ၊
  442. ဧဝံမေသုတံ ဤသို့အစရှိသောစကားသည်။
  443. Evaṃ me sutantiādi,
  444. The word 'evam me sutam, etc.'
  445. 2
  446. ကန္ဒရကသုတ္တံ၊
  447. ကန္ဒရကသုတ်တည်း။
  448. kandarakasuttaṃ,
  449. is the Kandaraka sutta
  450. `;
  451. document.getElementById('inputData').value = sampleData;
  452. });
  453. </script>
  454. </body>
  455. </html>