diff --git a/README.md b/README.md index d632887..c72a11b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@

- OpenPrint Logo + OpenPrint Logo

+

OpenPrint

@@ -32,6 +33,9 @@ > 纯前端架构,无需服务器,无需数据库,一个浏览器即可运行。 +![设计器界面](screenshots/designer-overview.png) +*所见即所得的打印模板设计器,拖拽控件、实时预览* + --- ## 目录 @@ -57,6 +61,9 @@ 告别手动拖拽,直接描述需求即可生成专业打印模板。支持快递面单、物流标签、仓储单据、零售小票等多种场景。 +![AI 助手](screenshots/ai-assistant.png) +*AI 自然语言生成打印模板,一句话完成面单设计* + ``` 用户输入:"帮我生成一张快递面单,含收件人、寄件人和运单号条码" AI 输出: 自动创建模板 → 布局文本控件 → 添加条码 → 一键使用 @@ -66,10 +73,16 @@ AI 输出: 自动创建模板 → 布局文本控件 → 添加条码 → 一 跨地域多门店远程打印,模板统一管理。浏览器直接打印,免装驱动。云端打印队列,断线自动重试,权限管控,打印日志审计。 +![云打印](screenshots/cloud-print.jpg) +*多门店远程打印,模板统一管理,打印队列监控* + ### 📊 Web 报表设计 分组表、交叉表、主从表等多级报表结构,支持求和/平均/计数等汇总函数,嵌入柱状图/折线图/饼图,条件格式高亮数据预警,多数据源拼接。 +![报表设计](screenshots/report-design.png) +*分组报表、交叉表、图表,满足复杂数据展示需求* + --- ## 功能特性 @@ -118,7 +131,7 @@ AI 输出: 自动创建模板 → 布局文本控件 → 添加条码 → 一 ```bash # 克隆仓库 -git clone https://github.com/your-repo/open-print.git +git clone https://gitee.com/haiming236/openprint.git cd open-print # 安装依赖 @@ -136,7 +149,7 @@ pnpm preview ### 在线演示 -访问 [open-print.example.com/demo](https://open-print.example.com/demo) 在线体验设计器。 +访问 [https://openprint.yy360.space](https://openprint.yy360.space) 在线体验设计器。 --- @@ -298,6 +311,9 @@ OpenPrint 提供配套的 **C++ Qt 桌面打印客户端**,支持: - **HTTP 接口** — 通过 REST API 接收打印任务 - **跨平台** — Windows / Linux / macOS +![桌面客户端](screenshots/desktop-client.jpg) +*C++ Qt 桌面客户端,静默打印,批量处理* + ### 技术栈 ![C++](https://img.shields.io/badge/C++-17-00599C?style=flat-square&logo=cplusplus&logoColor=white) @@ -316,6 +332,9 @@ OpenPrint 提供标准 HTTP API,3 分钟即可完成 ERP 对接: 2. **导出模板文件** — 导出 `.json` 模板文件 3. **API 对接** — ERP 系统通过 HTTP 推送数据 + 模板 ID,触发打印 +![ERP 对接](screenshots/erp-integration.jpg) +*标准 HTTP API 对接,3 分钟接入 ERP 系统* + ### API 示例 ```http @@ -404,7 +423,7 @@ pnpm vitest --coverage ## 许可证 -[MIT License](LICENSE) +[GPL3.0 License](LICENSE) Copyright (c) 2025 OpenPrint @@ -414,7 +433,7 @@ Copyright (c) 2025 OpenPrint OpenPrint — 让打印变得简单高效

- GitHub · - Gitee · - 在线演示 -

\ No newline at end of file + GitHub · + Gitee · + 在线演示 +

diff --git a/screenshots/README.md b/screenshots/README.md new file mode 100644 index 0000000..4d6d6df --- /dev/null +++ b/screenshots/README.md @@ -0,0 +1,12 @@ +# 截图素材 + +将以下截图文件放入此目录,替换 README 中的配图: + +| 文件 | 说明 | 建议尺寸 | +|------|------|----------| +| `designer-overview.png` | 设计器界面全貌,展示控件库 + 画布 + 属性面板 | 1920×1080 | +| `ai-assistant.png` | AI 助手聊天窗口 + 生成的面单模板 | 1200×800 | +| `cloud-print.png` | 云打印架构拓扑图 | 1200×800 | +| `report-design.png` | 报表设计效果(表格 + 图表) | 1200×800 | +| `desktop-client.png` | C++ Qt 桌面客户端界面 | 1200×800 | +| `erp-integration.png` | ERP 对接流程示意图 | 1200×800 | \ No newline at end of file diff --git a/screenshots/ai-assistant.png b/screenshots/ai-assistant.png new file mode 100644 index 0000000..ef87363 Binary files /dev/null and b/screenshots/ai-assistant.png differ diff --git a/screenshots/cloud-print.jpg b/screenshots/cloud-print.jpg new file mode 100644 index 0000000..9b739ab Binary files /dev/null and b/screenshots/cloud-print.jpg differ diff --git a/screenshots/designer-overview.png b/screenshots/designer-overview.png new file mode 100644 index 0000000..e5f0d62 Binary files /dev/null and b/screenshots/designer-overview.png differ diff --git a/screenshots/desktop-client.jpg b/screenshots/desktop-client.jpg new file mode 100644 index 0000000..ad07a3a Binary files /dev/null and b/screenshots/desktop-client.jpg differ diff --git a/screenshots/erp-integration.jpg b/screenshots/erp-integration.jpg new file mode 100644 index 0000000..9704b23 Binary files /dev/null and b/screenshots/erp-integration.jpg differ diff --git a/screenshots/report-design.png b/screenshots/report-design.png new file mode 100644 index 0000000..38ba268 Binary files /dev/null and b/screenshots/report-design.png differ diff --git a/src/core/chartkit/bar.ts b/src/core/chartkit/bar.ts index 894b04c..a9d815e 100644 --- a/src/core/chartkit/bar.ts +++ b/src/core/chartkit/bar.ts @@ -4,7 +4,7 @@ * 输出一串 (按序列分组并排)+ 坐标轴 + 可选网格 + 图例 + 数据标签。 * 全部用逻辑坐标(viewBox),实际尺寸由外层容器缩放,矢量清晰。 */ -import { escapeXml, fmt, niceMax, seriesColor, truncate, legendStartX, xAxisLabelPos } from './core' +import { escapeXml, fmt, niceMax, seriesColor, truncate, legendStartX } from './core' import type { ChartModel } from './types' export function renderBar(model: ChartModel): string { @@ -21,7 +21,7 @@ export function renderBar(model: ChartModel): string { const titleH = title ? 22 : 0 const mTop = 10 + titleH const mRight = 16 - const mBottom = (showAxis ? 34 : 18) + legendH + const mBottom = (showAxis ? 38 : 22) + legendH const mLeft = showAxis ? 44 : 16 const px0 = mLeft @@ -86,16 +86,15 @@ export function renderBar(model: ChartModel): string { parts.push(`${fmt(val)}`) } } - // x 轴标签 + // x 轴标签:始终以类目为基准居中于对应条带正下方,并与图表留一点间距 if (showAxis) { - const pos = xAxisLabelPos(opt.labelAlign, px0 + band * c, px0 + band * (c + 1), cx) - parts.push(`${escapeXml(truncate(String(cats[c] ?? '')))}`) + parts.push(`${escapeXml(truncate(String(cats[c] ?? '')))}`) } } // 图例 if (showLegend) { - const align = opt.labelAlign ?? 'left' + const align = opt.labelAlign ?? 'center' let totalW = 0 for (let s = 0; s < sCount; s++) { const name = model.series[s]!.name || `系列${s + 1}` @@ -103,7 +102,7 @@ export function renderBar(model: ChartModel): string { } const startX = legendStartX(align, W, totalW, px0) let lx = startX - let ly = py1 + (showAxis ? 24 : 10) + let ly = py1 + (showAxis ? 30 : 16) for (let s = 0; s < sCount; s++) { const name = model.series[s]!.name || `系列${s + 1}` const color = seriesColor(model.series[s]!, s, opt) diff --git a/src/core/chartkit/core.ts b/src/core/chartkit/core.ts index bc44389..7896d06 100644 --- a/src/core/chartkit/core.ts +++ b/src/core/chartkit/core.ts @@ -117,15 +117,3 @@ export function legendStartX( if (align === 'right') return Math.max(leftPad, W - totalW - 8) return leftPad } - -/** x 轴类目标签的水平对齐 → SVG text-anchor 与基准 x */ -export function xAxisLabelPos( - align: 'left' | 'center' | 'right' | undefined, - bandLeft: number, - bandRight: number, - bandCenter: number, -): { x: number; anchor: 'start' | 'middle' | 'end' } { - if (align === 'left') return { x: bandLeft + 4, anchor: 'start' } - if (align === 'right') return { x: bandRight - 4, anchor: 'end' } - return { x: bandCenter, anchor: 'middle' } -} diff --git a/src/core/chartkit/line.ts b/src/core/chartkit/line.ts index 2c6af4a..3e1641a 100644 --- a/src/core/chartkit/line.ts +++ b/src/core/chartkit/line.ts @@ -4,7 +4,7 @@ * 多序列折线 + 坐标轴 + 可选网格 + 图例 + 数据标签。 * 支持 smooth(Catmull-Rom 平滑曲线)与 area(折线下方面积填充)。 */ -import { escapeXml, fmt, niceMax, seriesColor, truncate, legendStartX, xAxisLabelPos } from './core' +import { escapeXml, fmt, niceMax, seriesColor, truncate, legendStartX } from './core' import type { ChartModel } from './types' interface Pt { x: number; y: number } @@ -43,7 +43,7 @@ export function renderLine(model: ChartModel): string { const titleH = title ? 22 : 0 const mTop = 10 + titleH const mRight = 16 - const mBottom = (showAxis ? 34 : 18) + legendH + const mBottom = (showAxis ? 38 : 22) + legendH const mLeft = showAxis ? 44 : 16 const px0 = mLeft @@ -101,8 +101,8 @@ export function renderLine(model: ChartModel): string { parts.push(``) for (let c = 0; c < n; c++) { const x = px0 + band * (c + 0.5) - const pos = xAxisLabelPos(opt.labelAlign, px0 + band * c, px0 + band * (c + 1), x) - parts.push(`${escapeXml(truncate(String(cats[c] ?? '')))}`) + // x 轴类目标签:始终以类目为基准居中于对应点正下方,并与图表留一点间距 + parts.push(`${escapeXml(truncate(String(cats[c] ?? '')))}`) } } @@ -132,7 +132,7 @@ export function renderLine(model: ChartModel): string { } if (showLegend) { - const align = opt.labelAlign ?? 'left' + const align = opt.labelAlign ?? 'center' let totalW = 0 for (let s = 0; s < seriesPts.length; s++) { const name = seriesPts[s]!.name || `系列${s + 1}` @@ -140,7 +140,7 @@ export function renderLine(model: ChartModel): string { } const startX = legendStartX(align, W, totalW, px0) let lx = startX - let ly = py1 + (showAxis ? 24 : 10) + let ly = py1 + (showAxis ? 30 : 16) for (let s = 0; s < seriesPts.length; s++) { const name = seriesPts[s]!.name || `系列${s + 1}` const color = seriesPts[s]!.color diff --git a/src/core/chartkit/pie.ts b/src/core/chartkit/pie.ts index fbcdc24..ee8f41f 100644 --- a/src/core/chartkit/pie.ts +++ b/src/core/chartkit/pie.ts @@ -63,7 +63,7 @@ export function renderPie(model: ChartModel): string { } if (showLegend) { - const align = opt.labelAlign ?? 'left' + const align = opt.labelAlign ?? 'center' let totalW = 0 for (let i = 0; i < data.length; i++) { const name = cats[i] ?? `类目${i + 1}` diff --git a/src/design/panels/props/ChartProps.vue b/src/design/panels/props/ChartProps.vue index a7db255..0cc062c 100644 --- a/src/design/panels/props/ChartProps.vue +++ b/src/design/panels/props/ChartProps.vue @@ -113,7 +113,7 @@ const valueLabel = computed({ set: (v: boolean) => patchOption('valueLabel', v), }) const labelAlign = computed({ - get: () => control.value?.options?.labelAlign ?? 'left', + get: () => control.value?.options?.labelAlign ?? 'center', set: (v: 'left' | 'center' | 'right') => patchOption('labelAlign', v), }) const smooth = computed({ @@ -220,7 +220,7 @@ const donut = computed({
- 标签对齐 + 图例对齐