|
|
@@ -0,0 +1,136 @@
|
|
|
+<template>
|
|
|
+ <div
|
|
|
+ :style="boxStyle"
|
|
|
+ class="relative overflow-hidden box-border flex items-center justify-center"
|
|
|
+ >
|
|
|
+ <canvas ref="barcodeCanvas" class="w-full h-full block"></canvas>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, ref, watch, onMounted } from 'vue'
|
|
|
+import { useWidgetStyle } from '../hooks/useWidgetStyle'
|
|
|
+import { encodeCode128B } from './code128'
|
|
|
+import { useProjectStore } from '@/store/modules/project'
|
|
|
+
|
|
|
+const DEFAULT_TEXT = 'https://www.sv-elec.com/'
|
|
|
+const DEFAULT_SCALE = 1
|
|
|
+const BASE_THICKNESS = 60
|
|
|
+
|
|
|
+const props = defineProps<{
|
|
|
+ id?: string
|
|
|
+ width: number
|
|
|
+ height: number
|
|
|
+ styles: any
|
|
|
+ state?: string
|
|
|
+ part?: string
|
|
|
+ lightColor?: string
|
|
|
+ darkColor?: string
|
|
|
+ scale?: number
|
|
|
+ direction?: 'horizontal' | 'vertical'
|
|
|
+ text?: string
|
|
|
+}>()
|
|
|
+
|
|
|
+const barcodeCanvas = ref<HTMLCanvasElement | null>(null)
|
|
|
+const projectStore = useProjectStore()
|
|
|
+
|
|
|
+const styleMap = useWidgetStyle({
|
|
|
+ widget: 'barcode',
|
|
|
+ props
|
|
|
+})
|
|
|
+
|
|
|
+const boxStyle = computed(() => {
|
|
|
+ return {
|
|
|
+ width: `${props.width}px`,
|
|
|
+ height: `${props.height}px`,
|
|
|
+ ...(styleMap.value?.mainStyle || {})
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const getAutoWidth = () => {
|
|
|
+ const pattern =
|
|
|
+ encodeCode128B((props.text || DEFAULT_TEXT).trim() || DEFAULT_TEXT) ||
|
|
|
+ encodeCode128B(DEFAULT_TEXT)
|
|
|
+ const totalUnits = pattern?.reduce((sum, unit) => sum + unit, 0) || 1
|
|
|
+ const scale = Math.max(1, Math.min(5, Math.round(Number(props.scale) || DEFAULT_SCALE)))
|
|
|
+
|
|
|
+ return props.direction === 'vertical' ? BASE_THICKNESS * scale : totalUnits * scale
|
|
|
+}
|
|
|
+
|
|
|
+const syncWidgetWidth = () => {
|
|
|
+ if (!props.id) return
|
|
|
+
|
|
|
+ const widget = projectStore.getWidgetById(props.id)
|
|
|
+ if (!widget?.props) return
|
|
|
+
|
|
|
+ const nextWidth = getAutoWidth()
|
|
|
+ if (widget.props.width !== nextWidth) {
|
|
|
+ widget.props.width = nextWidth
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const renderBarcode = () => {
|
|
|
+ if (!barcodeCanvas.value) return
|
|
|
+
|
|
|
+ const canvas = barcodeCanvas.value
|
|
|
+ const context = canvas.getContext('2d')
|
|
|
+ if (!context) return
|
|
|
+
|
|
|
+ const width = Math.max(1, props.width || 1)
|
|
|
+ const height = Math.max(1, props.height || 1)
|
|
|
+
|
|
|
+ canvas.width = width
|
|
|
+ canvas.height = height
|
|
|
+
|
|
|
+ context.clearRect(0, 0, width, height)
|
|
|
+ context.fillStyle = props.lightColor || '#ffffff'
|
|
|
+ context.fillRect(0, 0, width, height)
|
|
|
+
|
|
|
+ const pattern = encodeCode128B((props.text || DEFAULT_TEXT).trim() || DEFAULT_TEXT)
|
|
|
+ if (!pattern?.length) {
|
|
|
+ console.error('Failed to render barcode: unsupported content')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const totalUnits = pattern.reduce((sum, unit) => sum + unit, 0)
|
|
|
+ const isVertical = props.direction === 'vertical'
|
|
|
+ const moduleSize = (isVertical ? height : width) / totalUnits
|
|
|
+
|
|
|
+ let offset = 0
|
|
|
+ let isBar = true
|
|
|
+
|
|
|
+ context.fillStyle = props.darkColor || '#000000'
|
|
|
+ pattern.forEach((unit) => {
|
|
|
+ const segmentSize = unit * moduleSize
|
|
|
+ if (isBar) {
|
|
|
+ if (isVertical) {
|
|
|
+ context.fillRect(0, offset, width, segmentSize)
|
|
|
+ } else {
|
|
|
+ context.fillRect(offset, 0, segmentSize, height)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ offset += segmentSize
|
|
|
+ isBar = !isBar
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => [props.width, props.height, props.text, props.darkColor, props.lightColor, props.direction],
|
|
|
+ () => {
|
|
|
+ renderBarcode()
|
|
|
+ },
|
|
|
+ { immediate: true, flush: 'post' }
|
|
|
+)
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => [props.id, props.text, props.scale, props.direction],
|
|
|
+ () => {
|
|
|
+ syncWidgetWidth()
|
|
|
+ },
|
|
|
+ { immediate: true, flush: 'post' }
|
|
|
+)
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ renderBarcode()
|
|
|
+})
|
|
|
+</script>
|