@@ -8,12 +8,13 @@ import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui' ;
import { DICT _TYPE } from '@vben/constants' ;
import { getDictOptions } from '@vben/hooks' ;
import { IconifyIcon } from '@vben/icons' ;
import {
Alert ,
Button ,
Card ,
FormItem ,
DatePicker ,
Input ,
message ,
Segmented ,
@@ -138,7 +139,14 @@ const [Modal, modalApi] = useVbenModal({
fieldErrors . value = errors ;
rowErrors . value = new Map ( ) ;
if ( errors . size > 0 ) {
message . warning ( '请检查必填项' ) ;
const hasDuplicateContainerName = [ ... errors . values ( ) ] . some ( ( item ) =>
item . containerName ? . startsWith ( '子卷号重复' ) ,
) ;
message . warning (
hasDuplicateContainerName
? '子卷号不能重复,请检查后再提交'
: '请检查标红字段' ,
) ;
return ;
}
@@ -182,8 +190,14 @@ const [Modal, modalApi] = useVbenModal({
< div class = "batch-form" >
< div class = "toolbar" >
< div class = "toolbar-actions" >
< Button type = "primary" @click ="appendRow" > 新增一行 < / Button >
< Button class = "clear-button" @click ="clearRows" > 清空全部 < / Button >
< Button type = "primary" @click ="appendRow" >
< template # icon > < IconifyIcon icon = "ant-design:plus-outlined" / > < / template >
新增一行
< / Button >
< Button class = "clear-button" @click ="clearRows" >
< template # icon > < IconifyIcon icon = "ant-design:clear-outlined" / > < / template >
清空全部
< / Button >
< / div >
< Segmented
v -model :value = "viewMode"
@@ -211,9 +225,9 @@ const [Modal, modalApi] = useVbenModal({
< table class = "batch-table" >
< thead >
< tr >
< th > 序号 < / th > < th > < i > * < / i > 木箱唯一码 < / th > < th > < i > * < / i > 木箱类型 < / th >
< th > 序号 < / th > < th > < i > * < / i > 木箱唯一码 < / th > < th > < i > * < / i > 子卷号 < / th > < th > < i > * < / i > 木箱类型 < / th >
< th > 木箱长 < / th > < th > 木箱宽 < / th > < th > 木箱高 < / th >
< th > < i > * < / i > 保质期 ( 天 ) < / th > < th > < i > * < / i > 入库日期 < / th > < th > < i > * < / i > 子卷号 < / th >
< th > < i > * < / i > 保质期 ( 天 ) < / th > < th > < i > * < / i > 入库日期 < / th >
< th > < i > * < / i > 状态 < / th > < th > 操作 < / th >
< / tr >
< / thead >
@@ -222,15 +236,15 @@ const [Modal, modalApi] = useVbenModal({
< tr >
< td class = "sequence" > { { index + 1 } } < / td >
< td > < Input v -model :value = "row.packageBoxSn" : class = "{ invalid: fieldError(index, 'packageBoxSn') }" placeholder = "请输入木箱唯一码" @ update : value = "editField(index, 'packageBoxSn')" / > < small > { { fieldError ( index , 'packageBoxSn' ) } } < / small > < / td >
< td > < Input v -model :value = "row.containerName" : class = "{ invalid: fieldError(index, 'containerName') }" placeholder = "请输入子卷号" @ update : value = "editField(index, 'containerName')" / > < small > { { fieldError ( index , 'containerName' ) } } < / small > < / td >
< td > < Input :value = "row.boxType" readonly role = "button" tabindex = "0" aria -label = " 选择木箱类型 " : class = "{ invalid: fieldError(index, 'boxType') }" placeholder = "请选择木箱类型" @click ="selectBoxType(row.clientKey)" @keydown.enter ="selectBoxType(row.clientKey)" @keydown.space.prevent ="selectBoxType(row.clientKey)" / > < small > { { fieldError ( index , 'boxType' ) } } < / small > < / td >
< td > < Input v -model :value = "row.boxLength" readonly placeholder = "自动带出" / > < / td >
< td > < Input v -model :value = "row.boxWidth" readonly placeholder = "自动带出" / > < / td >
< td > < Input v -model :value = "row.boxHigh" readonly placeholder = "自动带出" / > < / td >
< td > < Input v -model :value = "row.qualityGuaranPeriod" : class = "{ invalid: fieldError(index, 'qualityGuaranPeriod') }" placeholder = "请输入保质期 " @ update : value = "editField(index, 'qualityGuaranPeriod')" / > < small > { { fieldError ( index , 'qualityGuaranPeriod' ) } } < / small > < / td >
< td > < Input v -model :value = "row.dateOfFgInbound" type = "date " : class = "{ invalid: fieldError(index, 'dateOfFgInbound') }" @ update : value = "editField(index, 'dateOfFgInbound')" / > < small > { { fieldError ( index , 'dateOfFgInbound' ) } } < / small > < / td >
< td > < Input v -model :value = "row.containerName" : class = "{ invalid: fieldError(index, 'containerName') }" placeholder = "请输入子卷号" @ update : value = "editField(index, 'containerName')" / > < small > { { fieldError ( index , 'containerName' ) } } < / small > < / td >
< td > < Input v -model :value = "row.qualityGuaranPeriod" type = "number" : class = "{ invalid: fieldError(index, 'qualityGuaranPeriod') }" placeholder = "例如: 90 " @ update : value = "editField(index, 'qualityGuaranPeriod')" > < template # suffix > < span class = "input-unit" > 天 < / span > < / template > < / Input > < small > { { fieldError ( index , 'qualityGuaranPeriod' ) } } < / small > < / td >
< td > < DatePicker v -model :value = "row.dateOfFgInbound" value -format = " YYYY -MM -DD " format = "YYYY-MM-DD" class = "w-full " : class = "{ invalid: fieldError(index, 'dateOfFgInbound') }" placeholder = "请选择入库日期" @change = "editField(index, 'dateOfFgInbound')" / > < small > { { fieldError ( index , 'dateOfFgInbound' ) } } < / small > < / td >
< td > < Select v -model :value = "row.status" :options = "statusOptions" : class = "{ invalid: fieldError(index, 'status') }" @change ="editField(index, 'status')" / > < small > { { fieldError ( index , 'status' ) } } < / small > < / td >
< td class = "operations" > < Button type = "link" @click ="duplicateRow(index)" > 复制 < / Button > < Button type = "link" danger @click ="removeRow(index)" > 删除< / Button > < / td >
< td class = "operations" > < Button type = "link" @click ="duplicateRow(index)" > < template # icon > < IconifyIcon icon = "ant-design:copy-outlined" / > < / template > 复制 < / Button > < Button type = "link" danger @click ="removeRow(index)" > < template # icon > < IconifyIcon icon = "ant-design:delete-outlined" / > < / template > 删除< / Button > < / td >
< / tr >
< tr v-if = "rowErrors.get(row.clientKey)" class="row-error"><td colspan="11" > 第 {{ index + 1 }} 行 : {{ rowErrors.get ( row.clientKey ) }} < / td > < / tr >
< / template >
@@ -239,21 +253,34 @@ const [Modal, modalApi] = useVbenModal({
< / div >
< div v-else class = "card-list" >
< Card v-for = "(row, index) in rows" :key="row.clientKey" size="small" >
< template # title > 第 { { index + 1 } } 条包装关系 < / template >
< template # extra > < Button type = "link" @click ="duplicateRow(index)" > 复制 < / Button > < Button type = "link" danger @click ="removeRow(index)" > 删除< / Button > < / template >
< Card v-for = "(row, index) in rows" :key="row.clientKey" >
< template # title > < div class = "record-title" > < span class = "record-number" > { { index + 1 } } < / span > 包装关系 { { String ( index + 1 ) . padStart ( 2 , '0' ) } } < / div > < / template >
< template # extra > < Button type = "link" @click ="duplicateRow(index)" > < template # icon > < IconifyIcon icon = "ant-design:copy-outlined" / > < / template > 复制 < / Button > < Button type = "link" danger @click ="removeRow(index)" > < template # icon > < IconifyIcon icon = "ant-design:delete-outlined" / > < / template > 删除< / Button > < / template >
< Alert v-if = "rowErrors.get(row.clientKey)" type="error" show-icon :message="rowErrors.get(row.clientKey)" class="row-alert" / >
< div class = "card-grid " >
< FormItem label = "木箱唯一码" required : validate -statu s =" fieldError ( index , ' packageBoxSn ' ) ? ' error ' : undefined " : help = "fieldError(index, 'packageBoxSn')" > < Input v -model :value = "row.packageBoxSn" @ update : value = "editField(index, 'packageBoxSn')" / > < / FormItem >
< FormItem label = "木箱类型" required : validate -status = " fieldError ( index , ' boxType ' ) ? ' error ' : undefined " : help = "fieldError(index, 'boxType')" > < Input :value = "row.boxType" readonly role = "button" tabindex = "0" aria -label = " 选择木箱类型 " placeholder = "请选择木箱类型" @click ="selectBoxType(row.clientKey)" @keydown.enter ="selectBoxType(row.clientKey)" @keydown.space.prevent ="selectBoxType(row.clientKey)" / > < / FormItem >
< FormItem label = "木箱长" > < Input v -model :value = "row.boxLength" readonly placeholder = "自动带出" / > < / FormItem >
< FormItem label = "木箱宽" > < Input v -model :value = "row.boxWidth" readonly placeholder = "自动带出" / > < / FormItem >
< FormItem label = "木箱高" > < Input v -model :value = "row.boxHigh" readonly placeholder = "自动带出" / > < / FormItem >
< FormItem label = "保质期" required : validate -status = " fieldError ( index , ' qualityGuaranPeriod ' ) ? ' error ' : undefined " : help = "fieldError(index, 'qualityGuaranPeriod')" > < Input v -model :value = "row.qualityGuaranPeriod" @ update : value = "editField(index, 'qualityGuaranPeriod')" / > < / FormItem >
< FormItem label = "入库日期" required : validate -status = " fieldError ( index , ' dateOfFgInbound ' ) ? ' error ' : undefined " : help = "fieldError(index, 'dateOfFgInbound')" > < Input v -model :value = "row.dateOfFgInbound" type = "date" @ update : value = "editField(index, 'dateOfFgInbound')" / > < / FormItem >
< FormItem label = "子卷号" required : validate -status = " fieldError ( index , ' containerName ' ) ? ' error ' : undefined " : help = "fieldError(index, 'containerName')" > < Input v -model :value = "row.containerName" @ update : value = "editField(index, 'containerName')" / > < / FormItem >
< FormItem label = "状态" required : validate -status = " fieldError ( index , ' status ' ) ? ' error ' : undefined " : help = "fieldError(index, 'status')" > < Select v -model :value = "row.status" :options = "statusOptions" @change ="editField(index, 'status')" / > < / FormItem >
< / div >
< section class = "card-section " >
< div clas s ="section-title" > < i > < / i > 识别信息 < / div >
< div class = "section-fields identity-fields" >
< div class = "card-field" : class = "{ 'has-error': fieldError(index, 'packageBoxSn') }" > < label class = "required" > 木箱唯一码 < / label > < Input v -model :value = "row.packageBoxSn" placeholder = "扫描或输入木箱唯一码" @ update : value = "editField(index, 'packageBoxSn')" / > < small > { { fieldError ( index , 'packageBoxSn' ) } } < / small > < / div >
< div class = "card-field" : class = "{ 'has-error': fieldError(index, 'containerName') }" > < label class = "required" > 子卷号 < / label > < Input v -model :value = "row.containerName" placeholder = "扫描或输入子卷号" @ update : value = "editField(index, 'containerName')" / > < small > { { fieldError ( index , 'containerName' ) } } < / small > < / div >
< div class = "card-field" : class = "{ 'has-error': fieldError(index, 'status') }" > < label class = "required" > 状态 < / label > < Select v -model :value = "row.status" class = "w-full" :options = "statusOptions" @change ="editField(index, 'status')" / > < small > { { fieldError ( index , 'status' ) } } < / small > < / div >
< / div >
< / section >
< section class = "card-section" >
< div c lass = "section-title" > < i > < / i > 木箱规格 < / div >
< div class = "section-fields specification-fields" >
< div class = "card-field" : class = "{ 'has-error': fieldError(index, 'boxType') }" > < label class = "required" > 木箱类型 < / label > < Input :value = "row.boxType" readonly role = "button" tabindex = "0" aria -label = " 选择木箱类型 " placeholder = "请选择木箱类型" @click ="selectBoxType(row.clientKey)" @keydown.enter ="selectBoxType(row.clientKey)" @keydown.space.prevent ="selectBoxType(row.clientKey)" / > < small > { { fieldError ( index , 'boxType' ) } } < / small > < / div >
< div class = "card-field" > < label > 木箱长 < / label > < Input v -model :value = "row.boxLength" readonly placeholder = "自动带出" > < template # suffix > < span class = "input-unit" > mm < / span > < / template > < / Input > < small > < / small > < / div >
< div class = "card-field" > < label > 木箱宽 < / label > < Input v -model :value = "row.boxWidth" readonly placeholder = "自动带出" > < template # suffix > < span class = "input-unit" > mm < / span > < / template > < / Input > < small > < / small > < / div >
< div class = "card-field" > < label > 木箱高 < / label > < Input v -model :value = "row.boxHigh" readonly placeholder = "自动带出" > < template # suffix > < span class = "input-unit" > mm < / span > < / template > < / Input > < small > < / small > < / div >
< / div >
< / section >
< section class = "card-section" >
< div class = "section-title" > < i > < / i > 时效信息 < / div >
< div class = "section-fields lifecycle-fields" >
< div class = "card-field" : class = "{ 'has-error': fieldError(index, 'qualityGuaranPeriod') }" > < label class = "required" > 保质期 < / label > < Input v -model :value = "row.qualityGuaranPeriod" type = "number" placeholder = "例如: 90" @ update : value = "editField(index, 'qualityGuaranPeriod')" > < template # suffix > < span class = "input-unit" > 天 < / span > < / template > < / Input > < small > { { fieldError ( index , 'qualityGuaranPeriod' ) } } < / small > < / div >
< div class = "card-field" : class = "{ 'has-error': fieldError(index, 'dateOfFgInbound') }" > < label class = "required" > 入库日期 < / label > < DatePicker v -model :value = "row.dateOfFgInbound" value -format = " YYYY -MM -DD " format = "YYYY-MM-DD" class = "w-full" placeholder = "请选择入库日期" @change ="editField(index, 'dateOfFgInbound')" / > < small > { { fieldError ( index , 'dateOfFgInbound' ) } } < / small > < / div >
< / div >
< / section >
< / Card >
< / div >
< / div >
@@ -377,9 +404,23 @@ const [Modal, modalApi] = useVbenModal({
}
. batch - table : deep ( . ant - input ) ,
. batch - table : deep ( . ant - input - affix - wrapper ) ,
. batch - table : deep ( . ant - picker ) ,
. batch - table : deep ( . ant - select - selector ) {
height : 36 px ;
min - height : 36 px ;
border - radius : 7 px ;
box - sizing : border - box ;
}
. batch - table : deep ( . ant - input - affix - wrapper ) {
padding - top : 0 ;
padding - bottom : 0 ;
}
. batch - table : deep ( . ant - input - affix - wrapper . ant - input ) {
height : auto ;
min - height : 0 ;
}
. batch - table : deep ( . ant - input [ readonly ] ) {
@@ -425,6 +466,10 @@ const [Modal, modalApi] = useVbenModal({
color : # ff4d4f ;
}
. input - unit {
color : hsl ( var ( -- muted - foreground ) ) ;
}
. invalid {
border - color : # ff4d4f ;
}
@@ -445,17 +490,147 @@ const [Modal, modalApi] = useVbenModal({
}
. card - list : deep ( . ant - card ) {
overflow : hidden ;
border - radius : 10 px ;
box - shadow : 0 2 px 8 px rgb ( 23 32 51 / 4 % ) ;
}
. card - grid {
display : grid ;
grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ;
column - gap : 24 px ;
. card - list : deep ( . ant - card - head ) {
min - height : 54 px ;
background : linear - gradient ( 90 deg , # f8faff , # fff ) ;
}
. card - grid : deep ( . ant - input [ readonly ] ) {
. card - list : deep ( . ant - card - body ) {
padding : 4 px 24 px 8 px ;
}
. record - title {
display : flex ;
align - items : center ;
gap : 12 px ;
color : # 172033 ;
font - weight : 600 ;
}
. record - number {
display : inline - flex ;
align - items : center ;
justify - content : center ;
width : 34 px ;
height : 34 px ;
color : # fff ;
font - size : 15 px ;
background : # 326 cff ;
border - radius : 10 px ;
}
. card - section {
display : grid ;
padding : 18 px 0 4 px ;
grid - template - columns : 170 px minmax ( 0 , 1 fr ) ;
gap : 18 px ;
}
. card - section + . card - section {
border - top : 1 px dashed # e3e7ef ;
}
. section - title {
display : flex ;
align - items : flex - start ;
gap : 10 px ;
padding - top : 8 px ;
color : # 576071 ;
font - size : 15 px ;
font - weight : 600 ;
}
. section - title i {
width : 8 px ;
height : 8 px ;
margin - top : 7 px ;
background : # 84 a9ff ;
border - radius : 50 % ;
}
. section - fields {
display : grid ;
gap : 18 px ;
}
. identity - fields {
grid - template - columns : 1.35 fr 1.35 fr 0.9 fr ;
}
. specification - fields {
grid - template - columns : 1.35 fr repeat ( 3 , 0.9 fr ) ;
}
. lifecycle - fields {
max - width : 75 % ;
grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ;
}
. card - field {
display : flex ;
min - width : 0 ;
flex - direction : column ;
}
. card - field label {
height : 24 px ;
margin - bottom : 7 px ;
color : # 596275 ;
line - height : 24 px ;
}
. card - field label . required : : before {
margin - right : 4 px ;
color : # ff4d4f ;
content : '*' ;
}
. card - field small {
display : block ;
height : 20 px ;
padding - top : 3 px ;
color : # ff4d4f ;
line - height : 17 px ;
}
. section - fields : deep ( . ant - input ) ,
. section - fields : deep ( . ant - input - affix - wrapper ) ,
. section - fields : deep ( . ant - picker ) ,
. section - fields : deep ( . ant - select - single . ant - select - selector ) {
height : 40 px ;
min - height : 40 px ;
border - radius : 8 px ;
box - sizing : border - box ;
}
. section - fields : deep ( . ant - input - affix - wrapper ) {
padding - top : 0 ;
padding - bottom : 0 ;
}
. section - fields : deep ( . ant - input - affix - wrapper . ant - input ) {
height : auto ;
min - height : 0 ;
}
. section - fields : deep ( . ant - select - single . ant - select - selection - item ) ,
. section - fields : deep ( . ant - select - single . ant - select - selection - placeholder ) {
line - height : 38 px ;
}
. card - field . has - error : deep ( . ant - input ) ,
. card - field . has - error : deep ( . ant - input - affix - wrapper ) ,
. card - field . has - error : deep ( . ant - picker ) ,
. card - field . has - error : deep ( . ant - select - selector ) {
border - color : # ff4d4f ;
}
. section - fields : deep ( . ant - input [ readonly ] ) {
color : # 667085 ;
background : # f2f4f7 ;
}
@@ -474,7 +649,14 @@ const [Modal, modalApi] = useVbenModal({
text - align : left ;
}
. card - grid {
. card - section {
grid - template - columns : 1 fr ;
}
. identity - fields ,
. specification - fields ,
. lifecycle - fields {
max - width : none ;
grid - template - columns : 1 fr ;
}
}