p-honggang.li
2025-09-03 f053c272d7a555f6e58ec9f90ec9bb97cef5b3cb
src/views/productManage/productPriceViewer/index.vue
@@ -1,12 +1,5 @@
<template>
  <el-dialog
    v-model="visible"
    :title="dialogTitle"
    :before-close="handleClose"
    destroy-on-close
    class="product-price-dialog"
    :width="dialogWidth"
  >
   <div>
    <div class="price-viewer-container" v-loading="loading">
      <!-- 价格对比表格 -->
      <div class="pricing-table-container" v-if="showPricePanel && priceList.length > 0">
@@ -19,7 +12,7 @@
          >
            <div class="pricing-table-wrapper">
              <table class="pricing-table">
                 <thead>
                                 <thead>
                   <tr>
                     <th class="feature-column">功能对比</th>
                     <th 
@@ -30,18 +23,19 @@
                       {{ group.productSuite }}
                     </th>
                   </tr>
                   <tr class="sub-header">
                     <th>销售形式</th>
                     <th
                       v-for="priceItem in tabData"
                       :key="priceItem.id"
                       :colspan="getColspan(priceItem)"
                     >
                       {{ getSalesFormText(priceItem.salesForm) }}
                     </th>
                   </tr>
                 </thead>
                <tbody>
                  <tr>
                    <td class="feature-label">销售形式</td>
                    <td
                      v-for="priceItem in tabData"
                      :key="priceItem.id"
                      :colspan="getColspan(priceItem)"
                      class="feature-value"
                    >
                      {{ getSalesFormText(priceItem.salesForm) }}
                    </td>
                  </tr>
                  <tr>
                    <td class="feature-label">客户对象</td>
                    <td 
@@ -113,16 +107,16 @@
                         </div>
                          <span class="price-value points">{{ formatNumber(priceItem.pointsAmount) }} / {{ getPriceUnitText(priceItem.priceUnit) }}</span>
                        </div>
                        <div
                          v-if="priceItem.priceSettings.includes('CURRENCY') && priceItem.currencyAmount > 0"
                          class="price-item"
                        >
                         <div class="price-lable-icon">
                          <el-icon class="price-icon currency"><Money /></el-icon>
                          <span class="price-label">货币</span>
                          </div>
                          <span class="price-value currency">{{ formatNumber(priceItem.currencyAmount) }} / {{ getPriceUnitText(priceItem.priceUnit) }}</span>
                        </div>
                                                 <div
                           v-if="priceItem.priceSettings.includes('CURRENCY') && priceItem.currencyAmount > 0"
                           class="price-item"
                         >
                          <div class="price-lable-icon">
                           <span class="price-icon currency">¥</span>
                           <span class="price-label">货币</span>
                           </div>
                           <span class="price-value currency">{{ formatNumber(priceItem.currencyAmount) }} / {{ getPriceUnitText(priceItem.priceUnit) }}</span>
                         </div>
                        <div 
                          v-if="priceItem.priceSettings.includes('FREE')"
                          class="price-item"
@@ -174,19 +168,19 @@
              <span class="label">产品名称:</span><span class="value strong">{{ productHeader.name }}</span>
            </div>
            <div class="grid-item">
              <span class="label">行业板块:</span><span class="value">{{ productHeader.industry }}</span>
              <span class="label">行业板块:</span><span class="value">{{ productHeader.industrialChainName }}</span>
            </div>
            <div class="grid-item">
              <span class="label">提供者:</span><span class="value">{{ productHeader.provider }}</span>
              <span class="label">提供者:</span><span class="value">{{ productHeader.submissionUnit }}</span>
            </div>
            <div class="grid-item">
              <span class="label">用户姓名:</span><span class="value">{{ userInfo.name }}</span>
            </div>
            <div class="grid-item">
              <span class="label">单位:</span><span class="value">{{ userInfo.unit }}</span>
              <span class="label">单位:</span><span class="value">{{ userInfo.unitName }}</span>
            </div>
            <div class="grid-item">
              <span class="label">部门:</span><span class="value">{{ userInfo.department }}</span>
              <span class="label">部门:</span><span class="value">{{ userInfo.departmentName }}</span>
            </div>
          </div>
        </el-card>
@@ -198,12 +192,12 @@
            <table class="suite-table">
              <thead>
                <tr>
                  <th class="th-left">详情</th>
                  <th class="th-left">规格</th>
                  <th class="th-left">单价</th>
                  <th class="th-center">数量</th>
                  <th class="th-center">年限</th>
                  <th class="th-center">操作</th>
                  <th class="th-detail">详情</th>
                  <th class="th-spec">规格</th>
                  <th class="th-price">单价</th>
                  <th class="th-quantity">数量</th>
                  <th class="th-years">年限</th>
                  <th class="th-action">操作</th>
                </tr>
              </thead>
              <tbody>
@@ -216,12 +210,12 @@
                  </td>
                  <td class="cell-spec cell-spec-gg">
                    <div class="spec-line-gg">
                      <div class="spec-line">销售形式:{{ getSalesFormText(suite.salesForm) }}</div>
                      <div class="spec-line">客户对象:{{ getCustomerObjectText(suite.customerObject) }}</div>
                      <div class="spec-line">销售形式:<span class="spec-value">{{ getSalesFormText(suite.salesForm) }}</span></div>
                      <div class="spec-line">客户对象:<span class="spec-value">{{ getCustomerObjectText(suite.customerObject) }}</span></div>
                    </div>
                    <div  class="spec-line-gg">
                      <div class="spec-line">账户数量:{{ suite.accountQuantityUnlimited ? '不限' : suite.accountQuantity }}</div>
                      <div class="spec-line">并发节点数:{{ suite.concurrentNodeQuantityUnlimited ? '不限' : suite.concurrentNodeQuantity }}</div>
                      <div class="spec-line">账户数量:<span class="spec-value">{{ suite.accountQuantityUnlimited ? '不限' : suite.accountQuantity }}</span></div>
                      <div class="spec-line">并发节点数:<span class="spec-value">{{ suite.concurrentNodeQuantityUnlimited ? '不限' : suite.concurrentNodeQuantity }}</span></div>
                    </div>
                  </td>
@@ -249,10 +243,27 @@
                    </div>
                  </td>
                  <td class="th-center">
                    <el-input-number v-model="suite.quantity" :min="1" :max="999" :controls="false" size="small" />
                    <el-input-number
                      v-model="suite.quantity"
                      :min="1"
                      :max="999"
                      :controls="true"
                      size="large"
                      class="quantity-input"
                      @change="(value) => handleQuantityChange(suite.id, value?value:1)"
                    />
                  </td>
                  <td class="th-center">
                    <el-input-number v-model="suite.duration" :min="1" :max="10" :controls="false" size="small" />
                      <el-input-number
                        v-model="suite.duration"
                        :min="1"
                        :max="100"
                        :controls="true"
                        size="large"
                        class="duration-input"
                        :disabled="suite.priceType === 'FREE'"
                        @change="(value) => handleDurationChange(suite.id, value?value:1)"
                      />
                  </td>
                  <td class="th-center">
                    <el-button type="danger" link @click="removeSuite(idx)">删除</el-button>
@@ -323,30 +334,30 @@
        <el-empty description="暂无价格信息" />
      </div>
    </div>
    <template #footer v-if="!showOrderStatus">
      <span class="dialog-footer">
        <template v-if="showPricePanel">
        <el-button @click="handleClose">关闭</el-button>
        <el-button type="primary" @click="handleOrder">立即订购</el-button>
        </template>
        <template v-else>
          <el-button @click="showOrderPanel = false">返回价格对比</el-button>
          <el-button type="primary" @click="submitOrder">提交申请</el-button>
        </template>
      </span>
    </template>
  </el-dialog>
    <div class="footer" v-if="showPricePanel">
     <el-button type="primary" @click="handleOrder">立即订购</el-button>
    </div>
    <div class="footer" v-else>
      <el-button @click="returnPricePanel">返回价格对比</el-button>
      <el-button type="primary" @click="submitOrder">提交申请</el-button>
    </div>
  </div>
  <!-- 产品订购对话框移除,改为内嵌展示 -->
</template>
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import { ref, watch, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router'
import productPricingApi from '@/api/productPricingApi'
import cartApi from '@/api/cartApi'
import orderApi from '@/api/orderApi'
import { ShoppingCart, Coin, Money } from '@element-plus/icons-vue'
import { useUserInfo } from '@/stores/modules/userInfo'
import { queryUserDetail } from '@/api/userInfo'
import productApi from '@/api/productApi'
import workFlowApi from '@/api/workFlowApi'
const route = useRoute()
@@ -367,36 +378,11 @@
  enableStatus: 'ENABLED' | 'DISABLED'
}
interface Props {
  modelValue: boolean
  productId?: string
  width?: string
}
interface Emits {
  (e: 'update:modelValue', value: boolean): void
  (e: 'order', selectedItems: PriceItem[]): void
}
// 兼容 path 参数 productId 与 query 参数 id/productId
const currentProductId = computed<string | undefined>(() => {
  return (route.params.productId as string) || (route.query.productId as string) || (route.query.id as string)
})
const props = withDefaults(defineProps<Props>(), {
  modelValue: false,
  productId: '',
  width: '90%'
})
const emit = defineEmits<Emits>()
const visible = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})
const loading = ref(false)
const activeTab = ref('enterprise')
@@ -406,6 +392,33 @@
const showOrderPanel = ref(false)
const showPricePanel = ref(true)
const showOrderStatus = ref(false)
// 模拟用户信息(实际应从用户状态获取)
const userStore = useUserInfo()
let currentUserId = computed(() => userStore.getUserId || userStore.getUserInfo?.userId)
const currentUnitId = computed(() => userStore.getUnitId || userStore.getUserInfo?.unitId || '')
onMounted(async () => {
  if (!currentUserId.value) {
    try {
      const res: any = await queryUserDetail()
      if (res?.code === 200 && res.data) {
        userStore.updateUserDetail(res.data)
        currentUserId = res.data.userId || res.data.id
        userInfo.value = res.data
      } else {
        ElMessage.error(res?.msg || '无法获取用户信息,请先登录')
        return
      }
    } catch (e) {
      console.error('获取用户详情失败:', e)
      ElMessage.error('获取用户信息失败,请稍后重试')
      return
    }
  }else{
    userInfo.value = userStore.getUserInfos
  }
})
type PriceTypeKey = 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'
interface OrderSuite extends PriceItem {
  selected: boolean
@@ -414,6 +427,11 @@
  priceType?: PriceTypeKey
}
const orderSuites = ref<OrderSuite[]>([])
// 购物车相关状态
const cartItems = ref<any[]>([])
const cartLoading = ref(false)
const idempotencyToken = ref<string>('')
// 订单状态相关
type OrderStatus = 'SUBMITTED' | 'AUTHORIZED' | 'CONFIRMED' | 'EVALUATED'
@@ -488,14 +506,15 @@
// 产品信息/用户信息(静态)
const productHeader = ref({
  name: '中交方远智能实测实量管理系统',
  industry: '公路,市政,建筑',
  provider: '中交建筑集团第一工程有限公司'
  name: '',
  industrialChainName: '',
  submissionUnit: '',
  createUserId: ''
})
const userInfo = ref({
  name: '张静',
  unit: '信科集团',
  department: '门户系统临时组'
  name: '',
  departmentName: '',
  unitName: ''
})
// 全选
@@ -522,17 +541,29 @@
  const map: Record<string, string> = { SET: '/套', SET_PER_YEAR: '/套/年', YEAR: '/年' }
  return map[unit || ''] || ''
}
const removeSuite = (index: number) => { orderSuites.value.splice(index, 1) }
const removeSuite = async (index: number) => {
  const suite = orderSuites.value[index]
  if (!suite) return
  try {
    await removeFromCart(String(suite.id))
    orderSuites.value.splice(index, 1)
  } catch (e) {
  }
}
// 订购成功回调
const handleOrderSuccess = (orderData: any) => {
  console.log('订购成功:', orderData)
  ElMessage.success('订购申请提交成功!')
  // 清空选择状态
  selectedSuiteIds.value.clear()
}
// 弹窗标题
const dialogTitle = computed(() => {
  return `【${productName.value}】产品定价`
})
// 弹窗宽度
const dialogWidth = computed(() => {
  return props.width
})
// 按客户对象分组的价格数据
const groupedPriceData = computed(() => {
@@ -658,57 +689,61 @@
  } finally {
    loading.value = false
  }
  //获取产品详情信息
  try {
    // productDetail.value = mockProductMap[productId] || null
    const data = {
      id: productId
    }
    const detailRes: any = await productApi.getProductById(data)
    if (detailRes?.code === 200) {
      productHeader.value = detailRes.data || { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
    } else {
      productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
      ElMessage.error(detailRes?.msg || '获取产品详情失败')
    }
  } catch (e) {
    productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
    ElMessage.error('获取产品详情失败')
  } finally {
    loading.value = false
  }
}
// 监听产品ID变化
watch(() => props.productId, (newProductId) => {
  if (newProductId && visible.value) {
watch(currentProductId, (newProductId) => {
  if (newProductId) {
    fetchProductData(newProductId)
  }
})
// 监听弹窗显示状态
watch(() => visible.value, (newVisible) => {
  if (newVisible && props.productId) {
    fetchProductData(props.productId)
  }
})
// 关闭弹窗
const handleClose = () => {
  visible.value = false
  showOrderPanel.value = false
  showOrderStatus.value = false
  showPricePanel.value = true
  orderSuites.value = []
  selectedSuiteIds.value.clear()
}
},{ immediate: true })
// 套件选择处理
const handleSuiteSelect = (suiteId: number, checked: any) => {
  const isChecked = Boolean(checked)
  if (isChecked) {
    selectedSuiteIds.value.add(suiteId)
    // 添加到购物车
    const priceItem = priceList.value.find(item => item.id === suiteId)
    if (priceItem) {
      addToCart(priceItem)
    }
  } else {
    selectedSuiteIds.value.delete(suiteId)
    // 从购物车移除
    removeFromCart(String(suiteId))
  }
}
// 立即订购
const handleOrder = () => {
  if (selectedSuiteIds.value.size === 0) {
    ElMessage.warning('请选择要订购的软件套件')
    return
  }
  const selectedItems = priceList.value.filter(item => selectedSuiteIds.value.has(item.id))
  // 构建内嵌订购列表
  orderSuites.value = selectedItems.map(it => ({
    ...it,
    selected: true,
    quantity: 1,
    duration: 1,
    priceType: getDefaultPriceType(it as any)
  })) as any
  // 查询购物车信息进行展示
  fetchCartItems()
  // 获取一次性防重复提交token
  orderApi.getIdempotencyToken(currentUserId.value).then((res: any) => {
    if (res?.code === 200) {
      idempotencyToken.value = res.data as string
    }
  }).catch(() => {})
  showOrderPanel.value = true
  showPricePanel.value = false
}
@@ -719,6 +754,12 @@
// 启用/停用按钮
function handleToggleEnableStatus(row: any) {
  // ... existing code ...
}
function returnPricePanel() {
  showOrderPanel.value = false
  showOrderStatus.value = false
  showPricePanel.value = true
}
async function submitOrder() {
@@ -734,33 +775,227 @@
    return
  }
  
  // 这里可对接实际下单接口;先做前端提示
  // emit('order', items as any)
  // 模拟订单状态数据
  orderStatus.value = {
    id: '4348442557619205545',
    productName: productHeader.value.name,
    provider: productHeader.value.provider,
    status: 'SUBMITTED',
    submitTime: new Date().toLocaleString(),
    applyTime: new Date().toLocaleString()
  if (!idempotencyToken.value) {
    ElMessage.error('下单Token获取失败,请返回重试')
    return
  }
  // 显示订单状态面板
  showOrderPanel.value = false
  showPricePanel.value = false
  showOrderStatus.value = true
  const hasPoints = items.some(item => item.priceType === 'POINTS')
  const hasAGREEMENT = items.some(item => item.priceType === 'AGREEMENT')
  let paymentType = ''
  if(hasPoints){
     paymentType = '积分'
  }else{
     paymentType = '协议'
  }
  // 组装创建订单参数(CreateOrderDTO)
  const payload = {
    userId: currentUserId.value,
    unitId: currentUnitId.value,
    productName: productHeader.value.name,
    providerName: productHeader.value.submissionUnit,
    providerId: productHeader.value.createUserId,
    paymentType: paymentType,
    buyerRemarks: '',
    items: items.map(it => ({
      pricingId: it.id,
      productId: it.productId,
      suiteName: getProductSuiteText(it.productSuite),
      salesForm: getSalesFormText(it.salesForm),
      customerType: getCustomerObjectText(it.customerObject),
      accountLimit: it.accountQuantityUnlimited ? '不限' : String(it.accountQuantity),
      concurrentNodes: it.concurrentNodeQuantityUnlimited ? '不限' : String(it.concurrentNodeQuantity),
      priceType: (it.priceType === 'POINTS' ? '积分' : it.priceType === 'CURRENCY' ? '货币' : it.priceType === 'AGREEMENT' ? '协议' : '免费'),
      priceUnit: getPriceUnitText(it.priceUnit),
      unitPrice: (it.priceType === 'POINTS' ? it.pointsAmount : it.currencyAmount) || 0,
      quantity: it.quantity,
      duration: it.duration,
      totalPrice: undefined,
      providerId: productHeader.value.createUserId,
      providerName: productHeader.value.submissionUnit,
      remarks: ''
    }))
  }
  try {
    const res: any = await orderApi.createOrder(payload, idempotencyToken.value)
    if (res?.code === 200) {
      ElMessage.success('订单创建成功')
      const data = res.data || {}
      orderStatus.value = {
        id: data.orderId || '—',
        productName: data.productName || productHeader.value.name,
        provider: data.providerName || productHeader.value.submissionUnit,
        status: 'SUBMITTED',
        submitTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString(),
        applyTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString()
      }
      // 调用工作流接口发起审批流程,拿到流程实例ID后回写订单workflow_id
      try {
        // 根据是否包含协议明细,配置不同流程定义与业务Key(先用静态值占位)
        // const processdefId = hasAGREEMENT ? 'Process_Agreement_Static' : 'Process_Points_Static'
        const businessKey = hasAGREEMENT ? 'agreement_biz_key' : 'points_biz_key'
        const type = hasAGREEMENT ? 'trade_agreement' : 'trade_point';
        // 获取工作流参数
        const  wkParamsRes: any = await workFlowApi.getWorkFlowParams({
          type: type,
          unitId: '1'
        })
        if(wkParamsRes?.code === 200 && wkParamsRes.data?.processTemplateId){
          const wfRes: any = await orderApi.startWorkflowAndComplete({
            processdefId: wkParamsRes.data.processTemplateId,
            userid: String(currentUserId.value || ''),
            businessKey: businessKey
          })
          if (wfRes?.code === 200 && wfRes.data?.processinstId) {
            await orderApi.updateWorkflowId(data.orderId, wfRes.data.processinstId)
          }
        }
      } catch (e) {
        console.warn('启动工作流失败或更新workflow_id失败', e)
      }
      // 清空购物车(后端 + 本地状态)
      try {
        const clearRes: any = await cartApi.clearCart(currentUserId.value, currentUnitId.value)
        if (clearRes?.code === 200) {
          cartItems.value = []
          orderSuites.value = []
          selectedSuiteIds.value.clear()
          selectAllChecked.value = false
        }
      } catch (e) { /* ignore */ }
      // 显示订单状态面板
      showOrderPanel.value = false
      showPricePanel.value = false
      showOrderStatus.value = true
    } else {
      ElMessage.error(res?.msg || '创建订单失败')
    }
  } catch (e) {
    ElMessage.error('创建订单失败')
  }
}
// 订购成功回调
const handleOrderSuccess = (orderData: any) => {
  console.log('订购成功:', orderData)
  ElMessage.success('订购申请提交成功!')
  // 清空选择状态
  selectedSuiteIds.value.clear()
  // 关闭价格查看器
  visible.value = false
// 购物车相关方法
const addToCart = async (priceItem: PriceItem) => {
  try {
    const cartData = {
      pricingId: priceItem.id,
      productId: priceItem.productId,
      productName: productName.value,
      suiteName: getProductSuiteText(priceItem.productSuite),
      salesForm: getSalesFormText(priceItem.salesForm),
      customerType: getCustomerObjectText(priceItem.customerObject),
      accountLimit: priceItem.accountQuantityUnlimited ? '不限' : String(priceItem.accountQuantity),
      concurrentNodes: priceItem.concurrentNodeQuantityUnlimited ? '不限' : String(priceItem.concurrentNodeQuantity),
      priceType: priceItem.priceSettings.map(item => getPriceSettingText(item)).join(','),
      priceUnit: getPriceUnitText(priceItem.priceUnit),
      unitPrice: priceItem.pointsAmount || priceItem.currencyAmount || 0,
      quantity: 1,
      duration: 1
    }
    const res: any = await cartApi.addToCart(cartData, currentUserId.value, currentUnitId.value)
    if (res?.code === 200) {
      ElMessage.success('已添加到购物车')
    } else {
      ElMessage.error(res?.msg || '添加到购物车失败')
    }
  } catch (error) {
    console.error('添加到购物车失败:', error)
    ElMessage.error('添加到购物车失败')
  }
}
const removeFromCart = async (pricingId: string) => {
  try {
    const res: any = await cartApi.removeFromCart(currentUserId.value, currentUnitId.value, pricingId)
    if (res?.code === 200) {
      ElMessage.success('已从购物车移除')
    } else {
      ElMessage.error(res?.msg || '从购物车移除失败')
    }
  } catch (error) {
    console.error('从购物车移除失败:', error)
    ElMessage.error('从购物车移除失败')
  }
}
const updateCartItem = async (pricingId: number, quantity: number, duration: number) => {
  try {
    // 这里需要根据实际接口调整,可能需要传递更多参数
    const res: any = await cartApi.updateCartItem(currentUserId.value, currentUnitId.value, pricingId, quantity)
    if (res?.code === 200) {
      ElMessage.success('购物车已更新')
    } else {
      ElMessage.error(res?.msg || '更新购物车失败')
    }
  } catch (error) {
    console.error('更新购物车失败:', error)
    ElMessage.error('更新购物车失败')
  }
}
const fetchCartItems = async () => {
  cartLoading.value = true
  try {
    const res: any = await cartApi.getCartItems(currentUserId.value, currentUnitId.value)
    if (res?.code === 200) {
      cartItems.value = res.data || []
      // 将购物车数据转换为订单套件格式
      orderSuites.value = cartItems.value.map((item: any) => ({
        id: item.pricingId,
        productId: String(item.productId),
        productSuite: item.suiteName,
        salesForm: mapSalesFormFromCN(item.salesForm),
        customerObject: mapCustomerFromCN(item.customerType),
        accountQuantity: item.accountLimit === '不限' ? 0 : Number(item.accountLimit),
        accountQuantityUnlimited: item.accountLimit === '不限',
        concurrentNodeQuantity: item.concurrentNodes === '不限' ? 0 : Number(item.concurrentNodes),
        concurrentNodeQuantityUnlimited: item.concurrentNodes === '不限',
        priceSettings: item.priceType.split(',').map((t: string) => mapPriceTypeFromCN(t)).filter(Boolean),
        pointsAmount: item.priceType.includes('积分') ? item.unitPrice : 0,
        currencyAmount: item.priceType.includes('货币') ? item.unitPrice : 0,
        priceUnit: mapUnitFromCN(item.priceUnit),
        enableStatus: 'ENABLED',
        selected: true,
        quantity: item.quantity || 1,
        duration: item.duration || 1,
        priceType: getDefaultPriceType({
          priceSettings: item.priceType.split(',').map((t: string) => mapPriceTypeFromCN(t)).filter(Boolean)
        } as any)
      })) as any
    } else {
      orderSuites.value = []
      ElMessage.error(res?.msg || '获取购物车信息失败')
    }
  } catch (error) {
    console.error('获取购物车信息失败:', error)
    orderSuites.value = []
    ElMessage.error('获取购物车信息失败')
  } finally {
    cartLoading.value = false
  }
}
// 处理数量变化
const handleQuantityChange = async (pricingId: number, quantity: number) => {
  try {
    await updateCartItem(pricingId, quantity, 1) // 暂时传递默认duration为1
  } catch (error) {
    console.error('更新数量失败:', error)
  }
}
// 处理年限变化
const handleDurationChange = async (pricingId: number, duration: number) => {
  try {
    // 这里可能需要根据实际接口调整,传递duration参数
    await updateCartItem(pricingId, 1, duration) // 暂时传递默认quantity为1
  } catch (error) {
    console.error('更新年限失败:', error)
  }
}
// 获取销售形式文本
@@ -868,121 +1103,301 @@
</script>
<style scoped lang="scss">
.default-main {
  padding: 20px;
  background-color: #f5f5f5;
  min-height: 100vh;
  position: relative;
  z-index: 1;
}
// 全局表头文字大小调整
:deep(.el-table__header) {
  th {
    .cell {
      font-size: 14px !important;
      font-weight: 700;
    }
  }
}
.search-card {
  margin-bottom: 20px;
}
.mt15 {
  margin-top: 15px;
}
.price-viewer-container {
  .pricing-table-container {
    .pricing-tabs {
      :deep(.el-tabs__header) {
        margin-bottom: 5px;
             .pricing-tabs {
             :deep(.el-tabs__header) {
         margin-bottom: 2px;
       }
      /* 居中 tabs 导航 */
      :deep(.el-tabs__nav-wrap) {
        display: flex;
        justify-content: center;
      }
      :deep(.el-tabs__item.is-active) {
        font-weight: bold;
      :deep(.el-tabs__nav) {
        margin: 0 auto;
        border: none !important;
      }
             /* 普通 tabs 样式 */
       :deep(.el-tabs--card > .el-tabs__header .el-tabs__nav) {
         border: none;
         border-radius: 0;
         background: transparent;
         box-shadow: none;
         display: flex;
         gap: 8px;
       }
       :deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
         border: 1px solid #e4e7ed;
         border-radius: 6px;
         margin: 0;
         padding: 14px 24px;
         color: #606266;
         background: #fff;
         transition: all .2s ease;
         font-size: 14px;
         font-weight: 500;
         box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
         &:hover {
           border-color: #409eff;
           box-shadow: 0 4px 8px rgba(64, 158, 255, 0.15);
         }
       }
             :deep(.el-tabs__item:hover) {
         color: #409eff;
         background: #f5f7fa;
       }
       :deep(.el-tabs__item.is-active) {
         font-weight: 600;
         color: #409eff;
         background: #ecf5ff;
         border-color: #409eff;
         box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
         &::after {
           content: '';
           position: absolute;
           bottom: -1px;
           left: 0;
           right: 0;
           height: 3px;
           background: #409eff;
           border-radius: 0 0 6px 6px;
         }
       }
    }
    .pricing-table-wrapper {
      overflow-x: auto;
         .pricing-table-wrapper {
       overflow-x: auto;
       background: #fff;
       border-radius: 8px;
       box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
       padding: 0;
       border: 1px solid #e4e7ed;
       margin-top: 2px;
      
      .pricing-table {
        width: 100%;
        border-collapse: collapse;
        border: 1px solid #e4e7ed;
        background: #fff;
        th, td {
          border: 1px solid #e4e7ed;
          padding: 6px 8px;
          text-align: center;
          vertical-align: middle;
          font-size: 16px;
        }
        tr th:first-child, td:first-child{
          font-weight: 700;
          color: #303133;
        }
        th {
          background-color: #f5f7fa;
          font-weight: 600;
          color: #303133;
        }
        .feature-column {
          width: 120px;
          background-color: #f5f7fa;
        }
        .sub-header {
          th {
            background-color: #fafafa;
            font-weight: 500;
              font-size: 16px;
            color: #606266;
             .pricing-table {
         width: 100%;
         border-collapse: collapse;
         background: #fff;
         border: 1px solid #e4e7ed;
         border-radius: 8px;
         overflow: visible;
         // 统一所有单元格样式
         th, td {
            border-top: 1px solid #e4e7ed !important;
            border-bottom: 1px solid #e4e7ed !important;
            border-left: none !important;
            border-right: none !important;
            padding: 8px 10px;
            text-align: center;
            vertical-align: middle;
            font-size: 14px;
            line-height: 1.5;
            color: #303133;
            background: #fff;
            transition: all 0.2s ease;
          }
        }
        .feature-label {
          background-color: #f5f7fa;
          font-weight: 500;
          color: #606266;
          text-align: center;
        }
        .feature-value {
          color: #606266;
          background: #fafafa; /* 行为灰色 */
        }
         // 第一列样式
         tr th:first-child, td:first-child{
           font-weight: 700;
           color: #303133;
           background: #f8fafc;
           text-align: left;
           padding-left: 16px;
         }
         // 表头样式
         th {
           background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
           font-weight: 600;
           color: #303133;
           border-bottom: 2px solid #e4e7ed !important;
           border-left: none !important;
           border-right: none !important;
           position: relative;
         }
         .feature-column {
           width: 140px;
           min-width: 140px;
         }
         // 子表头样式已移除,销售形式行现在是普通行
         // 功能标签样式
         .feature-label {
           font-weight: 600;
           color: #303133;
           text-align: left;
           background: #f8fafc;
           border-right: none !important;
         }
         // 功能值样式
         .feature-value {
           color: #303133;
           background: #fff;
           font-weight: 500;
         }
         // 行悬停效果
         tbody tr:hover td {
           background: #f0f7ff;
           border-color: #b3d8ff !important;
         }
         // 偶数行背景
         tbody tr:nth-child(even) td {
           background: #fafbfc;
         }
         tbody tr:nth-child(even):hover td {
           background: #e6f3ff;
         }
         // 强制确保最后一行有完整的边框
         tbody tr:last-child td {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 强制确保所有单元格都有完整的边框
         tbody td {
           border: 1px solid #e4e7ed !important;
         }
         // 特别处理表格底部边框
         tbody tr:last-child {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 使用更具体的选择器确保边框显示
         .pricing-table tbody tr:last-child td {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 确保表格本身有完整的边框
         .pricing-table {
           border: 1px solid #e4e7ed !important;
         }
         // 特别处理套件选择行的边框
         .pricing-table tbody tr:last-child .feature-label,
         .pricing-table tbody tr:last-child .feature-value {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 确保所有行都有完整的边框
         .pricing-table tbody tr td {
           border-top: 1px solid #e4e7ed !important;
           border-bottom: 1px solid #e4e7ed !important;
           border-left: none !important;
           border-right: none !important;
         }
        
        .order-methods {
          display: flex;
          flex-direction: column;
          gap: 4px;
          gap: 6px;
          align-items: center;
          
          .method-item {
            font-size: 16px;
            color: #409eff;
            font-size: 12px;
            color: #3a7afe;
            background: #f4f7ff;
            border: 1px solid #e3ebff;
            border-radius: 999px;
            padding: 2px 10px;
          }
        }
        
        .price-info {
          display: flex;
          flex-direction: column; /* 竖排 */
          gap: 8px;
          align-items: center;
          .price-item {
            display: flex;
            align-items: flex-start;
            flex-direction: column;
            gap: 6px;
            .price-lable-icon{
                 .price-info {
           display: flex;
           flex-direction: column;
           gap: 8px;
           align-items: center;
                       .price-item {
              display: flex;
              align-items: center;
              gap: 6px;
            }
            .price-label {
              font-size: 16px;
              color: #606266;
            }
            .price-value {
              font-weight: 500;
              font-size: 20px;
              &.points {
                color: #e7900d;
              }
              &.currency {
                color: #f56c6c;
              }
              &.free {
                color: #67c23a;
              }
            }
            .price-icon.points { color: #e6a23c; }
            .price-icon.currency { color: #f56c6c; }
          }
              padding: 6px 10px;
              border: 1px solid #f0f2f5;
              border-radius: 6px;
              background: #fff;
              box-shadow: inset 0 -1px 0 rgba(0,0,0,0.02);
             .price-lable-icon {
               display: flex;
               align-items: center;
               gap: 4px;
             }
             .price-label {
               font-size: 12px;
               color: #909399;
               white-space: nowrap;
             }
             .price-value {
               font-weight: 600;
               font-size: 16px;
               white-space: nowrap;
               &.points {
                 color: #e7900d;
               }
               &.currency {
                 color: #10b981;
               }
               &.free {
                 color: #67c23a;
               }
             }
             .price-icon.points {
               color: #e6a23c;
               font-size: 14px;
             }
             .price-icon.currency {
               color: #16a34a;
               font-size: 16px;
               font-weight: 600;
               font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
             }
           }
          
                     .add-checkbox {
             margin-top: 4px;
@@ -994,7 +1409,7 @@
           justify-content: center;
           align-items: center;
           padding: 8px 0;
           .add-cart-icon { color: #409eff; font-size:22px}
           .add-cart-icon { color: #3a7afe; font-size:22px}
         }
      }
    }
@@ -1009,7 +1424,11 @@
.no-price-data {
  text-align: center;
  padding: 40px 0;
  padding: 60px 0;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  margin: 20px 0;
}
.price-value.agreement {
@@ -1036,14 +1455,23 @@
}
.order-header-card { 
  :deep(.el-card__body) { padding: 10px; }
  .basic-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px 16px;}
  .grid-item { font-size: 16px; color:#606266; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .label { color:#606266; }
  :deep(.el-card__body) { padding: 16px; }
  .basic-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px 16px;}
  .grid-item {
    font-size: 14px;
    color:#606266;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1;
  }
  .label { color:#606266; font-weight: 500; }
  .value { color:#303133; }
  .value.strong { font-weight: 600; }
  margin-bottom: 20px;
  margin-bottom: 10px;
  margin-top: 10px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.points-info-card {
@@ -1062,6 +1490,8 @@
.selected-suites-card {
  :deep(.el-card__body) { padding: 8px; }
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.suite-table-wrapper { overflow-x: auto; width: 100%; }
@@ -1070,63 +1500,255 @@
  border-collapse: collapse;
  background: #fff;
  table-layout: fixed;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.suite-table th,
.suite-table td {
  border: 1px solid #e4e7ed;
  padding: 6px 8px;
  padding: 8px 10px;
  vertical-align: middle;
  font-size: 16px;
  font-size: 14px;
  line-height: 1.5;
  color: #303133;
  background: #fff;
  transition: all 0.2s ease;
  text-align: center;
}
.suite-table thead th { background: #f0f2f5; color: #303133; font-weight: 600; font-size: 16px;}
.table-toolbar { display:flex; justify-content: space-between; align-items:center; padding: 4px 0 8px; }
.toolbar-left { display:flex; align-items:center; gap: 12px; width: 100%;justify-content: space-between;}
.toolbar-right { display:flex; align-items:center; gap: 12px; }
.summary-info { font-size: 14px; color: #606266; }
.summary-count { color: #409eff; font-weight: 600; }
.summary-points { color: #e6a23c; font-weight: 600; }
.suite-table thead th {
  background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
  color: #303133;
  font-weight: 600;
  font-size: 14px;
  border-bottom: 2px solid #e4e7ed;
  position: relative;
}
.suite-table .th-left { text-align: center; width: 21%;}
.suite-table .th-center { text-align: center; }
.suite-table tbody tr:hover td {
  background: #f0f7ff;
  border-color: #b3d8ff;
}
.suite-table tbody tr:nth-child(even) td {
  background: #fafbfc;
}
.suite-table tbody tr:nth-child(even):hover td {
  background: #e6f3ff;
}
// 确保套件表格最后一行有完整的边框
.suite-table tbody tr:last-child td {
  border-bottom: 1px solid #e4e7ed;
}
// 确保套件表格所有单元格都有完整的边框
.suite-table tbody td {
  border: 1px solid #e4e7ed;
}
.table-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  background: #f8fafc;
  border-top: 1px solid #e4e7ed;
  border-radius: 0 0 8px 8px;
}
.toolbar-left {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  justify-content: space-between;
}
.toolbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.summary-info {
  font-size: 14px;
  color: #606266;
  display: flex;
  align-items: center;
  gap: 8px;
}
.summary-count {
  color: #409eff;
  font-weight: 600;
  background: #ecf5ff;
  padding: 4px 8px;
  border-radius: 4px;
}
.summary-points {
  color: #e6a23c;
  font-weight: 600;
  background: #fdf6ec;
  padding: 4px 8px;
  border-radius: 4px;
}
.suite-table .th-detail { text-align: center; width: 25%;}
.suite-table .th-spec { text-align: center; width: 25%;}
.suite-table .th-price { text-align: center; width: 18%;}
.suite-table .th-quantity { text-align: center; width: 12%;}
.suite-table .th-years { text-align: center; width: 12%;}
.suite-table .th-action { text-align: center; width: 8%;}
// 详情、规格、价格列的内容左对齐
.suite-table .cell-detail,
.suite-table .cell-spec,
.suite-table .cell-price {
  text-align: left !important;
}
.cell-detail .detail-content { display: inline-flex; align-items: center; gap: 8px; }
.suite-name { font-weight: 600; color: #303133; }
.cell-spec .spec-line { color: #606266; font-size: 16px; line-height: 20px; }
.cell-spec-gg {display: flex; flex-direction: column; gap: 16px;
  .spec-line-gg{
    display: flex;
    gap: 30px;
.cell-spec .spec-line {
  color: #606266;
  font-size: 13px;
  line-height: 18px;
  padding: 2px 0;
  // 规格列中数值的统一颜色
  .spec-value {
    color: #409eff;
    font-weight: 500;
  }
}
.cell-price .price-inline { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.cell-price .price-tag.points { color: #E6A23C; font-weight: 600; }
.cell-price .price-tag.currency { color: #F56C6C; font-weight: 600; }
.cell-price .price-free { color: #67C23A; font-weight: 600; }
.cell-price .price-agreement { color: #409EFF; }
.cell-spec-gg {
  display: flex;
  flex-direction: column;
  gap: 12px;
  .spec-line-gg{
    display: flex;
    gap: 24px;
  }
}
.cell-price .price-inline {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}
.cell-price .price-tag.points {
  color: #E6A23C;
  font-weight: 600;
  background: #fdf6ec;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-price .price-tag.currency {
  color: #F56C6C;
  font-weight: 600;
  background: #fef0f0;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-price .price-free {
  color: #67C23A;
  font-weight: 600;
  background: #f0f9ff;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-price .price-agreement {
  color: #409EFF;
  background: #ecf5ff;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-summary .summary-item { font-size: 14px; color: #606266; line-height: 1.5; }
.order-status-panel {
  .steps-wrapper { padding: 6px 8px 0 8px; }
  .result-text { text-align: center; margin: 18px 0 12px; font-size: 18px; color: #303133; }
  .steps-wrapper {
    padding: 20px 16px;
    background: #fff;
    border-radius: 8px 8px 0 0;
    border-bottom: 1px solid #e4e7ed;
  }
  .result-text {
    text-align: center;
    margin: 24px 0 20px;
    font-size: 18px;
    color: #303133;
    font-weight: 500;
  }
  .order-info-card {
    :deep(.el-card__body) { padding: 0; }
    border-radius: 0 0 8px 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  }
  .order-info-title {
    background: #f5f7fa;
    padding: 12px 16px;
    background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
    padding: 16px 20px;
    font-weight: 600;
    border-bottom: 1px solid #ebeef5;
    border-bottom: 1px solid #e4e7ed;
    color: #303133;
  }
  .order-info-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid #e4e7ed;
    border-radius: 8px;
    overflow: hidden;
    td {
      padding: 12px 16px;
      border-bottom: 1px solid #ebeef5;
      padding: 10px 12px;
      border-bottom: 1px solid #e4e7ed;
      border-right: 1px solid #e4e7ed;
      vertical-align: middle;
      font-size: 14px;
      line-height: 1.5;
      color: #303133;
      background: #fff;
      transition: all 0.2s ease;
    }
    .label { width: 120px; color: #606266; }
    .value { color: #303133; }
    .link { text-align: right; }
    td:last-child {
      border-right: none;
    }
    tr:last-child td {
      border-bottom: none;
    }
    .label {
      width: 120px;
      color: #606266;
      font-weight: 500;
      background: #f8fafc;
      text-align: center;
    }
    .value {
      color: #303133;
      background: #fff;
    }
    .link {
      text-align: right;
      background: #fff;
    }
    tr:hover td {
      background: #f0f7ff;
      border-color: #b3d8ff;
    }
    tr:nth-child(even) td {
      background: #fafbfc;
    }
    tr:nth-child(even):hover td {
      background: #e6f3ff;
    }
  }
}
@@ -1136,4 +1758,100 @@
.order-summary .summary-value.highlight{ color:#409eff; }
.order-summary .summary-value.total{ color:#e6a23c; }
.order-summary .summary-value.currency{ color:#f56c6c; }
.footer {
  display: flex;
  flex-direction: row-reverse;
  margin-right: 50px;
  gap: 20px;
  margin-top: 10px;
  padding: 10px 0;
  .el-button {
    border-radius: 6px;
    font-weight: 500;
    padding: 12px 24px;
    &.el-button--primary {
      background: linear-gradient(135deg, #409eff 0%, #3a7afe 100%);
      border: none;
      box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
      &:hover {
        background: linear-gradient(135deg, #66b1ff 0%, #5a8cff 100%);
        box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
      }
    }
    &:not(.el-button--primary) {
      border: 1px solid #dcdfe6;
      background: #fff;
      &:hover {
        border-color: #409eff;
        color: #409eff;
      }
    }
  }
}
// 确保页面整体层级正确
:deep(.el-card) {
  position: relative;
  z-index: 1;
}
// 修复可能的全局z-index冲突
:deep(.el-table__body-wrapper) {
  z-index: 1 !important;
}
:deep(.el-table__header-wrapper) {
  z-index: 1 !important;
}
// 数量和年限输入框样式
.quantity-input,
.duration-input {
  width: 100px !important;
  height: 35px !important;
  :deep(.el-input__wrapper) {
    padding: 0 8px !important;
  }
  :deep(.el-input__inner) {
    width: 30px !important;
    text-align: center !important;
    min-width: 30px !important;
    max-width: 30px !important;
  }
  :deep(.el-input-number__decrease),
  :deep(.el-input-number__increase) {
    width: 32px !important;
    height: 32px !important;
  }
}
// 使用更强的选择器确保样式生效
.suite-table .quantity-input,
.suite-table .duration-input {
  :deep(.el-input__inner) {
    width: 40px !important;
    text-align: center !important;
    min-width: 40px !important;
    max-width: 40px !important;
    box-sizing: border-box !important;
  }
}
// 全局样式覆盖,确保输入框内部宽度生效
:deep(.el-input-number.quantity-input .el-input__inner),
:deep(.el-input-number.duration-input .el-input__inner) {
  width: 40px !important;
  text-align: center !important;
  min-width: 40px !important;
  max-width: 40px !important;
  box-sizing: border-box !important;
}
</style>