357a4c941549c9d65d74629c38b989683f5db0b4..95cb69b9f79893d7cd1319d754064c93e3fa4e2f
2025-08-19 seatonwan9
提交源码
95cb69 对比 | 目录
2025-08-19 seatonwan9
订购管理
ade0cb 对比 | 目录
2025-08-19 seatonwan9
提交源码
bf303e 对比 | 目录
2025-08-19 seatonwan9
定价管理-产品价格
954446 对比 | 目录
3个文件已添加
4个文件已修改
1629 ■■■■ 已修改文件
src/api/cartApi.ts 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/orderApi.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productPricingApi.ts 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/layout/components/sideBar/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/price/index.vue 454 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/product/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/productPriceViewer/index.vue 1035 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cartApi.ts
New file
@@ -0,0 +1,63 @@
import createAxios from '@/utils/axios'
// 购物车管理 API
const baseUrl = '/admin/api/cart'
const cartApi = {
  // 添加商品到购物车
  addToCart(data: any, userId: number, unitId: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/add`,
      method: 'post',
      data,
      params: { userId, unitId }
    }) as ApiPromise
  },
  // 从购物车移除商品
  removeFromCart(userId: number, unitId: number, pricingId: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/remove`,
      method: 'delete',
      params: { userId, unitId, pricingId }
    }) as ApiPromise
  },
  // 更新购物车商品数量
  updateCartItem(userId: number, unitId: number, pricingId: number, quantity: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/update`,
      method: 'put',
      params: { userId, unitId, pricingId, quantity }
    }) as ApiPromise
  },
  // 获取购物车信息
  getCartInfo(userId: number, unitId: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/info`,
      method: 'get',
      params: { userId, unitId }
    }) as ApiPromise
  },
  // 获取购物车商品列表
  getCartItems(userId: number, unitId: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/items`,
      method: 'get',
      params: { userId, unitId }
    }) as ApiPromise
  },
  // 清空购物车
  clearCart(userId: number, unitId: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/clear`,
      method: 'delete',
      params: { userId, unitId }
    }) as ApiPromise
  }
}
export default cartApi
src/api/orderApi.ts
New file
@@ -0,0 +1,26 @@
import createAxios from '@/utils/axios'
const baseUrl = '/admin/api/order'
const orderApi = {
  // 获取一次性防重复提交 Token
  getIdempotencyToken(userId?: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/idempotency/token`,
      method: 'get',
      params: userId ? { userId } : {}
    }) as ApiPromise
  },
  // 创建订单(在 headers 中携带 Idempotency-Token)
  createOrder(data: any, token: string): ApiPromise {
    return createAxios({
      url: `${baseUrl}/create`,
      method: 'post',
      data,
      headers: { 'Idempotency-Token': token }
    }) as ApiPromise
  }
}
export default orderApi
src/api/productPricingApi.ts
New file
@@ -0,0 +1,43 @@
import createAxios from '@/utils/axios'
// 产品定价管理 API
const baseUrl = '/admin/product-pricing'
const productPricingApi = {
  // 新增产品定价
  add(data: object): ApiPromise {
    return createAxios({
      url: `${baseUrl}/add`,
      method: 'post',
      data,
    }) as ApiPromise
  },
  // 更新产品定价
  update(data: object): ApiPromise {
    return createAxios({
      url: `${baseUrl}/update`,
      method: 'put',
      data,
    }) as ApiPromise
  },
  // 根据产品ID获取定价列表
  listByProductId(productId: string | number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/product/${productId}`,
      method: 'get',
    }) as ApiPromise
  },
  // 删除定价
  remove(id: string | number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/delete/${id}`,
      method: 'delete',
    }) as ApiPromise
  },
}
export default productPricingApi
src/views/layout/components/sideBar/index.vue
@@ -160,7 +160,7 @@
        "id": "4fa90aab-2e3c-4bc4-abbf-fbe536723fa2",
        "icon": "",
        "name": "查看报价",
        "url": "/producePrice/priceViewer",
        "url": "/product/priceViewer",
        "parentId": "266e3ff1-ca98-4946-8975-7526ef83cd5c",
        "sort": "3"
    }]
src/views/productManage/price/index.vue
@@ -18,13 +18,13 @@
      </template>
      <el-descriptions v-if="productDetail" :column="2" border>
        <el-descriptions-item label="产品名称">{{ productDetail.name }}</el-descriptions-item>
        <el-descriptions-item label="提报单位">{{ productDetail.submitUnit }}</el-descriptions-item>
        <el-descriptions-item label="提报人">{{ productDetail.submitter }}</el-descriptions-item>
        <el-descriptions-item label="行业领域">{{ productDetail.industry }}</el-descriptions-item>
        <el-descriptions-item label="单位工程">{{ productDetail.projectUnit }}</el-descriptions-item>
        <el-descriptions-item label="产业阶段">{{ productDetail.industryStage }}</el-descriptions-item>
        <el-descriptions-item label="产品类型">{{ productDetail.productType }}</el-descriptions-item>
        <el-descriptions-item label="产品名称" label-width="10%">{{ productDetail.name }}</el-descriptions-item>
        <el-descriptions-item label="提报单位" label-width="10%">{{ productDetail.submitUnit }}</el-descriptions-item>
        <el-descriptions-item label="提报人" label-width="10%">{{ productDetail.submitter }}</el-descriptions-item>
        <el-descriptions-item label="行业领域" label-width="10%">{{ productDetail.industry }}</el-descriptions-item>
        <el-descriptions-item label="单位工程" label-width="10%">{{ productDetail.projectUnit }}</el-descriptions-item>
        <el-descriptions-item label="产业阶段" label-width="10%">{{ productDetail.industryStage }}</el-descriptions-item>
        <el-descriptions-item label="产品类型"label-width="10%">{{ productDetail.productType }}</el-descriptions-item>
        <el-descriptions-item label="产品简介" :span="3">
          <div class="intro">{{ productDetail.description }}</div>
        </el-descriptions-item>
@@ -43,16 +43,22 @@
          </div>
        </template>
        <div class="pricing-cards-container" v-if="priceList.length > 0">
          <div class="pricing-cards-wrapper">
            <div
              v-for="pricing in priceList"
              :key="pricing.id"
              class="pricing-card"
              :class="{
                'pricing-card-enabled': pricing.enableStatus === 'ENABLED',
                'pricing-card-disabled': pricing.enableStatus === 'DISABLED'
              }"
            >
          <div
            v-for="group in priceList"
            :key="group.suiteName"
            class="pricing-group"
          >
            <div class="group-header">{{ group.suiteName }}</div>
            <div class="pricing-cards-wrapper">
              <div
                v-for="pricing in group.items"
                :key="pricing.id"
                class="pricing-card"
                :class="{
                  'pricing-card-enabled': pricing.enableStatus === 'ENABLED',
                  'pricing-card-disabled': pricing.enableStatus === 'DISABLED'
                }"
              >
              <div class="pricing-card-table">
                <div class="pricing-row">
                  <div class="pricing-cell label-cell">销售形式</div>
@@ -71,19 +77,19 @@
                  <div class="pricing-cell value-cell purchase-methods-cell">
                    <div class="method-item">
                      <span class="method-label">积分:</span>
                      <span class="method-value points">{{ pricing.priceSettings?.includes('POINTS') ? (pricing.pointsAmount || '-') + '/套' : '-' }}</span>
                      <span class="method-value points">{{ pricing.priceSettings?.includes('POINTS') ? (pricing.pointsPrice || '-') + '/' + getPriceUnitText(pricing.priceUnit) : '-' }}</span>
                    </div>
                    <div class="method-item">
                    <div class="method-item" v-if="pricing.priceSettings?.includes('CURRENCY')">
                      <span class="method-label">货币:</span>
                      <span class="method-value currency">{{ pricing.priceSettings?.includes('CURRENCY') ? (pricing.currencyAmount || '-') + '/套' : '-' }}</span>
                      <span class="method-value currency">{{ (pricing.currencyPrice || '-') + '/' + getPriceUnitText(pricing.priceUnit) }}</span>
                    </div>
                    <div class="method-item">
                    <div class="method-item" v-if="pricing.priceSettings?.includes('AGREEMENT')">
                      <span class="method-label">协议:</span>
                      <span class="method-value agreement">{{ pricing.priceSettings?.includes('AGREEMENT') ? '每套' : '-' }}</span>
                      <span class="method-value agreement">{{ '/' + getPriceUnitText(pricing.priceUnit) }}</span>
                    </div>
                    <div class="method-item">
                    <div class="method-item" v-if="pricing.priceSettings?.includes('FREE')">
                      <span class="method-label">免费:</span>
                      <span class="method-value free">{{ pricing.priceSettings?.includes('FREE') ? '免费' : '-' }}</span>
                      <span class="method-value free">{{ '/' + getPriceUnitText(pricing.priceUnit) }}</span>
                    </div>
                  </div>
                </div>
@@ -109,6 +115,7 @@
                  </el-icon>
                  {{ pricing.enableStatus === 'ENABLED' ? '停用' : '启用' }}
                </span>
              </div>
              </div>
            </div>
          </div>
@@ -136,57 +143,48 @@
        size="large"
        class="price-form"
      >
        <el-row :gutter="8">
          <el-col :span="12">
            <el-form-item label="产品套件" prop="productSuite">
              <el-select v-model="formData.productSuite" placeholder="请选择" style="width: 100%">
        <el-form-item label="产品套件" prop="productSuite">
              <el-select v-model="formData.productSuite" placeholder="请选择" style="width: 60%">
                <el-option v-for="opt in productSuiteOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户对象" prop="customerObject">
        <el-form-item label="客户对象" prop="customerObject">
              <el-radio-group v-model="formData.customerObject">
                <el-radio v-for="opt in customerObjectOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        </el-form-item>
        <el-form-item label="销售形式" prop="salesForm" class="sales-form-item">
          <el-radio-group v-model="formData.salesForm" class="sales-form-group">
            <el-radio v-for="opt in salesFormOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-row :gutter="8">
          <el-col :span="12">
            <el-form-item label="账户数量" prop="accountQuantity">
              <div class="quantity-input-group">
                <el-input-number v-model="formData.accountQuantity" :min="1" :controls="false" style="width: 180px" :disabled="formData.accountQuantityUnlimited" />
                <el-checkbox v-model="formData.accountQuantityUnlimited" style="margin-left: 6px">不限</el-checkbox>
              </div>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="并发节点" prop="concurrentNodeQuantity">
              <div class="quantity-input-group">
                <el-input-number v-model="formData.concurrentNodeQuantity" :min="1" :controls="false" style="width: 180px" :disabled="formData.concurrentNodeQuantityUnlimited" />
                <el-checkbox v-model="formData.concurrentNodeQuantityUnlimited" style="margin-left: 6px">不限</el-checkbox>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="价格设置" prop="priceSettings">
          <div class="price-settings">
            <div class="price-setting-item">
              <el-checkbox label="POINTS" :disabled="formData.priceSettings.includes('FREE')" v-model="checkboxPoints">积分</el-checkbox>
              <el-input-number v-if="formData.priceSettings.includes('POINTS')" v-model="formData.pointsAmount" :min="0" :precision="2" :controls="false" placeholder="积分金额" style="width: 120px; margin-left: 6px" />
              <el-input-number v-if="formData.priceSettings.includes('POINTS')" v-model="formData.pointsPrice" :min="0" :precision="2" :controls="false" placeholder="积分金额" style="width: 120px; margin-left: 6px" />
            </div>
            <div class="price-setting-item">
              <el-checkbox label="CURRENCY" :disabled="formData.priceSettings.includes('FREE')" v-model="checkboxCurrency">货币</el-checkbox>
              <el-input-number v-if="formData.priceSettings.includes('CURRENCY')" v-model="formData.currencyAmount" :min="0" :precision="2" :controls="false" placeholder="货币金额" style="width: 120px; margin-left: 6px" />
              <el-input-number v-if="formData.priceSettings.includes('CURRENCY')" v-model="formData.currencyPrice" :min="0" :precision="2" :controls="false" placeholder="货币金额" style="width: 120px; margin-left: 6px" />
            </div>
            <div class="price-setting-item">
              <el-checkbox label="AGREEMENT" :disabled="formData.priceSettings.includes('FREE')" v-model="checkboxAgreement">协议</el-checkbox>
@@ -197,24 +195,20 @@
          </div>
        </el-form-item>
        <el-row :gutter="8">
          <el-col :span="12">
            <el-form-item label="价格单位" prop="priceUnit">
              <el-select v-model="formData.priceUnit" placeholder="请选择" style="width: 100%">
                <el-option v-for="opt in priceUnitOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="启用状态" prop="enableStatus">
              <el-radio-group v-model="formData.enableStatus">
                <el-radio v-for="opt in enableStatusOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注">
          <el-input type="textarea" v-model="formData.remark" :rows="3" placeholder="请输入备注" />
@@ -244,6 +238,8 @@
import { useRoute } from 'vue-router'
import { Goods, Lock, Unlock } from '@element-plus/icons-vue'
import ProductPriceViewer from '@/views/productManage/productPriceViewer/index.vue'
import productPricingApi from '@/api/productPricingApi'
interface ProductDetail {
  id: string
@@ -269,8 +265,8 @@
  concurrentNodeQuantity: number | ''
  concurrentNodeQuantityUnlimited: boolean
  priceSettings: Array<'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'>
  pointsAmount: number | ''
  currencyAmount: number | ''
  pointsPrice: number | ''
  currencyPrice: number | ''
  priceUnit: '' | 'SET' | 'SET_PER_YEAR' | 'YEAR'
  enableStatus: 'ENABLED' | 'DISABLED'
  authorizationStartTime?: string
@@ -302,8 +298,8 @@
// 模拟产品详情数据源
const mockProductMap: Record<string, ProductDetail> = {
  '10001': {
    id: '10001',
  '1': {
    id: '1',
    name: '数字化产品A',
    submitUnit: '中交一公局',
    submitter: '张三',
@@ -314,8 +310,8 @@
    description: '本产品定位为以建设期BIM数字资产作为数字底盘,结合项目运营维保需求的实时性、交互性、便捷性的三维可视化运维管理系统。系统提供项目数字化、智能化运维管理功能,能够解决建筑运行维护管理中的实际问题,实现信息快速整合与查询、信息有效共享与传递,提升项目综合管理与维护水平。',
    shelfStatus: '待上架'
  },
  '10002': {
    id: '10002',
  '2': {
    id: '2',
    name: '数字化产品B',
    submitUnit: '中交二航局',
    submitter: '李四',
@@ -343,13 +339,13 @@
  productSuite: '',
  salesForm: 'BUYOUT',
  customerObject: 'ENTERPRISE',
  accountQuantity: '',
  accountQuantity: 1,
  accountQuantityUnlimited: false,
  concurrentNodeQuantity: '',
  concurrentNodeQuantity: 1,
  concurrentNodeQuantityUnlimited: false,
  priceSettings: [],
  pointsAmount: '',
  currencyAmount: '',
  pointsPrice: '',
  currencyPrice: '',
  priceUnit: '',
  enableStatus: 'ENABLED',
  authorizationStartTime: undefined,
@@ -359,15 +355,15 @@
// 选项
const productSuiteOptions = [
  { label: '企业私有SaaS版许可', value: 'ENTERPRISE_PRIVATE_SAAS_LICENSE' },
  { label: '企业私有SaaS版OTA服务', value: 'ENTERPRISE_PRIVATE_SAAS_OTA' },
  { label: '企业私有SaaS版用户增量包', value: 'ENTERPRISE_PRIVATE_SAAS_USER_INCREMENT' },
  { label: '企业公有SaaS版许可', value: 'ENTERPRISE_PUBLIC_SAAS_LICENSE' },
  { label: '企业公有SaaS版OTA服务', value: 'ENTERPRISE_PUBLIC_SAAS_OTA' },
  { label: '企业公有SaaS版用户增量包', value: 'ENTERPRISE_PUBLIC_SAAS_USER_INCREMENT' },
  { label: '个人公有SaaS化许可', value: 'PERSONAL_PUBLIC_SAAS_LICENSE' },
  { label: 'web软件', value: 'WEB_SOFTWARE' },
  { label: '桌面软件', value: 'DESKTOP_SOFTWARE' }
  { label: '企业私有SaaS版许可', value: '企业私有SaaS版许可' },
  { label: '企业私有SaaS版OTA服务', value: '企业私有SaaS版OTA服务' },
  { label: '企业私有SaaS版用户增量包', value: '企业私有SaaS版用户增量包' },
  { label: '企业公有SaaS版许可', value: '企业公有SaaS版许可' },
  { label: '企业公有SaaS版OTA服务', value: '企业公有SaaS版OTA服务' },
  { label: '企业公有SaaS版用户增量包', value: '企业公有SaaS版用户增量包' },
  { label: '个人公有SaaS化许可', value: '个人公有SaaS化许可' },
  { label: 'web软件', value: 'web软件' },
  { label: '桌面软件', value: '桌面软件' }
]
const salesFormOptions = [
  { label: '买断', value: 'BUYOUT' },
@@ -418,8 +414,8 @@
    // 若选择 FREE,清空其他
    if (key === 'FREE') {
      formData.priceSettings = ['FREE']
      formData.pointsAmount = ''
      formData.currencyAmount = ''
      formData.pointsPrice = ''
      formData.currencyPrice = ''
      return
    }
    // 若此前有 FREE,去掉 FREE
@@ -456,8 +452,8 @@
    validator: (_: any, value: any[], cb: any) => {
      if (!value || value.length === 0) return cb(new Error('请至少选择一种价格设置'))
      if (value.includes('FREE') && value.length > 1) return cb(new Error('免费选项不能与其他选项同时选择'))
      if (value.includes('POINTS') && (formData.pointsAmount === '' || formData.pointsAmount === undefined)) return cb(new Error('请输入积分金额'))
      if (value.includes('CURRENCY') && (formData.currencyAmount === '' || formData.currencyAmount === undefined)) return cb(new Error('请输入货币金额'))
      if (value.includes('POINTS') && (formData.pointsPrice === '' || formData.pointsPrice === undefined)) return cb(new Error('请输入积分金额'))
      if (value.includes('CURRENCY') && (formData.currencyPrice === '' || formData.currencyPrice === undefined)) return cb(new Error('请输入货币金额'))
      cb()
    },
    trigger: 'change'
@@ -499,61 +495,164 @@
  return map[unit || ''] || unit || ''
}
function fetchProductDetail(productId?: string) {
async function fetchProductDetail(productId?: string) {
  if (!productId) {
    productDetail.value = null
    ElMessage.warning('未提供产品ID,无法加载产品详情')
    return
  }
  // 模拟异步
  loading.value = true
  setTimeout(() => {
  try {
    productDetail.value = mockProductMap[productId] || null
    // 构造卡片所需的演示定价数据
    priceList.value = productDetail.value
      ? [
          {
            id: 1,
            productId: productId,
            productSuite: 'STANDARD',
            salesForm: 'BUYOUT',
            customerObject: 'ENTERPRISE',
            accountQuantity: 50,
            accountQuantityUnlimited: false,
            concurrentNodeQuantity: 50,
            concurrentNodeQuantityUnlimited: false,
            priceSettings: ['POINTS'],
            pointsAmount: 50000,
            currencyAmount: 0,
            priceUnit: 'SET_PER_YEAR',
            enableStatus: 'ENABLED',
            authorizationStartTime: '2025-01-01',
            authorizationEndTime: '2025-12-31',
            remark: '演示数据'
          },
          {
            id: 2,
            productId: productId,
            productSuite: 'PRO',
            salesForm: 'PRIVATE_INCREMENT',
            customerObject: 'ENTERPRISE',
            accountQuantity: 100,
            accountQuantityUnlimited: false,
            concurrentNodeQuantity: 100,
            concurrentNodeQuantityUnlimited: false,
            priceSettings: ['CURRENCY'],
            pointsAmount: 0,
            currencyAmount: 7500,
            priceUnit: 'SET',
            enableStatus: 'DISABLED',
            authorizationStartTime: '2025-01-01',
            authorizationEndTime: '2025-06-30',
            remark: '演示数据'
          }
        ]
      : []
    // const detailRes: any = await productApi.getProductDetail({ id: productId })
    // if (detailRes?.code === 200) {
    //   productDetail.value = detailRes.data || null
    // } else {
    //   productDetail.value = null
    //   ElMessage.error(detailRes?.msg || '获取产品详情失败')
    // }
    await loadPricingList(productId)
  } catch (e) {
    productDetail.value = null
    ElMessage.error('获取产品详情失败')
  } finally {
    loading.value = false
  }, 300)
  }
}
function mapSalesFormToCN(val: string) {
  const map: Record<string, string> = {
    BUYOUT: '买断',
    LEASE: '租赁',
    PRIVATE_INCREMENT: '私有增包量',
    PUBLIC_INCREMENT: '公有增包量',
    OTA: 'OTA服务',
    CLOUD: '云服务',
    RESOURCE_PACKAGE: '资源包',
    PERSONAL: '个人',
  }
  return map[val] || val
}
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 mapCustomerToCN(val: string) {
  const map: Record<string, string> = { ENTERPRISE: '企业', PERSONAL: '个人', PROJECT_DEPARTMENT: '项目部' }
  return map[val] || val
}
function mapCustomerFromCN(val: string) {
  const map: Record<string, string> = { '企业': 'ENTERPRISE', '个人': 'PERSONAL', '项目部': 'PROJECT_DEPARTMENT' }
  return (map[val] as any) || val
}
function mapUnitToCN(val: string) {
  const map: Record<string, string> = { SET: '套', SET_PER_YEAR: '套/年', YEAR: '年' }
  return map[val] || val
}
function mapUnitFromCN(val: string) {
  const map: Record<string, string> = { '套': 'SET', '套/年': 'SET_PER_YEAR', '年': 'YEAR' }
  return (map[val] as any) || val
}
function mapPriceTypeToCN(key: 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE') {
  const map: Record<string, string> = { POINTS: '积分', CURRENCY: '货币', AGREEMENT: '协议', FREE: '免费' }
  return map[key]
}
function mapPriceTypeFromCN(val: string): 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE' | undefined {
  const map: Record<string, any> = { '积分': 'POINTS', '货币': 'CURRENCY', '协议': 'AGREEMENT', '免费': 'FREE' }
  return map[val]
}
async function loadPricingList(productId: string) {
  try {
    const res: any = await productPricingApi.listByProductId(productId)
    if (res?.code === 200) {
      const list = Array.isArray(res.data) ? res.data : []
      const newList = list.map((it: any) => {
        const priceSettings =  it.priceType.split(',').map((t:string) => mapPriceTypeFromCN(t))
        const pointsPrice = priceSettings.includes('POINTS') ? Number(it.pointsPrice || 0) : 0
        const currencyPrice = priceSettings.includes('CURRENCY') ? Number(it.currencyPrice || 0) : 0
        return {
          id: it.id,
          productId: String(it.productId || productId),
          productSuite: it.suiteName || '',
          salesForm: mapSalesFormFromCN(it.salesForm) as any,
          customerObject: mapCustomerFromCN(it.customerType) as any,
          accountQuantityUnlimited: (it.accountLimit === '不限'),
          accountQuantity: it.accountLimit === '不限' ? '' : Number(it.accountLimit || ''),
          concurrentNodeQuantityUnlimited: (it.concurrentNodes === '不限'),
          concurrentNodeQuantity: it.concurrentNodes === '不限' ? '' : Number(it.concurrentNodes || ''),
          priceSettings: priceSettings,
          pointsPrice,
          currencyPrice,
          priceUnit: mapUnitFromCN(it.priceUnit) as any,
          enableStatus: it.isActive ? 'ENABLED' : 'DISABLED',
          authorizationStartTime: undefined,
          authorizationEndTime: undefined,
          remark: it.description || '',
        }
      })
      const groupedList = newList.reduce((acc: any[], it: any) => {
        // 按productSuite分组
        const productSuite = it.productSuite
        if (!acc[productSuite]) {
          acc[productSuite] = []
        }
        acc[productSuite].push(it)
        return acc
      }, {})
      const newGroupedList:any  = []
      Object.entries(groupedList).forEach(([k,v]) =>{
        newGroupedList.push({
          suiteName: k,
          items: v
        })
      })
      priceList.value = newGroupedList
      console.log(priceList.value)
      console.log(groupedList)
    } else {
      priceList.value = []
    }
  } catch (e) {
    priceList.value = []
  }
}
function buildPayloadFromRow(row: any, overrides: Partial<any> = {}) {const priceTypeCN = row.priceSettings?.[0] ? mapPriceTypeToCN(row.priceSettings[0]) : undefined
  return {
    id: row.id,
    productId: Number(row.productId),
    productName: productDetail.value?.name,
    suiteName: row.productSuite,
    salesForm: mapSalesFormToCN(row.salesForm),
    customerType: mapCustomerToCN(row.customerObject),
    accountLimit: row.accountQuantityUnlimited ? '不限' : String(row.accountQuantity || ''),
    concurrentNodes: row.concurrentNodeQuantityUnlimited ? '不限' : String(row.concurrentNodeQuantity || ''),
    priceType: row.priceSettings.map((t:string)=>mapPriceTypeToCN(t as any)).join(','),
    priceUnit: mapUnitToCN(row.priceUnit || ''),
    pointsPrice:  row.priceSettings.includes('POINTS') ? row.pointsPrice : '',
    currencyPrice:  row.priceSettings.includes('CURRENCY') ? row.currencyPrice : '',
    isActive: row.enableStatus === 'ENABLED',
    description: row.remark || '',
    ...overrides,
  }
}
// 查看价格
@@ -570,7 +669,6 @@
  isEditMode.value = false
  resetForm()
  formData.productId = currentProductId.value
  formData.id = generateId()
  dialogVisible.value = true
}
@@ -579,7 +677,7 @@
  isEditMode.value = true
  resetForm()
  Object.assign(formData, {
    id: row.id ?? generateId(),
    id: row.id,
    productId: currentProductId.value,
    productSuite: row.productSuite || '',
    salesForm: row.salesForm || 'BUYOUT',
@@ -589,8 +687,8 @@
    concurrentNodeQuantity: row.concurrentNodeQuantity ?? '',
    concurrentNodeQuantityUnlimited: !!row.concurrentNodeQuantityUnlimited,
    priceSettings: Array.isArray(row.priceSettings) ? row.priceSettings : [],
    pointsAmount: row.pointsAmount ?? '',
    currencyAmount: row.currencyAmount ?? '',
    pointsPrice: row.pointsPrice ?? '',
    currencyPrice: row.currencyPrice ?? '',
    priceUnit: row.priceUnit || '',
    enableStatus: row.enableStatus || 'ENABLED',
    authorizationStartTime: row.authorizationStartTime,
@@ -611,10 +709,10 @@
      type: 'warning'
    }
  )
    .then(() => {
      const idx = priceList.value.findIndex((p: any) => p.id === row.id)
      if (idx !== -1) priceList.value.splice(idx, 1)
    .then(async () => {
      await productPricingApi.remove(row.id)
      ElMessage.success('删除成功')
      if (currentProductId.value) await loadPricingList(currentProductId.value)
    })
    .catch(() => {
      ElMessage.info('已取消删除')
@@ -637,13 +735,13 @@
    productSuite: '',
    salesForm: 'BUYOUT',
    customerObject: 'ENTERPRISE',
    accountQuantity: '',
    accountQuantity: 1,
    accountQuantityUnlimited: false,
    concurrentNodeQuantity: '',
    concurrentNodeQuantity: 1,
    concurrentNodeQuantityUnlimited: false,
    priceSettings: [],
    pointsAmount: '',
    currencyAmount: '',
    pointsPrice: '',
    currencyPrice: '',
    priceUnit: '',
    enableStatus: 'ENABLED',
    authorizationStartTime: undefined,
@@ -653,10 +751,6 @@
  if (formRef.value) formRef.value.clearValidate()
}
function generateId() {
  const maxId = priceList.value.reduce((m: number, it: any) => Math.max(m, Number(it.id || 0)), 0)
  return maxId + 1
}
async function handleSave() {
  try {
@@ -665,17 +759,77 @@
    return
  }
  if (!currentProductId.value) {
    ElMessage.warning('未提供产品ID,无法保存定价')
    return
  }
  const selectedTypes = formData.priceSettings
  if (!selectedTypes || selectedTypes.length === 0) {
    ElMessage.warning('请至少选择一种价格设置')
    return
  }
  const mapSalesFormToCN = (val: string) => ({
    BUYOUT: '买断',
    LEASE: '租赁',
    PRIVATE_INCREMENT: '私有增包量',
    PUBLIC_INCREMENT: '公有增包量',
    OTA: 'OTA服务',
    CLOUD: '云服务',
    RESOURCE_PACKAGE: '资源包',
    PERSONAL: '个人',
  } as any)[val] || val
  const mapCustomerToCN = (val: string) => ({ ENTERPRISE: '企业', PERSONAL: '个人', PROJECT_DEPARTMENT: '项目部' } as any)[val] || val
  const mapUnitToCN = (val: string) => ({ SET: '套', SET_PER_YEAR: '套/年', YEAR: '年' } as any)[val] || val
  const mapPriceTypeToCN = (key: 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE') => ({ POINTS: '积分', CURRENCY: '货币', AGREEMENT: '协议', FREE: '免费' } as any)[key]
  const buildPayload = () => {
    return {
      id: isEditMode.value && formData.id  ? formData.id : undefined,
      productId: Number(currentProductId.value),
      productName: productDetail.value?.name,
      suiteName: formData.productSuite,
      salesForm: mapSalesFormToCN(formData.salesForm),
      customerType: mapCustomerToCN(formData.customerObject),
      accountLimit: formData.accountQuantityUnlimited ? '不限' : String(formData.accountQuantity || ''),
      concurrentNodes: formData.concurrentNodeQuantityUnlimited ? '不限' : String(formData.concurrentNodeQuantity || ''),
      priceType: formData.priceSettings.map(t=>mapPriceTypeToCN(t)).join(','),
      priceUnit: mapUnitToCN(formData.priceUnit || ''),
      pointsPrice: formData.pointsPrice,
      currencyPrice: formData.currencyPrice,
      isActive: formData.enableStatus === 'ENABLED',
      description: formData.remark || '',
    }
  }
  saving.value = true
  try {
    const record: PriceItem = JSON.parse(JSON.stringify(formData))
    const idx = priceList.value.findIndex((p: any) => p.id === record.id)
    if (idx === -1) {
      priceList.value.push(record)
    if (isEditMode.value && formData.id) {
      const payload = buildPayload()
      const res: any = await productPricingApi.update(payload)
      if (res?.code === 200) {
        ElMessage.success('修改成功')
      } else {
        ElMessage.error(res?.msg || '修改失败')
        return
      }
    } else {
      priceList.value.splice(idx, 1, record)
      // const tasks = selectedTypes.map(t => productPricingApi.add(buildPayload(t)))
      // const results: any[] = await Promise.all(tasks)
      // const ok = results.every(r => r && r.code === 200)
      const results: any = await productPricingApi.add(buildPayload())
      if (results?.code !== 200) {
        ElMessage.error('部分定价保存失败')
        return
      }
      ElMessage.success('新建成功')
    }
    ElMessage.success(isEditMode.value ? '修改成功' : '新建成功')
    dialogVisible.value = false
    await loadPricingList(currentProductId.value)
  } finally {
    saving.value = false
  }
@@ -695,11 +849,10 @@
    '状态变更',
    { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
  )
    .then(() => {
      const idx = priceList.value.findIndex((p: any) => p.id === row.id)
      if (idx !== -1) {
        priceList.value[idx] = { ...priceList.value[idx], enableStatus: newStatus }
      }
    .then(async () => {
      const payload = buildPayloadFromRow(row, { isActive: newStatus === 'ENABLED' })
      await productPricingApi.update(payload)
      await loadPricingList(currentProductId.value as string)
      ElMessage.success(newStatus === 'ENABLED' ? '启用成功' : '停用成功')
    })
    .catch(() => {})
@@ -741,6 +894,11 @@
  .content-area {
    .price-card {
      /* 让价格列表可滚动 */
      :deep(.el-card__body) {
        max-height: 600px;
        overflow-y: auto;
      }
      .card-header {
        display: flex;
        justify-content: space-between;
@@ -801,7 +959,7 @@
.quantity-input-group { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.sales-form-item :deep(.el-form-item__content) { white-space: nowrap; }
.sales-form-group { display: inline-flex; gap: 12px; flex-wrap: nowrap; }
.price-form :deep(.el-form-item) { margin-bottom: 12px; }
.price-form :deep(.el-form-item) { margin-bottom: 20px; }
/* 上架状态颜色 */
.status-online { color: #67C23A; }
.status-pending { color: #E6A23C; }
src/views/productManage/product/index.vue
@@ -94,7 +94,7 @@
// 模拟产品数据
const mockProductList: ProductItem[] = [
  {
    id: '10001',
    id: '1',
    name: '数字化产品A',
    productType: '软件/平台',
    industry: '交通基础设施',
@@ -106,7 +106,7 @@
    shelfStatus: '待上架'
  },
  {
    id: '10002',
    id: '2',
    name: '数字化产品B',
    productType: '硬件/传感',
    industry: '市政工程',
@@ -118,7 +118,7 @@
    shelfStatus: '已上架'
  },
  {
    id: '10003',
    id: '3',
    name: '数字化产品C',
    productType: '软件/平台',
    industry: '建筑工程',
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,174 @@
          </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="true"
                      size="large"
                      @change="(value) => handleQuantityChange(suite.id, value?value:1)"
                    />
                  </td>
                  <td class="th-center">
                      <el-input-number
                        v-model="suite.duration"
                        :min="1"
                        :max="100"
                        :controls="true"
                        size="large"
                        :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>
                  </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 +339,33 @@
      </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="returnPricePanel">返回价格对比</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 cartApi from '@/api/cartApi'
import orderApi from '@/api/orderApi'
import { ShoppingCart, Coin, Money } from '@element-plus/icons-vue'
const route = useRoute()
interface PriceItem {
  id: number
@@ -219,11 +395,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 +420,152 @@
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)
// 模拟用户信息(实际应从用户状态获取)
const currentUserId = ref(1)
const currentUnitId = ref(1)
type PriceTypeKey = 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'
interface OrderSuite extends PriceItem {
  selected: boolean
  quantity: number
  duration: number
  priceType?: PriceTypeKey
}
const orderSuites = ref<OrderSuite[]>([])
// 购物车相关状态
const cartItems = ref<any[]>([])
const cartLoading = ref(false)
const idempotencyToken = ref<string>('')
// 订单状态相关
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 = async (index: number) => {
  const suite = orderSuites.value[index]
  if (!suite) return
  try {
    await removeFromCart(suite.id)
    orderSuites.value.splice(index, 1)
  } catch (e) {
  }
}
// 订购成功回调
const handleOrderSuccess = (orderData: any) => {
  console.log('订购成功:', orderData)
  ElMessage.success('订购申请提交成功!')
  // 清空选择状态
  selectedSuiteIds.value.clear()
  // 关闭价格查看器
  visible.value = false
}
// 弹窗标题
const dialogTitle = computed(() => {
@@ -277,197 +605,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 +650,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 +720,11 @@
// 关闭弹窗
const handleClose = () => {
  visible.value = false
  showOrderPanel.value = false
  showOrderStatus.value = false
  showPricePanel.value = true
  orderSuites.value = []
  selectedSuiteIds.value.clear()
}
// 套件选择处理
@@ -520,34 +732,247 @@
  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(suiteId)
  }
}
// 立即订购
const handleOrder = () => {
  if (selectedSuiteIds.value.size === 0) {
    ElMessage.warning('请选择要订购的软件套件')
    return
  }
  // 获取选中的价格项
  const selectedItems = priceList.value.filter(item => selectedSuiteIds.value.has(item.id))
  emit('order', selectedItems)
  // 打开订购对话框
  showOrderDialog.value = true
  // 查询购物车信息进行展示
  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
}
// 订购成功回调
const handleOrderSuccess = (orderData: any) => {
  console.log('订购成功:', orderData)
  ElMessage.success('订购申请提交成功!')
  // 清空选择状态
  selectedSuiteIds.value.clear()
  // 关闭价格查看器
  visible.value = false
// 处理订购结果
const handleOrderResult = (selectedItems: any[]) => {}
// 启用/停用按钮
function handleToggleEnableStatus(row: any) {
  // ... existing code ...
}
function returnPricePanel() {
  showOrderPanel.value = false
  showOrderStatus.value = false
  showPricePanel.value = true
}
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
  }
  if (!idempotencyToken.value) {
    ElMessage.error('下单Token获取失败,请返回重试')
    return
  }
  // 组装创建订单参数(CreateOrderDTO)
  const payload = {
    userId: currentUserId.value,
    unitId: currentUnitId.value,
    productName: productHeader.value.name,
    providerName: productHeader.value.provider,
    providerId: 3,
    paymentType: '积分',
    buyerRemarks: '',
    items: items.map(it => ({
      pricingId: it.id,
      productId: Number(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: 3,
      providerName: productHeader.value.provider,
      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.provider,
        status: 'SUBMITTED',
        submitTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString(),
        applyTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString()
      }
      // 清空购物车(后端 + 本地状态)
      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 addToCart = async (priceItem: PriceItem) => {
  try {
    const cartData = {
      pricingId: priceItem.id,
      productId: Number(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: number) => {
  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)
  }
}
// 获取销售形式文本
@@ -681,7 +1106,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 +1128,7 @@
          th {
            background-color: #fafafa;
            font-weight: 500;
              font-size: 12px;
              font-size: 16px;
            color: #606266;
          }
        }
@@ -712,7 +1141,8 @@
        }
        
        .feature-value {
          color: #303133;
          color: #606266;
          background: #fafafa; /* 行为灰色 */
        }
        
        .order-methods {
@@ -722,42 +1152,48 @@
          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 {
                color: #f56c6c;
                color: #e7900d;
              }
              
              &.free {
                color: #67c23a;
              }
            }
            .price-icon.points { color: #e6a23c; }
            .price-icon.currency { color: #e7900d; }
          }
          
                     .add-checkbox {
@@ -770,6 +1206,7 @@
           justify-content: center;
           align-items: center;
           padding: 8px 0;
           .add-cart-icon { color: #409eff; font-size:22px}
         }
      }
    }
@@ -792,6 +1229,7 @@
}
.product-price-dialog {
  :deep(.el-dialog__title) {
    font-size: 14px;
    font-weight: 600;
@@ -800,6 +1238,7 @@
  :deep(.el-dialog) {
    max-width: 95vw;
    min-width: 800px;
    height: 700px !important;
  }
  
  :deep(.el-dialog__body) {
@@ -807,4 +1246,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>