|
|
<template>
|
|
|
<div>
|
|
|
<!-- {{ options }}-->
|
|
|
<!-- <div style="width: 19vw; display: inline-block">-->
|
|
|
<!-- <span style="font-size: 14px"> 点击插入字段(光标所在位置) </span>-->
|
|
|
<!-- <div class="fieldList">-->
|
|
|
<!-- <div class="fieldListItem" v-for="i in fieldList" @click="insertText(i)">-->
|
|
|
<!-- {{ i.name }}-->
|
|
|
<!-- </div>-->
|
|
|
<!-- </div>-->
|
|
|
<!-- </div>-->
|
|
|
<div style="width: 100vw; display: inline-block; vertical-align: top">
|
|
|
<umo-editor v-bind="options" ref="editorRef" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { AlignmentType, Document, Footer, Packer, Paragraph, TextRun } from 'docx';
|
|
|
import { UmoEditor } from '@umoteam/editor';
|
|
|
|
|
|
const fieldList = ref([
|
|
|
{
|
|
|
name: '字段1',
|
|
|
value: '字段1'
|
|
|
},
|
|
|
{
|
|
|
name: '字段2',
|
|
|
value: '字段2'
|
|
|
}
|
|
|
]);
|
|
|
const editorRef = ref();
|
|
|
const docData = {
|
|
|
'甲方': '青岛xxx科技有限公司',
|
|
|
'合同表格表头': ['序号', '产品名称', '规格描述', '数量', '单位', '单价', '小记']
|
|
|
};
|
|
|
const createContractTable = () => {
|
|
|
const rows = 3;
|
|
|
const cols = docData['合同表格表头'].length;
|
|
|
const head = docData['合同表格表头'];
|
|
|
return {
|
|
|
type: 'table',
|
|
|
attrs: { id: 'contractTable', style: 'width:100%;border-collapse:collapse;' },
|
|
|
content: [
|
|
|
...Array.from({ length: rows }).map((_, k) => ({
|
|
|
type: 'tableRow',
|
|
|
content:
|
|
|
k === 0
|
|
|
? head.map((i) => ({
|
|
|
type: 'tableHeader',
|
|
|
attrs: { style: 'border:1px solid #ccc;padding:4px;height:40px;' },
|
|
|
content: [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: i }]
|
|
|
}
|
|
|
]
|
|
|
}))
|
|
|
: Array.from({ length: cols }).map((__, kk) => ({
|
|
|
type: 'tableCell',
|
|
|
attrs: { style: 'border:1px solid #ccc;padding:4px;height:40px;' },
|
|
|
content:
|
|
|
kk === 0
|
|
|
? [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '' + k }]
|
|
|
}
|
|
|
]
|
|
|
: [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: ' ' }]
|
|
|
}
|
|
|
]
|
|
|
}))
|
|
|
})),
|
|
|
{
|
|
|
type: 'tableRow',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'tableCell',
|
|
|
attrs: { colspan: 6 },
|
|
|
content: [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
attrs: { textAlign: 'center' },
|
|
|
content: [{ type: 'text', text: '合计' }]
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'tableCell',
|
|
|
attrs: {},
|
|
|
content: [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: ' ' }]
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'tableRow',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'tableCell',
|
|
|
attrs: { colspan: 7 },
|
|
|
content: [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
attrs: { textAlign: 'center' },
|
|
|
content: [{ type: 'text', text: '合同总价: ¥ 大写:人民币 整' }]
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'tableRow',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'tableCell',
|
|
|
attrs: { colspan: 7 },
|
|
|
content: [
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
attrs: { textAlign: 'left' },
|
|
|
content: [{ type: 'text', text: '备注:\t1、以上含13%增值税,开具增值税专用发票' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
attrs: { textAlign: 'left' },
|
|
|
content: [{ type: 'text', text: '\t\t2、价格含包装运输费,质保期一年。' }]
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
};
|
|
|
const options = ref({
|
|
|
templates: [
|
|
|
{
|
|
|
title: '合同',
|
|
|
description: '合同',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'heading',
|
|
|
attrs: { level: 1, textAlign: 'center' },
|
|
|
content: [{ type: 'text', text: '合 同' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
attrs: { textAlign: 'right' },
|
|
|
content: [{ type: 'text', text: '合同编号:111' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'text',
|
|
|
attrs: { textAlign: 'left' },
|
|
|
text: `\t甲方:${docData['甲方']}\t\t乙方:青岛海威物联科技有限公司`
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t1.产品名称、规格、供货范围和价款 币种:人民币 单位:元' }]
|
|
|
},
|
|
|
createContractTable(),
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t2.产品包装' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t\t2.1所供产品由乙方妥善包装。' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t3.产品交付' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t\t3.1交付期:合同签订收到预付款后1周内发货。' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t\t3.2乙方送货至甲方指定场所,运费由乙方承担。' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t4.验收及质保:货物签收即为验收确认无问题;且自设备验收之日起提供一年质保。' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t5.付款方式:\t电汇;合同签订后7日内支付全额货款。' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t\t\t\t开户银行:交通银行青岛崂山支行' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t\t\t\t银行账户:38200557001801018025' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t6.知识产权' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'text',
|
|
|
text: '\t\t与合同产品有关的所有技术资料的知识产权属于乙方所有,甲方应当严格保守乙方的技术秘密,未经许可不得泄露给第三方。否则,甲方应当赔偿由此给乙方造成的一切经济损失。'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t7.不可抗力' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'text',
|
|
|
text: '\t\t由于战争、严重的大火、水灾、暴风雪、瘟疫和地震等人力不可抗拒事件,致使乙方交货延迟或不能交货时,责任不在乙方。但乙方应立即将事故证明书,以最快的方式交给甲方。在上述情况下,乙方仍有采取一切必要措施从速交货的责任。'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t8.争议解决方式' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'text',
|
|
|
text: '\t\t在本合同执行过程中如发生争议,双方友好协商解决。协商不成,双方同意由原告所在地人民法院审理。'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: '\t9.其它' }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [
|
|
|
{
|
|
|
type: 'text',
|
|
|
text: '\t\t合同自双方盖章起生效。本合同一式二份,双方各执一份;合同扫描件、复印件、传真件与原件具有同等法律效力。'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: `\t甲方:${docData['甲方']}\t\t\t乙方:青岛海威物联科技有限公司` }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: `\t授权代表:\t\t\t\t\t\t授权代表:` }]
|
|
|
},
|
|
|
{
|
|
|
type: 'paragraph',
|
|
|
content: [{ type: 'text', text: `\t日期:\t\t\t\t\t\t\t日期:` }]
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
],
|
|
|
document: {
|
|
|
title: '',
|
|
|
content: {
|
|
|
type: 'doc',
|
|
|
content: []
|
|
|
},
|
|
|
placeholder: {
|
|
|
en_US: 'Please enter the document content...',
|
|
|
zh_CN: '请输入文档内容...'
|
|
|
},
|
|
|
enableSpellcheck: true,
|
|
|
enableMarkdown: true,
|
|
|
enableBubbleMenu: true,
|
|
|
enableBlockMenu: true,
|
|
|
readOnly: false,
|
|
|
autofocus: true,
|
|
|
characterLimit: 0,
|
|
|
typographyRules: {},
|
|
|
editorProps: {},
|
|
|
parseOptions: {
|
|
|
preserveWhitespace: 'full'
|
|
|
},
|
|
|
autoSave: {
|
|
|
enabled: true,
|
|
|
interval: 300000
|
|
|
}
|
|
|
},
|
|
|
onSave: async (e) => {
|
|
|
console.log(e);
|
|
|
// const editor = editorRef.value;
|
|
|
// const html = editor.getHTML();
|
|
|
// console.log(html);
|
|
|
// const doc = new Document({
|
|
|
// sections: [{ children: parseHTML(editor.getHTML()) }]
|
|
|
// });
|
|
|
// await download(doc);
|
|
|
},
|
|
|
onChanged: async (e) => {
|
|
|
// const editor = editorRef.value;
|
|
|
// const json = editor.getJSON();
|
|
|
// console.log(json);
|
|
|
// console.log(json.content.find((v) => v.type === 'table' && v.attrs?.id === 'contractTable'));
|
|
|
// json
|
|
|
// editor.setContent(json, { emitUpdate: false });
|
|
|
// const doc = new Document({
|
|
|
// sections: [{ children: prseHTML(editor.getHTML()) }]
|
|
|
// });
|
|
|
// await download(doc);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
function parseHTML(html) {
|
|
|
const div = document.createElement('div');
|
|
|
div.innerHTML = html;
|
|
|
const children = [];
|
|
|
|
|
|
div.childNodes.forEach((node) => {
|
|
|
if (node.nodeType === 3) {
|
|
|
children.push(new Paragraph(node.textContent));
|
|
|
} else if (node.nodeType === 1) {
|
|
|
console.log(node);
|
|
|
console.log(node.tagName);
|
|
|
if (node.tagName === 'H1') {
|
|
|
children.push(new Paragraph({ text: node.textContent, heading: 'Heading1' }));
|
|
|
} else if (node.tagName === 'P') {
|
|
|
const runs = [];
|
|
|
node.childNodes.forEach((n) => {
|
|
|
const props = {};
|
|
|
if (n.tagName === 'B' || n.tagName === 'STRONG') props.bold = true;
|
|
|
if (n.tagName === 'I' || n.tagName === 'EM') props.italics = true;
|
|
|
runs.push(new TextRun({ text: n.textContent, ...props }));
|
|
|
});
|
|
|
children.push(new Paragraph({ children: runs }));
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
return children;
|
|
|
}
|
|
|
|
|
|
// 创建文档
|
|
|
const doc = new Document({
|
|
|
sections: [
|
|
|
{
|
|
|
properties: {},
|
|
|
children: [
|
|
|
new Paragraph({
|
|
|
text: 'Hello World',
|
|
|
heading: 'Heading1'
|
|
|
}),
|
|
|
new Paragraph({
|
|
|
text: 'This is a simple DOCX file created using DOCX.js.'
|
|
|
})
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
});
|
|
|
|
|
|
const download = async (doc) => {
|
|
|
const blob = await Packer.toBlob(doc);
|
|
|
const link = document.createElement('a');
|
|
|
link.href = URL.createObjectURL(blob);
|
|
|
link.download = 'document.docx';
|
|
|
|
|
|
document.body.appendChild(link);
|
|
|
link.click();
|
|
|
document.body.removeChild(link);
|
|
|
};
|
|
|
// download(doc)
|
|
|
const insertText = (e) => {
|
|
|
const editor = editorRef.value;
|
|
|
console.log(editor);
|
|
|
console.log(editor.getSelectionNode());
|
|
|
console.log(editor.getSelectionText());
|
|
|
// const selection = editor.getSelection();
|
|
|
// editor.insertContent({
|
|
|
// type: 'text',
|
|
|
// text: e.value
|
|
|
// });
|
|
|
// if (selection) {
|
|
|
// editor.focus();
|
|
|
// editor.setSelection(selection.toEnd ? selection.toEnd() : selection);
|
|
|
// }
|
|
|
editor
|
|
|
.chain()
|
|
|
.focus()
|
|
|
.insertContent({
|
|
|
type: 'text',
|
|
|
text: e.value
|
|
|
})
|
|
|
.run();
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.fieldList {
|
|
|
width: 100%;
|
|
|
height: calc(100vh - 30px);
|
|
|
overflow: auto;
|
|
|
|
|
|
&::-webkit-scrollbar {
|
|
|
display: none;
|
|
|
}
|
|
|
|
|
|
.fieldListItem {
|
|
|
line-height: 40px;
|
|
|
font-size: 16px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/deep/ .t-button--theme-warning {
|
|
|
border-color: var(--td-warning-color-hover);
|
|
|
background-color: var(--td-warning-color-hover);
|
|
|
}
|
|
|
</style>
|
|
|
<style>
|
|
|
.umo-footer {
|
|
|
position: sticky;
|
|
|
bottom: 0px;
|
|
|
}
|
|
|
</style>
|