charging-cabinet/docs/build_contract.js

273 lines
11 KiB
JavaScript
Raw Permalink Normal View History

// 开发合同.docx 生成脚本
// 标准 Word 排版A4、宋体、清晰的层级
const {
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
AlignmentType, HeadingLevel, BorderStyle, WidthType, ShadingType,
PageNumber, Footer, Header, LevelFormat
} = require('docx');
const fs = require('fs');
// 边框样式
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const tableBorders = {
top: { style: BorderStyle.SINGLE, size: 6, color: "000000" },
bottom: { style: BorderStyle.SINGLE, size: 6, color: "000000" },
left: { style: BorderStyle.SINGLE, size: 6, color: "000000" },
right: { style: BorderStyle.SINGLE, size: 6, color: "000000" },
insideHorizontal: { style: BorderStyle.SINGLE, size: 4, color: "000000" },
insideVertical: { style: BorderStyle.SINGLE, size: 4, color: "000000" },
};
// 标题段落(一级)
function h1(text) {
return new Paragraph({
heading: HeadingLevel.HEADING_1,
spacing: { before: 360, after: 200 },
children: [new TextRun({ text, bold: true, size: 32, font: "SimSun" })],
});
}
// 标题段落(二级)
function h2(text) {
return new Paragraph({
heading: HeadingLevel.HEADING_2,
spacing: { before: 240, after: 120 },
children: [new TextRun({ text, bold: true, size: 28, font: "SimSun" })],
});
}
// 正文段落
function p(text, opts = {}) {
return new Paragraph({
spacing: { line: 360, before: 80, after: 80, lineRule: "auto" },
alignment: opts.align || AlignmentType.JUSTIFIED,
indent: opts.indent ? { firstLine: opts.indent } : undefined,
children: [new TextRun({ text, size: 24, font: "SimSun" })],
});
}
// 粗体段落
function pBold(text, opts = {}) {
return new Paragraph({
spacing: { line: 360, before: 80, after: 80, lineRule: "auto" },
alignment: opts.align || AlignmentType.LEFT,
children: [new TextRun({ text, bold: true, size: 24, font: "SimSun" })],
});
}
// 空行
function empty() {
return new Paragraph({ children: [new TextRun({ text: "" })] });
}
// 居中粗体大标题
function bigTitle(text) {
return new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { before: 200, after: 400 },
children: [new TextRun({ text, bold: true, size: 44, font: "SimHei" })],
});
}
// 表格单元格
function cell(text, opts = {}) {
return new TableCell({
borders: tableBorders,
width: { size: opts.width || 2000, type: WidthType.DXA },
shading: opts.header ? { fill: "E7E6E6", type: ShadingType.CLEAR, color: "auto" } : undefined,
margins: { top: 100, bottom: 100, left: 120, right: 120 },
children: [new Paragraph({
alignment: opts.align || AlignmentType.LEFT,
spacing: { line: 300, before: 0, after: 0 },
children: [new TextRun({
text: String(text),
bold: !!opts.header,
size: 24,
font: "SimSun",
})],
})],
});
}
// 创建表格
function makeTable(rows, colWidths) {
return new Table({
width: { size: colWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA },
columnWidths: colWidths,
rows: rows.map((row, i) => new TableRow({
children: row.map((text, j) => cell(text, { width: colWidths[j], header: i === 0 })),
})),
});
}
// ==================== 内容 ====================
const children = [];
// 大标题
children.push(bigTitle("36仓体直流电瓶车充电柜"));
children.push(bigTitle("开发合同"));
// 合同信息
children.push(p("合同编号_______________", { align: AlignmentType.LEFT }));
children.push(p("签订地点_______________", { align: AlignmentType.LEFT }));
children.push(p("签订日期______年______月______日", { align: AlignmentType.LEFT }));
children.push(empty());
// 第一条
children.push(h1("第一条 合同双方"));
children.push(pBold("甲方(委托方):"));
children.push(p("公司名称____________________________________"));
children.push(p("法定代表人________________"));
children.push(p("地址____________________________________"));
children.push(p("联系人________________ 联系电话________________"));
children.push(empty());
children.push(pBold("乙方(开发方):"));
children.push(p("公司名称____________________________________"));
children.push(p("法定代表人________________"));
children.push(p("地址____________________________________"));
children.push(p("联系人________________ 联系电话________________"));
// 第二条
children.push(h1("第二条 项目概述"));
children.push(p("1. 项目名称36仓体直流电瓶车充电柜开发项目。"));
children.push(p("2. 项目目标:开发充电柜配套的柜控主板、仓控主板、嵌入式固件、后台管理系统及微信小程序。"));
children.push(p("3. 开发周期合同生效后12周。"));
children.push(p("4. 交付成果:详见第五条及附件。"));
// 第三条
children.push(h1("第三条 开发内容"));
children.push(makeTable(
[
["序号", "开发内容", "详见附件"],
["1", "柜控主板硬件设计开发", "附件一"],
["2", "仓控主板硬件设计开发", "附件一"],
["3", "嵌入式固件开发(柜控板+仓控板)", "附件一"],
["4", "后台管理系统开发", "附件二"],
["5", "微信小程序开发", "附件二"],
["6", "通讯协议栈开发实现", "附件三"],
],
[1000, 5360, 3000]
));
children.push(empty());
children.push(p("乙方负责主板设计、元器件采购、焊接调试及联调支持。柜体、电源及外部器件由甲方提供。本合同不交付任何源代码。"));
// 第四条
children.push(h1("第四条 开发阶段"));
children.push(makeTable(
[
["阶段", "时间", "交付物"],
["M1 方案定稿", "第1-2周", "原理图、PCB、BOM、协议文档"],
["M2 开发完成", "第3-6周", "硬件样板、固件、后台、小程序"],
["M3 联调完成", "第7-9周", "集成测试报告"],
["M4 验收交付", "第10-12周", "完整成品、技术文档"],
],
[2500, 2000, 4860]
));
children.push(empty());
children.push(p("付款方式______________________________________________________________________"));
// 第五条
children.push(h1("第五条 交付与验收"));
children.push(p("交付物清单及验收标准详见附件三。整机验收时乙方到甲方现场完成联调,验收所需柜体及电源由甲方提供。"));
// 第六条
children.push(h1("第六条 知识产权"));
children.push(p("1. 乙方在本合同履行过程中产生的全部技术成果(包括但不限于源代码、设计图纸、技术文档等)的知识产权归甲乙双方共同所有。"));
children.push(p("2. 任何一方均有权在自身业务范围内自由使用上述技术成果,无需征得另一方同意或另行支付费用。"));
// 第七条
children.push(h1("第七条 保密条款"));
children.push(p("1. 双方对合同履行过程中知悉的对方商业秘密、技术秘密负有保密义务。"));
children.push(p("2. 保密期限自合同生效之日起3年。"));
children.push(p("3. 未经对方书面同意,不得向第三方披露任何与本合同相关的信息。"));
// 第八条
children.push(h1("第八条 质保与维护"));
children.push(p("1. 硬件质保期自验收通过之日起12个月质保期内免费维修或更换。"));
children.push(p("2. 软件质保期自验收通过之日起6个月质保期内免费修复Bug。"));
children.push(p("3. 质保期外的维护服务费用由双方另行协商。"));
// 第九条
children.push(h1("第九条 违约责任"));
children.push(p("1. 甲方逾期付款的,每逾期一日按应付未付金额的千分之三支付违约金。"));
children.push(p("2. 乙方逾期交付的,每逾期一日按合同总金额的千分之三支付违约金。"));
children.push(p("3. 任何一方违反知识产权或保密条款的,应赔偿对方全部损失。"));
// 第十条
children.push(h1("第十条 合同总金额"));
children.push(p("合同总金额人民币____________元大写__________________________。"));
// 第十一条
children.push(h1("第十一条 争议解决"));
children.push(p("本合同履行过程中发生争议的,双方应协商解决;协商不成的,提交合同签订地人民法院诉讼解决。"));
// 第十二条
children.push(h1("第十二条 其他"));
children.push(p("1. 本合同一式两份,甲乙双方各执一份,具有同等法律效力。"));
children.push(p("2. 本合同附件为合同的组成部分,与本合同具有同等法律效力。"));
children.push(p("3. 未尽事宜,双方可另行签订补充协议。"));
// 签章区
children.push(empty());
children.push(empty());
children.push(pBold("甲方盖章__________________________"));
children.push(p("授权代表签字________________________"));
children.push(p("日期______年______月______日"));
children.push(empty());
children.push(pBold("乙方盖章__________________________"));
children.push(p("授权代表签字________________________"));
children.push(p("日期______年______月______日"));
// ==================== 生成文档 ====================
const doc = new Document({
creator: "CodeBuddy",
title: "36仓体直流电瓶车充电柜开发合同",
styles: {
default: {
document: { run: { font: "SimSun", size: 24 } },
},
paragraphStyles: [
{
id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true,
run: { size: 32, bold: true, font: "SimHei" },
paragraph: { spacing: { before: 360, after: 200 }, outlineLevel: 0 },
},
{
id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true,
run: { size: 28, bold: true, font: "SimHei" },
paragraph: { spacing: { before: 240, after: 120 }, outlineLevel: 1 },
},
],
},
sections: [{
properties: {
page: {
size: { width: 11906, height: 16838 }, // A4
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
},
},
footers: {
default: new Footer({
children: [new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "第 ", size: 20, font: "SimSun" }),
new TextRun({ children: [PageNumber.CURRENT], size: 20, font: "SimSun" }),
new TextRun({ text: " 页 / 共 ", size: 20, font: "SimSun" }),
new TextRun({ children: [PageNumber.TOTAL_PAGES], size: 20, font: "SimSun" }),
new TextRun({ text: " 页", size: 20, font: "SimSun" }),
],
})],
}),
},
children,
}],
});
Packer.toBuffer(doc).then(buffer => {
fs.writeFileSync("开发合同.docx", buffer);
console.log("OK: 开发合同.docx 已生成 (" + buffer.length + " bytes)");
});