You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1695 lines
52 KiB

<template>
<div class="property-form">
<!-- 文本属性 -->
<div v-if="element.type === 'text'" class="form-section">
<el-form label-position="top" size="small">
<el-form-item label="数据类型">
<el-select v-model="element.dataType" size="mini" style="width: 100%;" @change="onDataTypeChange">
<el-option label="普通文本" value="text" />
<el-option label="数字" value="number" />
<el-option label="日期" value="date" />
<el-option label="字符串" value="string" />
</el-select>
</el-form-item>
<el-form-item label="文本内容">
<div class="input-with-button">
<el-input v-model="element.data" placeholder="请输入文本" />
<el-button type="primary" size="mini" @click="$emit('data-source', element)">
数据源
</el-button>
</div>
</el-form-item>
<el-form-item label="不显示规则(如:XXX=N)">
<div class="font-size-row">
<el-input
v-model="element.showElement"
controls-position="right"
size="mini"
class="font-size-input"
/>
<el-tooltip content="勾选后内容前面展示☑" placement="top">
<el-checkbox
v-model="element.isChecked"
size="small"
class="inline-checkbox"
>
打勾
</el-checkbox>
</el-tooltip>
</div>
</el-form-item>
<el-form-item >
<div class="form-row">
<el-form-item label="字体" class="form-item-half">
<el-select
v-model="element.fontFamily"
size="mini"
style="width: 100%;"
placeholder="选择字体"
filterable
:loading="fontLoading"
@focus="loadAvailableFonts"
@change="onFontFamilyChange"
>
<el-option-group
v-for="group in groupedFonts"
:key="group.category"
:label="group.label"
>
<el-option
v-for="font in group.fonts"
:key="font.value"
:label="font.name"
:value="font.value"
:disabled="!font.supported"
>
<span style="float: left">{{ font.name }}</span>
<span style="float: right; color: #8492a6; font-size: 11px">
{{ font.description }}
<i v-if="!font.supported" class="el-icon-warning" style="color: #f56c6c;" title="不支持ZPL打印"></i>
</span>
</el-option>
</el-option-group>
</el-select>
</el-form-item>
<el-form-item label="文本对齐" class="form-item-half">
<el-select v-model="element.textAlign" size="mini" style="width: 100%;" placeholder="对齐方式">
<el-option label="左对齐" value="left" />
<el-option label="居中" value="center" />
<el-option label="右对齐" value="right" />
</el-select>
</el-form-item>
</div>
</el-form-item>
<el-form-item label="字体大小">
<div class="font-size-row">
<el-input
v-model="element.fontSize"
:min="8"
:max="200"
controls-position="right"
size="mini"
class="font-size-input"
/>
<el-checkbox v-model="element.bold" size="small" class="inline-checkbox">加粗</el-checkbox>
<el-checkbox v-model="element.newline" size="small" class="inline-checkbox">自动换行</el-checkbox>
</div>
</el-form-item>
<!-- <el-form-item label="字体样式">
<div class="font-style-row">
<el-checkbox v-model="element.fontItalic" size="small" class="inline-checkbox">斜体</el-checkbox>
<el-checkbox v-model="element.fontUnderline" size="small" class="inline-checkbox">下划线</el-checkbox>
<div class="spacing-controls">
<span class="spacing-label">字符间距:</span>
<el-input
v-model="element.letterSpacing"
:min="0"
:max="20"
controls-position="right"
size="mini"
class="spacing-input"
placeholder="0"
/>
</div>
</div>
</el-form-item>-->
<template v-if="element.newline">
<div class="form-row">
<el-form-item label="文本宽度(mm)" class="form-item-half">
<el-input
v-model="element.lineWidth"
:min="50"
:max="1000"
controls-position="right"
size="mini"
/>
</el-form-item>
<el-form-item label="文本行数" class="form-item-half">
<el-input
v-model="element.lineRows"
:min="1"
:max="10"
controls-position="right"
size="mini"
/>
</el-form-item>
</div>
</template>
<template v-if="element.dataType === 'number'">
<el-form-item label="小数位数" >
<div class="font-size-row">
<el-input v-model="element.decimalPlaces" controls-position="right" size="mini"/>
<el-checkbox v-model="element.showDecimalPlaces" size="small" class="inline-checkbox">整数显示小数位</el-checkbox>
<el-checkbox v-model="element.thousandsSeparator" size="small" class="inline-checkbox">千位分隔符</el-checkbox>
<el-checkbox v-model="element.roundHalfUp" size="small" class="inline-checkbox">四舍五入</el-checkbox>
</div>
</el-form-item>
</template>
<!-- 日期类型设置 -->
<template v-if="element.dataType === 'date'">
<div class="form-row">
<el-form-item label="数据源" class="form-item-half">
<el-select
v-model="element.dateSourceType"
size="mini"
style="width: 100%;"
@change="onDateSourceTypeChange"
>
<el-option label="字段值" value="field" />
<el-option label="当前日期" value="current" />
</el-select>
</el-form-item>
<el-form-item label="提取类型" class="form-item-half">
<el-select v-model="element.dateExtractType" size="mini" style="width: 100%;">
<el-option label="完整日期" value="full" />
<el-option label="提取年" value="year" />
<el-option label="提取月" value="month" />
<el-option label="提取日" value="day" />
<el-option label="提取周别" value="week" />
<el-option label="提取周几" value="weekday" />
</el-select>
</el-form-item>
<el-form-item label="加减天数" class="form-item-half">
<el-input
v-model="element.dateOffsetDays"
controls-position="right" size="mini"
style="width: 100%;"/>
</el-form-item>
</div>
<template v-if="element.dateExtractType === 'week'">
<el-form-item label="第一周日期">
<el-date-picker
v-model="element.firstWeekDate"
type="date"
placeholder="选择第一周日期"
size="mini"
style="width: 100%;"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
</template>
<template v-if="element.dateExtractType === 'weekday'">
<el-form-item label="第一天是周几">
<el-select v-model="element.firstDayOfWeek" size="mini" style="width: 100%;">
<el-option label="周日" value="0" />
<el-option label="周一" value="1" />
<el-option label="周二" value="2" />
<el-option label="周三" value="3" />
<el-option label="周四" value="4" />
<el-option label="周五" value="5" />
<el-option label="周六" value="6" />
</el-select>
</el-form-item>
</template>
<template v-if="element.dateExtractType === 'full'">
<div class="form-row">
<el-form-item label="日期格式" class="form-item-half">
<el-select v-model="element.dateFormat" size="mini" style="width: 100%;">
<el-option label="年月日" value="ymd" />
<el-option label="日月年" value="dmy" />
<el-option label="月日年" value="mdy" />
</el-select>
</el-form-item>
<el-form-item label="分隔符" class="form-item-half">
<el-select v-model="element.dateSeparator" size="mini" style="width: 100%;">
<el-option label="-" value="-" />
<el-option label="/" value="/" />
<el-option label="." value="." />
<el-option label="无" value="" />
</el-select>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="年份位数" class="form-item-half">
<el-select v-model="element.yearDigits" size="mini" style="width: 100%;">
<el-option label="4位(2024)" value="4" />
<el-option label="2位(24)" value="2" />
</el-select>
</el-form-item>
<el-form-item label="月日位数" class="form-item-half">
<el-select v-model="element.monthDayDigits" size="mini" style="width: 100%;">
<el-option label="2位(01)" value="2" />
<el-option label="1位(1)" value="1" />
</el-select>
</el-form-item>
</div>
</template>
</template>
<!-- 字符串类型设置 -->
<template v-if="element.dataType === 'string'">
<el-form-item label="字符串处理类型">
<el-select v-model="element.stringProcessType" size="mini" style="width: 100%;">
<el-option label="无处理" value="none" />
<el-option label="截取前面字符" value="substring_start" />
<el-option label="截取后面字符" value="substring_end" />
<el-option label="按字符分割" value="split" />
<el-option label="替换字符" value="replace" />
</el-select>
</el-form-item>
<template v-if="element.stringProcessType === 'substring_start'">
<el-form-item label="截取前面位数">
<el-input
v-model="element.substringStartLength"
:min="1"
:max="1000"
controls-position="right"
size="mini"
placeholder="截取前面多少位"
/>
</el-form-item>
</template>
<template v-if="element.stringProcessType === 'substring_end'">
<el-form-item label="截取后面位数">
<el-input
v-model="element.substringEndLength"
:min="1"
:max="1000"
controls-position="right"
size="mini"
placeholder="截取后面多少位"
/>
</el-form-item>
</template>
<template v-if="element.stringProcessType === 'split'">
<div class="form-row">
<el-form-item label="分割字符" class="form-item-half">
<el-input
v-model="element.splitCharacter"
size="mini"
placeholder="分割字符"
/>
</el-form-item>
<el-form-item label="取第几段" class="form-item-half">
<el-input
v-model="element.splitIndex"
:min="0"
:max="100"
controls-position="right"
size="mini"
placeholder="0表示第1段"
/>
</el-form-item>
</div>
</template>
<template v-if="element.stringProcessType === 'replace'">
<div class="form-row">
<el-form-item label="查找字符" class="form-item-half">
<el-input
v-model="element.replaceFrom"
size="mini"
placeholder="要替换的字符"
/>
</el-form-item>
<el-form-item label="替换为" class="form-item-half">
<el-input
v-model="element.replaceTo"
size="mini"
placeholder="替换成的字符"
/>
</el-form-item>
</div>
</template>
</template>
</el-form>
</div>
<!-- 一维码属性 -->
<div v-else-if="element.type === 'onecode'" class="form-section">
<el-form label-position="top" size="small">
<el-form-item label="数据内容">
<div class="input-with-button">
<el-input
v-model="element.data"
placeholder="请输入一维码数据"
type="textarea"
:rows="2"
maxlength="1000"
show-word-limit
/>
<el-button type="primary" size="mini" @click="$emit('data-source', element)">
数据源
</el-button>
<el-button type="success" size="mini" @click="$emit('element-combination', element)">
元素组合
</el-button>
</div>
</el-form-item>
<el-form-item label="不显示规则(如:XXX=N)" class="form-item-half">
<el-input v-model="element.showElement" controls-position="right" size="mini"/>
</el-form-item>
<div class="form-row">
<el-form-item label="条码类型" class="form-item-half">
<el-select v-model="element.barcodeType" size="mini" style="width: 100%;">
<el-option label="CODE128" value="CODE128" />
<el-option label="CODE39" value="CODE39" />
<el-option label="CODE93" value="CODE93" />
<el-option label="EAN13" value="EAN13" />
<el-option label="EAN8" value="EAN8" />
<el-option label="UPCA" value="UPCA" />
<el-option label="UPCE" value="UPCE" />
</el-select>
</el-form-item>
<el-form-item label="明文显示" class="form-item-half">
<el-radio-group v-model="element.showContent" size="mini">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="窄条宽(范围0.085-0.85mm)" class="form-item-half">
<el-input
v-model="element.width"
controls-position="right"
size="mini"
/>
</el-form-item>
<el-form-item label="高度(mm)" class="form-item-half">
<el-input
v-model="element.height"
controls-position="right"
size="mini"
/>
</el-form-item>
</div>
</el-form>
</div>
<!-- 二维码属性 -->
<div v-else-if="element.type === 'qrcode'" class="form-section">
<el-form label-position="top" size="small">
<el-form-item label="数据内容">
<div class="input-with-button">
<el-input
v-model="element.data"
placeholder="请输入二维码数据"
type="textarea"
:rows="2"
maxlength="1000"
show-word-limit
/>
<el-button type="primary" size="mini" @click="$emit('data-source', element)">
数据源
</el-button>
<el-button type="success" size="mini" @click="$emit('element-combination', element)">
元素组合
</el-button>
</div>
</el-form-item>
<el-form-item label="不显示规则(如:XXX=N)" class="form-item-half">
<el-input v-model="element.showElement" controls-position="right" size="mini"/>
</el-form-item>
<div class="form-row">
<el-form-item label="尺寸(mm)" class="form-item-half">
<el-input
v-model="element.height"
:min="3"
:max="50"
:step="0.5"
controls-position="right"
size="mini"
@change="validateQRSize"
/>
</el-form-item>
<el-form-item label="明文显示" class="form-item-half">
<el-radio-group v-model="element.showContent" size="mini">
<el-radio :label="true">是</el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</div>
<div class="form-tip">
<div v-if="element.data && element.data.length > 200" class="tip-warning">
内容较长({{ element.data.length }}字符),建议尺寸≥15mm,系统将自动优化
</div>
<div v-else-if="element.data && element.data.length > 100" class="tip-info">
内容中等({{ element.data.length }}字符),建议尺寸≥12mm,系统将自动优化
</div>
<div v-else class="tip-normal">
内容较短({{ element.data ? element.data.length : 0 }}字符),当前尺寸足够
</div>
</div>
</el-form>
</div>
<!-- 图片属性 -->
<div v-else-if="element.type === 'pic'" class="form-section">
<el-form label-position="top" size="small">
<el-form-item label="图片上传">
<div class="image-upload">
<input
ref="fileInput"
type="file"
accept="image/*"
@change="handleImageUpload"
style="display: none;"
/>
<el-button type="primary"
icon="el-icon-upload"
@click="handleSelectImage">
选择图片
</el-button>
</div>
</el-form-item>
<div v-if="element.previewUrl" class="image-preview">
<img :src="element.previewUrl" alt="预览" />
</div>
<!-- 图片尺寸设置 -->
<div class="form-row">
<el-form-item label="宽度(像素)" class="form-item-half">
<el-input
v-model="element.width"
:min="50"
:max="800"
controls-position="right"
size="mini"
@change="onImageSizeChange"
/>
</el-form-item>
<el-form-item label="高度(像素)" class="form-item-half">
<el-input
v-model="element.height"
:min="50"
:max="800"
controls-position="right"
size="mini"
@change="onImageSizeChange"
/>
</el-form-item>
</div>
<el-form-item label="不显示规则(如:XXX=N)" class="form-item-half">
<el-input v-model="element.showElement" controls-position="right" size="mini"/>
</el-form-item>
</el-form>
</div>
<!-- 线条属性 -->
<div v-else-if="['hLine', 'vLine'].includes(element.type)" class="form-section">
<el-form label-position="top" size="small">
<div class="form-row">
<el-form-item label="宽度" class="form-item-half">
<el-input
v-model="element.width"
:min="1"
:max="1000"
controls-position="right"
size="mini"
/>
</el-form-item>
<el-form-item label="高度" class="form-item-half">
<el-input
v-model="element.height"
:min="1"
:max="1000"
controls-position="right"
size="mini"
/>
</el-form-item>
</div>
</el-form>
</div>
<!-- 流水号属性 -->
<div v-else-if="element.type === 'serialNumber'" class="form-section">
<el-form label-position="top" size="small">
<div class="form-row">
<el-form-item label="名称" class="form-item-half">
<el-input v-model="element.seqName" controls-position="right" size="mini"/>
</el-form-item>
<el-form-item v-if="parentSerialElements.length>0" label="父标签流水号" class="form-item-half">
<el-select clearable v-model="element.parentSerialLabelNo" size="mini" style="width: 100%;" filterable >
<el-option v-for="item in parentSerialElements" :key="item.label_no" :label="(item.seq_name?item.seq_name:'')+'('+item.data+')'"
:value="item.label_no"></el-option>
</el-select>
</el-form-item>
</div>
<div class="form-row" v-if="!element.parentSerialLabelNo">
<el-form-item label="位数" class="form-item-half">
<el-input
v-model="element.digits"
:min="1"
:max="10"
controls-position="right"
size="mini"
placeholder="6"
/>
</el-form-item>
<el-form-item label="步长" class="form-item-half">
<el-input
v-model="element.step"
:min="1"
:max="100"
controls-position="right"
size="mini"
placeholder="1"
/>
</el-form-item>
</div>
<div class="form-row" v-if="!element.parentSerialLabelNo">
<el-form-item label="字体大小" class="form-item-half">
<el-input
v-model="element.fontSize"
:min="8"
:max="200"
controls-position="right"
size="mini"
placeholder="30"
/>
</el-form-item>
<el-form-item label="加粗" class="form-item-half">
<el-checkbox v-model="element.bold" size="middle"></el-checkbox>
</el-form-item>
</div>
<div class="form-row" v-if="!element.parentSerialLabelNo">
<el-form-item label="流水号规则" class="form-item-half">
<el-input v-model="element.data" placeholder="请输入流水号规则" />
<el-button type="primary" size="mini" @click="$emit('data-source', element)">
数据源
</el-button>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="流水号信息" class="form-item-half">
<el-button type="primary" size="mini" @click="serialInfoModal(element)">
查看
</el-button>
</el-form-item>
<el-form-item label="是否显示" class="form-item-half">
<el-checkbox v-model="element.showSerialNumber" size="middle">显示流水号</el-checkbox>
</el-form-item>
</div>
</el-form>
</div>
<!-- 标签内容流水号信息 -->
<comShowLabelSerialInfo ref="comShowLabelSerialInfo" v-drag></comShowLabelSerialInfo>
</div>
</template>
<script>
import comShowLabelSerialInfo from "../com_show_label_serial_info";
import { availableFont,getParentLabelInfo ,getParentSerialElements} from '@/api/labelSetting/label_setting.js'
export default {
name: 'PropertyForm',
props: {
element: {
type: Object,
required: true
}
},
emits: ['data-source', 'image-upload', 'element-combination'],
data() {
return {
availableFonts: [],
fontLoading: false,
fontsLoaded: false,
parentSerialElements: [] // 父标签的流水号元素列表
}
},
computed: {
groupedFonts() {
if (!this.availableFonts.length) {
return []
}
const groups = {}
this.availableFonts.forEach(font => {
const category = font.category || 'other'
if (!groups[category]) {
groups[category] = {
category: category,
label: this.getCategoryLabel(category),
fonts: []
}
}
groups[category].fonts.push(font)
})
// 排序分组
const sortedGroups = Object.values(groups).sort((a, b) => {
const order = ['system', 'chinese', 'english', 'monospace', 'decorative', 'other']
return order.indexOf(a.category) - order.indexOf(b.category)
})
return sortedGroups
}
},
/*组件*/
components: {
comShowLabelSerialInfo,/*标签内容流水号信息的組件*/
},
mounted() {
// 初始化数据类型及相关设置
this.initializeTypeSettings()
// 初始化字体设置
this.initializeFontSettings()
if (this.element.type === 'serialNumber') {
this.getParentLabelInfo()
}
},
watch: {
// 监听元素变化,确保文件输入框状态正确
'element.id'() {
// 当切换到不同元素时,重置文件输入框
this.$nextTick(() => {
if (this.$refs.fileInput) {
this.$refs.fileInput.value = ''
}
})
},
'element'() {
// 当切换到不同元素时,获取父流水号
this.$nextTick(() => {
this.initializeTypeSettings()
if (this.element.type === 'serialNumber') {
this.getParentLabelInfo()
}
})
}
},
methods: {
setElementDefault(key, defaultValue) {
const value = this.element[key]
if (value === undefined || value === null || value === '') {
this.$set(this.element, key, defaultValue)
}
},
initializeTypeSettings() {
if (this.element.type !== 'text') {
return
}
this.setElementDefault('dataType', 'text')
if (this.element.dataType === 'date') {
this.setElementDefault('dateSourceType', this.inferDateSourceType(this.element.data))
this.setElementDefault('dateExtractType', 'full')
this.setElementDefault('dateFormat', 'ymd')
this.setElementDefault('dateSeparator', '-')
this.setElementDefault('yearDigits', '4')
this.setElementDefault('monthDayDigits', '2')
this.setElementDefault('firstWeekDate', '')
this.setElementDefault('firstDayOfWeek', '1')
this.setElementDefault('dateOffsetDays', 0)
} else if (this.element.dataType === 'number') {
this.setElementDefault('roundHalfUp', true)
} else if (this.element.dataType === 'string') {
this.setElementDefault('stringProcessType', 'none')
this.setElementDefault('substringStartLength', '')
this.setElementDefault('substringEndLength', '')
this.setElementDefault('splitCharacter', '')
this.setElementDefault('splitIndex', '0')
this.setElementDefault('replaceFrom', '')
this.setElementDefault('replaceTo', '')
}
},
// 获取父标签信息
async getParentLabelInfo() {
if (!this.element.reportId) return null
try {
// 使用API服务获取父标签信息
const response = await getParentLabelInfo({ labelNo: this.element.reportId })
if (response && response.data.code === 200) {
const parentInfo = response.data.data
if (parentInfo && parentInfo.parentLabelNo) {
const response2 = await getParentSerialElements({ labelNo: parentInfo.parentLabelNo })
if (response2.data && response2.data.code === 200) {
this.parentSerialElements = response2.data.data || []
} else {
// 如果API调用失败,使用默认数据
this.parentSerialElements = []
}
}
} else {
// 如果API调用失败,使用默认数据
this.parentSerialElements = []
}
} catch (error) {
console.error('获取父标签信息失败:', error)
this.parentSerialElements = []
}
},
onDataTypeChange(newType) {
// 重置相关设置
if (newType === 'date') {
this.$set(this.element, 'dateSourceType', this.inferDateSourceType(this.element.data))
this.$set(this.element, 'dateExtractType', 'full')
this.$set(this.element, 'dateFormat', 'ymd')
this.$set(this.element, 'dateSeparator', '-')
this.$set(this.element, 'yearDigits', '4')
this.$set(this.element, 'monthDayDigits', '2')
this.$set(this.element, 'firstWeekDate', '')
this.$set(this.element, 'firstDayOfWeek', '1')
this.$set(this.element, 'dateOffsetDays', 0)
} else if (newType === 'number') {
this.$set(this.element, 'roundHalfUp', true)
} else if (newType === 'string') {
this.$set(this.element, 'stringProcessType', 'none')
this.$set(this.element, 'substringStartLength', '')
this.$set(this.element, 'substringEndLength', '')
this.$set(this.element, 'splitCharacter', '')
this.$set(this.element, 'splitIndex', '0')
this.$set(this.element, 'replaceFrom', '')
this.$set(this.element, 'replaceTo', '')
}
},
inferDateSourceType(dataValue) {
return dataValue === '#{CURRENT_DATE_YYYY-MM-DD}' ? 'current' : 'field'
},
onDateSourceTypeChange(sourceType) {
if (sourceType === 'current') {
if (this.element.data && this.element.data !== '#{CURRENT_DATE_YYYY-MM-DD}') {
this.$set(this.element, 'dateFieldDataBackup', this.element.data)
}
this.$set(this.element, 'data', '#{CURRENT_DATE_YYYY-MM-DD}')
} else if (this.element.data === '#{CURRENT_DATE_YYYY-MM-DD}') {
const backupData = this.element.dateFieldDataBackup || ''
this.$set(this.element, 'data', backupData)
}
},
handleSelectImage() {
// 确保文件输入框被重置,然后触发点击
if (this.$refs.fileInput) {
this.$refs.fileInput.value = ''
this.$refs.fileInput.click()
}
},
/*流水号信息的modal*/
serialInfoModal(row){
let rowData = {
labelNo: row.reportId,
itemNo: row.itemNo,
};
//打开组件 去做复制其他标签业务
this.$nextTick(() => {
this.$refs.comShowLabelSerialInfo.init(rowData);
})
},
validateQRSize() {
const dataLength = this.element.data ? this.element.data.length : 0
// 毫米单位限制
if (this.element.height > 50) {
this.$message.warning('二维码最大尺寸为50mm')
this.element.height = 50
} else if (this.element.height < 3) {
this.element.height = 3
}
// 根据数据长度给出尺寸建议
if (dataLength > 200 && this.element.height < 15) {
this.$message.warning('长内容建议使用15mm或以上尺寸,以确保扫描成功')
} else if (dataLength > 100 && this.element.height < 12) {
this.$message.warning('中等长度内容建议使用12mm或以上尺寸')
}
},
/**
* 图片尺寸变化处理
*/
onImageSizeChange() {
// 限制图片尺寸范围
if (this.element.width > 800) {
this.$message.warning('图片宽度最大为800像素')
this.element.width = 800
} else if (this.element.width < 50) {
this.element.width = 50
}
if (this.element.height > 800) {
this.$message.warning('图片高度最大为800像素')
this.element.height = 800
} else if (this.element.height < 50) {
this.element.height = 50
}
// 如果已经有图片数据,重新生成ZPL数据
if (this.element.previewUrl) {
this.regenerateImageZPL()
}
},
/**
* 重新生成图片的ZPL数据
*/
regenerateImageZPL() {
if (!this.element.previewUrl) return
const img = new Image()
img.onload = () => {
try {
const { zplData } = this.convertImageToZPL(img)
this.element.zplData = zplData
// 触发父组件更新
this.$emit('image-upload', { zplData, previewUrl: this.element.previewUrl })
} catch (error) {
console.error('重新生成图片ZPL失败:', error)
}
}
img.src = this.element.previewUrl
},
async handleImageUpload(event) {
const file = event.target.files[0]
if (!file) return
try {
const imageData = await this.processImage(file)
this.$emit('image-upload', imageData)
// 重置文件输入框的值,确保下次选择相同文件时也能触发change事件
event.target.value = ''
} catch (error) {
this.$message.error('图片处理失败')
// 即使出错也要重置文件输入框
event.target.value = ''
}
},
processImage(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (e) => {
const img = new Image()
img.onload = () => {
try {
const { zplData, previewUrl } = this.convertImageToZPL(img)
resolve({ zplData, previewUrl })
} catch (error) {
reject(error)
}
}
img.onerror = reject
img.src = e.target.result
}
reader.onerror = reject
reader.readAsDataURL(file)
})
},
convertImageToZPL(img) {
// 使用元素的宽度和高度设置,如果没有设置则使用默认值
const targetWidth = parseInt(this.element.width) || 200
const targetHeight = parseInt(this.element.height) || Math.round(img.height * (targetWidth / img.width))
const canvas = document.createElement('canvas')
canvas.width = targetWidth
canvas.height = targetHeight
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
// 转换为ZPL格式
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const { width, height, data } = imageData
const bytesPerRow = Math.ceil(width / 8)
const totalBytes = bytesPerRow * height
let binaryData = ''
for (let y = 0; y < height; y++) {
for (let byteIdx = 0; byteIdx < bytesPerRow; byteIdx++) {
let byteStr = ''
for (let bit = 0; bit < 8; bit++) {
const x = byteIdx * 8 + bit
if (x >= width) {
byteStr += '0'
} else {
const i = (y * width + x) * 4
const r = data[i], g = data[i + 1], b = data[i + 2]
const grayscale = (r + g + b) / 3
byteStr += grayscale < 128 ? '1' : '0'
}
}
const hex = parseInt(byteStr, 2).toString(16).padStart(2, '0').toUpperCase()
binaryData += hex
}
}
return {
zplData: `${totalBytes},${totalBytes},${bytesPerRow},${binaryData}`,
previewUrl: canvas.toDataURL()
}
},
getSerialNumberPreview() {
if (this.element.type !== 'serialNumber') return ''
const prefix = this.element.prefix || ''
const startValue = parseInt(this.element.startValue) || 1
const digits = parseInt(this.element.digits) || 6
const paddedNumber = startValue.toString().padStart(digits, '0')
return `${prefix}${paddedNumber}`
},
// 初始化字体设置
initializeFontSettings() {
if (this.element.type === 'text') {
// 设置默认字体属性
if (!this.element.fontFamily) {
this.$set(this.element, 'fontFamily', 'default')
}
if (!this.element.textAlign) {
this.$set(this.element, 'textAlign', 'left')
}
if (this.element.letterSpacing === undefined) {
this.$set(this.element, 'letterSpacing', 0)
}
if (this.element.fontItalic === undefined) {
this.$set(this.element, 'fontItalic', false)
}
if (this.element.fontUnderline === undefined) {
this.$set(this.element, 'fontUnderline', false)
}
}
},
// 获取字体预览样式
getFontPreviewStyle() {
if (this.element.type !== 'text') return {}
const style = {}
// 字体族
if (this.element.fontFamily && this.element.fontFamily !== 'default') {
style.fontFamily = this.element.fontFamily
}
// 字体大小
if (this.element.fontSize) {
style.fontSize = this.element.fontSize + 'px'
}
// 加粗
if (this.element.bold) {
style.fontWeight = 'bold'
}
// 斜体
if (this.element.fontItalic) {
style.fontStyle = 'italic'
}
// 下划线
if (this.element.fontUnderline) {
style.textDecoration = 'underline'
}
// 文本对齐
if (this.element.textAlign) {
style.textAlign = this.element.textAlign
}
// 字符间距
if (this.element.letterSpacing) {
style.letterSpacing = this.element.letterSpacing + 'px'
}
return style
},
// 加载可用字体
async loadAvailableFonts() {
if (this.fontsLoaded || this.fontLoading) {
return
}
this.fontLoading = true
try {
const response = await availableFont({})
if (response.data.success) {
this.availableFonts = response.data.data || []
this.fontsLoaded = true
console.log('加载字体列表成功,共', this.availableFonts.length, '个字体')
} else {
console.error('加载字体列表失败:', response.data.message)
this.$message.warning('加载字体列表失败,使用默认字体')
this.availableFonts = this.getDefaultFonts()
}
} catch (error) {
console.error('加载字体列表异常:', error)
this.$message.error('加载字体列表异常')
this.availableFonts = this.getDefaultFonts()
} finally {
this.fontLoading = false
}
},
// 获取分类标签
getCategoryLabel(category) {
const labels = {
'system': '系统字体',
'chinese': '中文字体',
'english': '英文字体',
'monospace': '等宽字体',
'decorative': '装饰字体',
'other': '其他字体'
}
return labels[category] || '其他字体'
},
// 获取默认字体列表(备用方案)
getDefaultFonts() {
return [
{ name: '默认字体', value: 'default', category: 'system', description: '系统默认', supported: true },
{ name: '微软雅黑', value: 'Microsoft YaHei', category: 'chinese', description: '中文字体', supported: true },
{ name: '宋体', value: 'SimSun', category: 'chinese', description: '中文字体', supported: true },
{ name: '黑体', value: 'SimHei', category: 'chinese', description: '中文字体', supported: true },
{ name: 'Arial', value: 'Arial', category: 'english', description: '英文字体', supported: true },
{ name: 'Times New Roman', value: 'Times New Roman', category: 'english', description: '英文字体', supported: true },
{ name: 'Courier New', value: 'Courier New', category: 'monospace', description: '等宽字体', supported: true }
]
},
// 字体族变化处理
onFontFamilyChange(value) {
console.log('字体族变化:', value)
console.log('当前元素:', this.element)
// 确保字体值正确设置
this.$set(this.element, 'fontFamily', value)
// 触发父组件更新
this.$emit('font-changed', {
elementId: this.element.id,
fontFamily: value
})
console.log('字体设置完成,当前字体族:', this.element.fontFamily)
},
}
}
</script>
<style scoped>
.property-form {
overflow-y: auto;
}
.property-form::-webkit-scrollbar {
width: 4px;
}
.property-form::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 2px;
}
.property-form::-webkit-scrollbar-thumb {
//background: #409eff;
border-radius: 2px;
}
.form-section {
padding: 10px;
background: #fafafa;
border-radius: 4px;
border: 1px solid #e8e8e8;
}
.input-with-button {
display: flex;
gap: 6px;
align-items: flex-start;
}
.input-with-button .el-input {
flex: 1;
}
.input-with-button .el-button {
font-size: 11px;
padding: 5px 8px;
height: 28px;
}
.form-tip {
font-size: 11px;
color: #909399;
margin-top: 3px;
padding: 3px 6px;
background: #f8f9fa;
border-radius: 3px;
}
.form-tip .tip-warning {
color: #f56c6c;
font-weight: 500;
border-left: 2px solid #f56c6c;
padding-left: 6px;
}
.form-tip .tip-info {
color: #e6a23c;
font-weight: 500;
border-left: 2px solid #e6a23c;
padding-left: 6px;
}
.form-tip .tip-normal {
color: #67c23a;
font-weight: 500;
border-left: 2px solid #67c23a;
padding-left: 6px;
}
.image-upload {
margin-bottom: 8px;
}
.image-upload .el-button {
width: 100%;
height: 30px;
border: 1px dashed #d9d9d9;
border-radius: 4px;
background: #fafafa;
font-size: 12px;
color: black;
}
.image-upload .el-button:hover {
border-color: #409eff;
background: #f0f9ff;
}
.image-preview {
text-align: center;
padding: 8px;
background: #f8f9fa;
border-radius: 4px;
border: 1px solid #e8e8e8;
}
.image-preview img {
max-width: 80px;
max-height: 80px;
border-radius: 4px;
border: 1px solid #ddd;
}
/* Element UI 表单项紧凑化 */
.el-form-item {
margin-bottom: 2px;
}
.el-form-item:last-child {
margin-bottom: 0;
}
.el-form-item__label {
font-size: 12px !important;
padding-bottom: 6px !important;
line-height: 1.2 !important;
color: #606266 !important;
font-weight: 500 !important;
}
.el-input__inner {
height: 32px !important;
font-size: 13px !important;
border-radius: 6px !important;
border: 1px solid #dcdfe6 !important;
padding: 0 12px !important;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
}
.el-input__inner:hover {
border-color: #c0c4cc !important;
}
.el-input__inner:focus {
border-color: #409eff !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
}
.el-input {
width: 100% !important;
}
.el-input .el-input__inner {
text-align: left !important;
height: 32px !important;
padding: 0 35px 0 12px !important;
}
.el-checkbox {
font-size: 12px !important;
height: 20px !important;
line-height: 20px !important;
}
.el-checkbox__label {
font-size: 12px !important;
padding-left: 6px !important;
}
.el-checkbox__inner {
width: 12px !important;
height: 12px !important;
}
.el-checkbox__inner::after {
height: 5px !important;
left: 3px !important;
top: 1px !important;
width: 2px !important;
}
/* 行布局样式 */
.form-row {
display: flex;
gap: 2px;
align-items: flex-start;
margin-bottom: 2px;
}
.form-row:last-child {
margin-bottom: 0;
}
.form-item-half {
flex: 1;
margin-bottom: 0 !important;
}
.form-item-half .el-form-item__label {
margin-bottom: 2px !important;
}
/* 数字输入框统一样式 */
.form-item-half .el-input {
width: 100% !important;
}
.form-item-half .el-input .el-input__inner {
height: 32px !important;
font-size: 13px !important;
padding: 0 35px 0 12px !important;
border: 1px solid #dcdfe6 !important;
border-radius: 6px !important;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
}
.form-item-half .el-input .el-input__inner:hover {
border-color: #c0c4cc !important;
}
.form-item-half .el-input .el-input__inner:focus {
border-color: #409eff !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
}
.form-item-half .el-input .el-input__increase,
.form-item-half .el-input .el-input__decrease {
position: absolute !important;
right: 1px !important;
width: 32px !important;
height: 15px !important;
line-height: 15px !important;
background: #f5f7fa !important;
color: #606266 !important;
cursor: pointer !important;
font-size: 12px !important;
border: none !important;
border-radius: 0 !important;
transition: all 0.2s !important;
}
.form-item-half .el-input .el-input__increase {
top: 1px !important;
border-radius: 0 5px 0 0 !important;
}
.form-item-half .el-input .el-input__decrease {
bottom: 1px !important;
border-radius: 0 0 5px 0 !important;
}
.form-item-half .el-input .el-input__increase:hover,
.form-item-half .el-input .el-input__decrease:hover {
background: #e6a23c !important;
color: #fff !important;
}
.form-item-half .el-input .el-input__increase:active,
.form-item-half .el-input .el-input__decrease:active {
background: #cf9236 !important;
}
/* 复选框在行中的样式 */
.form-item-half .el-checkbox {
display: block !important;
margin-bottom: 1px !important;
margin-right: 0 !important;
padding-top: 5px;
padding-left: 1px;
}
.form-item-half .el-checkbox:last-child {
margin-bottom: 0 !important;
}
/* 单选按钮组样式 */
.form-item-half .el-radio-group {
display: flex !important;
gap: 8px !important;
align-items: center !important;
padding-top: 4px;
}
.form-item-half .el-radio {
margin-right: 0 !important;
font-size: 12px !important;
height: 20px !important;
line-height: 20px !important;
}
.form-item-half .el-radio__label {
font-size: 12px !important;
padding-left: 4px !important;
}
/* 字体样式行布局 */
.font-style-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.font-style-row .inline-checkbox {
margin-right: 0 !important;
}
.spacing-controls {
display: flex;
align-items: center;
gap: 6px;
}
.spacing-label {
font-size: 12px;
color: #606266;
white-space: nowrap;
}
.spacing-input {
width: 80px !important;
}
.spacing-input .el-input__inner {
height: 28px !important;
font-size: 12px !important;
padding: 0 30px 0 8px !important;
}
/* 字体预览样式 */
.font-preview {
background: #f8f9fa;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 8px;
margin-top: 8px;
text-align: center;
min-height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.font-preview-text {
font-size: 16px;
color: #333;
transition: all 0.3s ease;
}
.font-debug-info {
margin-top: 4px;
text-align: center;
font-size: 11px;
line-height: 1.2;
opacity: 0.8;
}
.form-item-half .el-radio__inner {
width: 12px !important;
height: 12px !important;
}
/* 下拉选择框样式 */
.form-item-half .el-select {
width: 100% !important;
}
.form-item-half .el-select .el-input__inner {
height: 32px !important;
font-size: 13px !important;
border: 1px solid #dcdfe6 !important;
border-radius: 6px !important;
padding: 0 30px 0 12px !important;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
}
.form-item-half .el-select .el-input__inner:hover {
border-color: #c0c4cc !important;
}
.form-item-half .el-select .el-input__inner:focus {
border-color: #409eff !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
}
/* 复选框组样式 */
.checkbox-group {
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
}
.checkbox-label {
font-size: 12px !important;
color: #606266;
margin-bottom: 4px !important;
font-weight: 500;
line-height: 1.2 !important;
height: 14px;
padding-bottom: 4px !important;
display: block;
}
.checkbox-group .el-checkbox {
margin-bottom: 2px !important;
align-self: flex-start;
height: 16px !important;
line-height: 16px !important;
display: flex !important;
align-items: center !important;
}
.checkbox-group .el-checkbox:last-child {
margin-bottom: 0 !important;
}
.checkbox-group .el-checkbox .el-checkbox__input {
line-height: 1 !important;
}
.checkbox-group .el-checkbox .el-checkbox__label {
line-height: 1 !important;
padding-left: 4px !important;
}
/* 字体大小行布局 */
.font-size-row {
display: flex;
align-items: center;
gap: 12px;
}
.font-size-input {
width: 120px !important;
flex-shrink: 0;
}
.font-size-input .el-input__inner {
height: 32px !important;
font-size: 13px !important;
border: 1px solid #dcdfe6 !important;
border-radius: 6px !important;
padding: 0 35px 0 12px !important;
background-color: #fff !important;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
}
.font-size-input .el-input__inner:hover {
border-color: #c0c4cc !important;
}
.font-size-input .el-input__inner:focus {
border-color: #409eff !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
}
.font-size-input .el-input__increase,
.font-size-input .el-input__decrease {
position: absolute !important;
right: 1px !important;
width: 32px !important;
height: 15px !important;
line-height: 15px !important;
background: #f5f7fa !important;
color: #606266 !important;
cursor: pointer !important;
font-size: 12px !important;
border: none !important;
border-radius: 0 !important;
transition: all 0.2s !important;
}
.font-size-input .el-input__increase {
top: 1px !important;
border-radius: 0 5px 0 0 !important;
}
.font-size-input .el-input__decrease {
bottom: 1px !important;
border-radius: 0 0 5px 0 !important;
}
.font-size-input .el-input__increase:hover,
.font-size-input .el-input__decrease:hover {
background: #e6a23c !important;
color: #fff !important;
}
.font-size-input .el-input__increase:active,
.font-size-input .el-input__decrease:active {
background: #cf9236 !important;
}
.inline-checkbox {
margin: 0 !important;
height: 28px !important;
line-height: 28px !important;
display: flex !important;
align-items: center !important;
}
.inline-checkbox .el-checkbox__label {
font-size: 12px !important;
padding-left: 6px !important;
line-height: 1 !important;
}
.inline-checkbox .el-checkbox__input {
line-height: 1 !important;
}
/* 统一所有输入框样式 */
.form-section .el-input {
width: 150px !important;
}
.form-section .el-input .el-input__inner {
height: 32px !important;
font-size: 13px !important;
border: 1px solid #dcdfe6 !important;
border-radius: 6px !important;
padding: 0 35px 0 12px !important;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
}
.form-section .el-input .el-input__inner:hover {
border-color: #c0c4cc !important;
}
.form-section .el-input .el-input__inner:focus {
border-color: #409eff !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
}
.form-section .el-input .el-input__increase,
.form-section .el-input .el-input__decrease {
position: absolute !important;
right: 1px !important;
width: 32px !important;
height: 15px !important;
line-height: 15px !important;
background: #f5f7fa !important;
color: #606266 !important;
cursor: pointer !important;
font-size: 12px !important;
border: none !important;
border-radius: 0 !important;
transition: all 0.2s !important;
}
.form-section .el-input .el-input__increase {
top: 1px !important;
border-radius: 0 5px 0 0 !important;
}
.form-section .el-input .el-input__decrease {
bottom: 1px !important;
border-radius: 0 0 5px 0 !important;
}
.form-section .el-input .el-input__increase:hover,
.form-section .el-input .el-input__decrease:hover {
background: #e6a23c !important;
color: #fff !important;
}
.form-section .el-input .el-input__increase:active,
.form-section .el-input .el-input__decrease:active {
background: #cf9236 !important;
}
</style>