219 lines
11 KiB
JavaScript
219 lines
11 KiB
JavaScript
import fs from 'fs';
|
||
import path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
import {
|
||
Document, Packer, Paragraph, TextRun, Header, Footer,
|
||
AlignmentType, PageNumber, PageBreak, HeadingLevel,
|
||
Table, TableRow, TableCell, WidthType, BorderStyle,
|
||
LevelFormat, ShadingType,
|
||
} from 'docx';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const OUT = path.resolve(__dirname, '..', 'docs', '软著登记申请表填写指南.docx');
|
||
|
||
const PAGE_W = 11906;
|
||
const PAGE_H = 16838;
|
||
const MARGIN = 1440;
|
||
const CONTENT_W = PAGE_W - MARGIN * 2;
|
||
|
||
const border = { style: BorderStyle.SINGLE, size: 1, color: '999999' };
|
||
const borders = { top: border, bottom: border, left: border, right: border };
|
||
|
||
function h(level, text) {
|
||
const sizes = { 1: 36, 2: 30, 3: 26 };
|
||
const map = { 1: HeadingLevel.HEADING_1, 2: HeadingLevel.HEADING_2, 3: HeadingLevel.HEADING_3 };
|
||
return new Paragraph({
|
||
heading: map[level],
|
||
spacing: { before: 240, after: 160 },
|
||
children: [new TextRun({ text, font: 'SimHei', size: sizes[level], bold: true })],
|
||
});
|
||
}
|
||
function p(text) {
|
||
return new Paragraph({
|
||
spacing: { before: 60, after: 60 },
|
||
children: [new TextRun({ text, font: 'SimSun', size: 24 })],
|
||
});
|
||
}
|
||
function bullet(text) {
|
||
return new Paragraph({
|
||
numbering: { reference: 'bullets', level: 0 },
|
||
spacing: { before: 40, after: 40 },
|
||
children: [new TextRun({ text, font: 'SimSun', size: 24 })],
|
||
});
|
||
}
|
||
function checklist(text) {
|
||
return new Paragraph({
|
||
spacing: { before: 40, after: 40 },
|
||
children: [new TextRun({ text: `□ ${text}`, font: 'SimSun', size: 24 })],
|
||
});
|
||
}
|
||
function tableRow(cells, isHeader = false) {
|
||
const colW = Math.floor(CONTENT_W / cells.length);
|
||
return new TableRow({
|
||
children: cells.map((c, i) => new TableCell({
|
||
borders,
|
||
width: { size: colW, type: WidthType.DXA },
|
||
shading: isHeader ? { fill: '2B579A', type: ShadingType.CLEAR } : undefined,
|
||
margins: { top: 80, bottom: 80, left: 100, right: 100 },
|
||
children: [new Paragraph({
|
||
spacing: { before: 20, after: 20 },
|
||
children: [new TextRun({ text: c, font: isHeader ? 'SimHei' : 'SimSun', size: 20, bold: isHeader, color: isHeader ? 'FFFFFF' : '333333' })],
|
||
})],
|
||
})),
|
||
});
|
||
}
|
||
|
||
const sections = [];
|
||
sections.push({
|
||
properties: {
|
||
page: { size: { width: PAGE_W, height: PAGE_H }, margin: { top: MARGIN, right: MARGIN, bottom: MARGIN, left: MARGIN } },
|
||
headers: {
|
||
default: new Header({
|
||
children: [new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
border: { bottom: { style: BorderStyle.SINGLE, size: 2, color: '2B579A', space: 4 } },
|
||
children: [new TextRun({ text: '软著登记申请表填写指南', font: 'SimSun', size: 18, color: '666666' })],
|
||
})],
|
||
}),
|
||
},
|
||
footers: {
|
||
default: new Footer({
|
||
children: [new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
children: [
|
||
new TextRun({ text: '第 ', font: 'SimSun', size: 18, color: '888888' }),
|
||
new TextRun({ children: [PageNumber.CURRENT], font: 'SimSun', size: 18, color: '888888' }),
|
||
new TextRun({ text: ' 页', font: 'SimSun', size: 18, color: '888888' }),
|
||
],
|
||
})],
|
||
}),
|
||
},
|
||
},
|
||
children: [
|
||
new Paragraph({ spacing: { before: 1200 }, children: [] }),
|
||
new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 200 },
|
||
children: [new TextRun({ text: '中国版权保护中心', font: 'SimHei', size: 32, bold: true, color: '2B579A' })],
|
||
}),
|
||
new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 200 },
|
||
children: [new TextRun({ text: '软件著作权登记申请表填写指南', font: 'SimHei', size: 40, bold: true, color: '333333' })],
|
||
}),
|
||
new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { after: 100 },
|
||
children: [new TextRun({ text: '对应项目:可视化大屏模板系统 V1.0', font: 'SimSun', size: 24, color: '555555' })],
|
||
}),
|
||
new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: '生成日期:2026年7月26日', font: 'SimSun', size: 24, color: '555555' })],
|
||
}),
|
||
new Paragraph({ children: [new PageBreak()] }),
|
||
|
||
h(1, '一、软件基本信息'),
|
||
new Table({
|
||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||
rows: [
|
||
tableRow(['表单字段', '建议填写内容', '说明'], true),
|
||
tableRow(['软件全称', '可视化大屏模板系统', '']),
|
||
tableRow(['软件简称', '大屏模板系统', '可留空;填写便于识别']),
|
||
tableRow(['版本号', 'V1.0', '与材料中版本号保持一致']),
|
||
tableRow(['权利范围', '全部权利', '原创作品,选择全部权利']),
|
||
tableRow(['软件分类', '应用软件', '在系统中选择对应分类']),
|
||
tableRow(['软件说明', '原创', '本项目为原创开发']),
|
||
tableRow(['开发方式', '单独开发', '个人著作权人,选择单独开发']),
|
||
tableRow(['开发完成日期', '2026-07-26', '以最终改造完成日期为准']),
|
||
tableRow(['发表状态', '未发表', '如尚未公开发布,选择未发表']),
|
||
],
|
||
}),
|
||
|
||
h(1, '二、著作权人信息'),
|
||
p('截图中已填写:中国 / 湖北武汉 / 自然人 / 陈银军 / 居民身份证。'),
|
||
p('请务必核对姓名、身份证号与身份证正反面扫描件完全一致;身份证需在有效期内、四角完整、文字清晰。'),
|
||
|
||
h(1, '三、开发/运行环境'),
|
||
new Table({
|
||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||
rows: [
|
||
tableRow(['表单字段', '建议填写内容'], true),
|
||
tableRow(['开发的硬件环境', 'PC 机;Intel Core i5 及以上 CPU;内存 8GB 及以上;硬盘 256GB 及以上;显示器分辨率 1920×1080 及以上。']),
|
||
tableRow(['运行的硬件环境', 'PC 机或服务器;Intel Core i5 及以上 CPU;内存 8GB 及以上;硬盘 256GB 及以上;支持 WebGL 的显卡;显示器分辨率 1920×1080 及以上。']),
|
||
tableRow(['开发该软件的操作系统', 'macOS 12 / Windows 10 / Windows 11']),
|
||
tableRow(['软件开发环境 / 开发工具', 'Node.js 18+、npm 9+、Vite 5、Visual Studio Code、TypeScript 5.7']),
|
||
tableRow(['该软件的运行平台 / 操作系统', 'Windows 10/11、macOS 11+、Linux 各发行版']),
|
||
tableRow(['软件运行支撑环境 / 支持软件', 'Chrome 90+、Edge 90+、Firefox 88+、Safari 14+']),
|
||
tableRow(['编程语言', 'TypeScript、HTML、CSS、Vue']),
|
||
tableRow(['源程序量', '约 105,000 行(197 个源文件)']),
|
||
],
|
||
}),
|
||
|
||
h(1, '四、软件功能与技术特点'),
|
||
h(2, '4.1 开发目的'),
|
||
p('为企业和个人提供一套开箱即用的数据可视化大屏模板集合,降低大屏开发成本,提升数据展示效率,适用于车辆监控、农业数据、交通运输、物流数据、电商数据、政务大数据、通用模板、大数据分析、行业垂直应用及布局组件等多种场景。'),
|
||
h(2, '4.2 面向领域 / 行业'),
|
||
p('数据可视化、大数据分析、企业监控、智慧城市、指挥调度、态势感知、物联网、电子商务、政务管理、物流运输。'),
|
||
h(2, '4.3 软件的主要功能'),
|
||
bullet('大屏模板导航:首页以卡片形式展示 10 大分类、66 个数据大屏模板,支持分类筛选、关键词搜索、滚动浏览。'),
|
||
bullet('模板预览:每个模板卡片展示预览图、名称和描述,帮助用户快速识别目标大屏。'),
|
||
bullet('大屏页面展示:点击卡片进入对应大屏详情页,集成 ECharts 图表、地图、数字滚动、轮播切换等可视化效果。'),
|
||
bullet('多屏模板支持:部分复杂模板包含多个子页面,支持自动轮播或手动切换。'),
|
||
bullet('独立页面封装:每个大屏页面自包含样式、图片、数据配置及子组件,便于单独拷贝复用。'),
|
||
bullet('响应式图表:图表随浏览器窗口尺寸变化自动缩放,适配主流显示分辨率。'),
|
||
bullet('路由管理:基于 Vue Router 实现单页面应用路由,共注册 85 条路由。'),
|
||
h(2, '4.4 软件的技术特点'),
|
||
bullet('前端框架:基于 Vue 3 + TypeScript + Vite 构建,采用 Composition API 与 <script setup> 语法。'),
|
||
bullet('路由方案:使用 Vue Router 4 实现 SPA 路由管理。'),
|
||
bullet('可视化引擎:集成 ECharts 4.x,支持柱状图、折线图、饼图、地图、雷达图、仪表盘、水球图、词云图等 20 余种图表。'),
|
||
bullet('地图能力:支持中国地图、省级地图及 GIS 地图展示。'),
|
||
bullet('组件化设计:公共组件(NavBar、CategorySection、TemplateCard)与页面组件解耦。'),
|
||
bullet('自包含结构:每个大屏模板独立存放,可单独拷贝使用。'),
|
||
bullet('代码规模:源程序约 105,000 行,197 个源文件,覆盖 85 条路由页面。'),
|
||
p('表单中「软件的技术特点」若为多选,可勾选:大数据软件、智慧城市软件、物联网软件;并在文本框中补充上述说明。'),
|
||
|
||
h(1, '五、鉴别材料上传'),
|
||
new Table({
|
||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||
rows: [
|
||
tableRow(['上传项', '应上传文件', '文件状态'], true),
|
||
tableRow(['程序鉴别材料', 'docs/源程序文档.pdf', '已生成,60 页(前 30 页 + 后 30 页),每页 60 行']),
|
||
tableRow(['文档鉴别材料', 'docs/软件说明书.pdf', '已生成,5 页(不足 60 页,全部提交)']),
|
||
tableRow(['其他相关证明文件', '无需上传', '个人原创单独开发,无合作/委托/任务开发协议']),
|
||
],
|
||
}),
|
||
p('注意:版权中心要求上传 PDF 格式。已生成的 PDF 文件位于 docs/ 目录下,请在上传前再次检查文件名、页数及清晰度。'),
|
||
|
||
h(1, '六、提交前核对清单'),
|
||
checklist('软件全称、版本号、开发完成日期与鉴别材料中一致'),
|
||
checklist('著作权人姓名、身份证号与身份证正反面扫描件一致'),
|
||
checklist('身份证扫描件四角完整、文字清晰、在有效期内'),
|
||
checklist('源程序文档 PDF 为 60 页(前 30 + 后 30)'),
|
||
checklist('软件说明书 PDF 已全部包含(5 页)'),
|
||
checklist('上传文件均为 PDF 格式,大小符合系统限制'),
|
||
checklist('申请表中所有带 * 号字段均已填写'),
|
||
checklist('确认「发表状态」选择正确(未发表/已发表)'),
|
||
],
|
||
});
|
||
|
||
const doc = new Document({
|
||
styles: {
|
||
default: { document: { run: { font: 'SimSun', size: 24 } } },
|
||
},
|
||
numbering: {
|
||
config: [{
|
||
reference: 'bullets',
|
||
levels: [{
|
||
level: 0, format: LevelFormat.BULLET, text: '•', alignment: AlignmentType.LEFT,
|
||
style: { paragraph: { indent: { left: 720, hanging: 360 } } },
|
||
}],
|
||
}],
|
||
},
|
||
sections,
|
||
});
|
||
|
||
if (!fs.existsSync(path.dirname(OUT))) fs.mkdirSync(path.dirname(OUT), { recursive: true });
|
||
const buffer = await Packer.toBuffer(doc);
|
||
fs.writeFileSync(OUT, buffer);
|
||
console.log(`Saved ${OUT}`);
|