docxTemplateDialog.vue 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545
  1. <template>
  2. <el-dialog
  3. :visible.sync="show"
  4. :append-to-body="true"
  5. :title="dialogTitle"
  6. width="60%"
  7. top="10vh"
  8. :fullscreen="true"
  9. :before-close="handleClose"
  10. class="dialog_diy"
  11. ref="docxTemplateDialogRef"
  12. >
  13. <div class="box" v-loading="loading">
  14. <div
  15. class="b_left"
  16. v-loading="wordContentLoading"
  17. ref="wordAreaRef"
  18. :contenteditable="downFileData"
  19. >
  20. <VueOfficeDocx
  21. ref="vueOfficeDocxRef"
  22. v-if="downFileData && !downFileData.txtUrl"
  23. :src="downFileData ? downFileData.url : ''"
  24. />
  25. <txtHtmlView
  26. ref="txtHtmlViewRef"
  27. v-if="downFileData && downFileData.txtUrl"
  28. :url="downFileData.txtUrl"
  29. @getTxtContent="getTxtContent"
  30. />
  31. <div class="cover_box" v-if="!downFileData">
  32. <el-button type="primary" @click.stop="uploadWord"
  33. >上传文档</el-button
  34. >
  35. </div>
  36. </div>
  37. <div class="b_right">
  38. <div class="d_box">
  39. <!-- <div class="d_b_step">
  40. <h2>第一步:下载模板文档</h2>
  41. <p>点击下载模板文档来下载指定文档</p>
  42. <el-button
  43. class="d_b_s_button"
  44. type="primary"
  45. :disabled="!downFileData"
  46. @click="downloadTemplateDocx()"
  47. >下载模板文档</el-button
  48. >
  49. </div> -->
  50. <div class="d_b_step">
  51. <h2>第一步:填入模板变量</h2>
  52. <p>按需制作排版Word模板文档,目前支持docx格式</p>
  53. <p>
  54. 在文件中指定位置输入下方的"字段变量",用于显示真实数据的准确位置
  55. </p>
  56. <h3>文本:</h3>
  57. <img
  58. src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_202410090922471728436994846.png"
  59. />
  60. <h3>选择题:</h3>
  61. <div class="d_b_s_imgList">
  62. <img
  63. src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/41728546186962.png"
  64. />
  65. <img
  66. src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/51728546189479.png"
  67. />
  68. </div>
  69. <div class="foldMenu">
  70. <span v-if="foldField" @click="foldField = false"
  71. >折叠字段变量</span
  72. >
  73. <span v-if="!foldField" @click="foldField = true"
  74. >显示字段变量</span
  75. >
  76. </div>
  77. <div
  78. v-for="(item, index) in fieldList"
  79. :key="index"
  80. class="d_b_s_fieldListItem"
  81. v-if="foldField"
  82. >
  83. <span
  84. >{{ item.name }}:{{
  85. "{" + (item.type == "image" ? "%" : "") + item.field + "}"
  86. }}</span
  87. >
  88. <span
  89. @click="
  90. copyContent(
  91. `{${item.type == 'image' ? '%' : ''}${item.field}}`
  92. )
  93. "
  94. >
  95. <svg
  96. width="14"
  97. height="14"
  98. viewBox="0 0 14 14"
  99. xmlns="http://www.w3.org/2000/svg"
  100. >
  101. <path
  102. fill-rule="evenodd"
  103. clip-rule="evenodd"
  104. d="M1.85645 2.28599C1.85645 2.0493 2.04832 1.85742 2.28502 1.85742H8.71359C8.95028 1.85742 9.14216 2.0493 9.14216 2.28599C9.14216 2.52269 8.95028 2.71456 8.71359 2.71456H2.71359V8.71456C2.71359 8.95126 2.52171 9.14314 2.28502 9.14314C2.04832 9.14314 1.85645 8.95126 1.85645 8.71456V2.28599Z"
  105. />
  106. <path
  107. fill-rule="evenodd"
  108. clip-rule="evenodd"
  109. d="M4.42871 4.85631C4.42871 4.61961 4.62059 4.42773 4.85728 4.42773H11.7144C11.9511 4.42773 12.143 4.61961 12.143 4.85631V11.7134C12.143 11.9501 11.9511 12.142 11.7144 12.142H4.85728C4.62059 12.142 4.42871 11.9501 4.42871 11.7134V4.85631ZM5.28585 5.28488V11.2849H11.2859V5.28488H5.28585Z"
  110. />
  111. </svg>
  112. </span>
  113. </div>
  114. </div>
  115. <!-- <div class="d_b_step">
  116. <h2>第三步:上传填入后的模板文档</h2>
  117. <div v-if="uploadTemplateDocxData" class="d_b_s_fileCard">
  118. <svg
  119. t="1728376433028"
  120. class="icon"
  121. viewBox="0 0 1024 1024"
  122. version="1.1"
  123. xmlns="http://www.w3.org/2000/svg"
  124. p-id="5172"
  125. width="200"
  126. height="200"
  127. >
  128. <path
  129. d="M815.5 160v704c0 17.7-14.3 32-32 32h-543c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h543c17.7 0 32 14.3 32 32z m0-96h-607c-35.3 0-64 28.7-64 64v768c0 35.3 28.7 64 64 64h607c35.3 0 64-28.7 64-64V128c0-35.3-28.7-64-64-64z"
  130. p-id="5173"
  131. ></path>
  132. <path
  133. d="M703.5 320h-384c-17.7 0-32-14.3-32-32s14.3-32 32-32h384c17.7 0 32 14.3 32 32s-14.3 32-32 32zM703.5 512h-384c-17.7 0-32-14.3-32-32s14.3-32 32-32h384c17.7 0 32 14.3 32 32s-14.3 32-32 32zM511.5 704h-192c-17.7 0-32-14.3-32-32s14.3-32 32-32h192c17.7 0 32 14.3 32 32s-14.3 32-32 32z"
  134. p-id="5174"
  135. ></path>
  136. </svg>
  137. <span>{{ uploadTemplateDocxData.name }}</span>
  138. <span class="d_b_s_f_c_del" @click="clearUploadTemplateDocxData()"
  139. >删除</span
  140. >
  141. </div>
  142. <el-button
  143. class="d_b_s_button"
  144. type="primary"
  145. :disabled="uploadTemplateDocxData != null"
  146. @click="uploadTemplateDocx()"
  147. >上传填入后的模板文档</el-button
  148. >
  149. </div> -->
  150. <div class="d_b_step">
  151. <h2>第二步:点击导出</h2>
  152. <p></p>
  153. <el-button
  154. class="d_b_s_button"
  155. type="primary"
  156. :disabled="!downFileData"
  157. @click="exportDocx3()"
  158. >导出Word文档</el-button
  159. >
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. </el-dialog>
  165. </template>
  166. <script>
  167. import PizZip from "pizzip";
  168. import Docxtemplater from "docxtemplater";
  169. import ImageModule from "docxtemplater-image-module-free";
  170. import { saveAs } from "file-saver";
  171. import topicVue from "../../testStudent/view/component/topic.vue";
  172. // import { renderAsync } from 'docx-preview/dist/docx-preview.js'
  173. import VueOfficeDocx from "@vue-office/docx";
  174. import "@vue-office/docx/lib/index.css";
  175. // import { renderAsync } from 'docx-preview/dist/docx-preview.js'
  176. import htmlDocx from "html-docx-js/dist/html-docx";
  177. import txtHtmlView from "./txtHtmlView.vue";
  178. import JSZip from "jszip";
  179. // import a from './docx-preview.js'
  180. const getFile = url => {
  181. return new Promise((resolve, reject) => {
  182. var credentials = {
  183. accessKeyId: "AKIATLPEDU37QV5CHLMH",
  184. secretAccessKey: "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"
  185. }; //秘钥形式的登录上传
  186. window.AWS.config.update(credentials);
  187. window.AWS.config.region = "cn-northwest-1"; //设置区域
  188. let url2 = url;
  189. let _url2 = "";
  190. if (
  191. url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
  192. ) {
  193. _url2 = url2.split(
  194. "https://view.officeapps.live.com/op/view.aspx?src="
  195. )[1];
  196. } else {
  197. _url2 = url2;
  198. }
  199. var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
  200. let name = decodeURIComponent(
  201. _url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]
  202. );
  203. var params = {
  204. Bucket: "ccrb",
  205. Key: name
  206. };
  207. s3.getObject(params, function(err, data) {
  208. if (err) {
  209. console.log(err, err.stack);
  210. resolve({ data: 1 });
  211. } else {
  212. resolve({ data: data.Body });
  213. console.log(data);
  214. }
  215. });
  216. });
  217. };
  218. export default {
  219. props: {},
  220. components: {
  221. topicVue,
  222. VueOfficeDocx,
  223. txtHtmlView
  224. },
  225. data() {
  226. return {
  227. show: false,
  228. loading: false,
  229. fieldList: [
  230. // { name: "第五题", field: "ti_05", type: "text", value: "第五题ti_05" },
  231. // {
  232. // name: "图片1",
  233. // field: "image_01",
  234. // type: "image",
  235. // value:
  236. // "https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_202410090922471728436994846.png"
  237. // }
  238. ],
  239. useFieldList: [],
  240. uploadTemplateDocxData: null, //上传的模板文档
  241. downFileData: null, // 下载模板文档的url
  242. checkJson: [],
  243. foldField: true,
  244. wordContent: "",
  245. wordContentLoading: true,
  246. courseId: "",
  247. testJson: null
  248. // fileList:[],
  249. };
  250. },
  251. methods: {
  252. handleClose(done) {
  253. this.close();
  254. done();
  255. },
  256. open(data) {
  257. this.init();
  258. console.log(data);
  259. let _fileData = data.fileData;
  260. this.downFileData = _fileData;
  261. this.courseId = data.courseId;
  262. this.checkJson = data.formData[0].array;
  263. this.testJson = data.testJson;
  264. this.fieldList = this.getFieldData(data.formData[0].array);
  265. let useFieldList = [];
  266. data.formData.forEach(i => {
  267. let obj = {
  268. name: i.name,
  269. id: i.id,
  270. userId: i.userid,
  271. fieldList: this.getFieldData(i.array),
  272. time: this.formatTime(i.time)
  273. };
  274. useFieldList.push(obj);
  275. });
  276. this.useFieldList = useFieldList;
  277. this.getWordContent(this.downFileData);
  278. this.show = true;
  279. },
  280. close() {
  281. this.show = false;
  282. this.init();
  283. },
  284. init() {
  285. // 初始化
  286. this.downFileData = null;
  287. this.fieldList = [];
  288. this.uploadTemplateDocxData = null;
  289. this.foldField = true;
  290. // this.fileList = [];
  291. },
  292. getFieldData(array) {
  293. let _list = [];
  294. let _index = 0;
  295. for (let i = 0; i < array.length; i++) {
  296. let _item = array[i];
  297. if (_item.type == 3) {
  298. //问答题
  299. let _item2 = _item.json;
  300. _list.push({
  301. name: _item2.title,
  302. field: `ti_${_index}`,
  303. type: "text",
  304. value: _item2.answer2
  305. });
  306. _index++;
  307. } else if (_item.type == 1) {
  308. //单选题
  309. let _item2 = _item.json;
  310. let choseTxt = ``;
  311. _item2.array.forEach((i, index2) => {
  312. choseTxt += `${
  313. _item2.answer2 === index2 || _item2.answer2.includes(index2)
  314. ? "☑"+i.option
  315. : ""
  316. }`;
  317. });
  318. _list.push({
  319. name: _item2.title,
  320. field: `ti_${_index}`,
  321. type: "text",
  322. value: choseTxt
  323. });
  324. _index++;
  325. } else if (_item.type == 8) {
  326. //日期
  327. let _item2 = _item.json;
  328. _list.push({
  329. name: _item2.title,
  330. field: `ti_${_index}`,
  331. type: "text",
  332. value: _item2.answer2
  333. });
  334. _index++;
  335. } else if (_item.type == 12) {
  336. //扫一扫
  337. let _item2 = _item.json;
  338. _list.push({
  339. name: _item2.title,
  340. field: `ti_${_index}`,
  341. type: "text",
  342. value: _item2.answer2
  343. });
  344. _index++;
  345. } else if (_item.type == 7) {
  346. //评分
  347. let _item2 = _item.json;
  348. _list.push({
  349. name: _item2.title,
  350. field: `ti_${_index}`,
  351. type: "text",
  352. value: _item2.answer2
  353. });
  354. _index++;
  355. } else if (_item.type == 5) {
  356. //附件
  357. let _item2 = _item.json;
  358. // let _value = ``;
  359. // _item2.file.forEach(f => {
  360. // if (/\.(jpeg|jpg|gif|png|svg|bmp|webp)$/i.test(f.url)) {
  361. // _value += `<img src="${f.url}" alt="${f.name}" width="100" height="100"/>
  362. // `;
  363. // } else {
  364. // _value += `<a href="${f.url}" target="_blank">${f.name}</a>
  365. // `;
  366. // }
  367. // });
  368. _list.push({
  369. name: _item2.title,
  370. field: `ti_${_index}`,
  371. type: "file",
  372. value: _item2.file
  373. });
  374. _index++;
  375. }
  376. }
  377. return _list;
  378. },
  379. downloadTemplateDocx() {
  380. const officeViewer = this.$refs.vueOfficeDocxRef;
  381. // console.log(this.$refs.docxTemplateDialogRef)
  382. // console.log(officeViewer)
  383. const blob = new Blob([officeViewer.$el.innerHTML], {
  384. type:
  385. "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
  386. });
  387. const url = URL.createObjectURL(blob);
  388. const a = document.createElement("a");
  389. a.href = url;
  390. a.download = this.downFileData.fileName;
  391. a.click();
  392. this.$message.success("下载成功");
  393. // console.log(officeViewer.$data)
  394. // officeViewer.exportDocx()
  395. // officeViewer.getContent().then(content => {
  396. // console.log(content)
  397. // })
  398. // console.log(this.$refs.vueOfficeDocxRef.$el);
  399. // const el = this.$refs.vueOfficeDocxRef.$el;
  400. // const body = el.querySelector(".docx");
  401. // console.log("👉",body);
  402. // this.generateDocx(this.downFileData.fileName,officeViewer.$el.innerHTML)
  403. return;
  404. getFile(this.downFileData.url).then(data => {
  405. if (data.data != 1) {
  406. // 下载文件, 并存成ArrayBuffer对象
  407. const file_name = this.downFileData.fileName; // 获取文件名
  408. const file_data = data.data; // 获取文件数据
  409. let url = window.URL.createObjectURL(new Blob([file_data]));
  410. let a = document.createElement("a");
  411. a.name = file_name;
  412. a.href = url;
  413. a.download = file_name;
  414. a.click();
  415. console.log(data);
  416. this.$message.success("下载成功");
  417. } else {
  418. this.$message.error("下载失败");
  419. }
  420. });
  421. },
  422. uploadTemplateDocx() {
  423. let input = document.createElement("input");
  424. input.type = "file";
  425. // input.accept = ".wav";
  426. // input.accept = "audio/*, .txt, .pdf, .xlsx";
  427. input.accept = ".docx";
  428. input.click();
  429. input.onchange = () => {
  430. this.loading = true;
  431. let file = input.files[0];
  432. if (!/\.(docx)$/i.test(file.name)) {
  433. this.loading = false;
  434. return this.$message.error("请上传.docx格式的文件");
  435. }
  436. console.log(file);
  437. this.uploadTemplateDocxData = file;
  438. this.loading = false;
  439. // this.uploadWavFileAndGetText(file);
  440. };
  441. },
  442. async exportDocx3() {
  443. if (!this.downFileData) return this.$message.error("请先上传模板文档");
  444. const el = this.$refs.vueOfficeDocxRef
  445. ? this.$refs.vueOfficeDocxRef.$el
  446. : this.$refs.txtHtmlViewRef.$el;
  447. const body = el.querySelector(".docx-wrapper");
  448. const html = body.innerHTML;
  449. let promiseList = [];
  450. this.useFieldList.forEach((i, index) => {
  451. promiseList.push(
  452. this.getUseFieldListFile({
  453. fileName: `${i.name}_${this.testJson.title}_${i.time}.docx`,
  454. fieldList: i.fieldList,
  455. html
  456. })
  457. );
  458. });
  459. Promise.all(promiseList).then(async res => {
  460. let fileList = res;
  461. if (fileList.length == 0) {
  462. this.$message.error("无文档导出");
  463. } else if (fileList.length == 1) {
  464. saveAs(fileList[0], fileList[0].name);
  465. this.$message.success("导出成功");
  466. } else {
  467. const zip = new JSZip();
  468. fileList.forEach(i => {
  469. zip.file(`${i.name}`, i, { binary: true });
  470. });
  471. zip.generateAsync({ type: "blob" }).then(content => {
  472. // 生成二进制流
  473. saveAs(content, `${this.testJson.title}.zip`); // 利用file-saver保存文件 自定义文件名
  474. });
  475. this.$message.success("导出成功");
  476. }
  477. if (
  478. !this.downFileData.txtUrl ||
  479. this.downFileData.txt !== el.innerHTML
  480. ) {
  481. let txt = el.innerHTML;
  482. // 创建Blob对象
  483. const blob = new Blob([txt], { type: "text/plain;charset=utf-8" });
  484. blob.lastModifiedDate = new Date();
  485. blob.name = `${this.downFileData.fileName}_wordHtml.txt`;
  486. let url = await this.uploadFile(blob);
  487. if (url && this.courseId) {
  488. this.downFileData.txt = txt;
  489. this.downFileData.txtUrl = url;
  490. this.changeDownFileData(this.downFileData);
  491. this.$forceUpdate();
  492. }
  493. }
  494. });
  495. },
  496. async getUseFieldListFile({ fileName, fieldList, html }) {
  497. return new Promise(async (resolve, reject) => {
  498. let _html = html;
  499. for (let i = 0; i < fieldList.length; i++) {
  500. if (fieldList[i].type == "image") {
  501. const img = await this.convertImageUrlToBase64(fieldList[i].value);
  502. _html = _html.replaceAll(
  503. `{%${fieldList[i].field}}`,
  504. `<img src="${img.url}" width="${img.width}" height="${img.height}" />`
  505. );
  506. } else if (fieldList[i].type == "text") {
  507. _html = _html.replaceAll(
  508. `{${fieldList[i].field}}`,
  509. fieldList[i].value
  510. );
  511. } else if (fieldList[i].type == "file") {
  512. let _text = ``;
  513. // this.fieldList[i].value.forEach(async f => {
  514. for (let j = 0; j < fieldList[i].value.length; j++) {
  515. let f = fieldList[i].value[j];
  516. if (/\.(jpeg|jpg|gif|png|svg|bmp|webp)$/i.test(f.url)) {
  517. // const img = await this.convertImageUrlToBase64(f.url);
  518. // _text += `<img src="${f.url}" width="${100}" height="100" style="object-fit:contain"/><br/>`;
  519. let _imageWidthAndHeight = await this.getImageWidthAndHeight(f.url);
  520. let _setWidth = 100;
  521. let _setHeight = (_imageWidthAndHeight.height / _imageWidthAndHeight.width) * _setWidth; // 根据比例计算高度
  522. _text += `<img src="${f.url}" width="${_setWidth}" height="${_setHeight}" style="object-fit:contain"/><br/>`;
  523. console.log(_text)
  524. } else {
  525. _text += `<a href="${f.url}" target="_blank">${f.name}</a><br/>`;
  526. }
  527. }
  528. _html = _html.replaceAll(`{${fieldList[i].field}}`, _text);
  529. // });
  530. }
  531. }
  532. let blob = await this.getGenerateDocxHtml(_html);
  533. const file = new File([blob], fileName, {
  534. type: ".docx",
  535. lastModified: new Date().getTime()
  536. });
  537. // this.fileList.push(file)
  538. resolve(file);
  539. });
  540. },
  541. async exportDocx2() {
  542. const el = this.$refs.vueOfficeDocxRef
  543. ? this.$refs.vueOfficeDocxRef.$el
  544. : this.$refs.txtHtmlViewRef.$el;
  545. console.log(el);
  546. const body = el.querySelector(".docx-wrapper");
  547. let _html = body.innerHTML;
  548. for (let i = 0; i < this.fieldList.length; i++) {
  549. if (this.fieldList[i].type == "image") {
  550. const img = await this.convertImageUrlToBase64(
  551. this.fieldList[i].value
  552. );
  553. _html = _html.replaceAll(
  554. `{%${this.fieldList[i].field}}`,
  555. `<img src="${img.url}" width="${img.width}" height="${img.height}" />`
  556. );
  557. } else if (this.fieldList[i].type == "text") {
  558. _html = _html.replaceAll(
  559. `{${this.fieldList[i].field}}`,
  560. this.fieldList[i].value
  561. );
  562. } else if (this.fieldList[i].type == "file") {
  563. let _text = ``;
  564. console.log(this.fieldList[i].value);
  565. // this.fieldList[i].value.forEach(async f=>{
  566. // for (let j = 0; j < this.fieldList[i].value.length; j++) {
  567. // let f = this.fieldList[i].value[j];
  568. // console.log("👉f",f)
  569. // if (/\.(jpeg|jpg|gif|png|svg|bmp|webp)$/i.test(f.url)) {
  570. // const img = await this.convertImageUrlToBase64(f.url);
  571. // _text += `<img src="${img.url}" width="${img.width}" height="${img.height}" />\n`;
  572. // } else {
  573. // _text += `<a href="${f.url}" target="_blank">${f.name}</a>\n`;
  574. // }
  575. // }
  576. // _html = _html.replaceAll(`{${this.fieldList[i].field}}`, _text);
  577. // })
  578. }
  579. }
  580. return;
  581. // this.fieldList.forEach(i => {
  582. // _html = _html.replace(`{${i.field}}`,i.value)
  583. // })
  584. // 下载word文档
  585. await this.generateDocx(this.downFileData.fileName, _html);
  586. if (!this.downFileData.txtUrl || this.downFileData.txt !== el.innerHTML) {
  587. let txt = el.innerHTML;
  588. // 创建Blob对象
  589. const blob = new Blob([txt], { type: "text/plain;charset=utf-8" });
  590. blob.lastModifiedDate = new Date();
  591. blob.name = `${this.downFileData.fileName}_wordHtml.txt`;
  592. let url = await this.uploadFile(blob);
  593. if (url && this.courseId) {
  594. console.log("修改文件");
  595. console.log(url);
  596. console.log(this.courseId);
  597. this.downFileData.txt = txt;
  598. this.downFileData.txtUrl = url;
  599. this.changeDownFileData(this.downFileData);
  600. this.$forceUpdate();
  601. }
  602. }
  603. // const uploadBlob = await this.getGenerateDocxHtml(body.out)
  604. // let reader = new FileReader();
  605. // reader.readAsArrayBuffer(uploadBlob);
  606. // reader.onload = async e => {
  607. // try {
  608. // const binary = new Uint8Array(reader.result);
  609. // //创建一个PizZip实例
  610. // const zip = new PizZip(binary);
  611. // // 将模板内容加载到 Docxtemplater 中
  612. // const doc = new Docxtemplater().loadZip(zip);
  613. // const output = doc.getZip().generate({
  614. // type: "blob",
  615. // mimeType:
  616. // "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  617. // compression: "DEFLATE"
  618. // });
  619. // const file = new File([output],this.downFileData.fileName,{type:".docx",lastModified:new Date().getTime()})
  620. // const url = await this.uploadFile(file)
  621. // console.log(url)
  622. // }catch(e){
  623. // console.log(e)
  624. // }
  625. // }
  626. // const file = new File([uploadBlob],this.downFileData.fileName,{type:".docx",lastModified:new Date().getTime()})
  627. // const url = await this.uploadFile(file)
  628. // console.log(url)
  629. // if(url){
  630. // }else{
  631. // }
  632. return;
  633. // const blob = await this.getGenerateDocxHtml(body.innerHTML);
  634. // if (!blob) return this.$message.error("导出失败");
  635. // let reader = new FileReader();
  636. // reader.readAsArrayBuffer(blob);
  637. // reader.onload = async e => {
  638. // try {
  639. // this.loading = true;
  640. // const binary = new Uint8Array(reader.result);
  641. // //创建一个PizZip实例
  642. // const zip = new PizZip(binary);
  643. // // 将模板内容加载到 Docxtemplater 中
  644. // const doc = new Docxtemplater().loadZip(zip);
  645. // let _data = {};
  646. // let _image = {};
  647. // // 设置模板值
  648. // // this.fieldList.forEach(i => {
  649. // // _data[i.field] = i.value;
  650. // // });
  651. // for (let i = 0; i < this.fieldList.length; i++) {
  652. // // console.log(this.fieldList[i])
  653. // if (this.fieldList[i].type == "text") {
  654. // //文本处理
  655. // _data[this.fieldList[i].field] = this.fieldList[i].value;
  656. // } else if (this.fieldList[i].type == "image") {
  657. // //图片处理
  658. // let _imageObj = await this.convertImageUrlToBase64(
  659. // this.fieldList[i].value
  660. // );
  661. // _data[this.fieldList[i].field] = _imageObj.url;
  662. // _image[this.fieldList[i].field] = {
  663. // width: _imageObj.width,
  664. // height: _imageObj.height
  665. // };
  666. // }
  667. // }
  668. // // return this.loading = false;
  669. // // 图片处理
  670. // const opts = {
  671. // centered: false,
  672. // fileType: "docx",
  673. // getImage: (value, value2, value3) => {
  674. // return this.base64DataURLToArrayBuffer(value);
  675. // },
  676. // getSize: (arrayValue, value, tagName) => {
  677. // // console.log(_image)
  678. // // console.log(tagName)
  679. // let newWidth = _image[tagName].width;
  680. // let newHeight = _image[tagName].height;
  681. // // let newWidth = 550;
  682. // // let newHeight = 100;
  683. // return [newWidth, newHeight];
  684. // }
  685. // };
  686. // console.log()
  687. // doc.attachModule(new ImageModule(opts));
  688. // //渲染模板
  689. // doc.setData(_data);
  690. // doc.render();
  691. // //获取渲染后的文本
  692. // const output = doc.getZip().generate({
  693. // type: "blob",
  694. // mimeType:
  695. // "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  696. // compression: "DEFLATE"
  697. // });
  698. // saveAs(output, `${this.downFileData.fileName}`);
  699. // // let link = document.createElement("a");
  700. // // link.download = this.uploadTemplateDocxData.name;
  701. // // link.style.display = "none";
  702. // // let blob = new Blob([output]);
  703. // // link.href = URL.createObjectURL(blob);
  704. // // document.body.appendChild(link);
  705. // // link.click();
  706. // // document.body.removeChild(link);
  707. // this.loading = false;
  708. // this.$message.success("导出成功");
  709. // } catch (error) {
  710. // console.log(error);
  711. // this.loading = false;
  712. // return this.$message.error("导出失败");
  713. // }
  714. // };
  715. },
  716. async exportDocx() {
  717. console.log(this.useFieldList);
  718. if (!this.uploadTemplateDocxData)
  719. return this.$message.error("请先上传模板文档");
  720. let reader = new FileReader();
  721. reader.readAsArrayBuffer(this.uploadTemplateDocxData);
  722. reader.onload = async e => {
  723. try {
  724. this.loading = true;
  725. const binary = new Uint8Array(reader.result);
  726. //创建一个PizZip实例
  727. const zip = new PizZip(binary);
  728. // 将模板内容加载到 Docxtemplater 中
  729. const doc = new Docxtemplater().loadZip(zip);
  730. let _data = {};
  731. let _image = {};
  732. // 设置模板值
  733. // this.fieldList.forEach(i => {
  734. // _data[i.field] = i.value;
  735. // });
  736. for (let i = 0; i < this.fieldList.length; i++) {
  737. // console.log(this.fieldList[i])
  738. if (this.fieldList[i].type == "text") {
  739. //文本处理
  740. _data[this.fieldList[i].field] = this.fieldList[i].value;
  741. } else if (this.fieldList[i].type == "image") {
  742. //图片处理
  743. let _imageObj = await this.convertImageUrlToBase64(
  744. this.fieldList[i].value
  745. );
  746. _data[this.fieldList[i].field] = _imageObj.url;
  747. _image[this.fieldList[i].field] = {
  748. width: _imageObj.width,
  749. height: _imageObj.height
  750. };
  751. }
  752. }
  753. // return this.loading = false;
  754. // 图片处理
  755. const opts = {
  756. centered: false,
  757. fileType: "docx",
  758. getImage: (value, value2, value3) => {
  759. return this.base64DataURLToArrayBuffer(value);
  760. },
  761. getSize: (arrayValue, value, tagName) => {
  762. // console.log(_image)
  763. // console.log(tagName)
  764. let newWidth = _image[tagName].width;
  765. let newHeight = _image[tagName].height;
  766. // let newWidth = 550;
  767. // let newHeight = 100;
  768. return [newWidth, newHeight];
  769. }
  770. };
  771. doc.attachModule(new ImageModule(opts));
  772. //渲染模板
  773. doc.setData(_data);
  774. doc.render();
  775. //获取渲染后的文本
  776. const output = doc.getZip().generate({
  777. type: "blob",
  778. mimeType:
  779. "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  780. compression: "DEFLATE"
  781. });
  782. saveAs(output, `${this.uploadTemplateDocxData.name}`);
  783. // let link = document.createElement("a");
  784. // link.download = this.uploadTemplateDocxData.name;
  785. // link.style.display = "none";
  786. // let blob = new Blob([output]);
  787. // link.href = URL.createObjectURL(blob);
  788. // document.body.appendChild(link);
  789. // link.click();
  790. // document.body.removeChild(link);
  791. this.loading = false;
  792. this.$message.success("导出成功");
  793. } catch (error) {
  794. console.log(error);
  795. this.loading = false;
  796. return this.$message.error("导出失败");
  797. }
  798. };
  799. },
  800. clearUploadTemplateDocxData() {
  801. this.uploadTemplateDocxData = null;
  802. },
  803. copyContent(content) {
  804. const input = document.createElement("input");
  805. // 设置 display为none会导致无法复制
  806. // input.style.display = "none";
  807. // 所以只能用其他方法隐藏
  808. input.style.opacity = 0;
  809. // 为了不影响布局
  810. input.style.position = "fixed";
  811. input.style.left = "-100%";
  812. input.style.top = "-100%";
  813. input.value = content;
  814. document.body.appendChild(input);
  815. input.select();
  816. const success = document.execCommand("copy");
  817. document.body.removeChild(input);
  818. if (!success) {
  819. return this.$message.error("复制失败");
  820. } else {
  821. return this.$message.success("复制成功");
  822. }
  823. },
  824. convertImageUrlToBase64(imageUrl) {
  825. return new Promise((resolve, reject) => {
  826. const img = new Image();
  827. img.crossOrigin = "Anonymous"; // 允许跨域请求
  828. img.src = imageUrl;
  829. img.onload = () => {
  830. const canvas = document.createElement("canvas");
  831. canvas.width = img.width;
  832. canvas.height = img.height;
  833. const ctx = canvas.getContext("2d");
  834. ctx.drawImage(img, 0, 0);
  835. const base64 = canvas.toDataURL("image/png");
  836. resolve({ url: base64, width: img.width, height: img.height });
  837. };
  838. img.onerror = error => {
  839. console.log("图片转base64失败");
  840. console.log(error);
  841. resolve({ url: "", width: 0, height: 0 });
  842. };
  843. });
  844. },
  845. getImageWidthAndHeight(imageUrl){
  846. return new Promise((resolve)=>{
  847. let _img = new Image();
  848. _img.src = imageUrl;
  849. _img.onload = () =>{
  850. resolve({width:_img.width,height:_img.height})
  851. }
  852. })
  853. },
  854. base64DataURLToArrayBuffer(dataURL) {
  855. const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
  856. if (!base64Regex.test(dataURL)) {
  857. return false;
  858. }
  859. const stringBase64 = dataURL.replace(base64Regex, "");
  860. let binaryString;
  861. if (typeof window !== "undefined") {
  862. binaryString = window.atob(stringBase64);
  863. } else {
  864. binaryString = new Buffer(stringBase64, "base64").toString("binary");
  865. }
  866. const len = binaryString.length;
  867. const bytes = new Uint8Array(len);
  868. for (let i = 0; i < len; i++) {
  869. const ascii = binaryString.charCodeAt(i);
  870. bytes[i] = ascii;
  871. }
  872. return bytes.buffer;
  873. },
  874. async getWordContent(fileData) {
  875. console.log(fileData);
  876. this.wordContentLoading = true;
  877. return (this.wordContentLoading = false);
  878. try {
  879. // console.log(a)
  880. // let response = await getFile(fileData.url);
  881. // if (response.data == 1) {
  882. // this.wordContentLoading = false;
  883. // return this.$message.error("文件不存在");
  884. // }
  885. // await renderAsync(response.data,this.$refs.wordAreaRef);
  886. this.wordContentLoading = false;
  887. } catch (error) {
  888. console.log(error);
  889. this.wordContentLoading = false;
  890. this.$message.error("加载文件失败");
  891. }
  892. },
  893. // 导出docx
  894. async generateDocx(name, html) {
  895. // 将html文件中需要用到的数据挂载到store上
  896. const content = `<!DOCTYPE html>
  897. <html xmlns:v='urn:schemas-microsoft-com
  898. :vml'xmlns:o='urn:schemas-microsoft-com:office
  899. :office'xmlns:w='urn:schemas-microsoft-com:office
  900. :word'xmlns:m='http://schemas.microsoft.com/office/2004/12/omml'
  901. xmlns='http://www.w3.org/TR/REC-html40'
  902. xmlns='http://www.w3.org/1999/xhtml'>
  903. <head>
  904. <meta charset="UTF-8">
  905. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  906. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  907. <title>${name}</title>
  908. <style>
  909. *{
  910. font-family: '宋体';
  911. margin:0;
  912. padding:0;
  913. line-height:1;
  914. }
  915. table {
  916. border-collapse: collapse; /* 折叠边框 */
  917. width: 100%;
  918. font-size:10.5pt;
  919. }
  920. th, td {
  921. border: 1px solid black; /* 线条样式 */
  922. padding: 8px;
  923. text-align: left;
  924. font-size:10.5pt;
  925. }
  926. ol,ul{
  927. margin:0;
  928. padding:0;
  929. margin-right:-1in;
  930. }
  931. li{
  932. margin-bottom:0.1in
  933. margin-right:-1in;
  934. }
  935. p{
  936. line-height:1;
  937. margin:0;
  938. padding:0
  939. }
  940. .vue-office-docx{height:100%;overflow-y:auto}
  941. .vue-office-docx .docx-wrapper>section.docx{margin-bottom:5px}
  942. @media screen and (max-width: 800px){
  943. .vue-office-docx .docx-wrapper{padding:10px}
  944. .vue-office-docx .docx-wrapper>section.docx{padding:10px!important;width:100%!important}
  945. }
  946. .docx-wrapper { background: gray; padding: 30px; padding-bottom: 0px; display: flex; flex-flow: column; align-items: center; }
  947. .docx-wrapper>section.docx { background: white; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); margin-bottom: 30px; }
  948. .docx { color: black; hyphens: auto; text-underline-position: from-font; }
  949. section.docx { box-sizing: border-box; display: flex; flex-flow: column nowrap; position: relative; overflow: hidden; }
  950. section.docx>article { margin-bottom: auto; z-index: 1; }
  951. section.docx>footer { z-index: 1; }
  952. .docx table { border-collapse: collapse; }
  953. .docx table td, .docx table th { vertical-align: top; }
  954. .docx p { margin: 0pt; min-height: 1em; }
  955. .docx span { white-space: pre-wrap; overflow-wrap: break-word; }
  956. .docx a { color: inherit; text-decoration: inherit; }
  957. .docx svg { fill: transparent; }
  958. .docx {
  959. --docx-majorHAnsi-font: Calibri Light;
  960. --docx-minorHAnsi-font: Calibri;
  961. --docx-dk1-color: #000000;
  962. --docx-lt1-color: #FFFFFF;
  963. --docx-dk2-color: #44546A;
  964. --docx-lt2-color: #E7E6E6;
  965. --docx-accent1-color: #5B9BD5;
  966. --docx-accent2-color: #ED7D31;
  967. --docx-accent3-color: #A5A5A5;
  968. --docx-accent4-color: #FFC000;
  969. --docx-accent5-color: #4472C4;
  970. --docx-accent6-color: #70AD47;
  971. --docx-hlink-color: #0563C1;
  972. --docx-folHlink-color: #954F72;
  973. }
  974. .docx span {
  975. font-family: Times New Roman;
  976. }
  977. .docx p, p.docx_1 {
  978. text-align: justify;
  979. }
  980. .docx p, p.docx_1 span {
  981. font-family: var(--docx-minorHAnsi-font);
  982. min-height: 10.50pt;
  983. font-size: 10.50pt;
  984. }
  985. .docx table, table.docx_2 td {
  986. padding-top: 0.00pt;
  987. padding-left: 5.40pt;
  988. padding-bottom: 0.00pt;
  989. padding-right: 5.40pt;
  990. }
  991. table.docx_3 p {
  992. text-align: justify;
  993. }
  994. table.docx_3 td {
  995. border-top: 0.50pt solid black;
  996. border-left: 0.50pt solid black;
  997. border-bottom: 0.50pt solid black;
  998. border-right: 0.50pt solid black;
  999. padding-top: 0.00pt;
  1000. padding-left: 5.40pt;
  1001. padding-bottom: 0.00pt;
  1002. padding-right: 5.40pt;
  1003. }
  1004. p.docx-num-2-0:before {
  1005. content: ""counter(docx-num-2-0, decimal)"、";
  1006. counter-increment: docx-num-2-0;
  1007. }
  1008. p.docx-num-2-0 {
  1009. display: list-item;
  1010. list-style-position: inside;
  1011. list-style-type: none;
  1012. }
  1013. p.docx-num-1-0:before {
  1014. content: ""counter(docx-num-1-0, decimal)"、";
  1015. counter-increment: docx-num-1-0;
  1016. }
  1017. p.docx-num-1-0 {
  1018. display: list-item;
  1019. list-style-position: inside;
  1020. list-style-type: none;
  1021. }
  1022. .docx-wrapper {
  1023. counter-reset: docx-num-2-0 4 docx-num-1-0 0;
  1024. }
  1025. </style>
  1026. </head>
  1027. <body>
  1028. ${html}
  1029. </body>
  1030. </html>`;
  1031. // console.log(content)
  1032. // return console.log(content)
  1033. // debugger
  1034. let blob = htmlDocx.asBlob(content);
  1035. // const uploadFile = new File([blob], `${name}.docx`, {
  1036. // type:
  1037. // "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
  1038. // });
  1039. saveAs(blob, `${name}.docx`);
  1040. return true;
  1041. // this.beforeUploadHtml(uploadFile);
  1042. },
  1043. async getGenerateDocxHtml(html) {
  1044. const content = `<!DOCTYPE html>
  1045. <html xmlns:v='urn:schemas-microsoft-com
  1046. :vml'xmlns:o='urn:schemas-microsoft-com:office
  1047. :office'xmlns:w='urn:schemas-microsoft-com:office
  1048. :word'xmlns:m='http://schemas.microsoft.com/office/2004/12/omml'
  1049. xmlns='http://www.w3.org/TR/REC-html40'
  1050. xmlns='http://www.w3.org/1999/xhtml'>
  1051. <head>
  1052. <meta charset="UTF-8">
  1053. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  1054. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  1055. <title>DOCX</title>
  1056. <style>
  1057. *{
  1058. font-family: '宋体';
  1059. margin:0;
  1060. padding:0;
  1061. line-height:1;
  1062. }
  1063. table {
  1064. border-collapse: collapse; /* 折叠边框 */
  1065. width: 100%;
  1066. font-size:10.5pt;
  1067. }
  1068. th, td {
  1069. border: 1px solid black; /* 线条样式 */
  1070. padding: 8px;
  1071. text-align: left;
  1072. font-size:10.5pt;
  1073. }
  1074. ol,ul{
  1075. margin:0;
  1076. padding:0;
  1077. margin-right:-1in;
  1078. }
  1079. li{
  1080. margin-bottom:0.1in
  1081. margin-right:-1in;
  1082. }
  1083. p{
  1084. line-height:1;
  1085. margin:0;
  1086. padding:0
  1087. }
  1088. .vue-office-docx{height:100%;overflow-y:auto}
  1089. .vue-office-docx .docx-wrapper>section.docx{margin-bottom:5px}
  1090. @media screen and (max-width: 800px){
  1091. .vue-office-docx .docx-wrapper{padding:10px}
  1092. .vue-office-docx .docx-wrapper>section.docx{padding:10px!important;width:100%!important}
  1093. }
  1094. .docx-wrapper { background: gray; padding: 30px; padding-bottom: 0px; display: flex; flex-flow: column; align-items: center; }
  1095. .docx-wrapper>section.docx { background: white; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); margin-bottom: 30px; }
  1096. .docx { color: black; hyphens: auto; text-underline-position: from-font; }
  1097. section.docx { box-sizing: border-box; display: flex; flex-flow: column nowrap; position: relative; overflow: hidden; }
  1098. section.docx>article { margin-bottom: auto; z-index: 1; }
  1099. section.docx>footer { z-index: 1; }
  1100. .docx table { border-collapse: collapse; }
  1101. .docx table td, .docx table th { vertical-align: top; }
  1102. .docx p { margin: 0pt; min-height: 1em; }
  1103. .docx span { white-space: pre-wrap; overflow-wrap: break-word; }
  1104. .docx a { color: inherit; text-decoration: inherit; }
  1105. .docx svg { fill: transparent; }
  1106. .docx {
  1107. --docx-majorHAnsi-font: Calibri Light;
  1108. --docx-minorHAnsi-font: Calibri;
  1109. --docx-dk1-color: #000000;
  1110. --docx-lt1-color: #FFFFFF;
  1111. --docx-dk2-color: #44546A;
  1112. --docx-lt2-color: #E7E6E6;
  1113. --docx-accent1-color: #5B9BD5;
  1114. --docx-accent2-color: #ED7D31;
  1115. --docx-accent3-color: #A5A5A5;
  1116. --docx-accent4-color: #FFC000;
  1117. --docx-accent5-color: #4472C4;
  1118. --docx-accent6-color: #70AD47;
  1119. --docx-hlink-color: #0563C1;
  1120. --docx-folHlink-color: #954F72;
  1121. }
  1122. .docx span {
  1123. font-family: Times New Roman;
  1124. }
  1125. .docx p, p.docx_1 {
  1126. text-align: justify;
  1127. }
  1128. .docx p, p.docx_1 span {
  1129. font-family: var(--docx-minorHAnsi-font);
  1130. min-height: 10.50pt;
  1131. font-size: 10.50pt;
  1132. }
  1133. .docx table, table.docx_2 td {
  1134. padding-top: 0.00pt;
  1135. padding-left: 5.40pt;
  1136. padding-bottom: 0.00pt;
  1137. padding-right: 5.40pt;
  1138. }
  1139. table.docx_3 p {
  1140. text-align: justify;
  1141. }
  1142. table.docx_3 td {
  1143. border-top: 0.50pt solid black;
  1144. border-left: 0.50pt solid black;
  1145. border-bottom: 0.50pt solid black;
  1146. border-right: 0.50pt solid black;
  1147. padding-top: 0.00pt;
  1148. padding-left: 5.40pt;
  1149. padding-bottom: 0.00pt;
  1150. padding-right: 5.40pt;
  1151. }
  1152. p.docx-num-2-0:before {
  1153. content: ""counter(docx-num-2-0, decimal)"、";
  1154. counter-increment: docx-num-2-0;
  1155. }
  1156. p.docx-num-2-0 {
  1157. display: list-item;
  1158. list-style-position: inside;
  1159. list-style-type: none;
  1160. }
  1161. p.docx-num-1-0:before {
  1162. content: ""counter(docx-num-1-0, decimal)"、";
  1163. counter-increment: docx-num-1-0;
  1164. }
  1165. p.docx-num-1-0 {
  1166. display: list-item;
  1167. list-style-position: inside;
  1168. list-style-type: none;
  1169. }
  1170. .docx-wrapper {
  1171. counter-reset: docx-num-2-0 4 docx-num-1-0 0;
  1172. }
  1173. </style>
  1174. </head>
  1175. <body>
  1176. ${html}
  1177. </body>
  1178. </html>`;
  1179. // console.log(content)
  1180. // return console.log(content)
  1181. // debugger
  1182. let blob = htmlDocx.asBlob(content);
  1183. return blob;
  1184. },
  1185. uploadFile(file) {
  1186. return new Promise((resolve, reject) => {
  1187. var credentials = {
  1188. accessKeyId: "AKIATLPEDU37QV5CHLMH",
  1189. secretAccessKey: "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"
  1190. }; //秘钥形式的登录上传
  1191. window.AWS.config.update(credentials);
  1192. window.AWS.config.region = "cn-northwest-1"; //设置区域
  1193. var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
  1194. var _this = this;
  1195. if (file) {
  1196. // this.loading = true;
  1197. var params = {
  1198. Key:
  1199. file.name.split(".")[0] +
  1200. new Date().getTime() +
  1201. "." +
  1202. file.name.split(".")[file.name.split(".").length - 1],
  1203. ContentType: file.type,
  1204. Body: file,
  1205. "Access-Control-Allow-Credentials": "*",
  1206. ACL: "public-read"
  1207. }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
  1208. var options = {
  1209. partSize: 2048 * 1024 * 1024,
  1210. queueSize: 2,
  1211. leavePartsOnError: true
  1212. };
  1213. bucket
  1214. .upload(params, options)
  1215. .on("httpUploadProgress", function(evt) {
  1216. //这里可以写进度条
  1217. _this.progressData.value = parseInt(
  1218. (evt.loaded * 100) / evt.total
  1219. );
  1220. // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
  1221. })
  1222. .send(function(err, data) {
  1223. if (err) {
  1224. _this.$message.error("上传失败");
  1225. } else {
  1226. resolve(data.Location);
  1227. }
  1228. });
  1229. }
  1230. });
  1231. },
  1232. changeDownFileData(data) {
  1233. let _data = JSON.parse(JSON.stringify(data));
  1234. delete _data.txt;
  1235. let params = [
  1236. {
  1237. cid: this.courseId,
  1238. ncover: JSON.stringify(_data)
  1239. }
  1240. ];
  1241. this.$emit("changeCover", JSON.stringify(_data));
  1242. this.ajax
  1243. .post(this.$store.state.api + "update_testCourseCoverById", params)
  1244. .then(res => {
  1245. console.log(res.data);
  1246. });
  1247. },
  1248. getTxtContent(txt) {
  1249. this.downFileData.txt = txt;
  1250. },
  1251. uploadWord() {
  1252. let input = document.createElement("input");
  1253. input.type = "file";
  1254. // input.accept = ".wav";
  1255. // input.accept = "audio/*, .txt, .pdf, .xlsx";
  1256. input.accept = ".docx";
  1257. input.click();
  1258. input.onchange = async () => {
  1259. this.loading = true;
  1260. let file = input.files[0];
  1261. if (!/\.(docx)$/i.test(file.name)) {
  1262. this.loading = false;
  1263. return this.$message.error("请上传.docx格式的文件");
  1264. }
  1265. let uploadData = await this.uploadFile(file);
  1266. if (uploadData == 1) {
  1267. this.loading = false;
  1268. return this.$message.error("文件上传失败");
  1269. }
  1270. let obj = {
  1271. fileName: file.name,
  1272. url: uploadData
  1273. };
  1274. this.downFileData = obj;
  1275. this.changeDownFileData(this.downFileData);
  1276. this.loading = false;
  1277. console.log(uploadData);
  1278. };
  1279. },
  1280. formatTime(timeString) {
  1281. let [datePart, timePart] = timeString.split(" ");
  1282. let formattedTime =
  1283. timePart.replace(/:/g, (match, offset) => {
  1284. if (offset === 2) return "时";
  1285. if (offset === 5) return "分";
  1286. return match;
  1287. }) + "秒";
  1288. return datePart + " " + formattedTime;
  1289. }
  1290. },
  1291. computed: {
  1292. dialogTitle() {
  1293. let _result = "word导出";
  1294. if (this.useFieldList.length == 0) {
  1295. _result = "word导出";
  1296. } else if (this.useFieldList.length == 1) {
  1297. _result = `${this.useFieldList[0].name}数据word导出`;
  1298. } else {
  1299. _result = `批量word导出`;
  1300. }
  1301. return _result;
  1302. }
  1303. }
  1304. };
  1305. </script>
  1306. <style scoped>
  1307. .dialog_diy >>> .el-dialog {
  1308. /* height: 100%; */
  1309. /* margin: 0 auto !important; */
  1310. }
  1311. .dialog_diy >>> .el-dialog__header {
  1312. padding: 15px 20px;
  1313. background-color: #454545 !important;
  1314. color: #fff !important;
  1315. }
  1316. .dialog_diy >>> .el-dialog__body {
  1317. height: calc(100% - 54px);
  1318. box-sizing: border-box;
  1319. padding: 0px;
  1320. }
  1321. .dialog_diy >>> .el-dialog__title {
  1322. color: #fff;
  1323. }
  1324. .dialog_diy >>> .el-dialog__headerbtn {
  1325. top: 19px;
  1326. }
  1327. .dialog_diy >>> .el-dialog__headerbtn .el-dialog__close {
  1328. color: #fff;
  1329. }
  1330. .dialog_diy >>> .el-dialog__headerbtn .el-dialog__close:hover {
  1331. color: #fff;
  1332. }
  1333. .dialog_diy >>> .el-dialog__body,
  1334. .dialog_diy >>> .el-dialog__footer {
  1335. background: #fff;
  1336. }
  1337. .box {
  1338. width: 100%;
  1339. height: 100%;
  1340. padding: 0 20px 15px;
  1341. display: flex;
  1342. box-sizing: border-box;
  1343. overflow: auto;
  1344. }
  1345. .b_left {
  1346. flex: 1;
  1347. height: 100%;
  1348. border: none;
  1349. outline: none;
  1350. box-sizing: border-box;
  1351. padding-top: 20px;
  1352. }
  1353. .b_right {
  1354. flex: 1;
  1355. min-width: 700px;
  1356. max-width: 700px;
  1357. height: 100%;
  1358. }
  1359. .d_box {
  1360. width: 100%;
  1361. height: 100%;
  1362. overflow: auto;
  1363. }
  1364. .d_b_step {
  1365. width: 100%;
  1366. max-width: 100%;
  1367. height: auto;
  1368. padding: 20px;
  1369. box-sizing: border-box;
  1370. /* background-color: red; */
  1371. }
  1372. .d_b_step > h2 {
  1373. margin-bottom: 10px;
  1374. }
  1375. .d_b_step > h3 {
  1376. margin-top: 20px;
  1377. }
  1378. .d_b_step > p {
  1379. font-size: 16px;
  1380. }
  1381. .d_b_step > img {
  1382. width: 100%;
  1383. margin-top: 10px;
  1384. }
  1385. .d_b_s_button {
  1386. margin-top: 10px;
  1387. }
  1388. .d_b_s_fieldListItem {
  1389. margin-top: 15px;
  1390. font-size: 16px;
  1391. display: flex;
  1392. align-items: center;
  1393. }
  1394. .d_b_s_fieldListItem > span {
  1395. display: flex;
  1396. align-items: center;
  1397. justify-content: center;
  1398. }
  1399. .d_b_s_fieldListItem > span > svg {
  1400. width: 15px;
  1401. height: 15px;
  1402. cursor: pointer;
  1403. fill: #000;
  1404. margin-left: 10px;
  1405. transition: 0.3s;
  1406. fill-opacity: 0.6;
  1407. }
  1408. .d_b_s_fieldListItem > span > svg:hover {
  1409. fill: #409eff;
  1410. fill-opacity: 1;
  1411. }
  1412. .d_b_s_fileCard {
  1413. width: 100%;
  1414. height: 45px;
  1415. border-radius: 2px;
  1416. margin-bottom: 10px;
  1417. display: flex;
  1418. align-items: center;
  1419. box-sizing: border-box;
  1420. padding: 0 10px;
  1421. border: solid 1px #dfdfdf;
  1422. position: relative;
  1423. cursor: default;
  1424. }
  1425. .d_b_s_fileCard > svg {
  1426. width: 30px;
  1427. height: 30px;
  1428. fill: #a3a8ac;
  1429. margin-right: 10px;
  1430. }
  1431. .d_b_s_fileCard > span {
  1432. max-width: calc(100% - 100px);
  1433. overflow: hidden;
  1434. text-overflow: ellipsis;
  1435. white-space: nowrap;
  1436. font-size: 16px;
  1437. }
  1438. .d_b_s_f_c_del {
  1439. position: absolute;
  1440. right: 10px;
  1441. cursor: pointer;
  1442. color: #e60012;
  1443. }
  1444. .d_b_s_imgList {
  1445. margin-top: 10px;
  1446. }
  1447. .d_b_s_imgList > img {
  1448. width: 100%;
  1449. }
  1450. .foldMenu {
  1451. width: 100%;
  1452. display: flex;
  1453. justify-content: flex-end;
  1454. margin: 10px 0;
  1455. }
  1456. .foldMenu > span {
  1457. cursor: pointer;
  1458. color: #409eff;
  1459. font-size: 16px;
  1460. }
  1461. .cover_box {
  1462. width: 100%;
  1463. height: 100%;
  1464. display: flex;
  1465. align-items: center;
  1466. justify-content: center;
  1467. }
  1468. </style>