├── dist ├── favicon.ico ├── index.html └── assets │ └── index-edcbd76c.css ├── public └── favicon.ico ├── .vscode └── extensions.json ├── src ├── main.js ├── assets │ ├── logo.svg │ ├── main.css │ └── base.css ├── components │ ├── icons │ │ ├── IconSupport.vue │ │ ├── IconTooling.vue │ │ ├── IconCommunity.vue │ │ ├── IconDocumentation.vue │ │ └── IconEcosystem.vue │ ├── HelloWorld.vue │ ├── WelcomeItem.vue │ └── TheWelcome.vue ├── libs │ ├── xlsx.js │ └── generator-timu.js └── App.vue ├── vite.config.js ├── index.html ├── .gitignore ├── package.json └── README.md /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjj5855/excel2word/develop/dist/favicon.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjj5855/excel2word/develop/public/favicon.ico -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | import './assets/main.css' 5 | 6 | createApp(App).mount('#app') 7 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/icons/IconSupport.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [vue()], 9 | resolve: { 10 | alias: { 11 | '@': fileURLToPath(new URL('./src', import.meta.url)) 12 | } 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | #dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excel2word", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "docx": "^7.8.2", 12 | "file-saver": "^2.0.5", 13 | "vue": "^3.2.45", 14 | "xlsx": "^0.18.5" 15 | }, 16 | "devDependencies": { 17 | "@vitejs/plugin-vue": "^4.0.0", 18 | "vite": "^4.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | #app { 4 | max-width: 1280px; 5 | margin: 0 auto; 6 | padding: 2rem; 7 | 8 | font-weight: normal; 9 | } 10 | 11 | a, 12 | .green { 13 | text-decoration: none; 14 | color: hsla(160, 100%, 37%, 1); 15 | transition: 0.4s; 16 | } 17 | 18 | @media (hover: hover) { 19 | a:hover { 20 | background-color: hsla(160, 100%, 37%, 0.2); 21 | } 22 | } 23 | 24 | @media (min-width: 1024px) { 25 | body { 26 | display: flex; 27 | place-items: center; 28 | } 29 | 30 | #app { 31 | display: grid; 32 | grid-template-columns: 1fr 1fr; 33 | padding: 0 2rem; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # excel2word 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 8 | 9 | ## Customize configuration 10 | 11 | See [Vite Configuration Reference](https://vitejs.dev/config/). 12 | 13 | ## Project Setup 14 | 15 | ```sh 16 | npm install 17 | ``` 18 | 19 | ### Compile and Hot-Reload for Development 20 | 21 | ```sh 22 | npm run dev 23 | ``` 24 | 25 | ### Compile and Minify for Production 26 | 27 | ```sh 28 | npm run build 29 | ``` 30 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | 21 | 44 | -------------------------------------------------------------------------------- /src/components/icons/IconTooling.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /src/components/icons/IconCommunity.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/icons/IconDocumentation.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/WelcomeItem.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 87 | -------------------------------------------------------------------------------- /src/components/icons/IconEcosystem.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /dist/assets/index-edcbd76c.css: -------------------------------------------------------------------------------- 1 | header[data-v-5a177b26]{line-height:1.5}.logo[data-v-5a177b26]{display:block;margin:0 auto 2rem}@media (min-width: 1024px){header[data-v-5a177b26]{display:flex;place-items:center;padding-right:calc(var(--section-gap) / 2)}.logo[data-v-5a177b26]{margin:0 2rem 0 0}header .wrapper[data-v-5a177b26]{display:flex;place-items:flex-start;flex-wrap:wrap}}:root{--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, .29);--vt-c-divider-light-2: rgba(60, 60, 60, .12);--vt-c-divider-dark-1: rgba(84, 84, 84, .65);--vt-c-divider-dark-2: rgba(84, 84, 84, .48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, .66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, .64)}:root{--color-background: var(--vt-c-white);--color-background-soft: var(--vt-c-white-soft);--color-background-mute: var(--vt-c-white-mute);--color-border: var(--vt-c-divider-light-2);--color-border-hover: var(--vt-c-divider-light-1);--color-heading: var(--vt-c-text-light-1);--color-text: var(--vt-c-text-light-1);--section-gap: 160px}@media (prefers-color-scheme: dark){:root{--color-background: var(--vt-c-black);--color-background-soft: var(--vt-c-black-soft);--color-background-mute: var(--vt-c-black-mute);--color-border: var(--vt-c-divider-dark-2);--color-border-hover: var(--vt-c-divider-dark-1);--color-heading: var(--vt-c-text-dark-1);--color-text: var(--vt-c-text-dark-2)}}*,*:before,*:after{box-sizing:border-box;margin:0;position:relative;font-weight:400}body{min-height:100vh;color:var(--color-text);background:var(--color-background);transition:color .5s,background-color .5s;line-height:1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#app{max-width:1280px;margin:0 auto;padding:2rem;font-weight:400}a,.green{text-decoration:none;color:#00bd7e;transition:.4s}@media (hover: hover){a:hover{background-color:#00bd7e33}}@media (min-width: 1024px){body{display:flex;place-items:center}#app{display:grid;grid-template-columns:1fr 1fr;padding:0 2rem}} 2 | -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: color 0.5s, background-color 0.5s; 67 | line-height: 1.6; 68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 70 | font-size: 15px; 71 | text-rendering: optimizeLegibility; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | } 75 | -------------------------------------------------------------------------------- /src/libs/xlsx.js: -------------------------------------------------------------------------------- 1 | import * as XLSX from 'xlsx' 2 | 3 | export default { 4 | workbook2blob, 5 | openDownloadDialog, 6 | readWorkbookFromLocalFile, 7 | getHeaderKeyList 8 | } 9 | // 将workbook装化成blob对象 10 | function workbook2blob(workbook) { 11 | // 生成excel的配置项 12 | var wopts = { 13 | // 要生成的文件类型 14 | bookType: 'xlsx', 15 | // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性 16 | bookSST: false, 17 | // 二进制类型 18 | type: 'binary' 19 | } 20 | var wbout = XLSX.write(workbook, wopts) 21 | var blob = new Blob([s2ab(wbout)], { 22 | type: 'application/octet-stream' 23 | }) 24 | return blob 25 | } 26 | 27 | // 将字符串转ArrayBuffer 28 | function s2ab(s) { 29 | var buf = new ArrayBuffer(s.length) 30 | var view = new Uint8Array(buf) 31 | for (var i = 0; i != s.length; ++i) { 32 | view[i] = s.charCodeAt(i) & 0xff 33 | } 34 | return buf 35 | } 36 | 37 | // 将blob对象创建bloburl,然后用a标签实现弹出下载框 38 | function openDownloadDialog(blob, fileName) { 39 | if (typeof blob == 'object' && blob instanceof Blob) { 40 | blob = URL.createObjectURL(blob) // 创建blob地址 41 | } 42 | var aLink = document.createElement('a') 43 | aLink.href = blob 44 | // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,有时候 file:///模式下不会生效 45 | aLink.download = fileName || '' 46 | var event 47 | if (window.MouseEvent) event = new MouseEvent('click') 48 | // 移动端 49 | else { 50 | event = document.createEvent('MouseEvents') 51 | event.initMouseEvent( 52 | 'click', 53 | true, 54 | false, 55 | window, 56 | 0, 57 | 0, 58 | 0, 59 | 0, 60 | 0, 61 | false, 62 | false, 63 | false, 64 | false, 65 | 0, 66 | null 67 | ) 68 | } 69 | aLink.dispatchEvent(event) 70 | URL.revokeObjectURL(blob) 71 | } 72 | 73 | // 读取本地excel文件 74 | function readWorkbookFromLocalFile(file, callback) { 75 | var reader = new FileReader(); 76 | reader.onload = function(e) { 77 | var data = e.target.result; 78 | // 读取二进制的excel 79 | var workbook = XLSX.read(data, {type: 'binary'}); 80 | if(callback) callback(workbook); 81 | }; 82 | reader.readAsBinaryString(file); 83 | } 84 | 85 | // 获取excel第一行的内容 86 | function getHeaderKeyList (sheet) { 87 | var wbData = sheet; // 读取的excel单元格内容 88 | var re = /^[A-Z]*1$/; // /^[A-Z]*1$/ 匹配excel第一行的内容 89 | var arr1 = []; 90 | for (var key in wbData) { // excel第一行内容赋值给数组 91 | if (wbData.hasOwnProperty(key)) { 92 | if (re.test(key)) { 93 | arr1.push(wbData[key].h); 94 | } 95 | } 96 | } 97 | return arr1; 98 | } 99 | -------------------------------------------------------------------------------- /src/components/TheWelcome.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 87 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 104 | 105 | 133 | -------------------------------------------------------------------------------- /src/libs/generator-timu.js: -------------------------------------------------------------------------------- 1 | import { 2 | AlignmentType, 3 | Document, 4 | HeadingLevel, 5 | Packer, 6 | Paragraph, 7 | TabStopPosition, 8 | TabStopType, 9 | TextRun, 10 | NumberValueElement, 11 | Numbering, 12 | TableBorders, 13 | WidthType, 14 | Table, 15 | TableCell, 16 | TableRow, BorderStyle, Columns, Column 17 | } from "docx"; 18 | 19 | export class DocumentCreator { 20 | create(sheet) { 21 | let timuList = sheet.timuList.sort((a, b) => a['总序'] < b['总序']) 22 | 23 | let danxuanList = timuList.filter(item => item['题型'] === '单选') 24 | let duoxuanList = timuList.filter(item => item['题型'] === '多选') 25 | 26 | const document = new Document({ 27 | styles: { 28 | paragraphStyles: [ // 段落样式 29 | { 30 | id: "subtitle", 31 | name: "subtitle", 32 | run: { 33 | size: 36, 34 | color: "#000000" 35 | }, 36 | paragraph: { // 段落 37 | spacing: { // 字间距 38 | before: 500, 39 | after: 300 40 | } 41 | } 42 | }, 43 | { 44 | id: "timu", 45 | name: "timu", 46 | run: { 47 | size: 24, 48 | color: "#000000" 49 | }, 50 | paragraph: { // 段落 51 | spacing: { // 字间距 52 | before: 300, 53 | after: 150 54 | } 55 | } 56 | }, 57 | { 58 | id: "daan", 59 | name: "daan", 60 | run: { 61 | size: 24, 62 | color: "#000000", 63 | // margin: { 64 | // top: 800, 65 | // bottom: 300 66 | // } 67 | }, 68 | paragraph: { // 段落 69 | spacing: { // 字间距 70 | before: 100, 71 | after: 100 72 | } 73 | } 74 | } 75 | ] 76 | }, 77 | numbering: { // 设置项目编号 78 | config: [ 79 | { 80 | reference: "my-crazy-numbering", 81 | levels: [ 82 | { 83 | level: 1, 84 | format: "decimal", 85 | text: "%2.", 86 | alignment: AlignmentType.LEFT, 87 | style: { 88 | paragraph: { 89 | indent: {left: 0, hanging: 360} 90 | } 91 | } 92 | }, 93 | ] 94 | }, 95 | { 96 | reference: "jiexi-numbering", 97 | levels: [ 98 | { 99 | level: 1, 100 | format: "decimal", 101 | text: "%2.", 102 | alignment: AlignmentType.LEFT, 103 | style: { 104 | paragraph: { 105 | indent: {left: 0, hanging: 360} 106 | } 107 | } 108 | } 109 | ] 110 | } 111 | ] 112 | }, 113 | sections: [ 114 | { 115 | children: [ 116 | new Paragraph({ 117 | text: sheet.title, 118 | heading: HeadingLevel.TITLE, 119 | alignment: 'center' 120 | }), 121 | new Paragraph({ 122 | text: '单项选择题', 123 | heading: 'subtitle', 124 | alignment: 'center' 125 | }), 126 | ...danxuanList.map((timu, index) => { 127 | return getTimuElement(timu) 128 | }).reduce((prev, curr) => prev.concat(curr), []), 129 | new Paragraph({ 130 | text: '多项选择题', 131 | heading: 'subtitle', 132 | alignment: 'center' 133 | }), 134 | ...duoxuanList.map((timu, index) => { 135 | return getTimuElement(timu, 'duo') 136 | }).reduce((prev, curr) => prev.concat(curr), []), 137 | new Paragraph({ 138 | text: '参考答案', 139 | heading: 'subtitle', 140 | alignment: 'center' 141 | }), 142 | new Paragraph({ 143 | children: [ 144 | new TextRun({ 145 | text: '一 单项选择题', 146 | bold: true, 147 | heading: 'timu' 148 | }) 149 | ] 150 | }), 151 | ...getDaanElement(danxuanList), 152 | new Paragraph({ 153 | children: [ 154 | new TextRun({ 155 | text: '二 多项选择题', 156 | bold: true, 157 | heading: 'timu' 158 | }) 159 | ] 160 | }), 161 | ...getDaanElement(duoxuanList, 'duo'), 162 | new Paragraph({ 163 | text: '答案解析', 164 | heading: 'subtitle', 165 | alignment: 'center' 166 | }), 167 | new Paragraph({ 168 | children: [ 169 | new TextRun({ 170 | text: '一 单项选择题', 171 | bold: true, 172 | heading: 'timu' 173 | }) 174 | ] 175 | }), 176 | ...danxuanList.map((timu, index) => { 177 | return getJiexiElement(timu) 178 | }).reduce((prev, curr) => prev.concat(curr), []), 179 | new Paragraph({ 180 | children: [ 181 | new TextRun({ 182 | text: '二 多项选择题', 183 | bold: true, 184 | heading: 'timu' 185 | }) 186 | ] 187 | }), 188 | ...duoxuanList.map((timu, index) => { 189 | return getJiexiElement(timu, 'duo') 190 | }).reduce((prev, curr) => prev.concat(curr), []), 191 | ] 192 | } 193 | ] 194 | }) 195 | 196 | return document 197 | } 198 | } 199 | 200 | function getTimuElement(timu, danOrDuo = 'dan') { 201 | const arr = [] 202 | 203 | // 添加题目 204 | const p = new Paragraph({ 205 | text: timu['题目'], 206 | heading: 'timu', 207 | numbering: { 208 | reference: 'my-crazy-numbering', 209 | level: 1 210 | } 211 | }) 212 | arr.push(p) 213 | 214 | // 添加选项 215 | let xuanxiangMax = Math.max(...[ 216 | timu['A'] && String(timu['A']).length || 0, 217 | timu['B'] && String(timu['B']).length || 0, 218 | timu['C'] && String(timu['C']).length || 0, 219 | timu['D'] && String(timu['D']).length || 0, 220 | timu['E'] && String(timu['E']).length || 0 221 | ]) 222 | if (xuanxiangMax > 10) { 223 | // 换行 224 | arr.push(new Paragraph({ 225 | text: `A.${timu['A']}`, 226 | heading: 'daan', 227 | })) 228 | arr.push(new Paragraph({ 229 | text: `B.${timu['B']}`, 230 | heading: 'daan', 231 | })) 232 | arr.push(new Paragraph({ 233 | text: `C.${timu['C']}`, 234 | heading: 'daan', 235 | })) 236 | arr.push(new Paragraph({ 237 | text: `D.${timu['D']}`, 238 | heading: 'daan', 239 | })) 240 | if (danOrDuo === 'duo') { 241 | arr.push(new Paragraph({ 242 | text: `E.${timu['E']}`, 243 | heading: 'daan', 244 | })) 245 | } 246 | } else { 247 | // 2个选项一排 248 | let E = [] 249 | if (danOrDuo === 'duo') { 250 | E = [ 251 | new TableRow({ 252 | children: [ 253 | new TableCell({ 254 | children: [ 255 | new Paragraph({ 256 | text: `E.${timu['E']}`, 257 | heading: 'daan', 258 | }) 259 | ] 260 | }), 261 | new TableCell({ 262 | children: [ 263 | new Paragraph({ 264 | text: ` `, 265 | heading: 'daan', 266 | }) 267 | ] 268 | }) 269 | ] 270 | }) 271 | ] 272 | } 273 | arr.push(new Table({ 274 | borders: TableBorders.NONE, 275 | width: { 276 | type: WidthType.PERCENTAGE, 277 | size: 100 278 | }, 279 | rows: [ 280 | new TableRow({ 281 | children: [ 282 | new TableCell({ 283 | children: [ 284 | new Paragraph({ 285 | text: `A.${timu['A']}`, 286 | heading: 'daan', 287 | }), 288 | ], 289 | }), 290 | new TableCell({ 291 | children: [ 292 | new Paragraph({ 293 | text: `B.${timu['B']}`, 294 | heading: 'daan', 295 | }), 296 | ], 297 | }) 298 | ], 299 | }), 300 | new TableRow({ 301 | children: [ 302 | new TableCell({ 303 | children: [ 304 | new Paragraph({ 305 | text: `C.${timu['C']}`, 306 | heading: 'daan', 307 | }), 308 | ] 309 | }), 310 | new TableCell({ 311 | children: [ 312 | new Paragraph({ 313 | text: `D.${timu['D']}`, 314 | heading: 'daan', 315 | }) 316 | ] 317 | }) 318 | ] 319 | }), 320 | ...E 321 | ], 322 | })) 323 | } 324 | return arr 325 | } 326 | 327 | function getDaanElement(list, danOrDuo = 'dan') { 328 | const arr = [] 329 | const rows = [] 330 | let curRow = null 331 | let count = danOrDuo === 'dan' ? 10 : 5 332 | list.map((item, index) => { 333 | if (index % count === 0) { 334 | curRow = new TableRow({ 335 | children: [ 336 | ] 337 | }) 338 | } 339 | curRow.addCellToIndex(new TableCell({ 340 | children: [ 341 | new Paragraph({ 342 | text: `${item['总序'] <= 9 ? ' ': ''}${item['总序']}. ${item['答案']}`, 343 | }), 344 | ] 345 | }), index % count) 346 | if (index % count === count - 1 || index === list.length - 1) { 347 | rows.push(curRow) 348 | } 349 | }) 350 | let table = new Table({ 351 | borders: TableBorders.NONE, 352 | width: { 353 | type: WidthType.PERCENTAGE, 354 | size: 100 355 | }, 356 | rows: rows 357 | }) 358 | arr.push(table) 359 | return arr 360 | } 361 | 362 | 363 | function getJiexiElement(timu, danOrDuo = 'dan') { 364 | const arr = [] 365 | const p = new Paragraph({ 366 | text: `【答案】 ${timu['答案']}. ${timu['答案_1']}`, 367 | heading: 'timu', 368 | numbering: { 369 | reference: 'jiexi-numbering', 370 | level: 1 371 | } 372 | }) 373 | arr.push(p) 374 | const jiexi = new Paragraph({ 375 | text: `【解析】 ${timu['解析']}`, 376 | heading: 'daan' 377 | }) 378 | arr.push(jiexi) 379 | return arr 380 | } 381 | --------------------------------------------------------------------------------