123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- <template>
- <el-dialog
- :visible.sync="show"
- :append-to-body="true"
- title="word导出"
- width="60%"
- top="10vh"
- :before-close="handleClose"
- class="dialog_diy"
- >
- <div class="box" v-loading="loading">
- <div class="b_left">
- <topicVue :cJson="checkJson" :checktype="2" :see="true" :isTeacher="1" title="" brief="" ref="topicVue"/>
- </div>
- <div class="b_right">
- <div class="d_box">
- <div class="d_b_step">
- <h2>第一步:下载模板文档</h2>
- <p>点击下载模板文档来下载指定文档</p>
- <el-button
- class="d_b_s_button"
- type="primary"
- :disabled="!downFileData"
- @click="downloadTemplateDocx()"
- >下载模板文档</el-button
- >
- </div>
- <div class="d_b_step">
- <h2>第二步:填入模板变量</h2>
- <p>按需制作排版Word模板文档,目前支持docx格式</p>
- <p>
- 在文件中指定位置输入下方的"字段变量",用于显示真实数据的准确位置
- </p>
-
- <h3>文本:</h3>
- <img
- 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"
- />
- <h3>选择题:</h3>
- <div class="d_b_s_imgList">
- <img src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/41728546186962.png">
- <img src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/51728546189479.png">
- </div>
- <div class="foldMenu">
- <span v-if="foldField" @click="foldField = false">折叠字段变量</span>
- <span v-if="!foldField" @click="foldField = true">显示字段变量</span>
- </div>
- <div
- v-for="(item, index) in fieldList"
- :key="index"
- class="d_b_s_fieldListItem"
- v-if="foldField"
- >
- <span
- >{{ item.name }}:{{
- "{" + (item.type == "image" ? "%" : "") + item.field + "}"
- }}</span
- >
- <span @click="copyContent(`{${item.type == 'image' ? '%' : ''}${item.field}}`)">
- <svg
- width="14"
- height="14"
- viewBox="0 0 14 14"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- fill-rule="evenodd"
- clip-rule="evenodd"
- 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"
- />
- <path
- fill-rule="evenodd"
- clip-rule="evenodd"
- 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"
- />
- </svg>
- </span>
- </div>
- </div>
- <div class="d_b_step">
- <h2>第三步:上传填入后的模板文档</h2>
- <div v-if="uploadTemplateDocxData" class="d_b_s_fileCard">
- <svg
- t="1728376433028"
- class="icon"
- viewBox="0 0 1024 1024"
- version="1.1"
- xmlns="http://www.w3.org/2000/svg"
- p-id="5172"
- width="200"
- height="200"
- >
- <path
- 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"
- p-id="5173"
- ></path>
- <path
- 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"
- p-id="5174"
- ></path>
- </svg>
- <span>{{ uploadTemplateDocxData.name }}</span>
- <span class="d_b_s_f_c_del" @click="clearUploadTemplateDocxData()"
- >删除</span
- >
- </div>
- <el-button
- class="d_b_s_button"
- type="primary"
- :disabled="uploadTemplateDocxData != null"
- @click="uploadTemplateDocx()"
- >上传填入后的模板文档</el-button
- >
- </div>
- <div class="d_b_step">
- <h2>第四步:点击导出</h2>
- <p></p>
- <el-button
- class="d_b_s_button"
- type="primary"
- :disabled="!uploadTemplateDocxData"
- @click="exportDocx()"
- >导出Word文档</el-button
- >
- </div>
- </div>
- </div>
- </div>
- </el-dialog>
- </template>
- <script>
- import PizZip from "pizzip";
- import Docxtemplater from "docxtemplater";
- import ImageModule from "docxtemplater-image-module-free";
- import { saveAs } from 'file-saver';
- import topicVue from "../../testStudent/view/component/topic.vue";
- const getFile = url => {
- return new Promise((resolve, reject) => {
- var credentials = {
- accessKeyId: "AKIATLPEDU37QV5CHLMH",
- secretAccessKey: "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"
- }; //秘钥形式的登录上传
- window.AWS.config.update(credentials);
- window.AWS.config.region = "cn-northwest-1"; //设置区域
- let url2 = url;
- let _url2 = "";
- if (
- url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
- ) {
- _url2 = url2.split(
- "https://view.officeapps.live.com/op/view.aspx?src="
- )[1];
- } else {
- _url2 = url2;
- }
- var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
- let name = decodeURIComponent(
- _url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]
- );
- var params = {
- Bucket: "ccrb",
- Key: name
- };
- s3.getObject(params, function(err, data) {
- if (err) {
- console.log(err, err.stack);
- resolve({ data: 1 });
- } else {
- resolve({ data: data.Body });
- console.log(data);
- }
- });
- });
- };
- export default {
- props: {},
- components:{
- topicVue
- },
- data() {
- return {
- show: false,
- loading: false,
- fieldList: [
- // { name: "第五题", field: "ti_05", type: "text", value: "第五题ti_05" },
- // {
- // name: "图片1",
- // field: "image_01",
- // type: "image",
- // value:
- // "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"
- // }
- ],
- uploadTemplateDocxData: null, //上传的模板文档
- downFileData: null, // 下载模板文档的url
- checkJson:[],
- foldField:false,
- };
- },
- methods: {
- handleClose(done) {
- this.close();
- done();
- },
- open(data) {
- this.init();
- let _fileData = data.fileData;
- this.downFileData = _fileData;
- this.checkJson = data.formData.array
- this.fieldList = this.getFieldData(data.formData.array)
- this.show = true;
- },
- close() {
- this.show = false;
- this.init();
- },
- init() { // 初始化
- this.downFileData = null;
- this.fieldList = [];
- this.uploadTemplateDocxData = null;
- this.foldField = false;
- },
- getFieldData(array){
- let _list = [];
- let _index = 0;
- for(let i=0;i<array.length;i++){
- let _item = array[i];
- if(_item.type == 3){
- let _item2 = _item.json;
- _list.push({name: _item2.title, field: `ti_${_index}`, type: "text", value: _item2.answer2})
- _index++;
- }else if(_item.type==1){
- let _item2 = _item.json;
- let choseTxt = ``
- _item2.array.forEach((i,index2)=>{
- choseTxt += `${(_item2.answer2===index2 || _item2.answer2.includes(index2)?'☑':'□')}${i.option} `
- })
- _list.push({name:_item2.title,field: `ti_${_index}`, type: "text", value: choseTxt})
- _index++;
- }
- }
- return _list;
- },
- downloadTemplateDocx() {
- getFile(this.downFileData.url).then(data => {
- if (data.data != 1) {
- // 下载文件, 并存成ArrayBuffer对象
- const file_name = this.downFileData.fileName; // 获取文件名
- const file_data = data.data; // 获取文件数据
- let url = window.URL.createObjectURL(new Blob([file_data]));
- let a = document.createElement("a");
- a.name = file_name;
- a.href = url;
- a.download = file_name;
- a.click();
- console.log(data);
- this.$message.success("下载成功");
- } else {
- this.$message.error("下载失败");
- }
- });
- },
- uploadTemplateDocx() {
- let input = document.createElement("input");
- input.type = "file";
- // input.accept = ".wav";
- // input.accept = "audio/*, .txt, .pdf, .xlsx";
- input.accept = ".docx";
- input.click();
- input.onchange = () => {
- this.loading = true;
- let file = input.files[0];
- if (!/\.(docx)$/i.test(file.name)) {
- this.loading = false;
- return this.$message.error("请上传.docx格式的文件");
- }
- console.log(file);
- this.uploadTemplateDocxData = file;
- this.loading = false;
- // this.uploadWavFileAndGetText(file);
- };
- },
- async exportDocx() {
- if (!this.uploadTemplateDocxData)
- return this.$message.error("请先上传模板文档");
- let reader = new FileReader();
- reader.readAsArrayBuffer(this.uploadTemplateDocxData);
- reader.onload = async e => {
- try {
- this.loading = true;
- const binary = new Uint8Array(reader.result);
- //创建一个PizZip实例
- const zip = new PizZip(binary);
- // 将模板内容加载到 Docxtemplater 中
- const doc = new Docxtemplater().loadZip(zip);
- let _data = {};
- let _image = {};
- // 设置模板值
- // this.fieldList.forEach(i => {
- // _data[i.field] = i.value;
- // });
- for (let i = 0; i < this.fieldList.length; i++) {
- // console.log(this.fieldList[i])
- if (this.fieldList[i].type == "text") {
- //文本处理
- _data[this.fieldList[i].field] = this.fieldList[i].value;
- } else if (this.fieldList[i].type == "image") {
- //图片处理
- let _imageObj = await this.convertImageUrlToBase64(
- this.fieldList[i].value
- );
- _data[this.fieldList[i].field] = _imageObj.url;
- _image[this.fieldList[i].field] = {
- width: _imageObj.width,
- height: _imageObj.height
- };
- }
- }
- // return this.loading = false;
- // 图片处理
- const opts = {
- centered: false,
- fileType: "docx",
- getImage: (value, value2, value3) => {
- return this.base64DataURLToArrayBuffer(value);
- },
- getSize: (arrayValue, value, tagName) => {
- // console.log(_image)
- // console.log(tagName)
- let newWidth = _image[tagName].width;
- let newHeight = _image[tagName].height;
- // let newWidth = 550;
- // let newHeight = 100;
- return [newWidth, newHeight];
- }
- };
- doc.attachModule(new ImageModule(opts));
- //渲染模板
- doc.setData(_data);
- doc.render();
- //获取渲染后的文本
- const output = doc.getZip().generate({
- type: "blob",
- mimeType:
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
- compression: "DEFLATE"
- });
- saveAs(output,`${this.uploadTemplateDocxData.name}`)
- // let link = document.createElement("a");
- // link.download = this.uploadTemplateDocxData.name;
- // link.style.display = "none";
- // let blob = new Blob([output]);
- // link.href = URL.createObjectURL(blob);
- // document.body.appendChild(link);
- // link.click();
- // document.body.removeChild(link);
- this.loading = false;
- this.$message.success("导出成功");
- } catch (error) {
- console.log(error);
- this.loading = false;
- return this.$message.error("导出失败");
- }
- };
- },
- clearUploadTemplateDocxData() {
- this.uploadTemplateDocxData = null;
- },
- copyContent(content) {
- const input = document.createElement("input");
- // 设置 display为none会导致无法复制
- // input.style.display = "none";
- // 所以只能用其他方法隐藏
- input.style.opacity = 0;
- // 为了不影响布局
- input.style.position = "fixed";
- input.style.left = "-100%";
- input.style.top = "-100%";
- input.value = content;
- document.body.appendChild(input);
- input.select();
- const success = document.execCommand("copy");
- document.body.removeChild(input);
- if (!success) {
- return this.$message.error("复制失败");
- } else {
- return this.$message.success("复制成功");
- }
- },
- convertImageUrlToBase64(imageUrl) {
- return new Promise((resolve, reject) => {
- const img = new Image();
- img.crossOrigin = "Anonymous"; // 允许跨域请求
- img.src = imageUrl;
- img.onload = () => {
- const canvas = document.createElement("canvas");
- canvas.width = img.width;
- canvas.height = img.height;
- const ctx = canvas.getContext("2d");
- ctx.drawImage(img, 0, 0);
- const base64 = canvas.toDataURL("image/png");
- resolve({ url: base64, width: img.width, height: img.height });
- };
- img.onerror = error => {
- console.log("图片转base64失败")
- console.log(error)
- resolve({url:"",width:0,height:0});
- };
- });
- },
- base64DataURLToArrayBuffer(dataURL) {
- const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
- if (!base64Regex.test(dataURL)) {
- return false;
- }
- const stringBase64 = dataURL.replace(base64Regex, "");
- let binaryString;
- if (typeof window !== "undefined") {
- binaryString = window.atob(stringBase64);
- } else {
- binaryString = new Buffer(stringBase64, "base64").toString("binary");
- }
- const len = binaryString.length;
- const bytes = new Uint8Array(len);
- for (let i = 0; i < len; i++) {
- const ascii = binaryString.charCodeAt(i);
- bytes[i] = ascii;
- }
- return bytes.buffer;
- }
- }
- };
- </script>
- <style scoped>
- .dialog_diy >>> .el-dialog {
- /* height: 100%; */
- /* margin: 0 auto !important; */
- }
- .dialog_diy >>> .el-dialog__header {
- background: #fff !important;
- padding: 15px 20px;
- }
- .dialog_diy >>> .el-dialog__body {
- height: calc(100% - 124px);
- box-sizing: border-box;
- padding: 0px;
- }
- .dialog_diy >>> .el-dialog__title {
- color: #000;
- }
- .dialog_diy >>> .el-dialog__headerbtn {
- top: 19px;
- }
- .dialog_diy >>> .el-dialog__headerbtn .el-dialog__close {
- color: #000;
- }
- .dialog_diy >>> .el-dialog__headerbtn .el-dialog__close:hover {
- color: #000;
- }
- .dialog_diy >>> .el-dialog__body,
- .dialog_diy >>> .el-dialog__footer {
- background: #fff;
- }
- .box {
- width: 100%;
- height: 70vh;
- padding: 0 20px 15px;
- display: flex;
- box-sizing: border-box;
- }
- .b_left {
- flex: 1;
- height: 100%;
- }
- .b_right {
- min-width: 700px;
- max-width: 700px;
- height: 100%;
- }
- .d_box {
- width: 100%;
- height: 100%;
- overflow: auto;
- }
- .d_b_step {
- width: 100%;
- max-width: 100%;
- height: auto;
- padding: 20px;
- box-sizing: border-box;
- /* background-color: red; */
- }
- .d_b_step > h2 {
- margin-bottom: 10px;
- }
- .d_b_step>h3{
- margin-top: 20px;
- }
- .d_b_step > p {
- font-size: 16px;
- }
- .d_b_step > img {
- width: 100%;
- margin-top: 10px;
- }
- .d_b_s_button {
- margin-top: 10px;
- }
- .d_b_s_fieldListItem {
- margin-top: 15px;
- font-size: 16px;
- display: flex;
- align-items: center;
- }
- .d_b_s_fieldListItem > span {
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .d_b_s_fieldListItem > span > svg {
- width: 15px;
- height: 15px;
- cursor: pointer;
- fill: #000;
- margin-left: 10px;
- transition: 0.3s;
- fill-opacity: 0.6;
- }
- .d_b_s_fieldListItem > span > svg:hover {
- fill: #409eff;
- fill-opacity: 1;
- }
- .d_b_s_fileCard {
- width: 100%;
- height: 45px;
- border-radius: 2px;
- margin-bottom: 10px;
- display: flex;
- align-items: center;
- box-sizing: border-box;
- padding: 0 10px;
- border: solid 1px #dfdfdf;
- position: relative;
- cursor: default;
- }
- .d_b_s_fileCard > svg {
- width: 30px;
- height: 30px;
- fill: #a3a8ac;
- margin-right: 10px;
- }
- .d_b_s_fileCard > span {
- max-width: calc(100% - 100px);
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- font-size: 16px;
- }
- .d_b_s_f_c_del {
- position: absolute;
- right: 10px;
- cursor: pointer;
- color: #e60012;
- }
- .d_b_s_imgList{
- margin-top: 10px;
- }
- .d_b_s_imgList>img{
- width: 100%;
- }
- .foldMenu{
- width: 100%;
- display: flex;
- justify-content: flex-end;
- margin: 10px 0;
-
- }
- .foldMenu>span{
- cursor: pointer;
- color: #409EFF;
- font-size: 16px;
- }
- </style>
|