foucui 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/QiuCanvasKit/QiuCanvasEditor.vue.d.ts +3 -1
- package/dist/components/QiuCanvasKit/components/DataModulesPanel.vue.d.ts +1 -0
- package/dist/components/QiuTable/index.vue.d.ts +12 -12
- package/dist/foucui.css +1 -1
- package/dist/foucui.js +7476 -7373
- package/dist/foucui.umd.cjs +19 -19
- package/package.json +23 -13
- package/components/QiuAffix/index.vue +0 -186
- package/components/QiuAlert/index.vue +0 -208
- package/components/QiuAnchor/context.ts +0 -18
- package/components/QiuAnchor/index.vue +0 -180
- package/components/QiuAnchorLink/index.vue +0 -67
- package/components/QiuApp/index.vue +0 -9
- package/components/QiuAutocomplete/index.vue +0 -408
- package/components/QiuAvatar/index.vue +0 -140
- package/components/QiuBacktop/index.vue +0 -121
- package/components/QiuBadge/api/index.ts +0 -389
- package/components/QiuBadge/components/BadgeImagePickerDialog.vue +0 -243
- package/components/QiuBadge/components/BadgeLibraryManagerDialog.vue +0 -30
- package/components/QiuBadge/components/BadgeLibraryPanel.vue +0 -182
- package/components/QiuBadge/composables/useTableHostHeight.ts +0 -53
- package/components/QiuBadge/index.ts +0 -18
- package/components/QiuBadge/shared/BadgeTemplateRenderer.vue +0 -32
- package/components/QiuBadge/shared/assetUrl.ts +0 -26
- package/components/QiuBadge/shared/badgeTemplate.ts +0 -309
- package/components/QiuBadge/utils/chinaAreaData.ts +0 -44
- package/components/QiuBadge/utils/hostRegion.ts +0 -22
- package/components/QiuBadge/utils/pca-code.json +0 -1
- package/components/QiuBadge/utils/pdfFromElement.ts +0 -348
- package/components/QiuBadge/views/BadgeLibraryView.vue +0 -32
- package/components/QiuBadge/views/BadgeManageView.vue +0 -1364
- package/components/QiuBadge/views/BadgeTemplateEditorView.vue +0 -10
- package/components/QiuBadge/views/BadgeTemplateListView.vue +0 -229
- package/components/QiuBadge/views/DeskLabelTemplateListView.vue +0 -227
- package/components/QiuBadge/views/DeskLabelView.vue +0 -683
- package/components/QiuBadgeDot/index.vue +0 -87
- package/components/QiuButton/index.vue +0 -247
- package/components/QiuButtonGroup/index.vue +0 -63
- package/components/QiuCanvasKit/QiuCanvasEditor.vue +0 -5503
- package/components/QiuCanvasKit/QiuCanvasRenderer.vue +0 -348
- package/components/QiuCanvasKit/assetUrl.ts +0 -11
- package/components/QiuCanvasKit/components/DataModulesPanel.vue +0 -352
- package/components/QiuCanvasKit/export/html.ts +0 -132
- package/components/QiuCanvasKit/export/pdf.ts +0 -48
- package/components/QiuCanvasKit/export/png.ts +0 -50
- package/components/QiuCanvasKit/index.ts +0 -20
- package/components/QiuCanvasKit/modules/badgeCompat.ts +0 -84
- package/components/QiuCanvasKit/modules/dataRegistry.ts +0 -207
- package/components/QiuCanvasKit/modules/merge.ts +0 -71
- package/components/QiuCanvasKit/presets/badge.ts +0 -67
- package/components/QiuCanvasKit/presets/banner.ts +0 -79
- package/components/QiuCanvasKit/types.ts +0 -138
- package/components/QiuCard/index.vue +0 -104
- package/components/QiuCarousel/context.ts +0 -16
- package/components/QiuCarousel/index.vue +0 -343
- package/components/QiuCarouselItem/index.vue +0 -47
- package/components/QiuCascader/index.vue +0 -879
- package/components/QiuCascader/types.ts +0 -48
- package/components/QiuCheckbox/QiuCheckboxGroup.vue +0 -78
- package/components/QiuCheckbox/index.vue +0 -231
- package/components/QiuCheckbox/types.ts +0 -8
- package/components/QiuCode/index.vue +0 -64
- package/components/QiuCol/col.scss +0 -55
- package/components/QiuCol/index.vue +0 -64
- package/components/QiuCollapse/context.ts +0 -10
- package/components/QiuCollapse/index.vue +0 -70
- package/components/QiuCollapseItem/index.vue +0 -167
- package/components/QiuColorField/index.vue +0 -296
- package/components/QiuColorPicker/README.md +0 -118
- package/components/QiuColorPicker/index.vue +0 -60
- package/components/QiuColorfulCard/index.vue +0 -101
- package/components/QiuCombobox/index.vue +0 -277
- package/components/QiuConfigProvider/index.vue +0 -31
- package/components/QiuContextMenu/index.vue +0 -209
- package/components/QiuDate/index.vue +0 -1134
- package/components/QiuDescriptions/context.ts +0 -20
- package/components/QiuDescriptions/index.vue +0 -115
- package/components/QiuDescriptionsItem/index.vue +0 -103
- package/components/QiuDialog/index.vue +0 -828
- package/components/QiuDigitalCard/index.vue +0 -159
- package/components/QiuDivider/index.vue +0 -185
- package/components/QiuDot/index.vue +0 -83
- package/components/QiuDrawer/index.vue +0 -350
- package/components/QiuDropdown/QiuDropdownItem.vue +0 -74
- package/components/QiuDropdown/QiuDropdownMenu.vue +0 -22
- package/components/QiuDropdown/context.ts +0 -9
- package/components/QiuDropdown/index.vue +0 -189
- package/components/QiuEmpty/index.vue +0 -73
- package/components/QiuForm/index.vue +0 -172
- package/components/QiuForm/types.ts +0 -39
- package/components/QiuFormItem/index.vue +0 -276
- package/components/QiuFullscreen/index.vue +0 -11
- package/components/QiuGrid/index.vue +0 -408
- package/components/QiuHoverCard/index.vue +0 -192
- package/components/QiuIcon/index.vue +0 -117
- package/components/QiuIconPicker/iconify.libraries.ts +0 -182
- package/components/QiuIconPicker/icons.catalog.ts +0 -3072
- package/components/QiuIconPicker/index.vue +0 -488
- package/components/QiuImage/index.vue +0 -177
- package/components/QiuInput/index.vue +0 -328
- package/components/QiuInputNumber/index.vue +0 -295
- package/components/QiuInputOtp/index.vue +0 -190
- package/components/QiuKbd/index.vue +0 -57
- package/components/QiuLayoutContainer/index.vue +0 -50
- package/components/QiuLinkPreview/index.vue +0 -224
- package/components/QiuLoading/index.ts +0 -176
- package/components/QiuLoading/spinner.vue +0 -820
- package/components/QiuLoading/types.ts +0 -44
- package/components/QiuMention/index.vue +0 -299
- package/components/QiuMenu/QiuMenuRoot.vue +0 -241
- package/components/QiuMenu/components/QiuMenuItem.vue +0 -340
- package/components/QiuMenu/components/QiuSubMenu.vue +0 -504
- package/components/QiuMenu/context.ts +0 -21
- package/components/QiuMenu/index.vue +0 -43
- package/components/QiuMenuNav/QiuMenuNavItem.vue +0 -100
- package/components/QiuMenuNav/QiuMenuNavSub.vue +0 -120
- package/components/QiuMenuNav/context.ts +0 -14
- package/components/QiuMenuNav/index.vue +0 -148
- package/components/QiuMsg/QiuMsgItem.vue +0 -180
- package/components/QiuMsg/index.ts +0 -158
- package/components/QiuMsg/types.ts +0 -23
- package/components/QiuMsgBox/index.ts +0 -147
- package/components/QiuNotification/QiuNotificationItem.vue +0 -168
- package/components/QiuNotification/index.ts +0 -155
- package/components/QiuNotification/types.ts +0 -14
- package/components/QiuPage/index.vue +0 -12
- package/components/QiuPageHeader/index.vue +0 -115
- package/components/QiuPageMain/index.vue +0 -78
- package/components/QiuPagination/index.vue +0 -421
- package/components/QiuPasswordStrength/index.vue +0 -101
- package/components/QiuPopconfirm/index.vue +0 -196
- package/components/QiuPopover/index.vue +0 -277
- package/components/QiuProgress/index.vue +0 -235
- package/components/QiuQrcode/index.vue +0 -302
- package/components/QiuQueryForm/components/QiuQueryFormBottomPanel.vue +0 -13
- package/components/QiuQueryForm/components/QiuQueryFormLeftPanel.vue +0 -37
- package/components/QiuQueryForm/components/QiuQueryFormRightPanel.vue +0 -37
- package/components/QiuQueryForm/components/QiuQueryFormTopPanel.vue +0 -13
- package/components/QiuQueryForm/index.vue +0 -117
- package/components/QiuRadio/QiuRadioGroup.vue +0 -75
- package/components/QiuRadio/index.vue +0 -191
- package/components/QiuRadio/types.ts +0 -9
- package/components/QiuRate/index.vue +0 -184
- package/components/QiuRefresh/index.vue +0 -32
- package/components/QiuResult/index.vue +0 -103
- package/components/QiuRow/index.vue +0 -60
- package/components/QiuRow/types.ts +0 -5
- package/components/QiuScrollbar/index.vue +0 -107
- package/components/QiuSearchBar/index.vue +0 -154
- package/components/QiuSegmented/index.vue +0 -120
- package/components/QiuSelect/QiuOption.vue +0 -93
- package/components/QiuSelect/index.vue +0 -452
- package/components/QiuSelect/types.ts +0 -15
- package/components/QiuSkeleton/index.vue +0 -78
- package/components/QiuSlider/index.vue +0 -266
- package/components/QiuSpace/index.vue +0 -95
- package/components/QiuSparkline/index.vue +0 -92
- package/components/QiuStatistic/index.vue +0 -4
- package/components/QiuStatistics/index.vue +0 -123
- package/components/QiuStep/index.vue +0 -220
- package/components/QiuSteps/context.ts +0 -25
- package/components/QiuSteps/index.vue +0 -84
- package/components/QiuSwitch/index.vue +0 -145
- package/components/QiuTabBar/index.vue +0 -218
- package/components/QiuTabBar/types.ts +0 -19
- package/components/QiuTabPane/index.vue +0 -82
- package/components/QiuTable/columns.ts +0 -16
- package/components/QiuTable/export.ts +0 -102
- package/components/QiuTable/index.ts +0 -24
- package/components/QiuTable/index.vue +0 -3503
- package/components/QiuTable/registry.ts +0 -75
- package/components/QiuTable/styles/table.scss +0 -1230
- package/components/QiuTable/types.ts +0 -413
- package/components/QiuTable/useArea.ts +0 -202
- package/components/QiuTable/useColumns.ts +0 -325
- package/components/QiuTable/useEdit.ts +0 -136
- package/components/QiuTable/useExtras.ts +0 -115
- package/components/QiuTable/useHistory.ts +0 -77
- package/components/QiuTable/useMerge.ts +0 -105
- package/components/QiuTable/useResize.ts +0 -86
- package/components/QiuTable/useScrollSync.ts +0 -152
- package/components/QiuTable/useVirtual.ts +0 -97
- package/components/QiuTable/utils.ts +0 -165
- package/components/QiuTableColumn/index.vue +0 -101
- package/components/QiuTag/index.vue +0 -143
- package/components/QiuText/index.vue +0 -99
- package/components/QiuTime/index.vue +0 -548
- package/components/QiuTimeAgo/index.vue +0 -103
- package/components/QiuTimeline/index.vue +0 -54
- package/components/QiuTimelineItem/index.vue +0 -140
- package/components/QiuTips/index.vue +0 -323
- package/components/QiuTooltip/index.vue +0 -237
- package/components/QiuTransfer/index.vue +0 -277
- package/components/QiuTree/QiuTreeNode.vue +0 -146
- package/components/QiuTree/index.vue +0 -143
- package/components/QiuTree/types.ts +0 -30
- package/components/QiuTreeSelect/index.vue +0 -259
- package/components/QiuTrend/index.vue +0 -92
- package/components/QiuUpload/index.vue +0 -628
- package/components/QiuUploadCrop/index.vue +0 -1267
- package/components/QiuWatermark/index.vue +0 -121
- package/components/useBodyScrollLock.ts +0 -28
- package/dist/components/QiuBadge/utils/pca-code.json.d.ts +0 -3
- package/plugins/qiu.ts +0 -83
- package/plugins/shortcuts.ts +0 -69
- package/src/bridge/@/api/media.ts +0 -8
- package/src/bridge/@/api/notice.ts +0 -5
- package/src/bridge/@/config/index.ts +0 -38
- package/src/bridge/@/config/media.config.ts +0 -37
- package/src/bridge/@/config/security.config.ts +0 -8
- package/src/bridge/@/i18n/index.ts +0 -6
- package/src/bridge/@/store/modules/bing.ts +0 -9
- package/src/bridge/@/store/modules/errorLog.ts +0 -9
- package/src/bridge/@/store/modules/media.ts +0 -10
- package/src/bridge/@/store/modules/routes.ts +0 -10
- package/src/bridge/@/store/modules/settings.ts +0 -9
- package/src/bridge/@/store/modules/tabs.ts +0 -9
- package/src/bridge/@/store/modules/user.ts +0 -9
- package/src/bridge/@/utils/convertToCamelCase.ts +0 -11
- package/src/bridge/@/utils/index.ts +0 -9
- package/src/bridge/@/utils/pageTitle.ts +0 -4
- package/src/bridge/@/utils/routes.ts +0 -14
- package/src/bridge/@/utils/validate.ts +0 -17
- package/src/bridge/@qiu/plugins/shortcuts.ts +0 -3
- package/src/i18n.ts +0 -8
- package/src/icons/qiu-logo.svg +0 -1
- package/styles/dark.scss +0 -194
- package/styles/normalize.css +0 -349
- package/styles/plain.scss +0 -423
- package/styles/qiu-tokens.scss +0 -40
- package/styles/qiu.scss +0 -1149
- package/styles/technology.scss +0 -452
- package/styles/var.scss +0 -382
- package/styles/variables.scss +0 -77
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Banner / 画布 HTML 导出(含链接热区)
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
import type { CanvasKitDoc, CanvasKitElement, MergeContext } from '../types'
|
|
8
|
-
import { mergeTemplateText, sortCanvasElements } from '../modules/merge'
|
|
9
|
-
import { downloadBlob } from './png'
|
|
10
|
-
|
|
11
|
-
const MM_TO_PX = 96 / 25.4
|
|
12
|
-
|
|
13
|
-
export type ExportHtmlOptions = {
|
|
14
|
-
fileName?: string
|
|
15
|
-
title?: string
|
|
16
|
-
/** 合并后再写死 URL / 文本 */
|
|
17
|
-
context?: MergeContext | Record<string, string>
|
|
18
|
-
/** 导出单位:优先 canvas.unit */
|
|
19
|
-
forceUnit?: 'mm' | 'px'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function canvasSize(doc: CanvasKitDoc, unit: 'mm' | 'px') {
|
|
23
|
-
const c = doc.canvas
|
|
24
|
-
if (unit === 'px') {
|
|
25
|
-
const w = c.widthPx ?? Math.round(c.widthMm * MM_TO_PX)
|
|
26
|
-
const h = c.heightPx ?? Math.round(c.heightMm * MM_TO_PX)
|
|
27
|
-
return { w, h, unit: 'px' as const }
|
|
28
|
-
}
|
|
29
|
-
return { w: c.widthMm, h: c.heightMm, unit: 'mm' as const }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function esc(s: string) {
|
|
33
|
-
return String(s || '')
|
|
34
|
-
.replace(/&/g, '&')
|
|
35
|
-
.replace(/</g, '<')
|
|
36
|
-
.replace(/>/g, '>')
|
|
37
|
-
.replace(/"/g, '"')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function resolveAsset(src: string) {
|
|
41
|
-
return src || ''
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function elBoxStyle(el: CanvasKitElement, unit: 'mm' | 'px') {
|
|
45
|
-
const x = unit === 'px' ? `${Math.round(el.xMm * MM_TO_PX)}px` : `${el.xMm}mm`
|
|
46
|
-
const y = unit === 'px' ? `${Math.round(el.yMm * MM_TO_PX)}px` : `${el.yMm}mm`
|
|
47
|
-
const w = unit === 'px' ? `${Math.round(el.wMm * MM_TO_PX)}px` : `${el.wMm}mm`
|
|
48
|
-
const h = unit === 'px' ? `${Math.round(el.hMm * MM_TO_PX)}px` : `${el.hMm}mm`
|
|
49
|
-
const transforms: string[] = []
|
|
50
|
-
if (el.rotation) transforms.push(`rotate(${el.rotation}deg)`)
|
|
51
|
-
if (el.flipX) transforms.push('scaleX(-1)')
|
|
52
|
-
if (el.flipY) transforms.push('scaleY(-1)')
|
|
53
|
-
return `position:absolute;left:${x};top:${y};width:${w};height:${h};z-index:${el.zIndex ?? 1};box-sizing:border-box;${
|
|
54
|
-
transforms.length ? `transform:${transforms.join(' ')};transform-origin:center center;` : ''
|
|
55
|
-
}`
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function renderElementHtml(el: CanvasKitElement, ctx: MergeContext, unit: 'mm' | 'px'): string {
|
|
59
|
-
const box = elBoxStyle(el, unit)
|
|
60
|
-
let inner = ''
|
|
61
|
-
if (el.type === 'text') {
|
|
62
|
-
const text = esc(mergeTemplateText(el.text, ctx))
|
|
63
|
-
const vertical = el.textDirection === 'vertical'
|
|
64
|
-
inner = `<div style="${box}display:flex;align-items:center;justify-content:flex-start;font-size:${el.fontSize}pt;font-family:${esc(
|
|
65
|
-
el.fontFamily
|
|
66
|
-
)};color:${esc(el.color)};white-space:pre-wrap;word-break:break-word;overflow:hidden;${
|
|
67
|
-
vertical ? 'writing-mode:vertical-rl;text-orientation:upright;' : ''
|
|
68
|
-
}">${text}</div>`
|
|
69
|
-
} else if (el.type === 'image' || el.type === 'seal') {
|
|
70
|
-
const src = esc(resolveAsset(el.src))
|
|
71
|
-
inner = `<div style="${box}display:flex;align-items:center;justify-content:center;"><img src="${src}" alt="" style="max-width:100%;max-height:100%;object-fit:contain"/></div>`
|
|
72
|
-
} else if (el.type === 'shape') {
|
|
73
|
-
const bg = esc(el.background || 'transparent')
|
|
74
|
-
const opacity = el.opacity != null ? `opacity:${el.opacity};` : ''
|
|
75
|
-
inner = `<div style="${box}${opacity}background:${bg};border-radius:${el.shape === 'circle' ? '50%' : '0'};"></div>`
|
|
76
|
-
} else {
|
|
77
|
-
return ''
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (el.link?.href) {
|
|
81
|
-
const href = esc(mergeTemplateText(el.link.href, ctx))
|
|
82
|
-
const target = el.link.target === '_self' ? '_self' : '_blank'
|
|
83
|
-
const rel = target === '_blank' ? ' noopener noreferrer' : ''
|
|
84
|
-
return `<a href="${href}" target="${target}" rel="${rel.trim()}" style="display:contents;text-decoration:none;color:inherit;">${inner}</a>`
|
|
85
|
-
}
|
|
86
|
-
return inner
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** 生成自包含 HTML 字符串(Banner 投放用) */
|
|
90
|
-
export function buildCanvasHtml(doc: CanvasKitDoc, options: ExportHtmlOptions = {}): string {
|
|
91
|
-
const ctx = (options.context || {}) as MergeContext
|
|
92
|
-
const unit = options.forceUnit || doc.canvas.unit || (doc.mode === 'banner' ? 'px' : 'mm')
|
|
93
|
-
const size = canvasSize(doc, unit)
|
|
94
|
-
const showBorder = doc.canvas.showBorder !== false && doc.mode !== 'banner'
|
|
95
|
-
const border = showBorder
|
|
96
|
-
? `border:${doc.canvas.borderWidthMm || 0.35}mm solid ${esc(doc.canvas.borderColor || '#334155')};`
|
|
97
|
-
: 'border:none;'
|
|
98
|
-
const bg = doc.canvas.backgroundImage
|
|
99
|
-
? `background-image:url(${esc(doc.canvas.backgroundImage)});background-size:cover;background-position:center;`
|
|
100
|
-
: `background:${esc(doc.canvas.background || 'transparent')};`
|
|
101
|
-
const els = sortCanvasElements(doc.elements || [])
|
|
102
|
-
.map((el) => renderElementHtml(el, ctx, unit))
|
|
103
|
-
.join('\n')
|
|
104
|
-
|
|
105
|
-
const title = esc(options.title || 'Banner')
|
|
106
|
-
return `<!DOCTYPE html>
|
|
107
|
-
<html lang="zh-CN">
|
|
108
|
-
<head>
|
|
109
|
-
<meta charset="utf-8"/>
|
|
110
|
-
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
111
|
-
<title>${title}</title>
|
|
112
|
-
<style>
|
|
113
|
-
html,body{margin:0;padding:0;background:#f8fafc;}
|
|
114
|
-
.qiu-canvas-banner{position:relative;overflow:hidden;margin:0 auto;${border}${bg}
|
|
115
|
-
width:${size.w}${size.unit};height:${size.h}${size.unit};}
|
|
116
|
-
a{color:inherit;}
|
|
117
|
-
</style>
|
|
118
|
-
</head>
|
|
119
|
-
<body>
|
|
120
|
-
<div class="qiu-canvas-banner">
|
|
121
|
-
${els}
|
|
122
|
-
</div>
|
|
123
|
-
</body>
|
|
124
|
-
</html>`
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function downloadCanvasHtml(doc: CanvasKitDoc, options: ExportHtmlOptions = {}) {
|
|
128
|
-
const html = buildCanvasHtml(doc, options)
|
|
129
|
-
const blob = new Blob([html], { type: 'text/html;charset=utf-8' })
|
|
130
|
-
downloadBlob(blob, options.fileName || `banner-${Date.now()}.html`)
|
|
131
|
-
return blob
|
|
132
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 画布导出:PDF(单页,基于 html2canvas + jspdf)
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
import { jsPDF } from 'jspdf'
|
|
8
|
-
import { exportElementToPng, downloadBlob } from './png'
|
|
9
|
-
|
|
10
|
-
export type ExportPdfOptions = {
|
|
11
|
-
fileName?: string
|
|
12
|
-
/** mm */
|
|
13
|
-
widthMm?: number
|
|
14
|
-
heightMm?: number
|
|
15
|
-
scale?: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function exportElementToPdf(
|
|
19
|
-
el: HTMLElement,
|
|
20
|
-
options: ExportPdfOptions = {}
|
|
21
|
-
): Promise<Blob> {
|
|
22
|
-
const png = await exportElementToPng(el, { scale: options.scale ?? 2 })
|
|
23
|
-
const dataUrl = await blobToDataUrl(png)
|
|
24
|
-
const w = options.widthMm ?? 210
|
|
25
|
-
const h = options.heightMm ?? 297
|
|
26
|
-
const pdf = new jsPDF({
|
|
27
|
-
orientation: w > h ? 'landscape' : 'portrait',
|
|
28
|
-
unit: 'mm',
|
|
29
|
-
format: [w, h],
|
|
30
|
-
})
|
|
31
|
-
pdf.addImage(dataUrl, 'PNG', 0, 0, w, h)
|
|
32
|
-
return pdf.output('blob')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function downloadElementPdf(el: HTMLElement, options: ExportPdfOptions = {}) {
|
|
36
|
-
const blob = await exportElementToPdf(el, options)
|
|
37
|
-
downloadBlob(blob, options.fileName || `canvas-${Date.now()}.pdf`)
|
|
38
|
-
return blob
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function blobToDataUrl(blob: Blob): Promise<string> {
|
|
42
|
-
return new Promise((resolve, reject) => {
|
|
43
|
-
const reader = new FileReader()
|
|
44
|
-
reader.onload = () => resolve(String(reader.result || ''))
|
|
45
|
-
reader.onerror = () => reject(reader.error)
|
|
46
|
-
reader.readAsDataURL(blob)
|
|
47
|
-
})
|
|
48
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 画布导出:PNG(基于 DOM 节点 html2canvas)
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
import html2canvas from 'html2canvas'
|
|
8
|
-
|
|
9
|
-
export type ExportPngOptions = {
|
|
10
|
-
fileName?: string
|
|
11
|
-
scale?: number
|
|
12
|
-
backgroundColor?: string | null
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function exportElementToPng(
|
|
16
|
-
el: HTMLElement,
|
|
17
|
-
options: ExportPngOptions = {}
|
|
18
|
-
): Promise<Blob> {
|
|
19
|
-
const canvas = await html2canvas(el, {
|
|
20
|
-
scale: options.scale ?? 2,
|
|
21
|
-
backgroundColor: options.backgroundColor === undefined ? '#ffffff' : options.backgroundColor,
|
|
22
|
-
useCORS: true,
|
|
23
|
-
logging: false,
|
|
24
|
-
})
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
26
|
-
canvas.toBlob(
|
|
27
|
-
(blob) => {
|
|
28
|
-
if (!blob) reject(new Error('PNG export failed'))
|
|
29
|
-
else resolve(blob)
|
|
30
|
-
},
|
|
31
|
-
'image/png',
|
|
32
|
-
1
|
|
33
|
-
)
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function downloadBlob(blob: Blob, fileName: string) {
|
|
38
|
-
const url = URL.createObjectURL(blob)
|
|
39
|
-
const a = document.createElement('a')
|
|
40
|
-
a.href = url
|
|
41
|
-
a.download = fileName
|
|
42
|
-
a.click()
|
|
43
|
-
URL.revokeObjectURL(url)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function downloadElementPng(el: HTMLElement, options: ExportPngOptions = {}) {
|
|
47
|
-
const blob = await exportElementToPng(el, options)
|
|
48
|
-
downloadBlob(blob, options.fileName || `canvas-${Date.now()}.png`)
|
|
49
|
-
return blob
|
|
50
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QiuCanvasKit — 统一证件 / 桌签 / Banner 画布套件
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
export { default as QiuCanvasEditor } from './QiuCanvasEditor.vue'
|
|
8
|
-
export { default as QiuCanvasRenderer } from './QiuCanvasRenderer.vue'
|
|
9
|
-
export { default as QiuCanvasDataModulesPanel } from './components/DataModulesPanel.vue'
|
|
10
|
-
|
|
11
|
-
export * from './types'
|
|
12
|
-
export * from './modules/dataRegistry'
|
|
13
|
-
export * from './modules/merge'
|
|
14
|
-
export * from './modules/badgeCompat'
|
|
15
|
-
export * from './presets/badge'
|
|
16
|
-
export * from './presets/banner'
|
|
17
|
-
export * from './export/png'
|
|
18
|
-
export * from './export/pdf'
|
|
19
|
-
export * from './export/html'
|
|
20
|
-
export { resolveAssetUrl } from './assetUrl'
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 证件套打:扁平行 → 合并表(兼容旧 API)
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
import { mapFlatRowToContext } from './dataRegistry'
|
|
8
|
-
import type { MergeContext } from '../types'
|
|
9
|
-
|
|
10
|
-
function deriveSchoolShortName(schoolName: string): string {
|
|
11
|
-
let name = String(schoolName ?? '').trim()
|
|
12
|
-
if (!name) return ''
|
|
13
|
-
name = name.replace(/\s+/g, '')
|
|
14
|
-
name = name.replace(/^[中国中华人民共和國共和国]+/, '')
|
|
15
|
-
const adminPrefix =
|
|
16
|
-
/^(?:[^省特别行政区]+省|内蒙古自治区|广西壮族自治区|宁夏回族自治区|新疆维吾尔自治区|西藏自治区|北京市|天津市|上海市|重庆市|香港特别行政区|澳门特别行政区|台湾省|[^市]+市|[^区县旗]+区|[^区县旗]+县|[^区县旗]+旗|[^镇乡街道]+镇|[^镇乡街道]+乡|[^镇乡街道]+街道)/
|
|
17
|
-
let stripped = true
|
|
18
|
-
while (stripped && name) {
|
|
19
|
-
stripped = false
|
|
20
|
-
const next = name.replace(adminPrefix, '')
|
|
21
|
-
if (next !== name) {
|
|
22
|
-
name = next
|
|
23
|
-
stripped = true
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return name || schoolName.trim()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** 将接口返回的一条证件记录转为占位符合并表(扁平,兼容旧模板) */
|
|
30
|
-
export function flattenBadgeForMerge(
|
|
31
|
-
b: Record<string, unknown>,
|
|
32
|
-
competitionNameFallback?: string
|
|
33
|
-
): Record<string, string> {
|
|
34
|
-
const cn =
|
|
35
|
-
(b.competition_name != null ? String(b.competition_name) : '') || (competitionNameFallback ?? '')
|
|
36
|
-
const schoolName = String(b.school_name ?? '')
|
|
37
|
-
const schoolShortName =
|
|
38
|
-
String(b.school_short_name ?? b.school_name_short ?? '').trim() || deriveSchoolShortName(schoolName)
|
|
39
|
-
return {
|
|
40
|
-
competition_name: cn,
|
|
41
|
-
student_name: String(b.student_name ?? ''),
|
|
42
|
-
competition_no: String(b.competition_no ?? ''),
|
|
43
|
-
school_name: schoolName,
|
|
44
|
-
school_short_name: schoolShortName,
|
|
45
|
-
age_group: String(b.age_group ?? ''),
|
|
46
|
-
competition_room: String(b.competition_room ?? ''),
|
|
47
|
-
seat_no: String(b.seat_no ?? ''),
|
|
48
|
-
floor: String(b.floor ?? ''),
|
|
49
|
-
teacher_name: String(b.teacher_name ?? ''),
|
|
50
|
-
date: String(b.date ?? b.competition_date ?? ''),
|
|
51
|
-
session: String(b.session ?? b.competition_session ?? ''),
|
|
52
|
-
enroll_teacher_name: String(b.enroll_teacher_name ?? ''),
|
|
53
|
-
teacher_phone: String(b.teacher_phone ?? ''),
|
|
54
|
-
signup_teacher_name: String(b.signup_teacher_name ?? ''),
|
|
55
|
-
signup_teacher_phone: String(b.signup_teacher_phone ?? ''),
|
|
56
|
-
signup_teacher_department: String(b.signup_teacher_department ?? ''),
|
|
57
|
-
signup_teacher_position: String(b.signup_teacher_position ?? ''),
|
|
58
|
-
signup_teacher_id_card: String(b.signup_teacher_id_card ?? ''),
|
|
59
|
-
signup_teacher_email: String(b.signup_teacher_email ?? ''),
|
|
60
|
-
signup_teacher_username: String(b.signup_teacher_username ?? ''),
|
|
61
|
-
venue_name: String(b.venue_name ?? ''),
|
|
62
|
-
venue_address: String(b.venue_address ?? ''),
|
|
63
|
-
competition_daily_start: String(b.competition_daily_start ?? ''),
|
|
64
|
-
competition_daily_end: String(b.competition_daily_end ?? ''),
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function flattenDeskLabelForMerge(
|
|
69
|
-
row: Record<string, unknown>,
|
|
70
|
-
competitionNameFallback?: string
|
|
71
|
-
): Record<string, string> {
|
|
72
|
-
return flattenBadgeForMerge(row, competitionNameFallback)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** 扁平行 → 嵌套 context(推荐套打入口) */
|
|
76
|
-
export function mapRowToContext(
|
|
77
|
-
row: Record<string, unknown>,
|
|
78
|
-
competitionNameFallback?: string
|
|
79
|
-
): MergeContext {
|
|
80
|
-
const flat = flattenBadgeForMerge(row, competitionNameFallback)
|
|
81
|
-
return mapFlatRowToContext(flat, competitionNameFallback)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export { deriveSchoolShortName }
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 画布数据模块注册:内置模块 + 自定义合并
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
import type { DataModuleDef, MergeContext } from '../types'
|
|
8
|
-
|
|
9
|
-
/** 旧扁平 key → module.field(兼容期) */
|
|
10
|
-
export const LEGACY_FLAT_KEY_MAP: Record<string, string> = {
|
|
11
|
-
competition_name: 'competition.name',
|
|
12
|
-
student_name: 'student.name',
|
|
13
|
-
competition_no: 'student.competition_no',
|
|
14
|
-
school_name: 'school.name',
|
|
15
|
-
school_short_name: 'school.short_name',
|
|
16
|
-
age_group: 'student.age_group',
|
|
17
|
-
competition_room: 'classroom.name',
|
|
18
|
-
seat_no: 'classroom.seat_no',
|
|
19
|
-
floor: 'classroom.floor',
|
|
20
|
-
teacher_name: 'teacher.name',
|
|
21
|
-
date: 'competition.date',
|
|
22
|
-
session: 'competition.session',
|
|
23
|
-
enroll_teacher_name: 'teacher.enroll_name',
|
|
24
|
-
teacher_phone: 'teacher.phone',
|
|
25
|
-
signup_teacher_name: 'teacher.signup_name',
|
|
26
|
-
signup_teacher_phone: 'teacher.signup_phone',
|
|
27
|
-
signup_teacher_department: 'teacher.signup_department',
|
|
28
|
-
signup_teacher_position: 'teacher.signup_position',
|
|
29
|
-
signup_teacher_id_card: 'teacher.signup_id_card',
|
|
30
|
-
signup_teacher_email: 'teacher.signup_email',
|
|
31
|
-
signup_teacher_username: 'teacher.signup_username',
|
|
32
|
-
venue_name: 'classroom.venue_name',
|
|
33
|
-
venue_address: 'classroom.venue_address',
|
|
34
|
-
competition_daily_start: 'competition.daily_start',
|
|
35
|
-
competition_daily_end: 'competition.daily_end',
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const BUILTIN_DATA_MODULES: DataModuleDef[] = [
|
|
39
|
-
{
|
|
40
|
-
id: 'student',
|
|
41
|
-
label: '学生',
|
|
42
|
-
builtin: true,
|
|
43
|
-
enabled: true,
|
|
44
|
-
fields: [
|
|
45
|
-
{ key: 'name', label: '姓名' },
|
|
46
|
-
{ key: 'competition_no', label: '参赛编号' },
|
|
47
|
-
{ key: 'age_group', label: '年龄段' },
|
|
48
|
-
],
|
|
49
|
-
sample: { name: '张三', competition_no: 'A001', age_group: '中班' },
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: 'teacher',
|
|
53
|
-
label: '教师',
|
|
54
|
-
builtin: true,
|
|
55
|
-
enabled: true,
|
|
56
|
-
fields: [
|
|
57
|
-
{ key: 'name', label: '监考教师' },
|
|
58
|
-
{ key: 'phone', label: '手机' },
|
|
59
|
-
{ key: 'enroll_name', label: '带队教师' },
|
|
60
|
-
{ key: 'signup_name', label: '报名教师' },
|
|
61
|
-
{ key: 'signup_phone', label: '报名教师手机' },
|
|
62
|
-
{ key: 'signup_department', label: '报名教师部门' },
|
|
63
|
-
{ key: 'signup_position', label: '报名教师职务' },
|
|
64
|
-
{ key: 'signup_id_card', label: '报名教师身份证' },
|
|
65
|
-
{ key: 'signup_email', label: '报名教师邮箱' },
|
|
66
|
-
{ key: 'signup_username', label: '报名教师账号' },
|
|
67
|
-
],
|
|
68
|
-
sample: { name: '李老师', phone: '13800000000', signup_name: '王老师' },
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
id: 'classroom',
|
|
72
|
-
label: '教室',
|
|
73
|
-
builtin: true,
|
|
74
|
-
enabled: true,
|
|
75
|
-
fields: [
|
|
76
|
-
{ key: 'name', label: '考场/教室' },
|
|
77
|
-
{ key: 'seat_no', label: '座位号' },
|
|
78
|
-
{ key: 'floor', label: '楼层' },
|
|
79
|
-
{ key: 'venue_name', label: '场地名称' },
|
|
80
|
-
{ key: 'venue_address', label: '场地地址' },
|
|
81
|
-
],
|
|
82
|
-
sample: { name: '301 教室', seat_no: '12', floor: '3F' },
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
id: 'school',
|
|
86
|
-
label: '学校',
|
|
87
|
-
builtin: true,
|
|
88
|
-
enabled: true,
|
|
89
|
-
fields: [
|
|
90
|
-
{ key: 'name', label: '学校全称' },
|
|
91
|
-
{ key: 'short_name', label: '学校简称' },
|
|
92
|
-
],
|
|
93
|
-
sample: { name: '示范幼儿园', short_name: '示范园' },
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: 'competition',
|
|
97
|
-
label: '比赛',
|
|
98
|
-
builtin: true,
|
|
99
|
-
enabled: true,
|
|
100
|
-
fields: [
|
|
101
|
-
{ key: 'name', label: '比赛名称' },
|
|
102
|
-
{ key: 'date', label: '比赛日期' },
|
|
103
|
-
{ key: 'session', label: '场次' },
|
|
104
|
-
{ key: 'daily_start', label: '当天开始' },
|
|
105
|
-
{ key: 'daily_end', label: '当天结束' },
|
|
106
|
-
],
|
|
107
|
-
sample: { name: '示例比赛', date: '2026-08-16', session: '上午场' },
|
|
108
|
-
},
|
|
109
|
-
]
|
|
110
|
-
|
|
111
|
-
export function cloneBuiltinModules(): DataModuleDef[] {
|
|
112
|
-
return BUILTIN_DATA_MODULES.map((m) => ({
|
|
113
|
-
...m,
|
|
114
|
-
fields: m.fields.map((f) => ({ ...f })),
|
|
115
|
-
sample: m.sample ? { ...m.sample } : undefined,
|
|
116
|
-
}))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** 合并内置 + 模板/业务传入模块 */
|
|
120
|
-
export function resolveDataModules(
|
|
121
|
-
override?: DataModuleDef[] | null,
|
|
122
|
-
docModules?: DataModuleDef[] | null
|
|
123
|
-
): DataModuleDef[] {
|
|
124
|
-
const map = new Map<string, DataModuleDef>()
|
|
125
|
-
for (const m of cloneBuiltinModules()) map.set(m.id, m)
|
|
126
|
-
for (const m of override || []) {
|
|
127
|
-
const prev = map.get(m.id)
|
|
128
|
-
map.set(m.id, prev ? { ...prev, ...m, fields: m.fields?.length ? m.fields : prev.fields } : { ...m })
|
|
129
|
-
}
|
|
130
|
-
for (const m of docModules || []) {
|
|
131
|
-
const prev = map.get(m.id)
|
|
132
|
-
map.set(m.id, prev ? { ...prev, ...m, fields: m.fields?.length ? m.fields : prev.fields } : { ...m })
|
|
133
|
-
}
|
|
134
|
-
return [...map.values()].filter((m) => m.enabled !== false)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function placeholderToken(moduleId: string, fieldKey: string) {
|
|
138
|
-
return `{{${moduleId}.${fieldKey}}}`
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function listInsertableFields(modules: DataModuleDef[]) {
|
|
142
|
-
const out: { moduleId: string; moduleLabel: string; key: string; label: string; token: string }[] = []
|
|
143
|
-
for (const m of modules) {
|
|
144
|
-
for (const f of m.fields) {
|
|
145
|
-
out.push({
|
|
146
|
-
moduleId: m.id,
|
|
147
|
-
moduleLabel: m.label,
|
|
148
|
-
key: f.key,
|
|
149
|
-
label: f.label,
|
|
150
|
-
token: placeholderToken(m.id, f.key),
|
|
151
|
-
})
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return out
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** 用模块 sample 拼预览上下文 */
|
|
158
|
-
export function sampleContextFromModules(modules: DataModuleDef[]): MergeContext {
|
|
159
|
-
const ctx: MergeContext = {}
|
|
160
|
-
for (const m of modules) {
|
|
161
|
-
ctx[m.id] = { ...(m.sample || {}) }
|
|
162
|
-
}
|
|
163
|
-
return ctx
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* 旧扁平行 → 嵌套 context(套打时)
|
|
168
|
-
* 同时保留扁平 key,便于兼容旧模板。
|
|
169
|
-
*/
|
|
170
|
-
export function mapFlatRowToContext(
|
|
171
|
-
row: Record<string, unknown>,
|
|
172
|
-
competitionNameFallback?: string
|
|
173
|
-
): MergeContext {
|
|
174
|
-
const flat: Record<string, string> = {}
|
|
175
|
-
for (const [k, v] of Object.entries(row)) {
|
|
176
|
-
if (v == null) continue
|
|
177
|
-
flat[k] = String(v)
|
|
178
|
-
}
|
|
179
|
-
if (!flat.competition_name && competitionNameFallback) {
|
|
180
|
-
flat.competition_name = competitionNameFallback
|
|
181
|
-
}
|
|
182
|
-
const ctx: MergeContext = { ...flat }
|
|
183
|
-
for (const [flatKey, path] of Object.entries(LEGACY_FLAT_KEY_MAP)) {
|
|
184
|
-
const [mod, field] = path.split('.')
|
|
185
|
-
const val = flat[flatKey] ?? ''
|
|
186
|
-
if (!ctx[mod] || typeof ctx[mod] === 'string') ctx[mod] = {}
|
|
187
|
-
;(ctx[mod] as Record<string, string>)[field] = val
|
|
188
|
-
}
|
|
189
|
-
return ctx
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function createCustomModule(label: string, fields?: DataModuleDef['fields']): DataModuleDef {
|
|
193
|
-
const id = `custom_${Date.now().toString(36)}`
|
|
194
|
-
return {
|
|
195
|
-
id,
|
|
196
|
-
label: label || '自定义模块',
|
|
197
|
-
builtin: false,
|
|
198
|
-
enabled: true,
|
|
199
|
-
fields: fields?.length
|
|
200
|
-
? fields
|
|
201
|
-
: [
|
|
202
|
-
{ key: 'name', label: '名称' },
|
|
203
|
-
{ key: 'value', label: '值' },
|
|
204
|
-
],
|
|
205
|
-
sample: { name: '示例', value: '123' },
|
|
206
|
-
}
|
|
207
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 模板占位符合并:支持 {{module.field}} 与旧扁平 {{student_name}}
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
import type { MergeContext } from '../types'
|
|
8
|
-
import { LEGACY_FLAT_KEY_MAP } from './dataRegistry'
|
|
9
|
-
|
|
10
|
-
function lookupPath(ctx: MergeContext, path: string): string {
|
|
11
|
-
const parts = path.split('.').filter(Boolean)
|
|
12
|
-
if (parts.length === 1) {
|
|
13
|
-
const v = ctx[parts[0]]
|
|
14
|
-
if (v == null) return ''
|
|
15
|
-
if (typeof v === 'string') return v
|
|
16
|
-
return ''
|
|
17
|
-
}
|
|
18
|
-
if (parts.length >= 2) {
|
|
19
|
-
const mod = ctx[parts[0]]
|
|
20
|
-
if (mod && typeof mod === 'object') {
|
|
21
|
-
const v = (mod as Record<string, string>)[parts[1]]
|
|
22
|
-
return v != null && v !== '' ? String(v) : ''
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return ''
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function resolveToken(ctx: MergeContext, raw: string): string {
|
|
29
|
-
const key = String(raw || '').trim()
|
|
30
|
-
if (!key) return ''
|
|
31
|
-
|
|
32
|
-
// 新式:module.field
|
|
33
|
-
if (key.includes('.')) {
|
|
34
|
-
const nested = lookupPath(ctx, key)
|
|
35
|
-
if (nested) return nested
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 扁平:student_name
|
|
39
|
-
const flat = ctx[key]
|
|
40
|
-
if (typeof flat === 'string' && flat !== '') return flat
|
|
41
|
-
|
|
42
|
-
// 旧扁平 → 新路径
|
|
43
|
-
const mapped = LEGACY_FLAT_KEY_MAP[key]
|
|
44
|
-
if (mapped) {
|
|
45
|
-
const via = lookupPath(ctx, mapped)
|
|
46
|
-
if (via) return via
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return ''
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* 合并模板文本。
|
|
54
|
-
* context 可为嵌套模块对象,也可混入扁平 string 字段。
|
|
55
|
-
*/
|
|
56
|
-
export function mergeTemplateText(text: string, context: MergeContext | Record<string, string>): string {
|
|
57
|
-
if (!text) return ''
|
|
58
|
-
const ctx = context as MergeContext
|
|
59
|
-
return text.replace(/\{\{\s*([a-zA-Z0-9_.]+)\s*\}\}/g, (_, k: string) => resolveToken(ctx, k))
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** @deprecated 使用 mergeTemplateText;保留旧名兼容 */
|
|
63
|
-
export function mergeBadgeText(text: string, data: Record<string, string>) {
|
|
64
|
-
return mergeTemplateText(text, data)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function sortCanvasElements<T extends { zIndex?: number }>(els: T[]): T[] {
|
|
68
|
-
return [...els].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export { sortCanvasElements as sortBadgeElements }
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 证件场景默认占位符列表(旧扁平 key,兼容)
|
|
3
|
-
* @author qiuye
|
|
4
|
-
* @email yjk150@qq.com
|
|
5
|
-
* @date 2026-08-16
|
|
6
|
-
*/
|
|
7
|
-
export const BADGE_PLACEHOLDER_KEYS = [
|
|
8
|
-
'competition_name',
|
|
9
|
-
'student_name',
|
|
10
|
-
'competition_no',
|
|
11
|
-
'school_name',
|
|
12
|
-
'school_short_name',
|
|
13
|
-
'age_group',
|
|
14
|
-
'competition_room',
|
|
15
|
-
'seat_no',
|
|
16
|
-
'floor',
|
|
17
|
-
'teacher_name',
|
|
18
|
-
'date',
|
|
19
|
-
'session',
|
|
20
|
-
'enroll_teacher_name',
|
|
21
|
-
'teacher_phone',
|
|
22
|
-
'signup_teacher_name',
|
|
23
|
-
'signup_teacher_phone',
|
|
24
|
-
'signup_teacher_department',
|
|
25
|
-
'signup_teacher_position',
|
|
26
|
-
'signup_teacher_id_card',
|
|
27
|
-
'signup_teacher_email',
|
|
28
|
-
'signup_teacher_username',
|
|
29
|
-
'venue_name',
|
|
30
|
-
'venue_address',
|
|
31
|
-
'competition_daily_start',
|
|
32
|
-
'competition_daily_end',
|
|
33
|
-
] as const
|
|
34
|
-
|
|
35
|
-
export type BadgePlaceholderKey = (typeof BADGE_PLACEHOLDER_KEYS)[number]
|
|
36
|
-
|
|
37
|
-
export const BADGE_PLACEHOLDER_LABELS: Record<string, string> = {
|
|
38
|
-
competition_name: '比赛名称',
|
|
39
|
-
student_name: '学生姓名',
|
|
40
|
-
competition_no: '参赛编号',
|
|
41
|
-
school_name: '学校',
|
|
42
|
-
school_short_name: '学校简称',
|
|
43
|
-
age_group: '年龄段',
|
|
44
|
-
competition_room: '考场/教室',
|
|
45
|
-
seat_no: '座位号',
|
|
46
|
-
floor: '楼层',
|
|
47
|
-
teacher_name: '监考教师',
|
|
48
|
-
date: '比赛日期',
|
|
49
|
-
session: '场次',
|
|
50
|
-
enroll_teacher_name: '报名/带队教师(扩展)',
|
|
51
|
-
teacher_phone: '教师手机(扩展)',
|
|
52
|
-
signup_teacher_name: '报名教师姓名',
|
|
53
|
-
signup_teacher_phone: '报名教师手机',
|
|
54
|
-
signup_teacher_department: '报名教师部门',
|
|
55
|
-
signup_teacher_position: '报名教师职务',
|
|
56
|
-
signup_teacher_id_card: '报名教师身份证',
|
|
57
|
-
signup_teacher_email: '报名教师邮箱',
|
|
58
|
-
signup_teacher_username: '报名教师账号',
|
|
59
|
-
venue_name: '场地名称',
|
|
60
|
-
venue_address: '场地地址',
|
|
61
|
-
competition_daily_start: '比赛当天开始时间',
|
|
62
|
-
competition_daily_end: '比赛当天结束时间',
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const DESK_LABEL_PLACEHOLDER_KEYS = BADGE_PLACEHOLDER_KEYS
|
|
66
|
-
export type DeskLabelPlaceholderKey = BadgePlaceholderKey
|
|
67
|
-
export const DESK_LABEL_PLACEHOLDER_LABELS: Record<string, string> = BADGE_PLACEHOLDER_LABELS
|