最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue3編寫一個打印模板設(shè)計器(print-canvas-designer)

 更新時間:2026年05月30日 09:57:47   作者:吃喬巴的糖  
在業(yè)務(wù)系統(tǒng)里,標(biāo)簽打印存在一個常見弊端,它往往不是一次性的輸出功能,而是一套需要持續(xù)維護(hù)的編輯能力,本文將用 Vue3 做一個可擴(kuò)展的打印模板設(shè)計器,感興趣的小伙伴可以了解下

在業(yè)務(wù)系統(tǒng)里,標(biāo)簽打印存在一個常見弊端:它往往不是一次性的輸出功能,而是一套需要持續(xù)維護(hù)的編輯能力。

例如:鞋盒標(biāo)可能需要展示款號、顏色、尺碼、品牌圖片、條形碼和二維碼;不同客戶的布局不同,同一個客戶后續(xù)也可能調(diào)整模板。僅靠開發(fā)人員在頁面里寫死位置,不但迭代慢,而且每一次排版變化都要重新發(fā)版。

我希望解決的是這樣一個問題:

讓業(yè)務(wù)人員通過拖拽搭建打印模板,同時讓開發(fā)人員可以控制數(shù)據(jù)、組件和接入方式。

基于這個目標(biāo),我做了一個 Vue 3 打印模板畫布 :print-canvas-designer,并另外制作了一個真實(shí)接入示例項(xiàng)目:print-canvas-examples、演示、參考文檔。

它解決什么問題

print-canvas-designer 主要面向標(biāo)簽、鞋盒標(biāo)、物流面單、商品貼紙等需要自由排版的打印場景。

當(dāng)前版本支持:

  • 文本、圖片、矩形、橫線、二維碼、條形碼等基礎(chǔ)組件。
  • A4、100 x 60 和自定義紙張。
  • 元素選中、拖拽、縮放、旋轉(zhuǎn)、復(fù)制、刪除、鎖定、隱藏和層級調(diào)整。
  • 標(biāo)尺、網(wǎng)格、參考線、頁邊距、安全區(qū)和縮放。
  • 文本字段渲染和換行策略。
  • 圖片地址設(shè)置,以及通過業(yè)務(wù)上傳方法寫入圖片地址。
  • 打印和導(dǎo)出 PDF。
  • 自定義業(yè)務(wù)組件及對應(yīng)的屬性編輯區(qū)域。
  • 完整編輯器接入,或只接入畫布、自行搭建外圍 UI。

注:打印設(shè)計器中最重要的不是固定的一套左側(cè)面板或右側(cè)表單,而是畫布本身。不同業(yè)務(wù)對組件和屬性的要求并不相同,因此畫布提供基礎(chǔ)編輯能力,業(yè)務(wù)決定需要出現(xiàn)什么組件。

三種接入方式

為了演示 npm 包在真實(shí) Vue3 項(xiàng)目中的接入方式,我創(chuàng)建了 print-canvas-examples。其中包括三類場景:

  1. 完整編輯器:直接使用默認(rèn)工具欄、組件面板、畫布與屬性面板。
  2. 自定義業(yè)務(wù)組件:將鞋盒標(biāo)信息塊作為業(yè)務(wù)組件注冊到畫布中。
  3. 只接入畫布:左側(cè)組件區(qū)、頂部工具欄、右側(cè)屬性區(qū)都由業(yè)務(wù)項(xiàng)目自己實(shí)現(xiàn)。

最快接入:使用完整編輯器

安裝依賴:

npm install print-canvas-designer

在入口文件引入樣式:

import 'print-canvas-designer/style.css'

頁面中使用完整編輯器:

<template>
  <PrintDesigner
    v-model="document"
    :data="printData"
    :upload-image="uploadImage"
    @save="handleSave"
    @change="handleChange"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {
  PrintDesigner,
  createDefaultDocument,
  type PrintDocument
} from 'print-canvas-designer'
import 'print-canvas-designer/style.css'
const document = ref<PrintDocument>(createDefaultDocument())
const printData = {
  styleColorSize: 'RUNNER-01 / BLACK / 42',
  barcode: '6901234567890'
}
const uploadImage = async (file: File) => {
  const form = new FormData()
  form.append('file', file)
  const response = await fetch('/api/upload', {
    method: 'POST',
    body: form
  })
  const result = await response.json()
  return result.url
}
const handleSave = (value: PrintDocument) => {
  // 將模板 JSON 保存到業(yè)務(wù)服務(wù)端
  console.log('save document', value)
}
const handleChange = (value: PrintDocument) => {
  console.log('document changed', value)
}
</script>

v-model 對應(yīng)的是模板數(shù)據(jù)。業(yè)務(wù)系統(tǒng)可以把這份 JSON 保存到數(shù)據(jù)庫,之后重新傳給組件即可回顯模板。

為什么支持只接入畫布

完整編輯器適合快速開始,但在真實(shí)項(xiàng)目里,已有系統(tǒng)通常有自己的頁面結(jié)構(gòu)和交互方式:

  • 左側(cè)可能不是基礎(chǔ)組件列表,而是“商品字段”“訂單字段”“客戶 Logo”等業(yè)務(wù)物料。
  • 右側(cè)可能需要結(jié)合權(quán)限、表單校驗(yàn)、字段綁定和業(yè)務(wù)規(guī)則。
  • 頂部操作區(qū)可能需要和系統(tǒng)已有的保存、審核、發(fā)布流程結(jié)合。

這時可以只接入 PrintCanvas,由業(yè)務(wù)自行組織頁面。

<template>
  <div class="designer-page">
    <aside>
      <button @click="addText">添加文本</button>
      <button @click="addBarcode">添加條形碼</button>
    </aside>
    <PrintCanvas :designer="designer" :data="printData" />
    <aside>
      <!-- 根據(jù) designer.activeElement.value 渲染自己的屬性表單 -->
    </aside>
  </div>
</template>
<script setup lang="ts">
import {
  PrintCanvas,
  createDefaultDocument,
  createPrintDesigner,
  providePrintDesigner
} from 'print-canvas-designer'
import 'print-canvas-designer/style.css'
const printData = {
  styleColorSize: 'RUNNER-01 / BLACK / 42'
}
const designer = createPrintDesigner({
  modelValue: createDefaultDocument(),
  data: printData,
  onChange(value) {
    console.log('template changed', value)
  },
  onSave(value) {
    console.log('save template', value)
  }
})
providePrintDesigner(designer)
const addText = () => {
  designer.addElement('text', { x: 24, y: 24 })
}
const addBarcode = () => {
  designer.addElement('barcode', { x: 24, y: 80 })
}
</script>

面板和畫布之間通過同一個 designer 通信。比如選中元素后,業(yè)務(wù)屬性表單可以調(diào)用:

designer.updateElement(activeId, { field: 'styleColorSize' })
designer.updateElementStyle(activeId, { width: 200, height: 42 })
designer.removeElement(activeId)
designer.undo()
designer.redo()
designer.save()

這樣,畫布負(fù)責(zé)編輯交互,業(yè)務(wù)系統(tǒng)負(fù)責(zé) UI 和數(shù)據(jù)規(guī)則。

自定義組件:以鞋盒標(biāo)信息塊為例

基礎(chǔ)文本組件已經(jīng)可以通過字段渲染業(yè)務(wù)內(nèi)容,但一些重復(fù)出現(xiàn)、結(jié)構(gòu)固定的區(qū)域,更適合封裝成業(yè)務(wù)組件。

例如鞋盒標(biāo)中的商品信息區(qū)域,可能固定包含:

  • 標(biāo)題,例如 SPORT SERIES
  • 主內(nèi)容,例如 RUNNER-01 / BLACK / 42。
  • 副內(nèi)容,例如 STYLE / COLOR / SIZE。
  • 強(qiáng)調(diào)顏色或品牌樣式。

業(yè)務(wù)可以定義一個組件,在畫布中負(fù)責(zé)展示結(jié)構(gòu),同時為它提供自己的屬性編輯 UI。組件內(nèi)容、業(yè)務(wù)字段和表單交互由業(yè)務(wù)實(shí)現(xiàn),畫布仍然提供選中、移動、縮放、旋轉(zhuǎn)、刪除和保存能力。

import {
  defaultPrintComponents,
  type PrintComponentDefinition
} from 'print-canvas-designer'
import ShoeInfoBlockRender from './ShoeInfoBlockRender.vue'
import ShoeInfoBlockInspector from './ShoeInfoBlockInspector.vue'

const shoeInfoBlock: PrintComponentDefinition = {
  type: 'shoe-info-block',
  label: '鞋盒標(biāo)信息塊',
  icon: 'i-lucide-tag',
  render: ShoeInfoBlockRender,
  inspector: ShoeInfoBlockInspector,
  createElement: (point) => ({
    id: `shoe_${Date.now()}`,
    type: 'shoe-info-block',
    name: '鞋盒標(biāo)信息塊',
    props: {
      title: 'SPORT SERIES',
      mainText: 'RUNNER-01 / BLACK / 42',
      subText: 'STYLE / COLOR / SIZE',
      accentColor: '#2563eb'
    },
    style: {
      position: 'absolute',
      left: point.x,
      top: point.y,
      width: 260,
      height: 92,
      rotate: 0
    }
  })
}

export const components = [
  ...defaultPrintComponents,
  shoeInfoBlock
]

將它傳給完整編輯器即可出現(xiàn)在物料列表和畫布中:

<PrintDesigner
  v-model="document"
  :components="components"
/>

在只接入畫布的模式下,也可以把同樣的 components 傳給 createPrintDesigner。因此自定義組件不是固定編輯器才有的能力,而是畫布 SDK 提供給業(yè)務(wù)的擴(kuò)展機(jī)制。

模板數(shù)據(jù)與業(yè)務(wù)數(shù)據(jù)如何結(jié)合

模板本身保存布局與元素配置,實(shí)際打印數(shù)據(jù)在運(yùn)行時傳入。

以文本為例,模板中可以保存字段名:

{
  "type": "text",
  "name": "款色碼",
  "field": "styleColorSize",
  "style": {
    "left": 24,
    "top": 32,
    "width": 220,
    "height": 42
  }
}

業(yè)務(wù)在打印前把多個字段拼接為需要展示的內(nèi)容:

const printData = {
  styleColorSize: [product.style, product.color, product.size].join(' / ')
}

這樣可以讓畫布繼續(xù)保持通用,不必為每一種業(yè)務(wù)字段組合設(shè)計專門的布局規(guī)則。

圖片、打印與導(dǎo)出

圖片組件既可以直接填寫圖片地址,也可以將上傳過程交給業(yè)務(wù)系統(tǒng):

const uploadImage = async (file: File) => {
  const url = await uploadToObjectStorage(file)
  return url
}

輸出方面,完整編輯器提供打印和導(dǎo)出 PDF 的交互。只接入畫布時,也可以通過 designer.print()designer.exportPdf() 接入自己的操作入口和輸出流程。

當(dāng)前階段與后續(xù)計劃

當(dāng)前版本主要聚焦于打印模板畫布的核心能力,以及業(yè)務(wù)擴(kuò)展所需要的組件機(jī)制。它已經(jīng)可以用于搭建標(biāo)簽類模板并驗(yàn)證實(shí)際接入方式。

后續(xù)我計劃繼續(xù)完善:

  • 更豐富的自定義業(yè)務(wù)組件示例。
  • 模板管理、保存與復(fù)用場景的參考實(shí)現(xiàn)。
  • 打印與導(dǎo)出流程在真實(shí)業(yè)務(wù)中的接入示例。

到此這篇關(guān)于基于Vue3編寫一個打印模板設(shè)計器(print-canvas-designer)的文章就介紹到這了,更多相關(guān)Vue3打印模板設(shè)計器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue中的父子組件傳值及傳方法

    Vue中的父子組件傳值及傳方法

    這篇文章主要介紹了Vue中的父子組件傳值及傳方法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue中手動封裝iconfont組件解析(三種引用方式的封裝和使用)

    vue中手動封裝iconfont組件解析(三種引用方式的封裝和使用)

    這篇文章主要介紹了vue中手動封裝iconfont組件(三種引用方式的封裝和使用),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue3+vite動態(tài)引入圖片方式

    vue3+vite動態(tài)引入圖片方式

    這篇文章主要介紹了vue3+vite動態(tài)引入圖片方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Vue中的計算屬性介紹

    Vue中的計算屬性介紹

    這篇文章主要介紹了Vue中的計算屬性,模板內(nèi)的表達(dá)式,用于簡單運(yùn)算,但是模板中放入太多的邏輯會讓模板過重且難以維護(hù),更多具體內(nèi)容一起進(jìn)入下面文章學(xué)習(xí)吧,需要的朋友也可以參考一下
    2021-12-12
  • 有關(guān)vue 組件切換,動態(tài)組件,組件緩存

    有關(guān)vue 組件切換,動態(tài)組件,組件緩存

    這篇文章主要介紹了有關(guān)vue 組件切換,動態(tài)組件,組件緩存,在組件化開發(fā)模式下,我們會把整個項(xiàng)目拆分成很多組件,然后按照合理的方式組織起來,達(dá)到預(yù)期效果,下面來看看文章的詳細(xì)內(nèi)容
    2021-11-11
  • Vue對Element中el-tab-pane添加@click事件無效問題解決

    Vue對Element中el-tab-pane添加@click事件無效問題解決

    這篇文章主要給大家介紹了關(guān)于Vue對Element中el-tab-pane添加@click事件無效問題的解決辦法,文中通過圖文以及代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • flutter使用tauri實(shí)現(xiàn)一個一鍵視頻轉(zhuǎn)4K軟件

    flutter使用tauri實(shí)現(xiàn)一個一鍵視頻轉(zhuǎn)4K軟件

    這篇文章主要為大家介紹了flutter使用tauri實(shí)現(xiàn)一個一鍵視頻轉(zhuǎn)4K軟件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 基于Vue3的全屏拖拽上傳組件

    基于Vue3的全屏拖拽上傳組件

    本文主要介紹了基于Vue3的全屏拖拽上傳組件,其實(shí)思路上與其他拖拽上傳組件基本一樣,都是指定一個區(qū)域可拖拽,然后讀取文件在上傳,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • 解決vue接口數(shù)據(jù)賦值給data沒有反應(yīng)的問題

    解決vue接口數(shù)據(jù)賦值給data沒有反應(yīng)的問題

    今天小編就為大家分享一篇解決vue接口數(shù)據(jù)賦值給data沒有反應(yīng)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue3中操作ref的四種使用方式示例代碼(建議收藏)

    Vue3中操作ref的四種使用方式示例代碼(建議收藏)

    這篇文章主要介紹了Vue3中操作ref的四種使用方式示例代碼(建議收藏),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04

最新評論

临夏市| 龙口市| 喜德县| 济宁市| 宁德市| 南涧| 稷山县| 罗山县| 玛曲县| 沿河| 海丰县| 武功县| 宜川县| 什邡市| 盐津县| 桃江县| 甘德县| 化德县| 玉田县| 衡水市| 东光县| 林芝县| 红河县| 旌德县| 莎车县| 桑植县| 奎屯市| 磐安县| 崇州市| 兴安盟| 南木林县| 儋州市| 弥渡县| 武安市| 璧山县| 孟连| 利辛县| 罗田县| 甘德县| 曲阳县| 莫力|