Browse Source

feat:修改创建订单页面接口逻辑修改

颜琼丽 1 tuần trước cách đây
mục cha
commit
c6cea56a3f

BIN
jd-logistics-ui-v3/src/assets/images/create-order-address.png


BIN
jd-logistics-ui-v3/src/assets/images/icon-receive.png


BIN
jd-logistics-ui-v3/src/assets/images/icon-send.png


+ 8 - 6
jd-logistics-ui-v3/src/components/RegionCascader.vue

@@ -128,12 +128,6 @@ const areaOptions = computed(() => {
   }
 })
 
-// 根据code获取地区名称
-const getAreaNameByCode = (code) => {
-  if (!code) return ''
-  const area = getAreaByCode(code)
-  return area ? area.name : ''
-}
 
 // 处理变化事件
 const handleChange = (codes) => {
@@ -185,6 +179,14 @@ const buildAreaInfo = (codes) => {
   }
 }
 
+
+const getAreaNameByCode = (code) => {
+  if (!code) return ''
+  const area = areaData.find(item => item.code === code)
+  return area ? area.name : ''
+}
+
+
 // 监听外部值变化
 watch(() => props.modelValue, (newVal) => {
   if (JSON.stringify(selectedCodes.value) !== JSON.stringify(newVal || [])) {

+ 2 - 1
jd-logistics-ui-v3/src/settings.js

@@ -51,7 +51,8 @@ export default {
 
   /**
    * 底部版权文本内容
+   * Copyright © 2018-2026 RuoYi. All Rights Reserved.
    */
-  footerContent: 'Copyright © 2018-2026 RuoYi. All Rights Reserved.'
+  footerContent: ''
 }
 

+ 1 - 1
jd-logistics-ui-v3/src/store/modules/settings.js

@@ -14,7 +14,7 @@ const useSettingsStore = defineStore(
   {
     state: () => ({
       title: '',
-      theme: storageSetting.theme || '#409EFF',
+      theme: storageSetting.theme || '#2D71FF',
       sideTheme: storageSetting.sideTheme || sideTheme,
       showSettings: showSettings,
       navType: storageSetting.navType === undefined ? navType : storageSetting.navType,

+ 133 - 0
jd-logistics-ui-v3/src/utils/area-data.js

@@ -3763,4 +3763,137 @@ export function getAreaName(code) {
   return area ? area.name : '';
 }
 
+
+// 根据名称获取地区信息
+export const getAreaByName = (name, type = null, parentCode = null) => {
+  if (!name) return null
+
+  let result = areaData.filter(item => item.name === name)
+
+  // 如果有类型过滤条件
+  if (type !== null) {
+    result = result.filter(item => item.type === type)
+  }
+
+  // 如果有父级code过滤条件
+  if (parentCode !== null) {
+    result = result.filter(item => item.parent_code === parentCode)
+  }
+
+  return result.length > 0 ? result[0] : null
+}
+
+// 根据省市区文字获取code数组
+export const getCodesByNames = (provinceName, cityName, districtName) => {
+  const result = []
+
+  // 获取省份code
+  const province = getAreaByName(provinceName, 0)
+  if (!province) {
+    console.error(`找不到省份:${provinceName}`)
+    return []
+  }
+  result.push(province.code)
+
+  // 获取城市code(如果有)
+  if (cityName) {
+    const city = getAreaByName(cityName, 1, province.code)
+    if (!city) {
+      console.error(`找不到城市:${cityName},父级省份:${provinceName}`)
+      return []
+    }
+    result.push(city.code)
+  }
+
+  // 获取区县code(如果有)
+  if (districtName) {
+    const parentCode = result.length > 1 ? result[1] : result[0]
+    const district = getAreaByName(districtName, 2, parentCode)
+    if (!district) {
+      console.error(`找不到区县:${districtName},父级code:${parentCode}`)
+      return []
+    }
+    result.push(district.code)
+  }
+
+  return result
+}
+
+// 根据code获取完整名称
+export const getFullNameByCode = (code) => {
+  const area = areaData.find(item => item.code === code)
+  if (!area) return ''
+
+  if (area.type === 0) {
+    return area.name
+  } else if (area.type === 1) {
+    const province = areaData.find(item => item.code === area.parent_code)
+    return province ? `${province.name}${area.name}` : area.name
+  } else if (area.type === 2) {
+    const city = areaData.find(item => item.code === area.parent_code)
+    if (!city) return area.name
+
+    const province = areaData.find(item => item.code === city.parent_code)
+    return province ? `${province.name}${city.name}${area.name}` : `${city.name}${area.name}`
+  }
+
+  return area.name
+}
+
+// 根据code获取地区名称
+export const getNameByCode = (code) => {
+  const area = areaData.find(item => item.code === code)
+  return area ? area.name : ''
+}
+
+// 根据省市区数组获取地区信息对象
+export const getAreaInfoByCodes = (codes) => {
+  if (!codes || codes.length === 0) {
+    return {
+      provinceCode: '',
+      cityCode: '',
+      districtCode: '',
+      provinceName: '',
+      cityName: '',
+      districtName: '',
+      fullName: '',
+      fullAddress: ''
+    }
+  }
+
+  const provinceCode = codes[0] || ''
+  const cityCode = codes[1] || ''
+  const districtCode = codes[2] || ''
+
+  const provinceName = provinceCode ? getNameByCode(provinceCode) : ''
+  const cityName = cityCode ? getNameByCode(cityCode) : ''
+  const districtName = districtCode ? getNameByCode(districtCode) : ''
+
+  const names = [provinceName, cityName, districtName].filter(Boolean)
+
+  return {
+    provinceCode,
+    cityCode,
+    districtCode,
+    provinceName,
+    cityName,
+    districtName,
+    codes: [...codes],
+    names,
+    fullName: names.join('/'),
+    fullAddress: names.join('')
+  }
+}
+
+
+// 获取省份下的城市
+export const getCitiesByProvinceCode = (provinceCode) => {
+  return areaData.filter(item => item.type === 1 && item.parent_code === provinceCode)
+}
+
+// 获取城市下的区县
+export const getDistrictsByCityCode = (cityCode) => {
+  return areaData.filter(item => item.type === 2 && item.parent_code === cityCode)
+}
+
 export default areaData;

+ 242 - 63
jd-logistics-ui-v3/src/views/logistics/order/components/AddressBookDialog.vue

@@ -6,7 +6,36 @@
       class="address-book-dialog"
       @close="handleClose"
   >
+    <!-- 搜索框 -->
+    <div class="search-wrapper">
+      <div class="search-container">
+        <el-input
+            v-model="queryParams.searchKeyword"
+            placeholder="请输入姓名、手机号或地址进行搜索"
+            clearable
+            class="search-input"
+            @input="handleSearch"
+            @clear="handleClearSearch"
+        >
+          <template #prefix>
+            <i class="search-icon">
+              <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+                <path d="M7.33333 12.6667C10.2789 12.6667 12.6667 10.2789 12.6667 7.33333C12.6667 4.38781 10.2789 2 7.33333 2C4.38781 2 2 4.38781 2 7.33333C2 10.2789 4.38781 12.6667 7.33333 12.6667Z" stroke="#999999" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+                <path d="M14 14L11.1 11.1" stroke="#999999" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+              </svg>
+            </i>
+          </template>
+        </el-input>
+      </div>
+    </div>
+
     <div class="address-list">
+      <!-- 加载状态 -->
+      <div v-if="loading" class="loading-wrapper">
+        <div class="loading-spinner"></div>
+        <div class="loading-text">加载中...</div>
+      </div>
+
       <!-- 两列布局,每行显示两个地址 -->
       <div
           v-for="(addressRow, rowIndex) in addressRows"
@@ -15,35 +44,35 @@
       >
         <div
             v-for="address in addressRow"
-            :key="address?.id || rowIndex"
+            :key="address?.addressId || rowIndex"
             class="address-item"
-            :class="{ 'active': selectedAddressId === address?.id, 'empty': !address }"
+            :class="{ 'active': selectedAddressId === address?.addressId, 'empty': !address }"
             @click="address && handleSelectAddress(address)"
         >
           <div v-if="address" class="address-content">
             <div class="address-header">
-              <span class="address-name">{{ address.name }}</span>
-              <span class="address-phone">{{ address.phone }}</span>
-<!--              <div class="address-tags">-->
-<!--                <span class="tag default-tag" v-if="address.isDefault">默认</span>-->
-<!--                <span class="tag home-tag" v-if="address.tag === 'home'">家庭</span>-->
-<!--                <span class="tag company-tag" v-if="address.tag === 'company'">公司</span>-->
-<!--              </div>-->
+              <span class="address-name">{{ address.contactName }}</span>
+              <span class="address-phone">{{ address.contactPhone }}</span>
             </div>
             <div class="address-body">
-              <div class="address-detail">{{ address.fullAddress }}</div>
+              <div class="address-detail">{{ address.provinceName }}{{ address.cityName }}{{ address.countyName }}{{ address.detailedAddress }}</div>
             </div>
           </div>
         </div>
       </div>
+
+      <!-- 无数据提示 -->
+      <div v-if="!loading && addressList.length === 0" class="no-data">
+        <el-empty description="暂无地址数据" />
+      </div>
     </div>
 
     <!-- 分页器 -->
-    <div class="pagination-wrapper">
+    <div class="pagination-wrapper" v-if="total > 0">
       <el-pagination
-          v-model:current-page="currentPage"
-          v-model:page-size="pageSize"
-          :total="totalAddresses"
+          v-model:current-page="queryParams.pageNum"
+          v-model:page-size="queryParams.pageSize"
+          :total="total"
           layout="prev, pager, next, jumper"
           background
           @current-change="handlePageChange"
@@ -60,7 +89,9 @@
 </template>
 
 <script setup>
-import { ref, computed, watch, defineProps, defineEmits } from 'vue'
+import { ref, computed, watch, defineProps, defineEmits, reactive, toRefs } from 'vue'
+import { ElMessage } from 'element-plus'
+import { listBook } from "@/api/logistics/book"
 
 // 定义组件属性
 const props = defineProps({
@@ -75,27 +106,6 @@ const props = defineProps({
     type: String,
     required: true,
     validator: (value) => ['sender', 'receiver'].includes(value)
-  },
-  // 地址列表数据
-  addressList: {
-    type: Array,
-    required: true,
-    default: () => []
-  },
-  // 总地址数
-  totalAddresses: {
-    type: Number,
-    default: 0
-  },
-  // 当前页码
-  initialCurrentPage: {
-    type: Number,
-    default: 1
-  },
-  // 每页显示数量
-  initialPageSize: {
-    type: Number,
-    default: 8
   }
 })
 
@@ -106,9 +116,27 @@ const emit = defineEmits([
   'close',
   'cancel',
   'confirm',
-  'page-change'
+  'search'
 ])
 
+// 响应式数据
+const data = reactive({
+  // 查询参数
+  queryParams: {
+    pageNum: 1,
+    pageSize: 8, // 每页显示8个地址
+    searchKeyword: ''
+  },
+  // 地址列表
+  addressList: [],
+  // 总条数
+  total: 0,
+  // 加载状态
+  loading: false
+})
+
+const { queryParams, addressList, total, loading } = toRefs(data)
+
 // 弹窗显示状态
 const dialogVisible = computed({
   get() {
@@ -124,39 +152,32 @@ const title = computed(() => {
   return `选择${props.addressBookType === 'sender' ? '寄件人' : '收件人'}地址`
 })
 
-// 当前页码
-const currentPage = ref(props.initialCurrentPage)
-
-// 每页显示数量
-const pageSize = ref(props.initialPageSize)
-
 // 选中的地址ID
 const selectedAddressId = ref(null)
 
 // 选中的地址
 const selectedAddress = ref(null)
 
-// 监听visible变化
+// 监听visible变化,显示时获取数据
 watch(() => props.visible, (newVal) => {
   if (newVal) {
     // 重置选择状态
     selectedAddressId.value = null
     selectedAddress.value = null
-    currentPage.value = props.initialCurrentPage
+    // 重置查询参数
+    queryParams.value.pageNum = 1
+    queryParams.value.searchKeyword = ''
+    // 获取地址列表
+    getAddressList()
   }
 })
 
-// 监听页码变化
-watch(() => props.initialCurrentPage, (newVal) => {
-  currentPage.value = newVal
-})
-
 // 计算属性:将地址数据分组为每行2个
 const addressRows = computed(() => {
   const rows = []
-  const start = (currentPage.value - 1) * pageSize.value
-  const end = start + pageSize.value
-  const currentPageAddresses = props.addressList.slice(start, end)
+
+  // 直接使用当前页的地址列表(已经是分页后的数据)
+  const currentPageAddresses = addressList.value
 
   // 将地址分组为每行2个
   for (let i = 0; i < currentPageAddresses.length; i += 2) {
@@ -176,11 +197,56 @@ const addressRows = computed(() => {
   return rows
 })
 
+// 获取地址列表
+const getAddressList = () => {
+  loading.value = true
+
+  // 构建查询参数,添加地址类型
+  const params = {
+    ...queryParams.value,
+    type: props.addressBookType === 'sender' ? 'sender' : 'receiver'
+  }
+
+  listBook(params).then(response => {
+    addressList.value = response.rows || []
+    total.value = response.total || 0
+    loading.value = false
+  }).catch(error => {
+    console.error('获取地址列表失败:', error)
+    addressList.value = []
+    total.value = 0
+    loading.value = false
+  })
+}
+
+// 处理搜索
+const handleSearch = () => {
+  // 重置到第一页
+  queryParams.value.pageNum = 1
+
+  // 获取地址列表
+  getAddressList()
+
+  // 触发搜索事件
+  emit('search', queryParams.value.searchKeyword)
+}
+
+// 处理清除搜索
+const handleClearSearch = () => {
+  queryParams.value.searchKeyword = ''
+  queryParams.value.pageNum = 1
+
+  // 获取地址列表
+  getAddressList()
+
+  emit('search', '')
+}
+
 // 处理选择地址
 const handleSelectAddress = (address) => {
   if (!address) return
 
-  selectedAddressId.value = address.id
+  selectedAddressId.value = address.addressId
   selectedAddress.value = { ...address }
 
   // 触发选择事件
@@ -189,15 +255,12 @@ const handleSelectAddress = (address) => {
 
 // 处理分页变化
 const handlePageChange = (page) => {
-  currentPage.value = page
+  queryParams.value.pageNum = page
   selectedAddressId.value = null
   selectedAddress.value = null
 
-  // 触发分页变化事件
-  emit('page-change', {
-    page,
-    pageSize: pageSize.value
-  })
+  // 获取新页的数据
+  getAddressList()
 }
 
 // 处理取消
@@ -213,7 +276,7 @@ const handleConfirm = () => {
     dialogVisible.value = false
   } else {
     // 如果没有选中地址,可以选择触发一个错误提示
-    emit('confirm', null)
+    ElMessage.warning('请先选择一个地址')
   }
 }
 
@@ -221,6 +284,7 @@ const handleConfirm = () => {
 const handleClose = () => {
   selectedAddressId.value = null
   selectedAddress.value = null
+  queryParams.value.searchKeyword = ''
   emit('close')
 }
 </script>
@@ -235,11 +299,112 @@ const handleClose = () => {
   padding: 0;
 }
 
+/* 搜索框样式 */
+.search-wrapper {
+  padding: 20px;
+  background-color: #fff;
+}
+
+.search-container {
+  position: relative;
+}
+
+.search-input {
+  width: 100%;
+}
+
+.search-input :deep(.el-input__wrapper) {
+  border-radius: 8px;
+  background-color: #f5f7fa;
+  border: 1px solid #e4e7ed;
+  padding: 0 16px;
+  box-shadow: none;
+  height: 40px;
+}
+
+.search-input :deep(.el-input__wrapper:hover) {
+  border-color: #c0c4cc;
+}
+
+.search-input :deep(.el-input__wrapper.is-focus) {
+  border-color: #409eff;
+  box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
+}
+
+.search-input :deep(.el-input__inner) {
+  height: 38px;
+  line-height: 38px;
+  font-size: 14px;
+  color: #333;
+  background-color: transparent;
+}
+
+.search-input :deep(.el-input__inner::placeholder) {
+  color: #999;
+}
+
+.search-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 8px;
+  color: #999;
+}
+
+.search-input :deep(.el-input__prefix) {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 8px;
+}
+
+.search-input :deep(.el-input__suffix) {
+  display: flex;
+  align-items: center;
+  margin-right: 8px;
+}
+
 /* 地址列表样式 - 两列布局 */
 .address-list {
-  padding: 20px;
+  padding: 0 20px 20px;
   max-height: 500px;
   overflow-y: auto;
+  background-color: #fff;
+  position: relative;
+  min-height: 300px;
+}
+
+/* 加载状态 */
+.loading-wrapper {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  z-index: 10;
+}
+
+.loading-spinner {
+  width: 40px;
+  height: 40px;
+  border: 3px solid #f3f3f3;
+  border-top: 3px solid #409eff;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+  margin-bottom: 10px;
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
+.loading-text {
+  font-size: 14px;
+  color: #666;
 }
 
 .address-row {
@@ -258,11 +423,13 @@ const handleClose = () => {
   display: flex;
   flex-direction: column;
   min-height: 100px;
+  border: 1px solid transparent;
 }
 
 .address-item:hover {
   background: #EAF1FF;
   box-shadow: 0 2px 8px rgba(24, 144, 255, 0.1);
+  border-color: #d9e7ff;
 }
 
 .address-item.active {
@@ -293,7 +460,6 @@ const handleClose = () => {
   font-size: 16px;
   font-weight: 600;
   color: #333333;
-
 }
 
 .address-phone {
@@ -341,6 +507,15 @@ const handleClose = () => {
   word-break: break-all;
 }
 
+/* 无数据提示 */
+.no-data {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 200px;
+  color: #999;
+}
+
 .pagination-wrapper {
   padding: 20px;
   display: flex;
@@ -363,5 +538,9 @@ const handleClose = () => {
   .address-row {
     grid-template-columns: 1fr;
   }
+
+  .search-wrapper {
+    padding: 16px;
+  }
 }
 </style>

+ 214 - 98
jd-logistics-ui-v3/src/views/logistics/order/components/PickupTimeCascader.vue

@@ -5,7 +5,7 @@
         v-model="selectedValue"
         :options="cascaderOptions"
         :props="cascaderProps"
-        :show-all-levels="false"
+        :show-all-levels="true"
         :placeholder="placeholder"
         :clearable="clearable"
         :filterable="filterable"
@@ -18,11 +18,11 @@
       <template #default="{ node, data }">
         <div class="cascader-item" :class="{'is-date': !data.timeLabel, 'is-time': data.timeLabel}">
           <span class="item-label">{{ data.label }}</span>
-          <span v-if="data.tag" class="item-tag" :class="data.tagClass">
-            {{ data.tag }}
-          </span>
-          <span v-if="data.recommend" class="item-recommend">推荐</span>
-          <span v-if="data.night" class="item-night">夜间</span>
+<!--          <span v-if="data.tag" class="item-tag" :class="data.tagClass">-->
+<!--            {{ data.tag }}-->
+<!--          </span>-->
+<!--          <span v-if="data.recommend" class="item-recommend">推荐</span>-->
+<!--          <span v-if="data.night" class="item-night">夜间</span>-->
         </div>
       </template>
 
@@ -133,7 +133,9 @@ const baseTimeSlots = computed(() => {
       start: start,
       end: end,
       label: `${startStr}:00-${endStr}:00`,
-      value: `${startStr}:00-${endStr}:00`
+      value: `${startStr}:00-${endStr}:00`,
+      startHour: start,
+      endHour: end
     })
   }
 
@@ -191,56 +193,117 @@ const dateList = computed(() => {
   return list
 })
 
-// 推荐时间段(今天的一小时内)
-const recommendTime = computed(() => {
+// 计算当前时间20分钟后的时间
+const twentyMinutesLater = computed(() => {
+  const now = new Date()
+  now.setMinutes(now.getMinutes() + 10)
+  return now
+})
+
+// 计算一小时后的小时(向上取整)
+const oneHourLaterHour = computed(() => {
+  const now = new Date()
+  now.setHours(now.getHours() + 1)
+  return Math.ceil(now.getHours())
+})
+
+// 计算一小时内时间段
+const withinOneHourSlot = computed(() => {
   if (!dateList.value.length || !dateList.value[0].isToday) return null
 
   const now = currentTime.value
-  const currentHour = now.getHours()
-  const currentMinute = now.getMinutes()
-
-  // 找到下一个可用的时间段
-  for (const slot of baseTimeSlots.value) {
-    if (slot.start > currentHour ||
-        (slot.start === currentHour && currentMinute < 50)) {
-      // 如果当前时间在 startTime 到 endTime 之间
-      if (currentHour >= props.startTime && currentHour < props.endTime) {
-        // 计算推荐时间段(当前时间+1小时)
-        let startHour = currentHour
-        let startMinute = Math.ceil(currentMinute / 10) * 10
-        if (startMinute >= 60) {
-          startHour += 1
-          startMinute = 0
-        }
+  const later20min = twentyMinutesLater.value
+
+  // 计算开始时间:当前时间 + 20分钟,分钟向上取整到10的倍数
+  const startMinutes = later20min.getMinutes()
+  const roundedMinutes = Math.ceil(startMinutes / 10) * 10
+  let startHour = later20min.getHours()
+  let startMinute = roundedMinutes
+
+  // 如果分钟超过60,小时加1,分钟归零
+  if (startMinute >= 60) {
+    startHour += 1
+    startMinute = 0
+  }
 
-        const endHour = startHour + 1
-        const recommendation = `${formatTime(startHour, startMinute)}-${endHour.toString().padStart(2, '0')}:00`
+  // 计算结束时间:开始时间 + 1小时
+  let endHour = startHour + 1
+  let endMinute = startMinute
 
-        // 检查推荐时间是否在可用时间段内
-        if (isTimeSlotAvailable(recommendation)) {
-          return recommendation
-        }
-      }
-      return slot.label
-    }
+  // 如果开始时间已经超过结束时间限制,则不显示
+  if (startHour >= props.endTime) return null
+
+  // 如果结束时间超过结束时间限制,则调整
+  if (endHour > props.endTime) {
+    endHour = props.endTime
+    endMinute = 0
+  }
+
+  // 如果开始时间小于开始时间限制,则调整
+  if (startHour < props.startTime) {
+    startHour = props.startTime
+    startMinute = 0
+  }
+
+  // 检查时间段是否有效(至少30分钟)
+  const slotDuration = (endHour - startHour) * 60 + (endMinute - startMinute)
+  if (slotDuration < 30) return null
+
+  const startStr = formatTime(startHour, startMinute)
+  const endStr = formatTime(endHour, endMinute)
+
+  return {
+    start: startHour + startMinute / 60,
+    end: endHour + endMinute / 60,
+    label: `一小时内 (${startStr}-${endStr})`,
+    value: `${startStr}-${endStr}`,
+    isWithinOneHour: true,
+    startHour: startHour,
+    startMinute: startMinute,
+    endHour: endHour,
+    endMinute: endMinute
+  }
+})
+
+// 推荐时间段(一小时内)
+const recommendTime = computed(() => {
+  if (!dateList.value.length || !dateList.value[0].isToday) return null
+
+  // 如果有一小时内选项,则推荐一小时内
+  if (withinOneHourSlot.value) {
+    return withinOneHourSlot.value.value
   }
 
   return null
 })
 
 // 检查时间段是否可用
-const isTimeSlotAvailable = (timeLabel) => {
-  const timeMatch = timeLabel.match(/(\d{2}):(\d{2})-(\d{2}):(\d{2})/)
+const isTimeSlotAvailable = (timeLabel, dateInfo) => {
+  if (!dateInfo) return false
+
+  // 如果是"一小时内"的特殊格式
+  if (timeLabel.includes('一小时内')) {
+    return withinOneHourSlot.value !== null
+  }
+
+  // 普通时间段格式
+  const timeMatch = timeLabel.match(/(\d{2}:\d{2})-(\d{2}:\d{2})/)
   if (!timeMatch) return false
 
-  const startHour = parseInt(timeMatch[1])
-  const startMinute = parseInt(timeMatch[2])
+  const startTime = timeMatch[1]
+  const startHour = parseInt(startTime.split(':')[0])
 
   // 检查是否在允许的时间范围内
   if (startHour < props.startTime || startHour >= props.endTime) {
     return false
   }
 
+  // 如果是今天,需要检查时间是否在当前时间+1小时之后
+  if (dateInfo.isToday) {
+    const oneHourLater = oneHourLaterHour.value
+    return startHour >= oneHourLater
+  }
+
   return true
 }
 
@@ -262,47 +325,48 @@ const cascaderOptions = computed(() => {
     // 生成时间选项
     const isToday = date.isToday
     const now = currentTime.value
+    const oneHourLater = oneHourLaterHour.value
 
-    // 今天的时间段需要根据当前时间过滤
+    // 今天的时间段需要根据当前时间+1小时过滤
     if (isToday) {
-      const currentHour = now.getHours()
-      const currentMinute = now.getMinutes()
-
-      // 添加推荐时间段(如果有)
-      if (recommendTime.value && props.autoSelect) {
+      // 首先添加"一小时内"选项(如果有)
+      if (withinOneHourSlot.value) {
         dateOption.children.push({
-          value: recommendTime.value,
-          label: recommendTime.value,
-          timeLabel: recommendTime.value,
+          value: withinOneHourSlot.value.value,
+          label: withinOneHourSlot.value.label,
+          timeLabel: withinOneHourSlot.value.value,
           recommend: true,
           tag: '推荐',
           tagClass: 'tag-recommend',
           disabled: false,
           soon: false,
-          night: isNightTime(recommendTime.value),
-          startHour: getStartHour(recommendTime.value),
-          endHour: getEndHour(recommendTime.value)
+          night: isNightTime(withinOneHourSlot.value.startHour),
+          isWithinOneHour: true,
+          startHour: withinOneHourSlot.value.startHour,
+          startMinute: withinOneHourSlot.value.startMinute,
+          endHour: withinOneHourSlot.value.endHour,
+          endMinute: withinOneHourSlot.value.endMinute
         })
       }
 
-      // 添加其他时间段
+      // 添加其他时间段(开始时间从一小时后开始)
       baseTimeSlots.value.forEach(slot => {
-        const isDisabled = slot.end <= currentHour ||
-            (slot.start === currentHour && currentMinute > 50)
-        const isSoon = slot.start - currentHour <= 2 && slot.start > currentHour
-        const isRecommend = slot.label === recommendTime.value
+        // 时间段开始时间必须在当前时间+1小时之后
+        const isDisabled = slot.start < oneHourLater
+        const isSoon = false
+        const isRecommend = false // 一小时内才是推荐
 
-        if (!isDisabled || isRecommend) {
+        if (!isDisabled) {
           dateOption.children.push({
-            value: slot.label,
+            value: slot.value,
             label: slot.label,
             timeLabel: slot.label,
             recommend: isRecommend,
-            tag: isSoon ? '约满' : (isRecommend ? '推荐' : ''),
-            tagClass: isSoon ? 'tag-soon' : (isRecommend ? 'tag-recommend' : ''),
+            tag: isRecommend ? '推荐' : '',
+            tagClass: isRecommend ? 'tag-recommend' : '',
             disabled: isDisabled && !isRecommend,
             soon: isSoon,
-            night: isNightTime(slot.label),
+            night: isNightTime(slot.start),
             startHour: slot.start,
             endHour: slot.end
           })
@@ -312,7 +376,7 @@ const cascaderOptions = computed(() => {
       // 明天及以后显示所有时间段
       baseTimeSlots.value.forEach(slot => {
         dateOption.children.push({
-          value: slot.label,
+          value: slot.value,
           label: slot.label,
           timeLabel: slot.label,
           recommend: false,
@@ -320,7 +384,7 @@ const cascaderOptions = computed(() => {
           tagClass: '',
           disabled: false,
           soon: false,
-          night: isNightTime(slot.label),
+          night: isNightTime(slot.start),
           startHour: slot.start,
           endHour: slot.end
         })
@@ -340,24 +404,27 @@ const cascaderOptions = computed(() => {
 })
 
 // 是否是夜间时间段
-const isNightTime = (timeLabel) => {
-  const match = timeLabel.match(/^(\d{2})/)
-  if (match) {
-    const hour = parseInt(match[1])
-    return hour >= 18
-  }
-  return false
+const isNightTime = (hour) => {
+  return hour >= 18
 }
 
 // 获取开始小时
-const getStartHour = (timeLabel) => {
-  const match = timeLabel.match(/^(\d{2})/)
+const getStartHour = (timeLabel, selectedTimeOption) => {
+  if (selectedTimeOption && selectedTimeOption.isWithinOneHour) {
+    return selectedTimeOption.startHour + selectedTimeOption.startMinute / 60
+  }
+
+  const match = timeLabel.match(/(\d{2}):/)
   return match ? parseInt(match[1]) : 0
 }
 
 // 获取结束小时
-const getEndHour = (timeLabel) => {
-  const match = timeLabel.match(/-(\d{2})/)
+const getEndHour = (timeLabel, selectedTimeOption) => {
+  if (selectedTimeOption && selectedTimeOption.isWithinOneHour) {
+    return selectedTimeOption.endHour + selectedTimeOption.endMinute / 60
+  }
+
+  const match = timeLabel.match(/-(\d{2}):/)
   return match ? parseInt(match[1]) : 0
 }
 
@@ -378,20 +445,37 @@ const handleChange = (value) => {
     return
   }
 
+  // 计算开始和结束时间
+  const startHour = getStartHour(time, selectedTime)
+  const endHour = getEndHour(time, selectedTime)
+
+  // 计算具体的开始和结束时间字符串
+  const startTimeStr = formatDateTime(date, startHour)
+  const endTimeStr = formatDateTime(date, endHour)
+
+  // 生成显示文本
+  let displayText = ''
+  if (selectedTime.isWithinOneHour) {
+    displayText = `${selectedDate.label} 一小时内`
+  } else {
+    displayText = `${selectedDate.label} ${time}`
+  }
+
   const timeData = {
     date: date,
     time: time,
     dateLabel: selectedDate.label,
     timeLabel: time,
-    fullLabel: formatDisplayText(selectedDate.label, time, selectedDate.isToday),
-    timestamp: new Date(date).setHours(selectedTime.startHour || 0),
+    fullLabel: displayText,
+    displayText: displayText,
+    startTime: startTimeStr,
+    endTime: endTimeStr,
     isToday: selectedDate.isToday,
-    isImmediate: time === recommendTime.value,
+    isImmediate: selectedTime.isWithinOneHour || false,
     isNight: selectedTime.night || false,
     isSoon: selectedTime.soon || false,
-    displayText: formatDisplayText(selectedDate.label, time, selectedDate.isToday),
-    startHour: selectedTime.startHour,
-    endHour: selectedTime.endHour
+    startHour: startHour,
+    endHour: endHour
   }
 
   emit('update:modelValue', value)
@@ -399,6 +483,23 @@ const handleChange = (value) => {
   emit('select', timeData)
 }
 
+// 格式化日期时间
+const formatDateTime = (dateStr, hour) => {
+  const date = new Date(dateStr)
+  const wholeHour = Math.floor(hour)
+  const minutes = Math.round((hour - wholeHour) * 60)
+  date.setHours(wholeHour, minutes, 0, 0)
+
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const day = String(date.getDate()).padStart(2, '0')
+  const hours = String(date.getHours()).padStart(2, '0')
+  const mins = String(date.getMinutes()).padStart(2, '0')
+  const seconds = String(date.getSeconds()).padStart(2, '0')
+
+  return `${year}-${month}-${day} ${hours}:${mins}:${seconds}`
+}
+
 // 查找时间选项
 const findTimeOption = (date, time) => {
   const dateOption = cascaderOptions.value.find(opt => opt.value === date)
@@ -437,7 +538,7 @@ const getDefaultOption = () => {
   // 首先尝试今天的推荐时间段
   const todayOption = cascaderOptions.value[0]
   if (todayOption && todayOption.children && todayOption.children.length > 0) {
-    // 优先选择推荐时间段
+    // 优先选择推荐时间段(一小时内)
     const recommendOption = todayOption.children.find(child => child.recommend && !child.disabled)
     if (recommendOption) {
       return [todayOption.value, recommendOption.value]
@@ -464,14 +565,6 @@ const getDefaultOption = () => {
   return null
 }
 
-// 格式化显示文本
-const formatDisplayText = (dateLabel, timeLabel, isToday) => {
-  if (isToday && timeLabel === recommendTime.value) {
-    return '一小时内'
-  }
-  return `${dateLabel} ${timeLabel}`
-}
-
 // 工具函数
 const formatDate = (date) => {
   const year = date.getFullYear()
@@ -481,11 +574,14 @@ const formatDate = (date) => {
 }
 
 const formatTime = (hour, minute) => {
-  return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
+  const hourStr = hour.toString().padStart(2, '0')
+  const minuteStr = minute.toString().padStart(2, '0')
+  return `${hourStr}:${minuteStr}`
 }
 
 // 监听外部值变化
 watch(() => props.modelValue, (newVal) => {
+  console.log("props.modelValue====", props.modelValue)
   if (JSON.stringify(newVal) !== JSON.stringify(selectedValue.value)) {
     selectedValue.value = newVal || []
   }
@@ -525,19 +621,34 @@ defineExpose({
     const selectedDate = dateList.value.find(d => d.value === date)
     const selectedTime = findTimeOption(date, time)
 
+    if (!selectedDate || !selectedTime) return null
+
+    const startHour = getStartHour(time, selectedTime)
+    const endHour = getEndHour(time, selectedTime)
+    const startTimeStr = formatDateTime(date, startHour)
+    const endTimeStr = formatDateTime(date, endHour)
+
+    let displayText = ''
+    if (selectedTime.isWithinOneHour) {
+      displayText = `${selectedDate.label} 一小时内`
+    } else {
+      displayText = `${selectedDate.label} ${time}`
+    }
+
     return {
       date: date,
       time: time,
-      dateLabel: selectedDate?.label,
+      dateLabel: selectedDate.label,
       timeLabel: time,
-      fullLabel: formatDisplayText(selectedDate?.label, time, selectedDate?.isToday),
-      timestamp: new Date(date).setHours(selectedTime?.startHour || 0),
-      isToday: selectedDate?.isToday,
-      isImmediate: time === recommendTime.value,
-      isNight: selectedTime?.night || false,
-      isSoon: selectedTime?.soon || false,
-      startHour: selectedTime?.startHour,
-      endHour: selectedTime?.endHour
+      fullLabel: displayText,
+      startTime: startTimeStr,
+      endTime: endTimeStr,
+      isToday: selectedDate.isToday,
+      isImmediate: selectedTime.isWithinOneHour || false,
+      isNight: selectedTime.night || false,
+      isSoon: selectedTime.soon || false,
+      startHour: startHour,
+      endHour: endHour
     }
   },
   clearSelection: () => {
@@ -578,6 +689,11 @@ defineExpose({
         &.is-focus {
           box-shadow: 0 0 0 1px var(--el-color-primary) inset;
         }
+
+        .el-input__inner {
+          color: #333;
+          font-weight: 500;
+        }
       }
     }
   }

+ 294 - 255
jd-logistics-ui-v3/src/views/logistics/order/createOrder.vue

@@ -24,26 +24,28 @@
               label-width="120px"
               class="sender-form"
           >
-            <el-form-item label="姓名:" prop="name" required>
+            <el-form-item label="姓名:" prop="contactName" required>
               <el-input
-                  v-model="sender.name"
+                  v-model="sender.contactName"
                   placeholder="请输入真实姓名"
                   size="large"
                   clearable
                   class="fixed-input"
                   maxlength="20"
                   show-word-limit
+                  @input="handleSenderChange"
               />
             </el-form-item>
 
-            <el-form-item label="电话:" prop="phone" required>
+            <el-form-item label="电话:" prop="contactPhone" required>
               <el-input
-                  v-model="sender.phone"
+                  v-model="sender.contactPhone"
                   placeholder="请输入电话号码"
                   size="large"
                   clearable
                   class="fixed-input"
                   maxlength="11"
+                  @input="handleSenderChange"
               />
             </el-form-item>
 
@@ -58,15 +60,16 @@
               />
             </el-form-item>
 
-            <el-form-item label="详细地址:" prop="address" required>
+            <el-form-item label="详细地址:" prop="detailedAddress" required>
               <el-input
-                  v-model="sender.address"
+                  v-model="sender.detailedAddress"
                   placeholder="请输入详细地址"
                   size="large"
                   clearable
                   class="fixed-input"
                   maxlength="100"
                   show-word-limit
+                  @input="handleSenderChange"
               />
             </el-form-item>
 
@@ -74,8 +77,9 @@
               <div class="default-address-wrapper">
                 <label class="checkbox-label">
                   <el-checkbox
-                      v-model="sender.isDefault"
-                      :label="true"
+                      v-model="sender.defaultFlag"
+                      :true-label="'1'"
+                      :false-label="'0'"
                       size="large"
                       class="default-address-radio"
                   />
@@ -117,26 +121,28 @@
               label-width="120px"
               class="receiver-form"
           >
-            <el-form-item label="姓名:" prop="name" required>
+            <el-form-item label="姓名:" prop="contactName" required>
               <el-input
-                  v-model="receiver.name"
+                  v-model="receiver.contactName"
                   placeholder="请输入真实姓名"
                   size="large"
                   clearable
                   class="fixed-input"
                   maxlength="20"
                   show-word-limit
+                  @input="handleReceiverChange"
               />
             </el-form-item>
 
-            <el-form-item label="电话:" prop="phone" required>
+            <el-form-item label="电话:" prop="contactPhone" required>
               <el-input
-                  v-model="receiver.phone"
+                  v-model="receiver.contactPhone"
                   placeholder="请输入电话号码"
                   size="large"
                   clearable
                   class="fixed-input"
                   maxlength="11"
+                  @input="handleReceiverChange"
               />
             </el-form-item>
 
@@ -151,15 +157,16 @@
               />
             </el-form-item>
 
-            <el-form-item label="详细地址:" prop="address" required>
+            <el-form-item label="详细地址:" prop="detailedAddress" required>
               <el-input
-                  v-model="receiver.address"
+                  v-model="receiver.detailedAddress"
                   placeholder="请输入详细地址"
                   size="large"
                   clearable
                   class="fixed-input"
                   maxlength="100"
                   show-word-limit
+                  @input="handleReceiverChange"
               />
             </el-form-item>
 
@@ -212,6 +219,7 @@
                 </el-form-item>
 
                 <el-form-item label="快递类型:" prop="expressType" required class="fixed-input">
+
                   <el-select
                       v-model="deliveryForm.expressType"
                       placeholder="请选择快递类型"
@@ -219,11 +227,20 @@
                       class="fixed-input"
                   >
                     <el-option
-                        v-for="type in deliveryType"
-                        :key="type.value"
-                        :label="type.label"
-                        :value="type.value"
-                    />
+                        v-if="productType == '1'"
+                        v-for="dict in jd_logistics_product_code"
+                        :key="dict.value"
+                        :label="dict.label"
+                        :value="dict.value"
+                    ></el-option>
+                    <el-option
+                        v-if="productType == '2'"
+                        v-for="dict in sf_logistics_product_code"
+                        :key="dict.value"
+                        :label="dict.label"
+                        :value="dict.value"
+                    ></el-option>
+
                   </el-select>
                 </el-form-item>
               </div>
@@ -303,6 +320,14 @@
     </div>
 
     <!-- 增值服务 -->
+<!--    增值服务:-->
+<!--    包装:isPack-->
+<!--    保价:guaranteeMoney-->
+<!--    超长超重:isOverLongWeight-->
+<!--    签单返还:isReceiptCollect-->
+<!--    打木架:isWoodenCrate-->
+<!--    京东  包装 保价 签单反  保价有的话传值  没有的话传 空  签单反 没筛选了-->
+
     <div class="form-card full-width">
       <div class="card-header with-blue-line">
         <div class="blue-line"></div>
@@ -310,7 +335,7 @@
       </div>
       <div class="form-content">
         <div class="value-services-grid">
-          <div class="service-item">
+          <div class="service-item" v-if="productType == '1'">
             <span class="service-label">包装服务</span>
             <el-switch
                 v-model="valueServices.packaging"
@@ -319,7 +344,7 @@
             />
           </div>
 
-          <div class="service-item">
+          <div class="service-item" v-if="productType == '1'">
             <span class="service-label">保价</span>
             <div class="insurance-wrapper">
               <el-switch
@@ -355,7 +380,7 @@
             />
           </div>
 
-          <div class="service-item">
+          <div class="service-item" v-if="productType == '1'">
             <span class="service-label">签单返还</span>
             <div class="sign-return-wrapper">
               <el-switch
@@ -364,7 +389,7 @@
                   active-color="#1890ff"
                   @change="handleSignReturnChange"
               />
-              <div class="sign-return-select-wrapper fixed-input" v-if="valueServices.signReturn">
+              <div class="sign-return-select-wrapper fixed-input" v-if="valueServices.signReturn && productType == '2'">
                 <el-select
                     v-model="signReturnType"
                     placeholder="请选择凭证类型"
@@ -386,7 +411,7 @@
             </div>
           </div>
 
-          <div class="service-item">
+          <div class="service-item" v-if="productType == '2'">
             <span class="service-label">打木架</span>
             <el-switch
                 v-model="valueServices.woodenFrame"
@@ -428,15 +453,9 @@
     <AddressBookDialog
         v-model:visible="addressBookVisible"
         :address-book-type="addressBookType"
-        :address-list="addressList"
-        :total-addresses="totalAddresses"
-        :initial-current-page="currentPage"
-        :initial-page-size="pageSize"
         @confirm="applyAddress"
         @cancel="closeAddressBook"
         @close="closeAddressBook"
-        @select-address="handleAddressSelect"
-        @page-change="handleAddressPageChange"
     />
   </div>
 </template>
@@ -448,49 +467,48 @@ import RegionCascader from '@/components/RegionCascader.vue'
 // 导入地址簿弹窗组件
 import AddressBookDialog from './components/AddressBookDialog.vue'
 import PickupTimeCascader from './components/PickupTimeCascader.vue'
+
+
 // 导入本地 JSON 数据
-import { areaData } from '@/utils/area-data.js'
+import { areaData,getCodesByNames  } from '@/utils/area-data.js'
+import { addBook } from "@/api/logistics/book"
+import { addOrder } from "@/api/logistics/order"
 
-// 根据code获取地区名称的辅助函数
-const getAreaNameByCode = (code) => {
-  if (!code) return ''
-  const area = areaData.find(item => item.code === code)
-  return area ? area.name : ''
-}
+
+const { proxy } = getCurrentInstance()
+const { jd_logistics_product_code, sf_logistics_product_code } = proxy.useDict("jd_logistics_product_code", "sf_logistics_product_code")
+
+import { useRoute } from 'vue-router';
+import {updateBook} from "../../../api/logistics/book.js";
+
+const route = useRoute();
+
+const productType = ref('1') //1表示京东  2表示顺丰
 
 // 寄件人信息
 const sender = reactive({
-  name: '',
-  phone: '',
-  region: [],
-  regionInfo: {
-    provinceCode: '',
-    cityCode: '',
-    districtCode: '',
-    provinceName: '',
-    cityName: '',
-    districtName: '',
-    fullName: ''
-  },
-  address: '',
-  isDefault: false
+  addressId:'',
+  contactName: '',
+  contactPhone: '',
+  provinceName: '',
+  cityName: '',
+  countyName: '',
+  detailedAddress: '',
+  region: [], // 用于绑定RegionCascader组件
+  defaultFlag: '0' //是否默认 0非默认 1默认
 })
 
 // 收件人信息
 const receiver = reactive({
-  name: '',
-  phone: '',
+  addressId:'',
+  contactName: '',
+  contactPhone: '',
+  provinceName: '',
+  cityName: '',
+  countyName: '',
+  detailedAddress: '',
   region: [],
-  regionInfo: {
-    provinceCode: '',
-    cityCode: '',
-    districtCode: '',
-    provinceName: '',
-    cityName: '',
-    districtName: '',
-    fullName: ''
-  },
-  address: ''
+  defaultFlag: '0'
 })
 
 // 寄件方式
@@ -499,7 +517,9 @@ const deliveryMethod = ref('pickup')
 // 寄件方式表单(用于验证)
 const deliveryForm = reactive({
   pickupTime: [],
-  expressType: ''
+  expressType: '',
+  pickupStartTime: '', // 新增:上门开始时间
+  pickupEndTime: ''    // 新增:上门结束时间
 })
 
 // 寄件方式验证规则(针对PickupTimeCascader组件)
@@ -558,7 +578,7 @@ const insuranceAmountError = ref('')
 
 // 签单返还类型
 const signReturnType = ref('')
-const signReturnTypeError = ref('123')
+const signReturnTypeError = ref('')
 const signReturnOptions = ref([
   { label: '电子凭证', value: 'electronic' },
   { label: '纸质凭证', value: 'paper' }
@@ -569,11 +589,11 @@ const agreed = ref(false)
 
 // 表单验证规则(已添加长度限制)
 const senderRules = {
-  name: [
+  contactName: [
     { required: true, message: '请输入寄件人姓名', trigger: 'blur' },
     { min: 2, max: 20, message: '姓名长度在2-20个字符之间', trigger: 'blur' }
   ],
-  phone: [
+  contactPhone: [
     { required: true, message: '请输入寄件人电话', trigger: 'blur' },
     { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
   ],
@@ -590,18 +610,18 @@ const senderRules = {
       trigger: 'change'
     }
   ],
-  address: [
+  detailedAddress: [
     { required: true, message: '请输入详细地址', trigger: 'blur' },
     { min: 5, max: 100, message: '地址长度在5-100个字符之间', trigger: 'blur' }
   ]
 }
 
 const receiverRules = {
-  name: [
+  contactName: [
     { required: true, message: '请输入收件人姓名', trigger: 'blur' },
     { min: 2, max: 20, message: '姓名长度在2-20个字符之间', trigger: 'blur' }
   ],
-  phone: [
+  contactPhone: [
     { required: true, message: '请输入收件人电话', trigger: 'blur' },
     { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
   ],
@@ -618,7 +638,7 @@ const receiverRules = {
       trigger: 'change'
     }
   ],
-  address: [
+  detailedAddress: [
     { required: true, message: '请输入详细地址', trigger: 'blur' },
     { min: 5, max: 100, message: '地址长度在5-100个字符之间', trigger: 'blur' }
   ]
@@ -685,15 +705,11 @@ const itemInfoForm = ref(null)
 // 地址簿弹窗相关(迁移到组件)
 const addressBookVisible = ref(false)
 const addressBookType = ref('sender')
-const selectedAddress = ref(null)
 
-// 分页相关
-const currentPage = ref(1)
-const pageSize = ref(8)
-const totalAddresses = ref(50)
+// 存储原始地址信息(用于判断是否修改)
+const senderOriginalAddress = ref(null)
+const receiverOriginalAddress = ref(null)
 
-// 模拟地址数据
-const addressList = ref([])
 
 // 监听快递类型变化
 watch(() => deliveryForm.expressType, (newVal) => {
@@ -733,16 +749,34 @@ watch(signReturnType, (newVal) => {
   }
 })
 
+
 // 处理寄件人地区变化
 const handleSenderRegionChange = (regionInfo) => {
-  Object.assign(sender.regionInfo, regionInfo)
   console.log('寄件人地区信息:', regionInfo)
+  // 同时更新省份、城市、区县名称
+  sender.provinceName = regionInfo.provinceName || ''
+  sender.cityName = regionInfo.cityName || ''
+  sender.countyName = regionInfo.countyName || ''
+  sender.region = [regionInfo.provinceCode,regionInfo.cityCode,regionInfo.districtCode]
+
+  // 如果是从地址簿选择的地址,修改后清空addressId
+  if (sender.addressId) {
+    sender.addressId = ''
+  }
 }
 
 // 处理收件人地区变化
 const handleReceiverRegionChange = (regionInfo) => {
-  Object.assign(receiver.regionInfo, regionInfo)
   console.log('收件人地区信息:', regionInfo)
+  receiver.provinceName = regionInfo.provinceName || ''
+  receiver.cityName = regionInfo.cityName || ''
+  receiver.countyName = regionInfo.countyName || ''
+  receiver.region = [regionInfo.provinceCode,regionInfo.cityCode,regionInfo.districtCode]
+
+  // 如果是从地址簿选择的地址,修改后清空addressId
+  if (receiver.addressId) {
+    receiver.addressId = ''
+  }
 }
 
 // 处理上门时间变化
@@ -752,60 +786,79 @@ const handlePickupTimeChange = (value) => {
   if (value && deliveryFormRef.value) {
     deliveryFormRef.value.clearValidate(['pickupTime'])
   }
+
+  let formattedPickupStartTime = ''
+  let formattedPickupEndTime = ''
+  if (Array.isArray(deliveryForm.pickupTime) && deliveryForm.pickupTime.length > 0) {
+    // 假设PickupTimeCascader返回的数组结构为 [日期, 时间段]
+    const  timeArr = deliveryForm.pickupTime[1].split('-')
+    console.log('deliveryForm.pickupTime===',deliveryForm.pickupTime)
+    console.log('timeArr===',timeArr)
+    console.log('deliveryForm.pickupTime[0] + timeArr[0]===',deliveryForm.pickupTime[0] + ' ' + timeArr[0])
+    formattedPickupStartTime = deliveryForm.pickupTime[0] + ' ' + timeArr[0] + ":00"
+    formattedPickupEndTime = deliveryForm.pickupTime[0] + ' ' + timeArr[1] + ":00"
+    console.log('formattedPickupStartTime===',formattedPickupStartTime)
+    console.log('formattedPickupEndTime===',formattedPickupEndTime)
+  }
+
+
+}
+
+// 处理寄件人表单变化
+const handleSenderChange = () => {
+  // 如果是从地址簿选择的地址,修改后清空addressId
+  if (sender.addressId) {
+    sender.addressId = ''
+  }
+}
+
+// 处理收件人表单变化
+const handleReceiverChange = () => {
+  // 如果是从地址簿选择的地址,修改后清空addressId
+  if (receiver.addressId) {
+    receiver.addressId = ''
+  }
 }
 
 // 方法:打开地址簿
 const openAddressBook = (type) => {
   addressBookType.value = type
-  loadAddressList()
   addressBookVisible.value = true
 }
 
 // 方法:关闭地址簿
 const closeAddressBook = () => {
   addressBookVisible.value = false
-  selectedAddress.value = null
-}
-
-// 方法:处理地址选择(从组件触发)
-const handleAddressSelect = (address) => {
-  selectedAddress.value = address
 }
 
-// 方法:处理地址分页变化
-const handleAddressPageChange = ({ page, pageSize: size }) => {
-  currentPage.value = page
-  pageSize.value = size
-  // 可以在这里重新加载数据
-  console.log('分页变化:', page, size)
-}
 
 // 方法:应用选择的地址(修改:清空验证错误)
 const applyAddress = (address) => {
   if (address) {
     if (addressBookType.value === 'sender') {
-      sender.name = address.name
-      sender.phone = address.phone
-      sender.region = address.region
-      sender.address = address.address
-      sender.isDefault = address.isDefault || false
-
-      // 设置地区信息
-      if (address.region && address.region.length > 0) {
-        const regionInfo = {
-          provinceCode: address.region[0] || '',
-          cityCode: address.region[1] || '',
-          districtCode: address.region[2] || '',
-          provinceName: getAreaNameByCode(address.region[0]) || '',
-          cityName: address.region[1] ? getAreaNameByCode(address.region[1]) : '',
-          districtName: address.region[2] ? getAreaNameByCode(address.region[2]) : '',
-          fullName: [
-            getAreaNameByCode(address.region[0]) || '',
-            address.region[1] ? getAreaNameByCode(address.region[1]) : '',
-            address.region[2] ? getAreaNameByCode(address.region[2]) : ''
-          ].filter(Boolean).join('/')
-        }
-        Object.assign(sender.regionInfo, regionInfo)
+      sender.addressId = address.addressId || ''
+      sender.contactName = address.contactName || ''
+      sender.contactPhone = address.contactPhone || ''
+
+      sender.provinceName = address.provinceName
+      sender.cityName = address.cityName
+      sender.countyName = address.countyName
+
+      sender.detailedAddress = address.detailedAddress || ''
+      sender.defaultFlag = address.defaultFlag || '0'
+
+      sender.region = getCodesByNames(sender.provinceName,sender.cityName,sender.countyName)
+      console.log('region===========', sender.region)
+
+      // 保存原始地址信息(用于判断是否修改)
+      senderOriginalAddress.value = {
+        contactName: address.contactName || '',
+        contactPhone: address.contactPhone || '',
+        provinceName: address.provinceName || '',
+        cityName: address.cityName || '',
+        countyName: address.countyName || '',
+        detailedAddress: address.detailedAddress || '',
+        defaultFlag: address.defaultFlag || '0'
       }
 
       // 清空寄件人表单的验证错误
@@ -813,27 +866,29 @@ const applyAddress = (address) => {
         senderForm.value.clearValidate()
       }
     } else {
-      receiver.name = address.name
-      receiver.phone = address.phone
-      receiver.region = address.region
-      receiver.address = address.address
-
-      // 设置地区信息
-      if (address.region && address.region.length > 0) {
-        const regionInfo = {
-          provinceCode: address.region[0] || '',
-          cityCode: address.region[1] || '',
-          districtCode: address.region[2] || '',
-          provinceName: getAreaNameByCode(address.region[0]) || '',
-          cityName: address.region[1] ? getAreaNameByCode(address.region[1]) : '',
-          districtName: address.region[2] ? getAreaNameByCode(address.region[2]) : '',
-          fullName: [
-            getAreaNameByCode(address.region[0]) || '',
-            address.region[1] ? getAreaNameByCode(address.region[1]) : '',
-            address.region[2] ? getAreaNameByCode(address.region[2]) : ''
-          ].filter(Boolean).join('/')
-        }
-        Object.assign(receiver.regionInfo, regionInfo)
+      receiver.addressId = address.addressId || ''
+      receiver.contactName = address.contactName || ''
+      receiver.contactPhone = address.contactPhone || ''
+
+      receiver.provinceName = address.provinceName
+      receiver.cityName = address.cityName
+      receiver.countyName = address.countyName
+
+      receiver.detailedAddress = address.detailedAddress || ''
+      receiver.defaultFlag = address.defaultFlag || '0'
+
+      receiver.region = getCodesByNames(receiver.provinceName,receiver.cityName,receiver.countyName)
+      console.log('region===========', receiver.region)
+
+      // 保存原始地址信息(用于判断是否修改)
+      receiverOriginalAddress.value = {
+        contactName: address.contactName || '',
+        contactPhone: address.contactPhone || '',
+        provinceName: address.provinceName || '',
+        cityName: address.cityName || '',
+        countyName: address.countyName || '',
+        detailedAddress: address.detailedAddress || '',
+        defaultFlag: address.defaultFlag || '0'
       }
 
       // 清空收件人表单的验证错误
@@ -841,7 +896,6 @@ const applyAddress = (address) => {
         receiverForm.value.clearValidate()
       }
     }
-
     ElMessage.success('已应用选择的地址')
   } else {
     ElMessage.warning('请先选择一个地址')
@@ -857,11 +911,9 @@ const addToAddressBook = async (type) => {
   if (type === 'sender') {
     formRef = senderForm.value
     formData = sender
-    regionInfo = sender.regionInfo
   } else {
     formRef = receiverForm.value
     formData = receiver
-    regionInfo = receiver.regionInfo
   }
 
   if (!formRef) return
@@ -878,80 +930,33 @@ const addToAddressBook = async (type) => {
   }
 
   const addressData = {
-    name: formData.name,
-    phone: formData.phone,
-    region: formData.region,
-    address: formData.address,
-    fullAddress: `${regionInfo.fullName || ''}${formData.address}`,
-    provinceName: regionInfo.provinceName || '',
-    cityName: regionInfo.cityName || '',
-    districtName: regionInfo.districtName || '',
-    isDefault: type === 'sender' ? formData.isDefault : false,
+    addressId: formData.addressId || '', // 如果有addressId则传递,为空则新增
+    contactName: formData.contactName,
+    contactPhone: formData.contactPhone,
+    detailedAddress: formData.detailedAddress,
+    provinceName: formData.provinceName || '',
+    cityName: formData.cityName || '',
+    countyName: formData.countyName || '',
+    defaultFlag: type === 'sender' ? formData.defaultFlag : '0',
     tag: 'custom',
-    createTime: new Date().toLocaleString()
   }
-
-  console.log(`保存${type === 'sender' ? '寄件人' : '收件人'}地址到地址簿:`, addressData)
-  ElMessage.success('地址已添加到地址簿')
-
-  if (type === 'sender' && formData.isDefault) {
-    console.log('更新其他地址的默认状态')
-  }
-}
-
-// 方法:加载地址列表
-const loadAddressList = () => {
-  const mockAddresses = []
-  const tags = ['home', 'company', 'custom']
-
-  // 从本地数据中获取示例省市区代码
-  const provinces = areaData.filter(item => item.type === 0)
-  const cities = areaData.filter(item => item.type === 1)
-  const districts = areaData.filter(item => item.type === 2)
-
-  for (let i = 1; i <= totalAddresses.value; i++) {
-    // 随机选择一个省市区
-    const randomProvince = provinces[i % provinces.length]
-    const provinceCities = cities.filter(city => city.parent_code === randomProvince.code)
-    const randomCity = provinceCities[i % (provinceCities.length || 1)]
-    const cityDistricts = districts.filter(district =>
-        randomCity && district.parent_code === randomCity.code
-    )
-    const randomDistrict = cityDistricts[i % (cityDistricts.length || 1)]
-
-    const region = [randomProvince.code]
-    if (randomCity) region.push(randomCity.code)
-    if (randomDistrict) region.push(randomDistrict.code)
-
-    // 获取地区名称
-    const provinceName = getAreaNameByCode(randomProvince.code) || '未知省份'
-    const cityName = randomCity ? getAreaNameByCode(randomCity.code) : ''
-    const districtName = randomDistrict ? getAreaNameByCode(randomDistrict.code) : ''
-
-    const streets = ['科技路', '创业大街', '创新路', '软件园', '创业园', '产业园']
-    const streetNumber = `${i % 100}号`
-
-    const addressDetail = `${streets[i % streets.length]}${streetNumber}`
-    const  temp11 = i % 2 == 0 ? "dddddddddddddddddddddddddddddddddddddvxcvcxv打发手动阀手动阀dddddddddddddddddddddd" : ""
-    const fullAddress = `${provinceName}${cityName}${districtName}${addressDetail}${temp11}`
-
-    mockAddresses.push({
-      id: i,
-      name: `用户${i}`,
-      phone: `138001380${String(i).padStart(2, '0')}`,
-      region: region,
-      address: addressDetail,
-      fullAddress: fullAddress,
-      provinceName: provinceName,
-      cityName: cityName,
-      districtName: districtName,
-      isDefault: i === 1,
-      tag: tags[i % 3],
-      createTime: new Date(Date.now() - i * 86400000).toLocaleDateString()
-    })
+  try {
+    // 如果是新增,更新addressId
+    let response = {}
+    if (formData.addressId) {
+      response = await updateBook(addressData)
+    }else {
+      response = await addBook(addressData)
+    }
+    if (response.code === 200) {
+      ElMessage.success('地址保存成功')
+    } else {
+      ElMessage.error(response.msg || '地址保存失败')
+    }
+  } catch (error) {
+    console.error('保存地址失败:', error)
+    ElMessage.error('地址保存失败')
   }
-
-  addressList.value = mockAddresses
 }
 
 // 方法:处理保价开关变化
@@ -1019,7 +1024,7 @@ const validateFormData = () => {
   }
 
   // 验证签单返还类型
-  if (valueServices.signReturn && !signReturnType.value) {
+  if (valueServices.signReturn && !signReturnType.value && productType == '2') {
     signReturnTypeError.value = '请选择签单返还凭证类型'
     isValid = false
   }
@@ -1095,58 +1100,92 @@ const submitOrder = async () => {
     return
   }
 
-  // 格式化上门时间数据
-  let formattedPickupTime = ''
+
+  let formattedPickupStartTime = ''
+  let formattedPickupEndTime = ''
   if (Array.isArray(deliveryForm.pickupTime) && deliveryForm.pickupTime.length > 0) {
     // 假设PickupTimeCascader返回的数组结构为 [日期, 时间段]
-    formattedPickupTime = deliveryForm.pickupTime.join(' ')
+    const  timeArr = deliveryForm.pickupTime[1].split('-')
+    console.log('deliveryForm.pickupTime===',deliveryForm.pickupTime)
+    console.log('timeArr===',timeArr)
+    console.log('deliveryForm.pickupTime[0] + timeArr[0]===',deliveryForm.pickupTime[0] + ' ' + timeArr[0])
+    formattedPickupStartTime = deliveryForm.pickupTime[0] + ' ' + timeArr[0] + ":00"
+    formattedPickupEndTime = deliveryForm.pickupTime[0] + ' ' + timeArr[1] + ":00"
+    console.log('formattedPickupStartTime===',formattedPickupStartTime)
+    console.log('formattedPickupEndTime===',formattedPickupEndTime)
   }
 
-  // 构建完整的订单数据
-  const orderData = {
-    sender: {
-      name: sender.name,
-      phone: sender.phone,
-      region: sender.region,
-      regionInfo: { ...sender.regionInfo },
-      address: sender.address,
-      isDefault: sender.isDefault,
-      fullAddress: `${sender.regionInfo.fullName || ''}${sender.address}`
-    },
-    receiver: {
-      name: receiver.name,
-      phone: receiver.phone,
-      region: receiver.region,
-      regionInfo: { ...receiver.regionInfo },
-      address: receiver.address,
-      fullAddress: `${receiver.regionInfo.fullName || ''}${receiver.address}`
-    },
-    deliveryMethod: deliveryMethod.value,
-    expressType: deliveryForm.expressType,
-    pickupTime: formattedPickupTime,
-    pickupTimeDetails: deliveryForm.pickupTime, // 保留原始数据
-    itemInfo: { ...itemInfo },
-    valueServices: {
+
+  // <!--    增值服务:-->
+  // <!--    包装:isPack-->
+  // <!--    保价:guaranteeMoney-->
+  // <!--    超长超重:isOverLongWeight-->
+  // <!--    签单返还:isReceiptCollect-->
+  // <!--    打木架:isWoodenCrate-->
+  // <!--    京东  包装 保价 签单反  保价有的话传值  没有的话传 空  签单反 没筛选了-->
+  let services = {
+    isPack:valueServices.isPack,
+    guaranteeMoney:valueServices.insurance ? insuranceAmount.value : null,
+    isReceiptCollect: valueServices.signReturn ? signReturnType.value : null
+  }
+
+  if(productType == '1'){ //京东
+    services = {
+      isPack:valueServices.isPack,
+      guaranteeMoney:valueServices.insurance ? insuranceAmount.value : null,
+      isReceiptCollect: valueServices.signReturn ? signReturnType.value : null
+    }
+  }else {
+    services = {
       ...valueServices,
       insuranceAmount: valueServices.insurance ? insuranceAmount.value : null,
       signReturnType: valueServices.signReturn ? signReturnType.value : null
-    },
-    agreement: agreed.value,
-    createTime: new Date().toISOString()
+    }
+  }
+
+  // 构建完整的订单数据
+  const orderData = {
+    orderType: productType.value,  //1-非时效件(京东),2-时效件(顺丰)
+    senderName:sender.contactName,
+    senderPhone:sender.contactPhone,
+    senderProvince:sender.provinceName,
+    senderCity:sender.cityName,
+    senderCounty:sender.countyName,
+    senderAddress:sender.detailedAddress,
+    receiverName:sender.contactName,
+    receiverPhone:sender.contactPhone,
+    receiverProvince:sender.provinceName,
+    receiverCity:sender.cityName,
+    receiverCounty:sender.countyName,
+    receiverAddress:sender.detailedAddress,
+
+    goodsName:itemInfo.type,
+    goodsWeight:itemInfo.weight,
+    goodsVolume:itemInfo.volume,
+    goodsQty:itemInfo.quantity,
+
+    sendStartTime:formattedPickupStartTime,//上门取件时间
+    sendEndTime:formattedPickupEndTime,//上门取件时间
+
+    productCode: deliveryForm.expressType, //快递类型
+    addedService: JSON.stringify(services),
+
   }
 
   console.log('提交订单数据:', orderData)
-  ElMessage.success('订单提交成功!')
 
-  // 这里可以添加实际提交到后端的逻辑
-  // try {
-  //   const response = await axios.post('/api/orders', orderData)
-  //   console.log('订单提交成功:', response.data)
-  //   ElMessage.success('订单创建成功!')
-  // } catch (error) {
-  //   console.error('订单提交失败:', error)
-  //   ElMessage.error('订单提交失败,请重试')
-  // }
+  try {
+    const response = await addOrder(orderData)
+    if(response.code == 200){
+      console.log('订单提交成功:', response.data)
+      ElMessage.success('订单创建成功!')
+    } else {
+      ElMessage.error(response.msg || '订单创建失败!')
+    }
+  } catch (error) {
+    console.error('订单提交失败:', error)
+    ElMessage.error('订单提交失败,请重试')
+  }
 }
 
 // 初始化加载
@@ -1154,8 +1193,8 @@ onMounted(() => {
   console.log('快递下单页面已加载')
   console.log('省市区数据总数:', areaData.length)
 
-  // 初始化地址列表
-  loadAddressList()
+  productType.value = route.query.productType || '1';
+  console.log("===productType=", productType.value)
 })
 </script>