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.
 
 
 
 
 

820 lines
21 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="文本内容">
<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="字体大小">
<div class="font-size-row">
<el-input style="margin-right: 36px"
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>
<template v-if="element.newline">
<div class="form-row">
<el-form-item label="文本宽度" 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>
</el-form>
</div>
<!-- 条码/一维码属性 -->
<div v-else-if="['barcode', 'onecode'].includes(element.type)" 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="请输入条码数据" />
<el-button type="primary" size="mini" @click="$emit('data-source', element)">
数据源
</el-button>
</div>
</el-form-item>
<div class="form-row">
<el-form-item label="宽度" class="form-item-half">
<el-input
v-model="element.width"
:min="1"
:max="10"
controls-position="right"
size="mini"
/>
</el-form-item>
<el-form-item label="高度" class="form-item-half">
<el-input
v-model="element.height"
:min="10"
:max="500"
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="请输入二维码数据" />
<el-button type="primary" size="mini" @click="$emit('data-source', element)">
数据源
</el-button>
</div>
</el-form-item>
<el-form-item label="尺寸">
<el-input
v-model="element.height"
:min="1"
:max="10"
controls-position="right"
@change="validateQRSize"
/>
<div class="form-tip">二维码最大尺寸为10</div>
</el-form-item>
</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>
</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.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">
<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">
<el-form-item label="流水号规则" class="form-item-half">
<el-input v-model="element.data" placeholder="请输入流水号规则" @focus="$emit('data-source', element)"/>
</el-form-item>
<el-form-item label="流水号信息" class="form-item-half">
<el-button type="primary" size="mini" @click="serialInfoModal(element)">
查看
</el-button>
</el-form-item>
</div>
</el-form>
</div>
<!-- 标签内容流水号信息 -->
<comShowLabelSerialInfo ref="comShowLabelSerialInfo" @refreshCurrentPageTable="refreshCurrentPageTable" v-drag></comShowLabelSerialInfo>
</div>
</template>
<script>
import comShowLabelSerialInfo from "../com_show_label_serial_info";
export default {
name: 'PropertyForm',
props: {
element: {
type: Object,
required: true
}
},
emits: ['data-source', 'image-upload'],
/*组件*/
components: {
comShowLabelSerialInfo,/*标签内容流水号信息的組件*/
},
watch: {
// 监听元素变化,确保文件输入框状态正确
'element.id'() {
// 当切换到不同元素时,重置文件输入框
this.$nextTick(() => {
if (this.$refs.fileInput) {
this.$refs.fileInput.value = ''
}
})
}
},
methods: {
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() {
if (this.element.height > 10) {
this.$message.warning('二维码最大尺寸为10')
this.element.height = 10
}
},
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 = 200
const scale = targetWidth / img.width
const canvas = document.createElement('canvas')
canvas.width = targetWidth
canvas.height = Math.round(img.height * scale)
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}`
}
}
}
</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;
border-left: 2px solid #409eff;
}
.image-upload {
margin-bottom: 8px;
}
.image-upload .el-button {
width: 100%;
height: 60px;
border: 1px dashed #d9d9d9;
border-radius: 4px;
background: #fafafa;
font-size: 12px;
}
.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;
}
/* 复选框组样式 */
.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>