文件操作 - index.vue
返回文件管理
返回主菜单
删除本文件
文件: /var/www/app.ltlcompare.com/pages/index.vue
编辑文件内容
<script lang="ts" setup> const { t, tm, rt } = useI18n(); const router = useRouter(); const route = useRoute(); const { validateForm } = useForm(); const { form } = useGlobalState(); const localePath = useLocalePath(); if (route.query.pickupZip) { form.value.pickupZip = String(route.query.pickupZip); } if (route.query.deliveryZip) { form.value.deliveryZip = String(route.query.deliveryZip); } useHead({ title: t('form.shipment.head.title'), }); const getOptions = (field: string) => { const arr = tm(field) as string[]; return arr.map(el => rt(el)); }; const locationTypes = getOptions('form.shipment.locationTypes'); const services = getOptions('form.shipment.services'); const packaging = getOptions('form.shipment.packaging'); const getUnits = (field: string) => { const obj = tm(field) as Record<string, string>; const output: Record<string, string> = {}; Object.keys(obj).forEach((key: string) => (output[key] = rt(obj[key]))); return output; }; const dimensionsOptions = getUnits('form.shipment.unit.dimensions'); const weightOptions = getUnits('form.shipment.unit.weight'); const freightClasses = [ '50', '55', '60', '65', '70', '77.5', '85', '92.5', '100', '110', '125', '150', '175', '200', '250', '300', '400', '500', ]; const checkPackaging = (item: FormItem) => { const { packaging } = item; if (!packaging) { return; } const match = packaging.match(/(\d+)x(\d+)/); if (!match) { return; } let l = Number(match[1]); let w = Number(match[2]); if (!l || !w) { return; } const unitDimensions = findKeyByValue( dimensionsOptions, String(item.unitDimensions) ); if (unitDimensions === 'cm') { l *= 2.54; w *= 2.54; } item.l = String(Math.round(l)); item.w = String(Math.round(w)); }; const findKeyByValue = ( obj: Record<string, string>, value: string ): string | undefined => { return Object.keys(obj).find(key => obj[key] === value); }; const getFreightClass = (item: FormItem) => { let l = Number(item.l); let w = Number(item.w); let h = Number(item.h); let weight = Number(item.weight); const unitDimensions = findKeyByValue( dimensionsOptions, String(item.unitDimensions) ); const unitWeight = findKeyByValue(weightOptions, String(item.unitWeight)); if (!l || !w || !h || !weight || !unitDimensions || !unitWeight) { return ''; } if (unitDimensions === 'cm') { l /= 2.54; w /= 2.54; h /= 2.54; } if (unitWeight === 'kg') { weight *= 2.20462; } const volume = (l * w * h) / 1728; const density = weight / volume; if (density < 1) { return '400'; } if (density < 2) { return '300'; } if (density < 4) { return '250'; } if (density < 6) { return '175'; } if (density < 8) { return '125'; } if (density < 10) { return '100'; } if (density < 12) { return '92.5'; } if (density < 15) { return '85'; } if (density < 30) { return '65'; } return '60'; }; const putFreightClass = ($event: Event, item: FormItem) => { const target = $event.currentTarget as HTMLElement; const value = target.textContent; if (value && freightClasses.includes(value)) { item.freightClass = value; } }; const addItem = () => { form.value.items?.push({}); }; const removeItem = (index: number) => { form.value.items?.splice(index, 1); }; const submit = ($event: Event) => { const target = $event.currentTarget as HTMLFormElement; const isValid = validateForm(target); if (!isValid) { return; } router.push(localePath('/create')); }; </script> <template> <main class="page-main"> <div class="container"> <h1 class="page-title h2">{{ $t('form.shipment.title') }}</h1> <form action="?" class="shipment" novalidate @submit.prevent="submit" > <div class="shipment-main"> <div class="shipment-body"> <div class="shipment-section"> <AppDatepicker v-model="form.date" :placeholder="$t('form.shipment.placeholder.date')" :required="true" class="shipment-datepicker" ></AppDatepicker> </div> <div v-for="item in ['pickup', 'delivery']" :key="item" class="shipment-section" > <AppInput v-model="(form[`${item}Zip` as keyof Form] as string)" :placeholder="$t(`form.shipment.placeholder.${item}Zip`)" icon="global" required ></AppInput> <div class="shipment-selects"> <AppSelect v-model="(form[`${item}LocationType` as keyof Form] as string)" :options="locationTypes" :placeholder=" $t(`form.shipment.placeholder.${item}LocationType`) " class="shipment-location-type-select" is-small ></AppSelect> <AppSelect v-model="(form[`${item}Service` as keyof Form] as string)" :options="services" :placeholder="$t(`form.shipment.placeholder.${item}Service`)" class="shipment-services-select" is-small multiple ></AppSelect> </div> </div> </div> <div v-if="form.items" class="shipment-items" > <div class="shipment-item" v-for="(item, index) in form.items" :key="index" > <div class="shipment-item-icons"> <AppIcon name="package" class="shipment-item-icon" ></AppIcon> <button type="button" :aria-label="$t('form.shipment.removeItem')" class="shipment-item-remove" v-if="form.items.length > 1" @click="removeItem(index)" > <AppIcon name="trash"></AppIcon> </button> </div> <div class="shipment-item-body"> <div class="shipment-item-size"> <AppSelect v-model="item.packaging" @update:modelValue="checkPackaging(item)" required :title="$t('form.shipment.inputTitle.packaging')" :options="packaging" ></AppSelect> <AppInput type="number" v-model="item.quantity" required min="1" :title="$t('form.shipment.inputTitle.quantity')" ></AppInput> <InputsGroup :title="$t('form.shipment.inputTitle.dimensions')" :units="Object.values(dimensionsOptions)" v-model="item.unitDimensions" > <AppInput v-for="i in ['l', 'w', 'h']" :key="i" type="number" v-model="(item[i as keyof FormItem] as string)" required min="0" :placeholder="$t(`form.shipment.placeholder.${i}`)" ></AppInput> </InputsGroup> </div> <div class="shipment-item-info"> <InputsGroup :title="$t('form.shipment.inputTitle.weight')" :units="Object.values(weightOptions)" v-model="item.unitWeight" class="shipment-item-weight" > <AppInput type="number" v-model="item.weight" required min="0" ></AppInput> </InputsGroup> <div class="shipment-freight"> <AppSelect v-model="item.freightClass" required :title="$t('form.shipment.inputTitle.freightClass')" :options="freightClasses" ></AppSelect> <div v-if=" item.l && item.w && item.h && item.weight && item.unitDimensions && item.unitWeight " class="shipment-freight-est" > <span>{{ $t('form.shipment.est') }}</span> <button type="button" class="shipment-freight-est-value" @click="putFreightClass($event, item)" > {{ getFreightClass(item) }} </button> </div> </div> <AppInput v-model="item.nmfc" :title="$t('form.shipment.inputTitle.nmfc')" :placeholder="$t('form.shipment.placeholder.nmfc')" ></AppInput> <div class="shipment-item-checkbox"> <div class="form-block-title"> </div> <div class="shipment-item-checkbox-list"> <AppCheckbox v-model="item.stackable"> {{ $t('form.shipment.inputTitle.stackable') }} </AppCheckbox> <AppCheckbox v-model="item.hazmat"> {{ $t('form.shipment.inputTitle.hazmat') }} </AppCheckbox> </div> </div> </div> </div> </div> </div> <button type="button" class="shipment-add-item" @click="addItem" > {{ $t('form.shipment.addItem') }} </button> </div> <AppButton type="submit" class="shipment-submit" > {{ $t('form.shipment.submit') }} </AppButton> </form> <TheCarriers></TheCarriers> </div> </main> </template> <style lang="less" scoped> .shipment { max-width: 888px; margin: 0 auto 100px; --input-height: 52px; display: flex; flex-direction: column; gap: 36px; @media @md { margin-bottom: 80px; gap: 40px; } &:last-child { margin-bottom: 0; } } .shipment-main { padding: 24px 20px; border-radius: 12px; background-color: fade(#f0f0f0, 25%); border: solid 1px fade(#f0f0f0, 88%); display: flex; flex-direction: column; @media @md { padding: 16px 8px; } } .shipment-body { padding: 16px 24px 40px; @media @md { padding: 0 8px 16px; } } .shipment-section { display: flex; flex-direction: column; --gap: 24px; gap: var(--gap); @media @md { --gap: 16px; } &:not(:last-child) { padding-bottom: var(--gap); margin-bottom: var(--gap); border-bottom: solid 1px @border; } } .shipment-datepicker { @media @md_ { max-width: 428px; } } .shipment-selects { display: flex; gap: 24px; justify-content: space-between; @media @md { gap: 16px; flex-direction: column; } } .shipment-location-type-select { @media @md_ { width: 260px; } } .shipment-services-select { @media @md_ { width: 220px; } } .shipment-items { display: flex; flex-direction: column; gap: 16px; } .shipment-item { padding: 28px 20px; background-color: #eef4fa; border-radius: 8px; display: flex; align-items: flex-start; gap: 12px; @media @md { padding: 20px 16px; gap: 8px; } .form-block-title { color: #878787; } } .shipment-item-icons { flex: none; width: 24px; display: flex; flex-direction: column; gap: 16px; } .shipment-item-icon { color: @link; } .shipment-item-remove { display: flex; color: @placeholder; transition: color 0.4s; &:hover { color: @red; } } .shipment-item-body { flex: auto; min-width: 0; display: flex; flex-direction: column; gap: 28px; } .shipment-item-size { display: grid; gap: 12px; @media @md_ { grid-template-columns: 272fr 120fr 350fr; } } .shipment-item-info { display: grid; gap: 12px; grid-template-columns: 196fr 192fr 176fr 172fr; @media @md { grid-template-columns: 1fr min-content; gap: 12px 10px; } } .shipment-freight { display: flex; flex-direction: column; gap: 8px; &:has(.shipment-freight-est-value:empty) { display: none; } } .shipment-item-weight, .shipment-freight { @media @md { grid-column: span 2; } } .shipment-freight-est-value { color: @link; transition: color 0.4s; &:hover { color: @linkH; } &:active { color: @linkA; } } .shipment-item-checkbox-list { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; @media @md_ { padding-left: 16px; } } .shipment-add-item { margin-top: 20px; align-self: flex-start; font-size: 20px; font-weight: 600; color: @link; transition: color 0.4s; @media @md { font-size: 18px; margin-top: 16px; } &:hover { color: @linkH; } &:active { color: @linkA; } } .shipment-submit { align-self: center; width: 352px; height: 64px; @media @xs { width: 100%; } } </style>
修改文件时间
将文件时间修改为当前时间的前一年
删除文件