273 lines
11 KiB
JavaScript
273 lines
11 KiB
JavaScript
// 开发合同.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)");
|
||
});
|