2026-10-03 04:35:55 +08:00
< script setup lang = "ts" >
2026-10-20 23:34:11 +08:00
import '@/styles/adminWorkbench.css'
2026-10-09 22:05:51 +08:00
import { computed , nextTick , onBeforeUnmount , reactive , ref , watch } from 'vue'
2026-10-18 16:37:37 +08:00
import { ElMessage , ElMessageBox } from 'element-plus'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { overseasMasterDataPermissions as permission } from '../permissions'
2026-10-03 04:35:55 +08:00
import AppDialog from '@/shared/components/AppDialog.vue'
import DataTable , { type DataTableColumn } from '@/shared/components/DataTable.vue'
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import { ApiResponseError , getApiErrorDetails , type ApiErrorDetails } from '@/shared/api/http'
import { getTripIncoming , startTripIncoming , acceptTripIncoming , bindTripBookingDispatch } from '../api/tripBookings'
2026-10-09 22:05:51 +08:00
import { getInquiryFulfillment , commandInquiryFulfillment } from '../api/capacityOffers'
import { useFleetDraftGuard } from '../useFleetDraftGuard'
2026-10-03 04:35:55 +08:00
import type { FleetTrip } from '../tripTypes'
import type { TripBooking , TripBookingResult , TripIncomingPreview , TripIncomingStart , TripIncomingAccept , TripBookingBind } from '../bookingTypes'
const open = defineModel < boolean >({ default : false })
2026-10-18 16:37:37 +08:00
const props = defineProps < { trip : Pick < FleetTrip , ' tripNo ' | ' recordVersion ' | ' plannedDepartureAt ' > & Partial < Pick < FleetTrip , ' status ' > > ; initialPreview ?: TripIncomingPreview | null ; pool ?: boolean ; arrivalAt ?: string | null ; instructions ?: string | null ; booking : TripBooking | null ; mode : 'incoming' | 'receipt' | 'bind' | 'view' } > ()
2026-10-03 04:35:55 +08:00
const emit = defineEmits < { saved : [ value : TripBookingResult ] } > ()
2026-10-18 16:37:37 +08:00
const access = useAccessPreviewStore ()
2026-10-03 04:35:55 +08:00
const loading = ref ( false ), busy = ref ( false ), error = ref < ApiErrorDetails | null >( null ), preview = ref < TripIncomingPreview | null >( null )
const spaces = reactive < Record < string , number > > ({}), unitPrice = ref < number | null >( null ), dispatchNo = ref ( '' )
2026-10-18 16:37:37 +08:00
const measurements = reactive ({ weightKg : {} as Record < string , number | undefined >, volumeCbm : {} as Record < string , number | undefined > })
2026-10-03 04:35:55 +08:00
const form = reactive ({ estimatedArrivalAt : '' , deliveryInstructions : '' , privateAddress : '' , postalCode : '' , recipientName : '' , recipientPhone : '' })
const pending = ref < { target : string ; body : TripIncomingStart | TripIncomingAccept | TripBookingBind } | null > ( null )
2026-10-22 23:10:09 +08:00
const guard = useFleetDraftGuard ( 'booking-fulfillment' , open , reactive ({ form , spaces , measurements , unitPrice , dispatchNo }), busy )
2026-10-09 22:05:51 +08:00
defineExpose ({ hasPending : computed (() => !! pending . value ) })
2026-10-03 04:35:55 +08:00
let generation = 0
const target = computed (() => ` ${ props . trip . tripNo } : ${ props . booking ? . bookingNo } : ${ props . mode } ` )
2026-10-09 22:05:51 +08:00
const titles = { incoming : '登记外接来货' , receipt : '核对外接实收与运费' , bind : '确认排入本车' , view : '外接来货详情' }
2026-10-03 04:35:55 +08:00
const ready = computed (() => preview . value ? . incoming && [ 'WAIT_CUSTOMER_CONFIRMATION' , 'READY_FOR_DISPATCH' ]. includes ( preview . value . incoming . status ))
2026-10-18 16:37:37 +08:00
const receiptAllowed = computed (() => props . pool || props . trip . status === 'PLANNED' && !! props . booking
&& [ 'CONFIRMED' , 'RECEIVED' ]. includes ( props . booking . status ) && !! props . booking . incomingNo && ! props . booking . dispatchNo && access . can ( permission . tripBookingConfirm ))
2026-10-03 04:35:55 +08:00
const totalSpaces = computed (() => Object . values ( spaces ). reduce (( a , b ) => a + ( b || 0 ), 0 ))
const amount = computed (() => unitPrice . value == null ? null : Math . round ( unitPrice . value * ( props . booking ? . cargo . priceBasis === 'ORDER' ? 1 : totalSpaces . value ) * 100 ) / 100 )
2026-10-09 22:05:51 +08:00
const statuses : Record < string , string > = { WAIT_PDA_RECEIVING : '等待仓库收货' , RECEIVING : '仓库收货中' , WAIT_CUSTOMER_CONFIRMATION : '实收完成,待确认' , READY_FOR_DISPATCH : '已确认实收,可安排排车' , OUTBOUNDED : '已出库' }
2026-10-11 13:07:52 +08:00
const receiptComparison = computed (() => {
const incoming = preview . value ? . incoming
const pallets = incoming ? . pallets || []
2026-10-18 16:37:37 +08:00
const previous = props . booking ? . status === 'RECEIVED' ? props . booking . receipt : null
const forecast = props . booking ? . status === 'CONFIRMED' || previous ? props . booking ? . cargo : null
const measured = ( row : typeof pallets [ number ], key : 'weightKg' | 'volumeCbm' ) => row [ key ] ?? measurements [ key ][ row . warehousePalletId ]
const sum = ( key : 'weightKg' | 'volumeCbm' ) => pallets . length && pallets . every ( row => measured ( row , key ) != null && Number . isFinite ( measured ( row , key )))
? pallets . reduce (( total , row ) => total + measured ( row , key ) ! , 0 ) : null
2026-10-11 13:07:52 +08:00
const values = [
2026-10-18 16:37:37 +08:00
{ label : '箱数' , expected : previous ? . source . receivedPackages ?? forecast ? . packageCount , actual : incoming ? . receivedPackages },
{ label : '实托数' , expected : previous ? previous . source . pallets . length : forecast ? props . booking ? . expectedPalletCount : null , actual : pallets . length ? pallets . length : null },
{ label : '标准托位(待核对)' , expected : previous ? previous . pallets . reduce (( total , row ) => total + row . palletSpaces , 0 ) : forecast ? . palletSpaces , actual : pallets . length ? totalSpaces . value : null },
2026-10-11 13:07:52 +08:00
{ label : '重量(kg) ' , expected : forecast ? . weightKg , actual : sum ( 'weightKg' ) },
{ label : '体积(m³)' , expected : forecast ? . volumeCbm , actual : sum ( 'volumeCbm' ) },
]
return values . map ( row => ({ ... row , difference : row . expected == null || row . actual == null ? null : Number (( row . actual - row . expected ). toFixed ( 3 )) }))
})
2026-10-18 16:37:37 +08:00
const receiptChanged = computed (() => receiptComparison . value . some ( row => row . difference != null && row . difference !== 0 )
|| unitPrice . value !== props . booking ? . cargo . unitPrice
|| !! props . booking ? . receipt && ( props . booking . receipt . source . receiptHash !== preview . value ? . incoming ? . receiptHash
|| props . booking . receipt . pallets . some ( row => spaces [ row . warehousePalletId ] !== row . palletSpaces )))
2026-10-11 13:07:52 +08:00
const quantityText = ( value : number | null | undefined ) => value == null ? '—' : String ( Number ( value . toFixed ( 3 )))
2026-10-10 19:04:09 +08:00
const incomingStatusLabel = computed (() => {
const status = preview . value ? . incoming ? . status
if ( status === 'READY_FOR_DISPATCH' && props . booking ? . status === 'BOUND' ) return '已确认实收,已排入本车'
return statuses [ status || '' ] || '等待读取'
})
2026-10-03 04:35:55 +08:00
const columns = computed < DataTableColumn [] >(() => [{ key : 'palletNo' , prop : 'palletNo' , label : '实收托盘' , minWidth : 180 }, { key : 'packages' , prop : 'packages' , label : '箱数' , width : 95 },
2026-10-18 16:37:37 +08:00
{ key : 'weightKg' , prop : 'weightKg' , label : '实测重量(kg) ' , minWidth : 150 , ...( props . mode === 'receipt' ? { slotName : 'weightKg' , singleLine : false , showOverflowTooltip : false } : {}) }, { key : 'volumeCbm' , prop : 'volumeCbm' , label : '实测体积(m³)' , minWidth : 150 , ...( props . mode === 'receipt' ? { slotName : 'volumeCbm' , singleLine : false , showOverflowTooltip : false } : {}) },
2026-10-11 13:07:52 +08:00
...( props . mode === 'receipt' ? [{ key : 'spaces' , label : '占用标准托位' , slotName : 'spaces' , width : 180 , singleLine : false , showOverflowTooltip : false }] : [])])
2026-10-18 16:37:37 +08:00
function resetMeasurements () {
for ( const field of [ 'weightKg' , 'volumeCbm' ] as const ) Object . keys ( measurements [ field ]). forEach ( key => delete measurements [ field ][ key ])
}
2026-10-03 04:35:55 +08:00
async function load () {
if ( ! props . booking ) return
const current = generation ; loading . value = true ; error . value = null
try {
2026-10-09 22:05:51 +08:00
const value = props . pool ? ( await getInquiryFulfillment ( props . booking . bookingNo )). preview : await getTripIncoming ( props . trip . tripNo , props . booking . bookingNo )
2026-10-03 04:35:55 +08:00
if ( current !== generation ) return
2026-10-18 16:37:37 +08:00
preview . value = value ; resetMeasurements (); Object . keys ( spaces ). forEach ( key => delete spaces [ key ])
2026-10-03 04:35:55 +08:00
for ( const pallet of value . incoming ? . pallets || []) spaces [ pallet . warehousePalletId ] = props . booking . receipt ? . pallets . find ( row => row . warehousePalletId === pallet . warehousePalletId ) ? . palletSpaces || 1
2026-10-09 22:05:51 +08:00
await nextTick (); guard . capture ()
2026-10-03 04:35:55 +08:00
} catch ( cause ) { if ( current === generation ) error . value = getApiErrorDetails ( cause , '来货信息读取失败' ) }
finally { if ( current === generation ) loading . value = false }
}
watch (() => [ open . value , target . value ], () => {
generation ++ ; busy . value = false ; error . value = null ; preview . value = null
if ( ! open . value || ! props . booking ) return
2026-10-09 22:05:51 +08:00
if ( pending . value ) return
2026-10-03 04:35:55 +08:00
unitPrice . value = props . booking . cargo . unitPrice ; dispatchNo . value = props . booking . dispatchNo || ''
2026-10-09 22:05:51 +08:00
Object . assign ( form , { estimatedArrivalAt : props . arrivalAt || '' , deliveryInstructions : props . instructions || props . booking . cargo . remark || '' , privateAddress : '' , postalCode : '' , recipientName : '' , recipientPhone : '' })
if ( props . initialPreview && ( props . mode === 'receipt' || props . mode === 'view' )) {
2026-10-18 16:37:37 +08:00
preview . value = props . initialPreview ; resetMeasurements (); Object . keys ( spaces ). forEach ( key => delete spaces [ key ])
2026-10-09 22:05:51 +08:00
for ( const pallet of props . initialPreview . incoming ? . pallets || []) spaces [ pallet . warehousePalletId ] = props . booking . receipt ? . pallets . find ( row => row . warehousePalletId === pallet . warehousePalletId ) ? . palletSpaces || 1
void nextTick (() => guard . capture ())
} else if ( props . mode === 'receipt' || props . mode === 'view' ) void load ()
else void nextTick (() => guard . capture ())
2026-10-03 04:35:55 +08:00
}, { immediate : true })
async function save () {
if ( busy . value || loading . value || props . mode === 'view' || ! props . booking ) return
2026-10-18 16:37:37 +08:00
if ( props . mode === 'receipt' && ! receiptAllowed . value ) return
2026-10-03 04:35:55 +08:00
const current = generation , booking = props . booking , mode = props . mode
if ( ! pending . value ) {
const versions = { tripRecordVersion : preview . value ? . tripRecordVersion ?? props . trip . recordVersion , recordVersion : preview . value ? . recordVersion ?? booking . recordVersion , idempotencyKey : crypto . randomUUID () }
2026-10-09 22:05:51 +08:00
if ( mode === 'incoming' && ! form . estimatedArrivalAt ) { ElMessage . warning ( '请确认预计到仓时间' ); return }
2026-10-03 04:35:55 +08:00
if ( mode === 'incoming' ) pending . value = { target : target . value , body : { ... versions , ... form , estimatedArrivalAt : form . estimatedArrivalAt || null } }
else if ( mode === 'bind' ) {
2026-10-09 22:05:51 +08:00
if ( ! props . pool && ! dispatchNo . value . trim ()) { ElMessage . warning ( '请输入已预排的客户排车单号' ); return }
2026-10-03 04:35:55 +08:00
pending . value = { target : target . value , body : { ... versions , dispatchNo : dispatchNo . value . trim () } }
} else {
if ( ! ready . value || ! preview . value ? . incoming ? . pallets . length || unitPrice . value == null || totalSpaces . value < 1 ) { ElMessage . warning ( '请等待实际收货完成,并核对托位及约定价格' ); return }
2026-10-18 16:37:37 +08:00
if ( preview . value . incoming . pallets . some ( row => [ 'weightKg' , 'volumeCbm' ]. some ( key => {
const field = key as 'weightKg' | 'volumeCbm' , value = row [ field ] ?? measurements [ field ][ row . warehousePalletId ]
return value == null || ! Number . isFinite ( value ) || value <= 0
}))) { ElMessage . warning ( '拆合托后的来源须重新填写该票货物在每托的实测重量和体积,不沿用旧托测量' ); return }
if ( receiptChanged . value ) {
busy . value = true
2026-10-20 23:34:11 +08:00
try { await ElMessageBox . confirm ( `当前实托、货量、托位或约定价格存在变化,确认按当前完整实收和 ${ amount . value ? . toFixed ( 2 ) } ${ booking . cargo . currency || '' } 核实?容量仍由服务端复核,超容量不会保存。` , '确认实收差异' , { customClass : 'admin-density-confirm admin-workbench is-dialog' , confirmButtonText : '确认当前实收与运费' , cancelButtonText : '返回核对' , type : 'warning' }) }
2026-10-18 16:37:37 +08:00
catch { return }
2026-10-22 23:10:09 +08:00
finally { pending . value = null ; busy . value = false }
2026-10-18 16:37:37 +08:00
if ( current !== generation || ! receiptAllowed . value ) return
}
2026-10-03 04:35:55 +08:00
pending . value = { target : target . value , body : { ... versions , receiptHash : preview . value . incoming . receiptHash , unitPrice : unitPrice . value ,
2026-10-18 16:37:37 +08:00
pallets : preview . value . incoming . pallets . map ( row => ({ warehousePalletId : row . warehousePalletId , palletSpaces : spaces [ row . warehousePalletId ] ! , ...( row . weightKg == null ? { weightKg : measurements . weightKg [ row . warehousePalletId ] ! } : {}), ...( row . volumeCbm == null ? { volumeCbm : measurements . volumeCbm [ row . warehousePalletId ] ! } : {}) })) } }
2026-10-03 04:35:55 +08:00
}
}
busy . value = true ; error . value = null
try {
const body = pending . value . body
2026-10-09 22:05:51 +08:00
const value = props . pool ? await commandInquiryFulfillment ( booking . bookingNo , mode === 'incoming' ? 'incoming' : mode === 'receipt' ? 'receipt-confirmation' : 'arrange-dispatch' , mode === 'bind' ? { tripRecordVersion : ( body as TripBookingBind ). tripRecordVersion , recordVersion : ( body as TripBookingBind ). recordVersion , idempotencyKey : ( body as TripBookingBind ). idempotencyKey } : body ) : mode === 'incoming' ? await startTripIncoming ( props . trip . tripNo , booking . bookingNo , body as TripIncomingStart )
2026-10-03 04:35:55 +08:00
: mode === 'bind' ? await bindTripBookingDispatch ( props . trip . tripNo , booking . bookingNo , body as TripBookingBind )
: await acceptTripIncoming ( props . trip . tripNo , booking . bookingNo , body as TripIncomingAccept )
2026-10-11 15:33:09 +08:00
if ( current === generation ) { pending . value = null ; guard . capture (); emit ( 'saved' , value ); open . value = false ; ElMessage . success ( value . booking . status === 'BOUND' ? '已排入本车,尚未装车或发车' : value . booking . status === 'RECEIVED' ? '实收货量及约定运费已核实' : value . booking . incomingNo ? '已安排来货,等待仓库实际收货' : '履约命令已完成,请查看最新进度' ) }
2026-10-03 04:35:55 +08:00
} catch ( cause ) { if ( current === generation ) { error . value = getApiErrorDetails ( cause , '外接履约操作未完成' ); if ( cause instanceof ApiResponseError && /^A04/ . test ( cause . code )) pending . value = null } }
2026-10-22 23:10:09 +08:00
finally { pending . value = null ; busy . value = false }
2026-10-03 04:35:55 +08:00
}
onBeforeUnmount (() => { generation ++ })
</ script >
< template >
2026-10-22 23:10:09 +08:00
< AppDialog v-model = "open" :before-close="guard.beforeClose" class="admin-density-dialog admin-workbench is-dialog" :title="titles[mode]" width="1080px" :show-confirm="mode !== 'view'" :confirm-text="mode === 'receipt' ? '确认实收与运费' : mode === 'bind' ? '确认排入本车' : '生成来货单'" :confirm-disabled="loading || (mode === 'receipt' && (!receiptAllowed || (!pending && !ready)))" :confirm-loading="busy" @confirm="save" >
2026-10-03 04:35:55 +08:00
< LoadErrorState v-if = "error" title="操作未完成" :message="error.message" @retry="load"><template #details><ApiErrorMeta :details="error" /></ template ></ LoadErrorState >
< div v-if = "booking" v-loading="loading" >
2026-10-22 23:10:09 +08:00
< p >{{ booking . bookingNo }} · {{ booking . customerName }} · {{ booking . destinationLabel }}</ p >
2026-10-03 04:35:55 +08:00
< el-form v-if = "mode === 'incoming'" label-position="top" class="density-form incoming-form" :disabled="busy || !!pending" >
< el-form-item label = "预计到仓时间" >< el-date-picker v-model = "form.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ssZ" /></ el-form-item >
< el-form-item label = "交货说明" >< el-input v-model = "form.deliveryInstructions" placeholder="选填,默认客户自送至本起运仓" /></ el-form-item >
< template v-if = "booking.cargo.destinationKey.startsWith('PRIVATE:')"><el-form-item label="收件地址"><el-input v-model="form.privateAddress" placeholder="填写约定卸货地址" /></el-form-item><el-form-item label="邮编"><el-input v-model="form.postalCode" placeholder="填写收件邮编" /></el-form-item><el-form-item label="收件人"><el-input v-model="form.recipientName" placeholder="填写收件人" /></el-form-item><el-form-item label="收件电话"><el-input v-model="form.recipientPhone" placeholder="填写收件电话" /></ el-form-item ></ template >
</ el-form >
< template v-if = "mode === 'receipt' || mode === 'view'" >
2026-10-18 16:37:37 +08:00
< p v-if = "mode === 'receipt' && !receiptAllowed" role="alert" > 当前车次或预订不能核对实收 , 请刷新并确认车次仍在计划中 、 预订未绑定排车且具有核对实收权限 。 </ p >
2026-10-10 19:04:09 +08:00
< el-button :loading = "loading" : disabled = "busy || !!pending" @click ="load" > 刷新收货进度 </ el -button >< p > 来货单 : {{ preview ? . incoming ? . incomingNo || '—' }} · {{ incomingStatusLabel }}</ p >
2026-10-20 23:34:11 +08:00
< div class = "workbench-table" >< DataTable class = "receipt-pallet-table" : data = "preview?.incoming?.pallets || []" :columns = "columns" row -key =" warehousePalletId " empty -text =" 仓库尚未完成实际打托 " >< template # spaces = "{ row }" >< div class = "receipt-spaces-control" >< el-input-number v-model = "spaces[row.warehousePalletId]" :min="1" :precision="0" :disabled="busy || !!pending" placeholder="实际占用托位" /></div></template><template v-for="field in (['weightKg', 'volumeCbm'] as const)" #[field]="{ row }"><div v-if="row[field] == null" class="receipt-spaces-control"><el-input-number v-model="measurements[field][row.warehousePalletId]" :min="0.001" :max="999999999.999" :precision="3" :disabled="busy || !!pending" :aria-label="`${row.palletNo} ${field === 'weightKg' ? '实测重量' : '实测体积'}`" placeholder="重新实测该票货物" /></ div >< span v-else >{{ row [ field ] }}</ span ></ template ></ DataTable ></ div >
2026-10-18 16:37:37 +08:00
< p v-if = "mode === 'receipt'">托盘组成变化后须填写该票外接货的当前实测重量、体积;未变化托盘只读沿用原收货证据,不填写整托混合货的总量。</p><section v-if="mode === 'receipt'" aria-label="预报与实收对照" >
< p >{{ booking . status === 'RECEIVED' && booking . receipt ? '上次核实与当前实收对照' : '预报与实收对照' }} ; 差异须明确确认 , 容量与费用仍由服务端核对 。 </ p >
< p v-if = "booking.status !== 'CONFIRMED' && !booking.receipt" > 本票已核量 , 当前记录无法还原原始预报 , 预报及差异显示 “—”。 </ p >
< table class = "density-table" >< thead >< tr >< th > 项目 </ th >< th >{{ booking . status === 'RECEIVED' && booking . receipt ? '上次核实' : '承运预报' }}</ th >< th > 当前实收 / 核对值 </ th >< th > 差异 ( 实收减预报 ) </ th ></ tr ></ thead >
2026-10-11 13:07:52 +08:00
< tbody >< tr v-for = "item in receiptComparison" :key="item.label" >< td >{{ item . label }}</ td >< td >{{ quantityText ( item . expected ) }}</ td >< td >{{ quantityText ( item . actual ) }}</ td >< td >{{ item . difference == null ? '—' : item . difference > 0 ? `+ ${ quantityText ( item . difference ) } ` : quantityText ( item . difference ) }}</ td ></ tr ></ tbody >
</ table >
</ section >
2026-10-03 04:35:55 +08:00
< el-form v-if = "mode === 'receipt'" label-position="top" class="density-form incoming-form" :disabled="busy || !!pending"><el-form-item :label="booking.cargo.priceBasis === 'ORDER' ? '确认整单价格' : '确认每托位价格'"><el-input-number v-model="unitPrice" :min="0" :precision="2" placeholder="核对约定价格" /></ el-form-item ></ el-form >
< p v-if = "mode === 'receipt'" > 原约定 {{ booking.agreedAmount ? .toFixed ( 2 ) }} {{ booking.cargo.currency }} ; 本次实收 {{ preview ? .incoming ? .receivedPackages || 0 }} 箱 、 {{ totalSpaces }} 托位 , 确认金额 {{ amount ? .toFixed ( 2 ) || '—' }} {{ booking.cargo.currency }} 。 转派送时按本次确认报价登记应收 。 </ p >
</ template >
2026-10-09 22:05:51 +08:00
< el-form v-if = "mode === 'bind'" label-position="top" class="density-form" :disabled="busy || !!pending"><p v-if="pool">将本票全部实收货物排入原车次。系统复用合格排车单,没有时生成并关联;来源冲突或货量不完整时停止。成功后仍需核对容量并完成审核,不代表已装车或发车。</p><p v-else>先将来货库存预排为客户排车单,再填写排车单号。关联后重新核对整车装载并完成审核。</p><el-form-item v-if="!pool" label="排车单号"><el-input v-model="dispatchNo" placeholder="输入包含本次实收货物的排车单号" /></ el-form-item ></ el-form >
2026-10-03 04:35:55 +08:00
</ div >
</ AppDialog >
</ template >
2026-10-11 13:07:52 +08:00
< style scoped >. incoming - form { display : grid ; grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr )); gap : var ( -- admin - space - 4 ) var ( -- admin - space - 6 )}. incoming - form : deep (. el - date - editor ),. incoming - form : deep (. el - input - number ){ width : 100 % }. receipt - spaces - control { display : flex ; width : 100 % ; min - width : 0 }. receipt - spaces - control : deep (. el - input - number ){ width : 100 % ; min - width : 0 ; max - width : 100 % }</ style >