seatonwan9
2025-08-19 954446f5a88735b0033b49492ea7b658f2bb9e07
src/views/productManage/productPriceViewer/index.vue
@@ -9,7 +9,7 @@
  >
    <div class="price-viewer-container" v-loading="loading">
      <!-- 价格对比表格 -->
      <div class="pricing-table-container" v-if="priceList.length > 0">
      <div class="pricing-table-container" v-if="showPricePanel && priceList.length > 0">
        <el-tabs v-model="activeTab" type="card" class="pricing-tabs">
          <el-tab-pane 
            v-for="(tabData, tabKey) in groupedPriceData" 
@@ -19,7 +19,7 @@
          >
            <div class="pricing-table-wrapper">
              <table class="pricing-table">
                                 <thead>
                 <thead>
                   <tr>
                     <th class="feature-column">功能对比</th>
                     <th 
@@ -107,25 +107,27 @@
                          v-if="priceItem.priceSettings.includes('POINTS') && priceItem.pointsAmount > 0"
                          class="price-item"
                        >
                          <span class="price-label">积分:</span>
                          <span class="price-value points">
                            {{ formatNumber(priceItem.pointsAmount) }} 积分{{ getPriceUnitText(priceItem.priceUnit) }}
                          </span>
                         <div class="price-lable-icon">
                          <el-icon class="price-icon points"><Coin /></el-icon>
                          <span class="price-label">积分</span>
                         </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"
                        >
                          <span class="price-label">¥货币:</span>
                          <span class="price-value currency">
                            {{ formatNumber(priceItem.currencyAmount) }} 元{{ getPriceUnitText(priceItem.priceUnit) }}
                          </span>
                         <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('FREE')"
                          class="price-item"
                        >
                          <span class="price-label">¥货币:</span>
                          <span class="price-label">费用</span>
                          <span class="price-value free">免费</span>
                        </div>
                                                 <div 
@@ -151,7 +153,7 @@
                           :model-value="selectedSuiteIds.has(priceItem.id)"
                           @change="handleSuiteSelect(priceItem.id, $event)"
                         >
                           添加
                          <el-icon class="add-cart-icon"><ShoppingCart /></el-icon>
                         </el-checkbox>
                       </div>
                     </td>
@@ -162,6 +164,159 @@
          </el-tab-pane>
        </el-tabs>
      </div>
      <!-- 内嵌订购信息面板 -->
      <div class="order-panel" v-else-if="showOrderPanel">
        <!-- 第一部分:产品信息 + 汇总 + 操作(静态数据) -->
        <el-card class="order-header-card" shadow="never">
          <div class="basic-grid">
            <div class="grid-item">
              <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>
            </div>
            <div class="grid-item">
              <span class="label">提供者:</span><span class="value">{{ productHeader.provider }}</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>
            </div>
            <div class="grid-item">
              <span class="label">部门:</span><span class="value">{{ userInfo.department }}</span>
            </div>
          </div>
        </el-card>
        <!-- 第二部分:选购产品列表 -->
        <el-card class="selected-suites-card" shadow="never">
          <div v-if="orderSuites.length > 0" class="suite-table-wrapper">
            <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>
                </tr>
              </thead>
              <tbody>
                <tr v-for="(suite, idx) in orderSuites" :key="suite.id">
                  <td class="cell-detail">
                    <div class="detail-content">
                      <el-checkbox v-model="suite.selected" />
                      <span class="suite-name">{{ getProductSuiteText(suite.productSuite) }}</span>
                    </div>
                  </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>
                    <div  class="spec-line-gg">
                      <div class="spec-line">账户数量:{{ suite.accountQuantityUnlimited ? '不限' : suite.accountQuantity }}</div>
                      <div class="spec-line">并发节点数:{{ suite.concurrentNodeQuantityUnlimited ? '不限' : suite.concurrentNodeQuantity }}</div>
                    </div>
                  </td>
                  <td class="cell-price">
                    <div class="price-inline">
                      <el-select v-model="suite.priceType" size="large" style="width: 90px">
                        <el-option v-for="t in getAvailablePriceTypes(suite)" :key="t.value" :label="t.label" :value="t.value" />
                      </el-select>
                      <div class="price-display">
                        <template v-if="suite.priceType === 'POINTS'">
                          <span class="price-tag points">{{ formatNumber(suite.pointsAmount) }}</span>
                          <span class="price-unit">{{ getUnitSuffix(suite.priceUnit) }}</span>
                        </template>
                        <template v-else-if="suite.priceType === 'CURRENCY'">
                          <span class="price-tag currency">{{ formatNumber(suite.currencyAmount) }}</span>
                          <span class="price-unit">{{ getUnitSuffix(suite.priceUnit) }}</span>
                        </template>
                        <template v-else-if="suite.priceType === 'AGREEMENT'">
                          <span class="price-agreement">/{{ getPriceUnitText(suite.priceUnit) }}</span>
                        </template>
                        <template v-else>
                          <span class="price-free">/{{ getPriceUnitText(suite.priceUnit) }}</span>
                        </template>
                      </div>
                    </div>
                  </td>
                  <td class="th-center">
                    <el-input-number v-model="suite.quantity" :min="1" :max="999" :controls="false" size="small" />
                  </td>
                  <td class="th-center">
                    <el-input-number v-model="suite.duration" :min="1" :max="10" :controls="false" size="small" />
                  </td>
                  <td class="th-center">
                    <el-button type="danger" link @click="removeSuite(idx)">删除</el-button>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
          <el-empty v-else description="暂无选中的软件套件" />
          <div class="table-toolbar">
            <div class="toolbar-left">
              <el-checkbox v-model="selectAllChecked">全选</el-checkbox>
              <span class="summary-info">
                已选 <span class="summary-count">{{ selectedCount }}</span> 件产品
                总计 <span class="summary-points">{{ formatNumber(totalByType.points) }}</span> 积分
              </span>
            </div>
          </div>
        </el-card>
      </div>
      <!-- 订单状态展示面板 -->
      <div class="order-status-panel" v-else-if="showOrderStatus">
        <div class="steps-wrapper">
          <el-steps :active="activeStep" align-center finish-status="success" process-status="process">
            <el-step title="提交申请" :description="orderStatus.submitTime" />
            <el-step title="待授权" />
            <el-step title="交易确认" />
            <el-step title="评价" />
          </el-steps>
        </div>
        <div class="result-text">
          您的产品订购申请已成功提交, 请等待授权
        </div>
        <el-card class="order-info-card" shadow="never">
          <div class="order-info-title">订单信息</div>
          <table class="order-info-table">
            <tbody>
              <tr>
                <td class="label">订单编号:</td>
                <td class="value">{{ orderStatus.id }}</td>
                <td class="label">申请时间:</td>
                <td class="value">{{ orderStatus.applyTime }}</td>
              </tr>
              <tr>
                <td class="label">产品名称:</td>
                <td class="value">{{ orderStatus.productName }}</td>
                <td class="label">提供者:</td>
                <td class="value">{{ orderStatus.provider }}</td>
              </tr>
              <tr>
                <td class="label">订单状态:</td>
                <td class="value">{{ statusText(orderStatus.status) }}</td>
                <td></td>
                <td class="value link">
                  <el-link type="primary" @click="viewOrder">查看订单信息</el-link>
                </td>
              </tr>
            </tbody>
          </table>
        </el-card>
      </div>
      
      <!-- 无价格数据时的提示 -->
      <div v-else class="no-price-data">
@@ -169,27 +324,31 @@
      </div>
    </div>
    <template #footer>
    <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>
  <!-- 产品订购对话框 -->
  <ProductOrderDialog
    v-model="showOrderDialog"
    :product-id="props.productId"
    :suite-ids="Array.from(selectedSuiteIds)"
    @order-success="handleOrderSuccess"
  />
  <!-- 产品订购对话框移除,改为内嵌展示 -->
</template>
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import { ElMessage } from 'element-plus'
import ProductOrderDialog from '../productOrderDialog/index.vue'
import { useRoute } from 'vue-router'
import productPricingApi from '@/api/productPricingApi'
import { ShoppingCart, Coin, Money } from '@element-plus/icons-vue'
const route = useRoute()
interface PriceItem {
  id: number
@@ -219,11 +378,18 @@
  (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>()
@@ -237,7 +403,126 @@
const priceList = ref<PriceItem[]>([])
const productName = ref('')
const selectedSuiteIds = ref<Set<number>>(new Set())
const showOrderDialog = ref(false)
const showOrderPanel = ref(false)
const showPricePanel = ref(true)
const showOrderStatus = ref(false)
type PriceTypeKey = 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'
interface OrderSuite extends PriceItem {
  selected: boolean
  quantity: number
  duration: number
  priceType?: PriceTypeKey
}
const orderSuites = ref<OrderSuite[]>([])
// 订单状态相关
type OrderStatus = 'SUBMITTED' | 'AUTHORIZED' | 'CONFIRMED' | 'EVALUATED'
interface OrderStatusDetail {
  id: string
  productName: string
  provider: string
  status: OrderStatus
  submitTime: string
  applyTime: string
}
const orderStatus = ref<OrderStatusDetail>({
  id: '',
  productName: '',
  provider: '',
  status: 'SUBMITTED',
  submitTime: '',
  applyTime: ''
})
const activeStep = computed(() => {
  switch (orderStatus.value?.status) {
    case 'SUBMITTED': return 1
    case 'AUTHORIZED': return 2
    case 'CONFIRMED': return 3
    case 'EVALUATED': return 4
    default: return 1
  }
})
const statusText = (s: OrderStatus) => {
  const map: Record<OrderStatus, string> = {
    SUBMITTED: '待授权',
    AUTHORIZED: '已授权,待确认交易',
    CONFIRMED: '交易已确认,待评价',
    EVALUATED: '已完成'
  }
  return map[s]
}
const viewOrder = () => {
  window.alert('这里可跳转到订单详情页(示例)')
}
const selectedCount = computed(() => orderSuites.value.filter(s => s.selected).length)
const totalByType = computed(() => {
  const result = { points: 0, currency: 0 }
  orderSuites.value
    .filter(s => s.selected)
    .forEach(s => {
      const q = s.quantity || 1
      const d = s.duration || 1
      const multiplier = (s.priceUnit === 'SET_PER_YEAR' || s.priceUnit === 'YEAR') ? d : 1
      switch (s.priceType) {
        case 'POINTS':
          result.points += (s.pointsAmount || 0) * q * multiplier
          break
        case 'CURRENCY':
          result.currency += (s.currencyAmount || 0) * q * multiplier
          break
      }
    })
  return result
})
// 第一部分:静态积分信息展示
const pointsInfo = ref({
  accountName: '个人积分账户',
  balance: 50000,
  frozen: 0,
})
// 产品信息/用户信息(静态)
const productHeader = ref({
  name: '中交方远智能实测实量管理系统',
  industry: '公路,市政,建筑',
  provider: '中交建筑集团第一工程有限公司'
})
const userInfo = ref({
  name: '张静',
  unit: '信科集团',
  department: '门户系统临时组'
})
// 全选
const selectAllChecked = ref(false)
watch(selectAllChecked, (val) => {
  orderSuites.value.forEach(s => { s.selected = !!val })
})
const getAvailablePriceTypes = (suite: OrderSuite) => {
  const t: { label: string; value: PriceTypeKey }[] = []
  if (suite.priceSettings.includes('POINTS')) t.push({ label: '积分', value: 'POINTS' })
  if (suite.priceSettings.includes('CURRENCY')) t.push({ label: '货币', value: 'CURRENCY' })
  if (suite.priceSettings.includes('AGREEMENT')) t.push({ label: '协议', value: 'AGREEMENT' })
  if (suite.priceSettings.includes('FREE')) t.push({ label: '免费', value: 'FREE' })
  return t
}
const getDefaultPriceType = (suite: OrderSuite): PriceTypeKey => {
  if (suite.priceSettings.includes('POINTS')) return 'POINTS'
  if (suite.priceSettings.includes('CURRENCY')) return 'CURRENCY'
  if (suite.priceSettings.includes('AGREEMENT')) return 'AGREEMENT'
  return 'FREE'
}
const getUnitSuffix = (unit?: string) => {
  const map: Record<string, string> = { SET: '/套', SET_PER_YEAR: '/套/年', YEAR: '/年' }
  return map[unit || ''] || ''
}
const removeSuite = (index: number) => { orderSuites.value.splice(index, 1) }
// 弹窗标题
const dialogTitle = computed(() => {
@@ -277,197 +562,43 @@
  )
})
// 模拟价格数据
const mockPriceData: Record<string, PriceItem[]> = {
  '10001': [
    {
      id: 1,
      productId: '10001',
      productSuite: 'ENTERPRISE_PRIVATE_SAAS_LICENSE',
      salesForm: 'BUYOUT',
      customerObject: 'ENTERPRISE',
      accountQuantity: 50,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['POINTS', 'CURRENCY', 'AGREEMENT'],
      pointsAmount: 50000,
      currencyAmount: 50000,
      priceUnit: 'SET',
      enableStatus: 'ENABLED'
    },
    {
      id: 2,
      productId: '10001',
      productSuite: 'ENTERPRISE_PRIVATE_SAAS_LICENSE',
      salesForm: 'LEASE',
      customerObject: 'ENTERPRISE',
      accountQuantity: 0,
      accountQuantityUnlimited: true,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['POINTS', 'CURRENCY', 'AGREEMENT'],
      pointsAmount: 800000,
      currencyAmount: 800000,
      priceUnit: 'YEAR',
      enableStatus: 'ENABLED'
    },
    {
      id: 3,
      productId: '10001',
      productSuite: 'ENTERPRISE_PRIVATE_SAAS_OTA',
      salesForm: 'OTA',
      customerObject: 'ENTERPRISE',
      accountQuantity: 50,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['CURRENCY'],
      pointsAmount: 0,
      currencyAmount: 7500,
      priceUnit: 'SET_PER_YEAR',
      enableStatus: 'ENABLED'
    },
    {
      id: 4,
      productId: '10001',
      productSuite: 'ENTERPRISE_PRIVATE_SAAS_USER_INCREMENT',
      salesForm: 'PRIVATE_INCREMENT',
      customerObject: 'ENTERPRISE',
      accountQuantity: 100,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 100,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['CURRENCY'],
      pointsAmount: 0,
      currencyAmount: 10000,
      priceUnit: 'YEAR',
      enableStatus: 'ENABLED'
    },
    {
      id: 9,
      productId: '10001',
      productSuite: 'PROJECT_PRIVATE_SAAS_OTA',
      salesForm: 'OTA',
      customerObject: 'PROJECT_DEPARTMENT',
      accountQuantity: 50,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['FREE'],
      pointsAmount: 0,
      currencyAmount: 0,
      priceUnit: 'SET',
      enableStatus: 'ENABLED'
    },
    {
      id: 10,
      productId: '10001',
      productSuite: 'PROJECT_PRIVATE_SAAS_USER_INCREMENT',
      salesForm: 'PRIVATE_INCREMENT',
      customerObject: 'PROJECT_DEPARTMENT',
      accountQuantity: 50,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['CURRENCY'],
      pointsAmount: 0,
      currencyAmount: 50000,
      priceUnit: 'YEAR',
      enableStatus: 'ENABLED'
    },
    {
      id: 5,
      productId: '10001',
      productSuite: 'ENTERPRISE_PUBLIC_SAAS_LICENSE',
      salesForm: 'LEASE',
      customerObject: 'PERSONAL',
      accountQuantity: 50,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['CURRENCY'],
      pointsAmount: 0,
      currencyAmount: 15000,
      priceUnit: 'YEAR',
      enableStatus: 'ENABLED'
    },
    {
      id: 6,
      productId: '10001',
      productSuite: 'ENTERPRISE_PUBLIC_SAAS_USER_INCREMENT',
      salesForm: 'PUBLIC_INCREMENT',
      customerObject: 'PERSONAL',
      accountQuantity: 50,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 50,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['CURRENCY'],
      pointsAmount: 0,
      currencyAmount: 5000,
      priceUnit: 'YEAR',
      enableStatus: 'ENABLED'
    },
    {
      id: 7,
      productId: '10001',
      productSuite: 'ENTERPRISE_PUBLIC_SAAS_CLOUD',
      salesForm: 'CLOUD',
      customerObject: 'PERSONAL',
      accountQuantity: 1,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 1,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['FREE'],
      pointsAmount: 0,
      currencyAmount: 0,
      priceUnit: 'SET',
      enableStatus: 'ENABLED'
    }
  ],
  '10002': [
    {
      id: 8,
      productId: '10002',
      productSuite: 'ENTERPRISE_PRIVATE_SAAS_LICENSE',
      salesForm: 'BUYOUT',
      customerObject: 'ENTERPRISE',
      accountQuantity: 30,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 30,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['POINTS', 'CURRENCY', 'AGREEMENT'],
      pointsAmount: 30000,
      currencyAmount: 30000,
      priceUnit: 'SET',
      enableStatus: 'ENABLED'
    },
    {
      id: 11,
      productId: '10002',
      productSuite: 'PROJECT_PRIVATE_SAAS_OTA',
      salesForm: 'OTA',
      customerObject: 'PROJECT_DEPARTMENT',
      accountQuantity: 30,
      accountQuantityUnlimited: false,
      concurrentNodeQuantity: 30,
      concurrentNodeQuantityUnlimited: false,
      priceSettings: ['FREE'],
      pointsAmount: 0,
      currencyAmount: 0,
      priceUnit: 'SET',
      enableStatus: 'ENABLED'
    }
  ]
// 映射工具
function mapSalesFormFromCN(val: string) {
  const map: Record<string, string> = {
    '买断': 'BUYOUT',
    '租赁': 'LEASE',
    '私有增包量': 'PRIVATE_INCREMENT',
    '公有增包量': 'PUBLIC_INCREMENT',
    'OTA服务': 'OTA',
    '云服务': 'CLOUD',
    '资源包': 'RESOURCE_PACKAGE',
    '个人': 'PERSONAL',
  }
  return (map[val] as any) || val
}
function mapCustomerFromCN(val: string) {
  const map: Record<string, string> = { '企业': 'ENTERPRISE', '个人': 'PERSONAL', '项目部': 'PROJECT_DEPARTMENT' }
  return (map[val] as any) || val
}
function mapUnitFromCN(val: string) {
  const map: Record<string, string> = { '套': 'SET', '套/年': 'SET_PER_YEAR', '年': 'YEAR' }
  return (map[val] as any) || val
}
function mapPriceTypeFromCN(val: string): 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE' | undefined {
  const map: Record<string, any> = { '积分': 'POINTS', '货币': 'CURRENCY', '协议': 'AGREEMENT', '免费': 'FREE' }
  return map[val]
}
// 模拟产品名称数据
const mockProductNames: Record<string, string> = {
  '10001': '数字化产品A',
  '10002': '数字化产品B'
  '1': '数字化产品A',
  '2': '数字化产品B'
}
// 获取价格信息
// 获取价格信息(改为接口获取定价列表)
const fetchProductData = async (productId: string) => {
  if (!productId) {
    ElMessage.warning('未提供产品ID')
@@ -476,20 +607,53 @@
  loading.value = true
  try {
    // 模拟API调用
    await new Promise(resolve => setTimeout(resolve, 500))
    // 获取产品名称
    productName.value = mockProductNames[productId] || '未知产品'
    // 获取价格数据
    priceList.value = mockPriceData[productId] || []
    const res: any = await productPricingApi.listByProductId(productId)
    if (res?.code === 200) {
      const list = Array.isArray(res.data) ? res.data : []
      const mapped: PriceItem[] = list.map((it: any) => {
        const priceSettings = String(it.priceType || '')
          .split(',')
          .map((t: string) => mapPriceTypeFromCN(t))
          .filter(Boolean)
        const hasPoints = (priceSettings as string[]).includes('POINTS')
        const hasCurrency = (priceSettings as string[]).includes('CURRENCY')
        const accountUnlimited = it.accountLimit === '不限'
        const concurrentUnlimited = it.concurrentNodes === '不限'
        return {
          id: it.id,
          productId: String(it.productId || productId),
          productSuite: it.suiteName || '',
          salesForm: mapSalesFormFromCN(it.salesForm) as any,
          customerObject: mapCustomerFromCN(it.customerType) as any,
          accountQuantity: accountUnlimited ? 0 : Number(it.accountLimit || 0),
          accountQuantityUnlimited: accountUnlimited,
          concurrentNodeQuantity: concurrentUnlimited ? 0 : Number(it.concurrentNodes || 0),
          concurrentNodeQuantityUnlimited: concurrentUnlimited,
          priceSettings: priceSettings as any,
          pointsAmount: hasPoints ? Number(it.pointsPrice || 0) : 0,
          currencyAmount: hasCurrency ? Number(it.currencyPrice || 0) : 0,
          priceUnit: mapUnitFromCN(it.priceUnit) as any,
          enableStatus: it.isActive ? 'ENABLED' : 'DISABLED',
        }
      })
      priceList.value = mapped
    if (priceList.value.length === 0) {
      ElMessage.warning('该产品暂无价格信息')
      }
      // 如果后端返回了产品名称可以在此赋值;否则保持现有标题格式
      if (res.data && res.data.length > 0 && res.data[0].productName) {
        productName.value = res.data[0].productName
      }
    } else {
      priceList.value = []
      ElMessage.error(res?.msg || '获取定价列表失败')
    }
  } catch (error) {
    console.error('获取价格数据失败:', error)
    priceList.value = []
    ElMessage.error('获取价格数据失败')
  } finally {
    loading.value = false
@@ -513,6 +677,11 @@
// 关闭弹窗
const handleClose = () => {
  visible.value = false
  showOrderPanel.value = false
  showOrderStatus.value = false
  showPricePanel.value = true
  orderSuites.value = []
  selectedSuiteIds.value.clear()
}
// 套件选择处理
@@ -531,13 +700,57 @@
    ElMessage.warning('请选择要订购的软件套件')
    return
  }
  // 获取选中的价格项
  const selectedItems = priceList.value.filter(item => selectedSuiteIds.value.has(item.id))
  emit('order', selectedItems)
  // 构建内嵌订购列表
  orderSuites.value = selectedItems.map(it => ({
    ...it,
    selected: true,
    quantity: 1,
    duration: 1,
    priceType: getDefaultPriceType(it as any)
  })) as any
  showOrderPanel.value = true
  showPricePanel.value = false
}
// 处理订购结果
const handleOrderResult = (selectedItems: any[]) => {}
// 启用/停用按钮
function handleToggleEnableStatus(row: any) {
  // ... existing code ...
}
async function submitOrder() {
  const items = orderSuites.value.filter(s => s.selected)
  if (items.length === 0) {
    ElMessage.warning('请至少选择一个套件')
    return
  }
  // 检查是否有货币交易
  const hasCurrency = items.some(item => item.priceType === 'CURRENCY')
  if (hasCurrency) {
    ElMessage.error('暂不支持货币交易,请选择积分或协议支付方式')
    return
  }
  
  // 打开订购对话框
  showOrderDialog.value = true
  // 这里可对接实际下单接口;先做前端提示
  // 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()
  }
  // 显示订单状态面板
  showOrderPanel.value = false
  showPricePanel.value = false
  showOrderStatus.value = true
}
// 订购成功回调
@@ -681,7 +894,11 @@
          padding: 6px 8px;
          text-align: center;
          vertical-align: middle;
          font-size: 12px;
          font-size: 16px;
        }
        tr th:first-child, td:first-child{
          font-weight: 700;
          color: #303133;
        }
        
        th {
@@ -699,7 +916,7 @@
          th {
            background-color: #fafafa;
            font-weight: 500;
              font-size: 12px;
              font-size: 16px;
            color: #606266;
          }
        }
@@ -712,7 +929,8 @@
        }
        
        .feature-value {
          color: #303133;
          color: #606266;
          background: #fafafa; /* 行为灰色 */
        }
        
        .order-methods {
@@ -722,32 +940,36 @@
          align-items: center;
          
          .method-item {
            font-size: 13px;
            font-size: 16px;
            color: #409eff;
          }
        }
        
        .price-info {
          display: flex;
          flex-direction: column;
          flex-direction: column; /* 竖排 */
          gap: 8px;
          align-items: center;
          
          .price-item {
            display: flex;
            align-items: center;
            gap: 4px;
            align-items: flex-start;
            flex-direction: column;
            gap: 6px;
            .price-lable-icon{
              display: flex;
              gap: 6px;
            }
            .price-label {
              font-size: 13px;
              font-size: 16px;
              color: #606266;
            }
            
            .price-value {
              font-weight: 500;
              font-size: 20px;
              &.points {
                color: #e6a23c;
                color: #e7900d;
              }
              
              &.currency {
@@ -758,6 +980,8 @@
                color: #67c23a;
              }
            }
            .price-icon.points { color: #e6a23c; }
            .price-icon.currency { color: #f56c6c; }
          }
          
                     .add-checkbox {
@@ -770,6 +994,7 @@
           justify-content: center;
           align-items: center;
           padding: 8px 0;
           .add-cart-icon { color: #409eff; font-size:22px}
         }
      }
    }
@@ -792,6 +1017,7 @@
}
.product-price-dialog {
  :deep(.el-dialog__title) {
    font-size: 14px;
    font-weight: 600;
@@ -800,6 +1026,7 @@
  :deep(.el-dialog) {
    max-width: 95vw;
    min-width: 800px;
    height: 700px !important;
  }
  
  :deep(.el-dialog__body) {
@@ -807,4 +1034,106 @@
    overflow-y: auto;
  }
}
.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; }
  .value { color:#303133; }
  .value.strong { font-weight: 600; }
  margin-bottom: 20px;
  margin-top: 10px;
}
.points-info-card {
  :deep(.el-card__body) { padding: 8px 10px; }
  .points-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 8px 12px;
    .points-item { font-size: 12px; color: #606266; }
    .label { color: #606266; }
    .value { font-weight: 600; color: #303133; }
    .value.emphasis { color: #409eff; }
    .value.warn { color: #e6a23c; }
  }
}
.selected-suites-card {
  :deep(.el-card__body) { padding: 8px; }
}
.suite-table-wrapper { overflow-x: auto; width: 100%; }
.suite-table {
  width: 100%;
  border-collapse: collapse;
  background: #fff;
  table-layout: fixed;
}
.suite-table th,
.suite-table td {
  border: 1px solid #e4e7ed;
  padding: 6px 8px;
  vertical-align: middle;
  font-size: 16px;
}
.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 .th-left { text-align: center; width: 21%;}
.suite-table .th-center { text-align: center; }
.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-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-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; }
  .order-info-card {
    :deep(.el-card__body) { padding: 0; }
  }
  .order-info-title {
    background: #f5f7fa;
    padding: 12px 16px;
    font-weight: 600;
    border-bottom: 1px solid #ebeef5;
  }
  .order-info-table {
    width: 100%;
    border-collapse: collapse;
    td {
      padding: 12px 16px;
      border-bottom: 1px solid #ebeef5;
    }
    .label { width: 120px; color: #606266; }
    .value { color: #303133; }
    .link { text-align: right; }
  }
}
.order-summary { padding: 10px 0; display:flex; align-items:center; justify-content:flex-end; gap: 8px; }
.order-summary .summary-label { color:#606266; }
.order-summary .summary-value { font-weight:600; }
.order-summary .summary-value.highlight{ color:#409eff; }
.order-summary .summary-value.total{ color:#e6a23c; }
.order-summary .summary-value.currency{ color:#f56c6c; }
</style>