|
|
<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>
|