upload.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  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>Word模板替换</title>
  7. <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  8. <!-- 添加docx-preview库 -->
  9. <script src="https://unpkg.com/docx-preview@0.1.18/dist/docx-preview.js"></script>
  10. <style>
  11. /* 全局样式 */
  12. body {
  13. font-family: Arial, sans-serif;
  14. max-width: 800px;
  15. margin: 0 auto;
  16. padding: 20px;
  17. background-color: #f5f5f5;
  18. }
  19. .container {
  20. background-color: white;
  21. padding: 20px;
  22. border-radius: 8px;
  23. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  24. }
  25. h1 {
  26. color: #333;
  27. text-align: center;
  28. }
  29. /* 表单样式 */
  30. .form {
  31. margin-top: 20px;
  32. }
  33. .form-group {
  34. margin-bottom: 15px;
  35. }
  36. label {
  37. display: block;
  38. margin-bottom: 5px;
  39. font-weight: bold;
  40. }
  41. .text-input {
  42. width: 100%;
  43. padding: 8px 10px;
  44. border: 1px solid #ddd;
  45. border-radius: 4px;
  46. box-sizing: border-box;
  47. }
  48. textarea.text-input {
  49. resize: vertical;
  50. min-height: 60px;
  51. font-family: Arial, sans-serif;
  52. }
  53. /* 按钮样式 */
  54. .btn {
  55. background-color: #4caf50;
  56. color: white;
  57. padding: 12px 20px;
  58. border: none;
  59. border-radius: 4px;
  60. cursor: pointer;
  61. font-size: 16px;
  62. display: block;
  63. width: 100%;
  64. margin-top: 20px;
  65. }
  66. .btn:hover {
  67. background-color: #45a049;
  68. }
  69. .btn:disabled {
  70. background-color: #cccccc;
  71. cursor: not-allowed;
  72. }
  73. /* 状态消息样式 */
  74. .status {
  75. margin-top: 20px;
  76. padding: 15px;
  77. border-radius: 4px;
  78. display: none;
  79. }
  80. .success {
  81. background-color: #dff0d8;
  82. color: #3c763d;
  83. border: 1px solid #d6e9c6;
  84. }
  85. .error {
  86. background-color: #f2dede;
  87. color: #a94442;
  88. border: 1px solid #ebccd1;
  89. }
  90. /* 下载链接样式 */
  91. .download-link {
  92. display: inline-block;
  93. margin-top: 15px;
  94. padding: 8px 15px;
  95. background-color: #337ab7;
  96. color: white;
  97. text-decoration: none;
  98. border-radius: 4px;
  99. }
  100. .download-link:hover {
  101. background-color: #286090;
  102. }
  103. /* 字段操作样式 */
  104. .field-actions {
  105. margin-top: 20px;
  106. text-align: center;
  107. }
  108. .add-field-btn {
  109. background-color: #5bc0de;
  110. color: white;
  111. padding: 8px 15px;
  112. border: none;
  113. border-radius: 4px;
  114. cursor: pointer;
  115. margin-right: 10px;
  116. }
  117. .add-field-btn:hover {
  118. background-color: #46b8da;
  119. }
  120. .field-row {
  121. display: flex;
  122. margin-bottom: 10px;
  123. align-items: flex-start;
  124. }
  125. .field-key {
  126. flex: 1;
  127. margin-right: 10px;
  128. }
  129. .field-value {
  130. flex: 2;
  131. margin-right: 10px;
  132. }
  133. .field-label {
  134. width: 100px;
  135. text-align: left;
  136. color: #666;
  137. font-size: 14px;
  138. }
  139. .remove-field-btn {
  140. background-color: #d9534f;
  141. color: white;
  142. border: none;
  143. border-radius: 4px;
  144. cursor: pointer;
  145. padding: 5px 10px;
  146. }
  147. .remove-field-btn:hover {
  148. background-color: #c9302c;
  149. }
  150. /* 预览按钮样式 */
  151. .preview-btn {
  152. display: inline-block;
  153. margin-top: 15px;
  154. margin-left: 10px;
  155. padding: 8px 15px;
  156. background-color: #f0ad4e;
  157. color: white;
  158. text-decoration: none;
  159. border-radius: 4px;
  160. cursor: pointer;
  161. }
  162. .preview-btn:hover {
  163. background-color: #ec971f;
  164. }
  165. /* 预览容器样式 */
  166. #previewContainer {
  167. margin-top: 20px;
  168. padding: 20px;
  169. border: 1px solid #ddd;
  170. border-radius: 4px;
  171. display: none;
  172. background-color: white;
  173. max-height: 500px;
  174. overflow-y: auto;
  175. }
  176. /* 文档操作区域样式 */
  177. .doc-actions {
  178. display: flex;
  179. justify-content: center;
  180. gap: 10px;
  181. }
  182. /* 加载中样式 */
  183. .loading {
  184. text-align: center;
  185. padding: 20px;
  186. font-style: italic;
  187. color: #666;
  188. }
  189. </style>
  190. </head>
  191. <body>
  192. <div class="container">
  193. <h1>Word模板替换系统</h1>
  194. <div class="form">
  195. <h3>输入模板变量</h3>
  196. <p>系统将使用固定模板,根据下方输入的字段自动替换模板中的变量。</p>
  197. <p>
  198. 请填写以下字段,系统将自动替换模板中对应的变量(如{name}、{address}等)。
  199. </p>
  200. <div id="fieldsContainer">
  201. <!-- 动态字段将在这里添加 -->
  202. <div class="field-row">
  203. <input
  204. type="text"
  205. class="text-input field-key"
  206. placeholder="字段名"
  207. value="person1"
  208. readonly
  209. />
  210. <input
  211. type="text"
  212. class="text-input field-value"
  213. placeholder="甲方"
  214. value="杭州遁地科技"
  215. />
  216. <span class="field-label">甲方</span>
  217. <button
  218. class="remove-field-btn"
  219. onclick="removeField(this)"
  220. style="visibility: hidden"
  221. >
  222. 删除
  223. </button>
  224. </div>
  225. <div class="field-row">
  226. <input
  227. type="text"
  228. class="text-input field-key"
  229. placeholder="字段名"
  230. value="person2"
  231. readonly
  232. />
  233. <input
  234. type="text"
  235. class="text-input field-value"
  236. placeholder="乙方"
  237. value="杭州飞天科技"
  238. />
  239. <span class="field-label">乙方</span>
  240. <button
  241. class="remove-field-btn"
  242. onclick="removeField(this)"
  243. style="visibility: hidden"
  244. >
  245. 删除
  246. </button>
  247. </div>
  248. <div class="field-row">
  249. <input
  250. type="text"
  251. class="text-input field-key"
  252. placeholder="字段名"
  253. value="name"
  254. readonly
  255. />
  256. <input
  257. type="text"
  258. class="text-input field-value"
  259. placeholder="货物名称"
  260. />
  261. <span class="field-label">货物名称</span>
  262. <button
  263. class="remove-field-btn"
  264. onclick="removeField(this)"
  265. style="visibility: hidden"
  266. >
  267. 删除
  268. </button>
  269. </div>
  270. <div class="field-row">
  271. <input
  272. type="text"
  273. class="text-input field-key"
  274. placeholder="字段名"
  275. value="address"
  276. readonly
  277. />
  278. <input
  279. type="text"
  280. class="text-input field-value"
  281. placeholder="请输入原产地"
  282. />
  283. <span class="field-label">原产地</span>
  284. <button
  285. class="remove-field-btn"
  286. onclick="removeField(this)"
  287. style="visibility: hidden"
  288. >
  289. 删除
  290. </button>
  291. </div>
  292. <div class="field-row">
  293. <input
  294. type="text"
  295. class="text-input field-key"
  296. placeholder="字段名"
  297. value="address1"
  298. readonly
  299. />
  300. <input
  301. type="text"
  302. class="text-input field-value"
  303. placeholder="请输入目的地口岸"
  304. />
  305. <span class="field-label">目的地口岸</span>
  306. <button
  307. class="remove-field-btn"
  308. onclick="removeField(this)"
  309. style="visibility: hidden"
  310. >
  311. 删除
  312. </button>
  313. </div>
  314. <div class="field-row">
  315. <input
  316. type="text"
  317. class="text-input field-key"
  318. placeholder="字段名"
  319. value="addType"
  320. readonly
  321. />
  322. <input
  323. type="text"
  324. class="text-input field-value"
  325. placeholder="请输入交货方式"
  326. />
  327. <span class="field-label">交货方式</span>
  328. <button
  329. class="remove-field-btn"
  330. onclick="removeField(this)"
  331. style="visibility: hidden"
  332. >
  333. 删除
  334. </button>
  335. </div>
  336. <div class="field-row" style="height: 300px">
  337. <input
  338. type="text"
  339. class="text-input field-key"
  340. placeholder="字段名"
  341. value="productGuide"
  342. readonly
  343. />
  344. <textarea
  345. class="text-input field-value"
  346. placeholder="请输入产品规格"
  347. rows="15"
  348. >
  349. 我是一瓶番茄酱+沙拉酱
  350. 我是一瓶番茄酱+沙拉酱
  351. 我是一瓶番茄酱+沙拉酱
  352. 我是一瓶番茄酱+沙拉酱
  353. 我是一瓶番茄酱+沙拉酱
  354. 我是一瓶番茄酱+沙拉酱
  355. 我是一瓶番茄酱+沙拉酱
  356. 我是一瓶番茄酱+沙拉酱
  357. 我是一瓶番茄酱+沙拉酱
  358. 我是一瓶番茄酱+沙拉酱
  359. 我是一瓶番茄酱+沙拉酱
  360. 我是一瓶番茄酱+沙拉酱
  361. 我是一瓶番茄酱+沙拉酱
  362. 我是一瓶番茄酱+沙拉酱
  363. 我是一瓶番茄酱+沙拉酱
  364. </textarea
  365. >
  366. <span class="field-label">产品规格</span>
  367. <button
  368. class="remove-field-btn"
  369. onclick="removeField(this)"
  370. style="visibility: hidden"
  371. >
  372. 删除
  373. </button>
  374. </div>
  375. </div>
  376. <div class="field-actions">
  377. <!-- 移除添加字段按钮 -->
  378. </div>
  379. <button id="generateBtn" class="btn">生成文档</button>
  380. </div>
  381. <!-- 状态消息区域 -->
  382. <div id="statusMessage" class="status"></div>
  383. <!-- 下载区域 -->
  384. <div id="downloadSection" style="display: none; margin-top: 20px">
  385. <p>文档已成功生成:</p>
  386. <div class="doc-actions">
  387. <a id="downloadLink" href="#" class="download-link" download>下载文档</a>
  388. <button id="previewBtn" class="preview-btn">预览文档</button>
  389. </div>
  390. </div>
  391. <!-- 文档预览区域 -->
  392. <div id="previewContainer">
  393. <div id="previewLoading" class="loading">正在加载预览,请稍候...</div>
  394. <div id="previewContent"></div>
  395. </div>
  396. </div>
  397. <script>
  398. // DOM元素引用
  399. const generateBtn = document.getElementById("generateBtn");
  400. const statusMessage = document.getElementById("statusMessage");
  401. const downloadSection = document.getElementById("downloadSection");
  402. const downloadLink = document.getElementById("downloadLink");
  403. const fieldsContainer = document.getElementById("fieldsContainer");
  404. const previewBtn = document.getElementById("previewBtn");
  405. const previewContainer = document.getElementById("previewContainer");
  406. const previewLoading = document.getElementById("previewLoading");
  407. const previewContent = document.getElementById("previewContent");
  408. // 当前文件名
  409. let currentFileName = null;
  410. // 获取当前页面的主机名和端口,用于API请求
  411. const apiBaseUrl = `${window.location.protocol}//${window.location.hostname}:5000`;
  412. // 收集所有字段
  413. function collectFields() {
  414. const data = {};
  415. const fieldRows = fieldsContainer.querySelectorAll(".field-row");
  416. fieldRows.forEach((row) => {
  417. const key = row.querySelector(".field-key").value.trim();
  418. const value = row.querySelector(".field-value").value.trim();
  419. if (key) {
  420. data[key] = value;
  421. }
  422. });
  423. return data;
  424. }
  425. // 生成文档
  426. generateBtn.addEventListener("click", function (e) {
  427. e.preventDefault();
  428. // 收集字段数据
  429. const data = collectFields();
  430. // 更新UI状态为处理中
  431. generateBtn.disabled = true;
  432. generateBtn.textContent = "处理中...";
  433. statusMessage.className = "status";
  434. statusMessage.style.display = "none";
  435. // 隐藏预览容器
  436. previewContainer.style.display = "none";
  437. // 发送请求到Flask后端
  438. axios
  439. .post(`${apiBaseUrl}/process_file`, data, {
  440. headers: {
  441. "Content-Type": "application/json",
  442. },
  443. })
  444. .then((response) => {
  445. // 处理成功响应
  446. statusMessage.className = "status success";
  447. statusMessage.style.display = "block";
  448. statusMessage.textContent = `成功: ${response.data.message}`;
  449. console.log("成功:", response.data);
  450. // 保存当前文件名
  451. currentFileName = response.data.output_filename;
  452. // 更新下载链接
  453. downloadLink.href = `${apiBaseUrl}/download-attachment/${currentFileName}`;
  454. // 显示下载部分
  455. downloadSection.style.display = "block";
  456. })
  457. .catch((error) => {
  458. // 处理错误响应
  459. statusMessage.className = "status error";
  460. statusMessage.style.display = "block";
  461. if (error.response) {
  462. statusMessage.textContent = `错误: ${
  463. error.response.data.error || "处理失败"
  464. }`;
  465. } else {
  466. statusMessage.textContent = "错误: 服务器连接失败";
  467. }
  468. console.error("处理错误:", error);
  469. })
  470. .finally(() => {
  471. // 恢复按钮状态
  472. generateBtn.disabled = false;
  473. generateBtn.textContent = "生成文档";
  474. });
  475. });
  476. // 预览文档
  477. previewBtn.addEventListener("click", function(e) {
  478. e.preventDefault();
  479. if (!currentFileName) {
  480. alert("没有可预览的文档");
  481. return;
  482. }
  483. // 显示预览容器和加载状态
  484. previewContainer.style.display = "block";
  485. previewLoading.style.display = "block";
  486. previewContent.innerHTML = "";
  487. // 获取文档二进制数据
  488. axios.get(`${apiBaseUrl}/download/${currentFileName}`, {
  489. responseType: 'arraybuffer'
  490. })
  491. .then(response => {
  492. // 使用docx-preview渲染文档
  493. window.docx.renderAsync(response.data, previewContent, null, {
  494. className: 'docx-viewer',
  495. inWrapper: true
  496. })
  497. .then(() => {
  498. // 预览加载完成,隐藏加载状态
  499. previewLoading.style.display = "none";
  500. // 滚动到预览区域
  501. previewContainer.scrollIntoView({ behavior: 'smooth' });
  502. })
  503. .catch(error => {
  504. console.error('预览渲染错误:', error);
  505. previewContent.innerHTML = `<div class="error">预览加载失败: ${error.message}</div>`;
  506. previewLoading.style.display = "none";
  507. });
  508. })
  509. .catch(error => {
  510. console.error('文档下载错误:', error);
  511. previewContent.innerHTML = `<div class="error">文档下载失败</div>`;
  512. previewLoading.style.display = "none";
  513. });
  514. });
  515. </script>
  516. </body>
  517. </html>