1个文件已添加
13个文件已修改
1061 ■■■■ 已修改文件
components.d.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/orderApi.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/pointsApi.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/sysUser.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/workFlowApi.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/orderWorkflow.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/approveManage/tradeApproval/list.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/layout/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/productPriceViewer/index.vue 854 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/buyer/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/confirm/index.vue 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/evaluate/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views_back/layout/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
components.d.ts
@@ -12,6 +12,7 @@
    Editor: typeof import('./src/components/Editor/src/Editor.vue')['default']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCard: typeof import('element-plus/es')['ElCard']
    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
@@ -26,11 +27,15 @@
    ElIcon: typeof import('element-plus/es')['ElIcon']
    ElImage: typeof import('element-plus/es')['ElImage']
    ElInput: typeof import('element-plus/es')['ElInput']
    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
    ElLink: typeof import('element-plus/es')['ElLink']
    ElMenu: typeof import('element-plus/es')['ElMenu']
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElRadio: typeof import('element-plus/es')['ElRadio']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    ElStep: typeof import('element-plus/es')['ElStep']
@@ -38,6 +43,8 @@
    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
    ElTabs: typeof import('element-plus/es')['ElTabs']
    ElTag: typeof import('element-plus/es')['ElTag']
    ElUpload: typeof import('element-plus/es')['ElUpload']
    FileInfoPreview: typeof import('./src/components/fileInfoPreview/index.vue')['default']
src/api/orderApi.ts
@@ -123,7 +123,17 @@
      method: 'get'
    }) as ApiPromise
  },
  updateOrderStatusToIsEvaluate(orderId: string): ApiPromise {
    return createAxios({
      url: `${url}/status/isEvaluate`,
      method: 'post',
      params: {
        orderId
      }
    }) as ApiPromise
  },
  updateOrderStatusToNext(orderId: string): ApiPromise {
    return createAxios({
      url: `${url}/status/next`,
src/api/pointsApi.ts
@@ -89,6 +89,32 @@
      method: 'get',
    }) as ApiPromise
  },
  // 获取用户积分余额 - 使用已存在的接口
  getUserPoints(userId: number): ApiPromise {
    return createAxios({
      url: `${url}total/user/${userId}`,  // 修改为实际存在的路径
      method: 'get',
    }) as ApiPromise
  },
  // 使用专门的积分扣减接口
  deductPointsByFlow(userId: string, unitId: string, points: number, orderId: string, remark?: string, dataCategory?: string, providerId?: String): ApiPromise {
    return createAxios({
      url: `${url}user/deduct`,  // 使用新的专门积分扣减接口
      method: 'post',
      data: {
        userId,
        unitId,
        points,                    // 直接传入要扣减的积分数量
        orderId,
        remark: remark || `订单${orderId}交易扣减积分`,
        dataCategory: dataCategory || 'resource_transaction',
        dataType: 1,               // 1表示消耗
        providerId: providerId
      },
    }) as ApiPromise
  },
}
export default pointsApi
src/api/sysUser.ts
@@ -26,6 +26,7 @@
    // 获取用户详情
    getUserdetail(data: object = {}): ApiPromise {
        return createAxios({
            baseURL: '/api',
            url: `${url}/detail`,
            headers: {
                'Content-Type': 'application/json;charset=UTF-8'
src/api/workFlowApi.ts
New file
@@ -0,0 +1,15 @@
import createAxios from '@/utils/axios'
const workFlowApi = {
  getWorkFlowParams(data: object = {}) : ApiPromise{
    return createAxios({
        baseURL: '/api',
        url: `/approval/templateRelation/relationByType`,
        headers: {
            'Content-Type': 'application/json;charset=UTF-8'
        },
        data: data
    }) as ApiPromise
  }
}
export default  workFlowApi
src/main.ts
@@ -11,17 +11,17 @@
// import pagination from '@/components/Pagination/index.vue'
import store from '@/stores' // pinia状态管理器全局调用
import directives from '@/directives/index'
import FcDesigner from '@form-create/designer';
import formCreate from '@form-create/element-ui'; // 引入 FormCreate
// import FcDesigner from '@form-create/designer';
// import formCreate from '@form-create/element-ui'; // 引入 FormCreate
const app = createApp(App)
app.use(store)
app.use(router)
app.use(ElementPlus)
app.use(directives)
app.use(FcDesigner);
app.use(FcDesigner.formCreate);
app.use(formCreate);
// app.use(FcDesigner);
// app.use(FcDesigner.formCreate);
// app.use(formCreate);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
src/utils/orderWorkflow.ts
@@ -20,7 +20,8 @@
  AUTHORIZE = '授权',
  CONFIRM_TRADE = '交易确认',
  EVALUATE = '评价',
  CANCEL_ORDER = '取消订单'
  CANCEL_ORDER = '取消订单',
  COMPLETED = '已完成'
}
// 页面类型枚举
src/views/approveManage/tradeApproval/list.vue
@@ -820,12 +820,16 @@
    gap: 20px;
    align-items: center;
    overflow: hidden;
    .status-item{
      flex-direction: row-reverse;
    }
    .order-item {
      display: flex;
      align-items: center;
      gap: 8px;
      flex-shrink: 0;
      flex: 1;
      
      .label {
        color: #909399;
src/views/layout/index.vue
@@ -129,7 +129,7 @@
.content-wrapper {
  width: 100%;
  height: calc(100vh - 70px);
  height: calc(100vh);
  display: flex;
}
.content-wrapperall{
src/views/productManage/productPriceViewer/index.vue
@@ -23,18 +23,19 @@
                       {{ group.productSuite }}
                     </th>
                   </tr>
                   <tr class="sub-header">
                     <th>销售形式</th>
                     <th
                       v-for="priceItem in tabData"
                       :key="priceItem.id"
                       :colspan="getColspan(priceItem)"
                     >
                       {{ getSalesFormText(priceItem.salesForm) }}
                     </th>
                   </tr>
                 </thead>
                <tbody>
                  <tr>
                    <td class="feature-label">销售形式</td>
                    <td
                      v-for="priceItem in tabData"
                      :key="priceItem.id"
                      :colspan="getColspan(priceItem)"
                      class="feature-value"
                    >
                      {{ getSalesFormText(priceItem.salesForm) }}
                    </td>
                  </tr>
                  <tr>
                    <td class="feature-label">客户对象</td>
                    <td 
@@ -106,16 +107,16 @@
                         </div>
                          <span class="price-value points">{{ formatNumber(priceItem.pointsAmount) }} / {{ getPriceUnitText(priceItem.priceUnit) }}</span>
                        </div>
                        <div
                          v-if="priceItem.priceSettings.includes('CURRENCY') && priceItem.currencyAmount > 0"
                          class="price-item"
                        >
                         <div class="price-lable-icon">
                          <el-icon class="price-icon currency"><Money /></el-icon>
                          <span class="price-label">货币</span>
                          </div>
                          <span class="price-value currency">{{ formatNumber(priceItem.currencyAmount) }} / {{ getPriceUnitText(priceItem.priceUnit) }}</span>
                        </div>
                                                 <div
                           v-if="priceItem.priceSettings.includes('CURRENCY') && priceItem.currencyAmount > 0"
                           class="price-item"
                         >
                          <div class="price-lable-icon">
                           <span class="price-icon currency">¥</span>
                           <span class="price-label">货币</span>
                           </div>
                           <span class="price-value currency">{{ formatNumber(priceItem.currencyAmount) }} / {{ getPriceUnitText(priceItem.priceUnit) }}</span>
                         </div>
                        <div 
                          v-if="priceItem.priceSettings.includes('FREE')"
                          class="price-item"
@@ -191,12 +192,12 @@
            <table class="suite-table">
              <thead>
                <tr>
                  <th class="th-left">详情</th>
                  <th class="th-left">规格</th>
                  <th class="th-left">单价</th>
                  <th class="th-center">数量</th>
                  <th class="th-center">年限</th>
                  <th class="th-center">操作</th>
                  <th class="th-detail">详情</th>
                  <th class="th-spec">规格</th>
                  <th class="th-price">单价</th>
                  <th class="th-quantity">数量</th>
                  <th class="th-years">年限</th>
                  <th class="th-action">操作</th>
                </tr>
              </thead>
              <tbody>
@@ -209,12 +210,12 @@
                  </td>
                  <td class="cell-spec cell-spec-gg">
                    <div class="spec-line-gg">
                      <div class="spec-line">销售形式:{{ getSalesFormText(suite.salesForm) }}</div>
                      <div class="spec-line">客户对象:{{ getCustomerObjectText(suite.customerObject) }}</div>
                      <div class="spec-line">销售形式:<span class="spec-value">{{ getSalesFormText(suite.salesForm) }}</span></div>
                      <div class="spec-line">客户对象:<span class="spec-value">{{ getCustomerObjectText(suite.customerObject) }}</span></div>
                    </div>
                    <div  class="spec-line-gg">
                      <div class="spec-line">账户数量:{{ suite.accountQuantityUnlimited ? '不限' : suite.accountQuantity }}</div>
                      <div class="spec-line">并发节点数:{{ suite.concurrentNodeQuantityUnlimited ? '不限' : suite.concurrentNodeQuantity }}</div>
                      <div class="spec-line">账户数量:<span class="spec-value">{{ suite.accountQuantityUnlimited ? '不限' : suite.accountQuantity }}</span></div>
                      <div class="spec-line">并发节点数:<span class="spec-value">{{ suite.concurrentNodeQuantityUnlimited ? '不限' : suite.concurrentNodeQuantity }}</span></div>
                    </div>
                  </td>
@@ -248,6 +249,7 @@
                      :max="999" 
                      :controls="true" 
                      size="large"
                      class="quantity-input"
                      @change="(value) => handleQuantityChange(suite.id, value?value:1)"
                    />
                  </td>
@@ -258,6 +260,7 @@
                        :max="100" 
                        :controls="true" 
                        size="large"
                        class="duration-input"
                        :disabled="suite.priceType === 'FREE'"
                        @change="(value) => handleDurationChange(suite.id, value?value:1)"
                      />
@@ -354,6 +357,7 @@
import { useUserInfo } from '@/stores/modules/userInfo'
import { queryUserDetail } from '@/api/userInfo'
import productApi from '@/api/productApi'
import workFlowApi from '@/api/workFlowApi'
const route = useRoute()
@@ -828,16 +832,25 @@
      // 调用工作流接口发起审批流程,拿到流程实例ID后回写订单workflow_id
      try {
        // 根据是否包含协议明细,配置不同流程定义与业务Key(先用静态值占位)
        const processdefId = hasAGREEMENT ? 'Process_Agreement_Static' : 'Process_Points_Static'
        // const processdefId = hasAGREEMENT ? 'Process_Agreement_Static' : 'Process_Points_Static'
        const businessKey = hasAGREEMENT ? 'agreement_biz_key' : 'points_biz_key'
        const wfRes: any = await orderApi.startWorkflowAndComplete({
          processdefId,
          userid: String(currentUserId.value || ''),
          businessKey
        const type = hasAGREEMENT ? 'trade_agreement' : 'trade_point';
        // 获取工作流参数
        const  wkParamsRes: any = await workFlowApi.getWorkFlowParams({
          type: type,
          unitId: '1'
        })
        if (wfRes?.code === 200 && wfRes.data?.processinstId) {
          await orderApi.updateWorkflowId(data.orderId, wfRes.data.processinstId)
        if(wkParamsRes?.code === 200 && wkParamsRes.data?.processTemplateId){
          const wfRes: any = await orderApi.startWorkflowAndComplete({
            processdefId: wkParamsRes.data.processTemplateId,
            userid: String(currentUserId.value || ''),
            businessKey: businessKey
          })
          if (wfRes?.code === 200 && wfRes.data?.processinstId) {
            await orderApi.updateWorkflowId(data.orderId, wfRes.data.processinstId)
          }
        }
      } catch (e) {
        console.warn('启动工作流失败或更新workflow_id失败', e)
      }
@@ -1090,12 +1103,38 @@
</script>
<style scoped lang="scss">
.default-main {
  padding: 20px;
  background-color: #f5f5f5;
  min-height: 100vh;
  position: relative;
  z-index: 1;
}
// 全局表头文字大小调整
:deep(.el-table__header) {
  th {
    .cell {
      font-size: 14px !important;
      font-weight: 700;
    }
  }
}
.search-card {
  margin-bottom: 20px;
}
.mt15 {
  margin-top: 15px;
}
.price-viewer-container {
  .pricing-table-container {
    .pricing-tabs {
      :deep(.el-tabs__header) {
        margin-bottom: 12px;
      }
             .pricing-tabs {
             :deep(.el-tabs__header) {
         margin-bottom: 2px;
       }
      /* 居中 tabs 导航 */
      :deep(.el-tabs__nav-wrap) {
        display: flex;
@@ -1105,95 +1144,185 @@
        margin: 0 auto;
        border: none !important;
      }
      /* 卡片型 tabs 美化为圆角胶囊效果 */
      :deep(.el-tabs--card > .el-tabs__header .el-tabs__nav) {
        border: none;
      }
      :deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
        border: none;
      }
      :deep(.el-tabs__item) {
        border-radius: 999px;
        padding: 8px 18px;
        margin: 0 6px;
        color: #606266;
        background: #f6f8fb;
        transition: all .2s ease;
      }
      :deep(.el-tabs__item:hover) {
        color: #3a7afe;
        background: #eff4ff;
      }
      :deep(.el-tabs__item.is-active) {
        font-weight: 600;
        color: #fff;
        background: #3a7afe;
        box-shadow: 0 6px 16px rgba(58, 122, 254, 0.25);
      }
             /* 普通 tabs 样式 */
       :deep(.el-tabs--card > .el-tabs__header .el-tabs__nav) {
         border: none;
         border-radius: 0;
         background: transparent;
         box-shadow: none;
         display: flex;
         gap: 8px;
       }
       :deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
         border: 1px solid #e4e7ed;
         border-radius: 6px;
         margin: 0;
         padding: 14px 24px;
         color: #606266;
         background: #fff;
         transition: all .2s ease;
         font-size: 14px;
         font-weight: 500;
         box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
         &:hover {
           border-color: #409eff;
           box-shadow: 0 4px 8px rgba(64, 158, 255, 0.15);
         }
       }
             :deep(.el-tabs__item:hover) {
         color: #409eff;
         background: #f5f7fa;
       }
       :deep(.el-tabs__item.is-active) {
         font-weight: 600;
         color: #409eff;
         background: #ecf5ff;
         border-color: #409eff;
         box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
         &::after {
           content: '';
           position: absolute;
           bottom: -1px;
           left: 0;
           right: 0;
           height: 3px;
           background: #409eff;
           border-radius: 0 0 6px 6px;
         }
       }
    }
    .pricing-table-wrapper {
      overflow-x: auto;
      background: #fff;
      border-radius: 12px;
      box-shadow: 0 6px 24px rgba(0, 0, 0, 0.06);
      padding: 8px;
         .pricing-table-wrapper {
       overflow-x: auto;
       background: #fff;
       border-radius: 8px;
       box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
       padding: 0;
       border: 1px solid #e4e7ed;
       margin-top: 2px;
      
      .pricing-table {
        width: 100%;
        border-collapse: separate;
        border-spacing: 0;
        background: #fff;
        border-radius: 10px;
        th, td {
          border-bottom: 1px solid #eef2f7;
          border-right: 1px solid #f3f5f7;
          padding: 12px 14px;
          text-align: center;
          vertical-align: middle;
          font-size: 14px;
        }
        tr th:first-child, td:first-child{
          font-weight: 700;
          color: #303133;
        }
        th {
          background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
          font-weight: 600;
          color: #303133;
        }
        .feature-column {
          width: 120px;
          background-color: #f5f7fa;
        }
        .sub-header {
          th {
            background-color: #f8fafc;
            font-weight: 500;
            font-size: 13px;
            color: #606266;
             .pricing-table {
         width: 100%;
         border-collapse: collapse;
         background: #fff;
         border: 1px solid #e4e7ed;
         border-radius: 8px;
         overflow: visible;
         // 统一所有单元格样式
         th, td {
            border-top: 1px solid #e4e7ed !important;
            border-bottom: 1px solid #e4e7ed !important;
            border-left: none !important;
            border-right: none !important;
            padding: 8px 10px;
            text-align: center;
            vertical-align: middle;
            font-size: 14px;
            line-height: 1.5;
            color: #303133;
            background: #fff;
            transition: all 0.2s ease;
          }
        }
        .feature-label {
          background-color: #f5f7fa;
          font-weight: 500;
          color: #606266;
          text-align: center;
        }
        .feature-value {
          color: #303133;
          background: #fff;
        }
         // 第一列样式
         tr th:first-child, td:first-child{
           font-weight: 700;
           color: #303133;
           background: #f8fafc;
           text-align: left;
           padding-left: 16px;
         }
         // 表头样式
         th {
           background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
           font-weight: 600;
           color: #303133;
           border-bottom: 2px solid #e4e7ed !important;
           border-left: none !important;
           border-right: none !important;
           position: relative;
         }
         .feature-column {
           width: 140px;
           min-width: 140px;
         }
         // 子表头样式已移除,销售形式行现在是普通行
         // 功能标签样式
         .feature-label {
           font-weight: 600;
           color: #303133;
           text-align: left;
           background: #f8fafc;
           border-right: none !important;
         }
         // 功能值样式
         .feature-value {
           color: #303133;
           background: #fff;
           font-weight: 500;
         }
        tbody tr:hover td {
          background: #fafcff;
        }
         // 行悬停效果
         tbody tr:hover td {
           background: #f0f7ff;
           border-color: #b3d8ff !important;
         }
         // 偶数行背景
         tbody tr:nth-child(even) td {
           background: #fafbfc;
         }
         tbody tr:nth-child(even):hover td {
           background: #e6f3ff;
         }
         // 强制确保最后一行有完整的边框
         tbody tr:last-child td {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 强制确保所有单元格都有完整的边框
         tbody td {
           border: 1px solid #e4e7ed !important;
         }
         // 特别处理表格底部边框
         tbody tr:last-child {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 使用更具体的选择器确保边框显示
         .pricing-table tbody tr:last-child td {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 确保表格本身有完整的边框
         .pricing-table {
           border: 1px solid #e4e7ed !important;
         }
         // 特别处理套件选择行的边框
         .pricing-table tbody tr:last-child .feature-label,
         .pricing-table tbody tr:last-child .feature-value {
           border-bottom: 1px solid #e4e7ed !important;
         }
         // 确保所有行都有完整的边框
         .pricing-table tbody tr td {
           border-top: 1px solid #e4e7ed !important;
           border-bottom: 1px solid #e4e7ed !important;
           border-left: none !important;
           border-right: none !important;
         }
        
        .order-methods {
          display: flex;
@@ -1211,49 +1340,64 @@
          }
        }
        
        .price-info {
          display: flex;
          flex-direction: column; /* 竖排 */
          gap: 10px;
          align-items: center;
          .price-item {
            display: flex;
            align-items: flex-start;
            flex-direction: column;
            gap: 6px;
            padding: 10px 12px;
            border: 1px solid #f0f2f5;
            border-radius: 10px;
            background: #fff;
            box-shadow: inset 0 -1px 0 rgba(0,0,0,0.02);
            .price-lable-icon{
                 .price-info {
           display: flex;
           flex-direction: column;
           gap: 8px;
           align-items: center;
                       .price-item {
              display: flex;
              align-items: center;
              gap: 6px;
            }
            .price-label {
              font-size: 12px;
              color: #909399;
            }
            .price-value {
              font-weight: 600;
              font-size: 18px;
              &.points {
                color: #e7900d;
              }
              &.currency {
                color: #10b981;
              }
              &.free {
                color: #67c23a;
              }
            }
            .price-icon.points { color: #e6a23c; }
            .price-icon.currency { color: #16a34a; }
          }
              padding: 6px 10px;
              border: 1px solid #f0f2f5;
              border-radius: 6px;
              background: #fff;
              box-shadow: inset 0 -1px 0 rgba(0,0,0,0.02);
             .price-lable-icon {
               display: flex;
               align-items: center;
               gap: 4px;
             }
             .price-label {
               font-size: 12px;
               color: #909399;
               white-space: nowrap;
             }
             .price-value {
               font-weight: 600;
               font-size: 16px;
               white-space: nowrap;
               &.points {
                 color: #e7900d;
               }
               &.currency {
                 color: #10b981;
               }
               &.free {
                 color: #67c23a;
               }
             }
             .price-icon.points {
               color: #e6a23c;
               font-size: 14px;
             }
             .price-icon.currency {
               color: #16a34a;
               font-size: 16px;
               font-weight: 600;
               font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
             }
           }
          
                     .add-checkbox {
             margin-top: 4px;
@@ -1280,7 +1424,11 @@
.no-price-data {
  text-align: center;
  padding: 40px 0;
  padding: 60px 0;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  margin: 20px 0;
}
.price-value.agreement {
@@ -1307,14 +1455,23 @@
}
.order-header-card { 
  :deep(.el-card__body) { padding: 10px; }
  .basic-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px 16px;}
  .grid-item { font-size: 16px; color:#606266; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .label { color:#606266; }
  :deep(.el-card__body) { padding: 16px; }
  .basic-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px 16px;}
  .grid-item {
    font-size: 14px;
    color:#606266;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1;
  }
  .label { color:#606266; font-weight: 500; }
  .value { color:#303133; }
  .value.strong { font-weight: 600; }
  margin-bottom: 20px;
  margin-bottom: 10px;
  margin-top: 10px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.points-info-card {
@@ -1333,6 +1490,8 @@
.selected-suites-card {
  :deep(.el-card__body) { padding: 8px; }
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.suite-table-wrapper { overflow-x: auto; width: 100%; }
@@ -1341,63 +1500,255 @@
  border-collapse: collapse;
  background: #fff;
  table-layout: fixed;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.suite-table th,
.suite-table td {
  border: 1px solid #e4e7ed;
  padding: 6px 8px;
  padding: 8px 10px;
  vertical-align: middle;
  font-size: 16px;
  font-size: 14px;
  line-height: 1.5;
  color: #303133;
  background: #fff;
  transition: all 0.2s ease;
  text-align: center;
}
.suite-table thead th { background: #f0f2f5; color: #303133; font-weight: 600; font-size: 16px;}
.table-toolbar { display:flex; justify-content: space-between; align-items:center; padding: 4px 0 8px; }
.toolbar-left { display:flex; align-items:center; gap: 12px; width: 100%;justify-content: space-between;}
.toolbar-right { display:flex; align-items:center; gap: 12px; }
.summary-info { font-size: 14px; color: #606266; }
.summary-count { color: #409eff; font-weight: 600; }
.summary-points { color: #e6a23c; font-weight: 600; }
.suite-table thead th {
  background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
  color: #303133;
  font-weight: 600;
  font-size: 14px;
  border-bottom: 2px solid #e4e7ed;
  position: relative;
}
.suite-table .th-left { text-align: center; width: 21%;}
.suite-table .th-center { text-align: center; }
.suite-table tbody tr:hover td {
  background: #f0f7ff;
  border-color: #b3d8ff;
}
.suite-table tbody tr:nth-child(even) td {
  background: #fafbfc;
}
.suite-table tbody tr:nth-child(even):hover td {
  background: #e6f3ff;
}
// 确保套件表格最后一行有完整的边框
.suite-table tbody tr:last-child td {
  border-bottom: 1px solid #e4e7ed;
}
// 确保套件表格所有单元格都有完整的边框
.suite-table tbody td {
  border: 1px solid #e4e7ed;
}
.table-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  background: #f8fafc;
  border-top: 1px solid #e4e7ed;
  border-radius: 0 0 8px 8px;
}
.toolbar-left {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  justify-content: space-between;
}
.toolbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.summary-info {
  font-size: 14px;
  color: #606266;
  display: flex;
  align-items: center;
  gap: 8px;
}
.summary-count {
  color: #409eff;
  font-weight: 600;
  background: #ecf5ff;
  padding: 4px 8px;
  border-radius: 4px;
}
.summary-points {
  color: #e6a23c;
  font-weight: 600;
  background: #fdf6ec;
  padding: 4px 8px;
  border-radius: 4px;
}
.suite-table .th-detail { text-align: center; width: 25%;}
.suite-table .th-spec { text-align: center; width: 25%;}
.suite-table .th-price { text-align: center; width: 18%;}
.suite-table .th-quantity { text-align: center; width: 12%;}
.suite-table .th-years { text-align: center; width: 12%;}
.suite-table .th-action { text-align: center; width: 8%;}
// 详情、规格、价格列的内容左对齐
.suite-table .cell-detail,
.suite-table .cell-spec,
.suite-table .cell-price {
  text-align: left !important;
}
.cell-detail .detail-content { display: inline-flex; align-items: center; gap: 8px; }
.suite-name { font-weight: 600; color: #303133; }
.cell-spec .spec-line { color: #606266; font-size: 16px; line-height: 20px; }
.cell-spec-gg {display: flex; flex-direction: column; gap: 16px;
  .spec-line-gg{
    display: flex;
    gap: 30px;
.cell-spec .spec-line {
  color: #606266;
  font-size: 13px;
  line-height: 18px;
  padding: 2px 0;
  // 规格列中数值的统一颜色
  .spec-value {
    color: #409eff;
    font-weight: 500;
  }
}
.cell-price .price-inline { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.cell-price .price-tag.points { color: #E6A23C; font-weight: 600; }
.cell-price .price-tag.currency { color: #F56C6C; font-weight: 600; }
.cell-price .price-free { color: #67C23A; font-weight: 600; }
.cell-price .price-agreement { color: #409EFF; }
.cell-spec-gg {
  display: flex;
  flex-direction: column;
  gap: 12px;
  .spec-line-gg{
    display: flex;
    gap: 24px;
  }
}
.cell-price .price-inline {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}
.cell-price .price-tag.points {
  color: #E6A23C;
  font-weight: 600;
  background: #fdf6ec;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-price .price-tag.currency {
  color: #F56C6C;
  font-weight: 600;
  background: #fef0f0;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-price .price-free {
  color: #67C23A;
  font-weight: 600;
  background: #f0f9ff;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-price .price-agreement {
  color: #409EFF;
  background: #ecf5ff;
  padding: 4px 8px;
  border-radius: 4px;
}
.cell-summary .summary-item { font-size: 14px; color: #606266; line-height: 1.5; }
.order-status-panel {
  .steps-wrapper { padding: 6px 8px 0 8px; }
  .result-text { text-align: center; margin: 18px 0 12px; font-size: 18px; color: #303133; }
  .steps-wrapper {
    padding: 20px 16px;
    background: #fff;
    border-radius: 8px 8px 0 0;
    border-bottom: 1px solid #e4e7ed;
  }
  .result-text {
    text-align: center;
    margin: 24px 0 20px;
    font-size: 18px;
    color: #303133;
    font-weight: 500;
  }
  .order-info-card {
    :deep(.el-card__body) { padding: 0; }
    border-radius: 0 0 8px 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  }
  .order-info-title {
    background: #f5f7fa;
    padding: 12px 16px;
    background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
    padding: 16px 20px;
    font-weight: 600;
    border-bottom: 1px solid #ebeef5;
    border-bottom: 1px solid #e4e7ed;
    color: #303133;
  }
  .order-info-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid #e4e7ed;
    border-radius: 8px;
    overflow: hidden;
    td {
      padding: 12px 16px;
      border-bottom: 1px solid #ebeef5;
      padding: 10px 12px;
      border-bottom: 1px solid #e4e7ed;
      border-right: 1px solid #e4e7ed;
      vertical-align: middle;
      font-size: 14px;
      line-height: 1.5;
      color: #303133;
      background: #fff;
      transition: all 0.2s ease;
    }
    .label { width: 120px; color: #606266; }
    .value { color: #303133; }
    .link { text-align: right; }
    td:last-child {
      border-right: none;
    }
    tr:last-child td {
      border-bottom: none;
    }
    .label {
      width: 120px;
      color: #606266;
      font-weight: 500;
      background: #f8fafc;
      text-align: center;
    }
    .value {
      color: #303133;
      background: #fff;
    }
    .link {
      text-align: right;
      background: #fff;
    }
    tr:hover td {
      background: #f0f7ff;
      border-color: #b3d8ff;
    }
    tr:nth-child(even) td {
      background: #fafbfc;
    }
    tr:nth-child(even):hover td {
      background: #e6f3ff;
    }
  }
}
@@ -1407,5 +1758,100 @@
.order-summary .summary-value.highlight{ color:#409eff; }
.order-summary .summary-value.total{ color:#e6a23c; }
.order-summary .summary-value.currency{ color:#f56c6c; }
.footer { display: flex; flex-direction: row-reverse; margin-right: 50px; gap: 50px; margin-top: 20px;}
.footer {
  display: flex;
  flex-direction: row-reverse;
  margin-right: 50px;
  gap: 20px;
  margin-top: 10px;
  padding: 10px 0;
  .el-button {
    border-radius: 6px;
    font-weight: 500;
    padding: 12px 24px;
    &.el-button--primary {
      background: linear-gradient(135deg, #409eff 0%, #3a7afe 100%);
      border: none;
      box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
      &:hover {
        background: linear-gradient(135deg, #66b1ff 0%, #5a8cff 100%);
        box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
      }
    }
    &:not(.el-button--primary) {
      border: 1px solid #dcdfe6;
      background: #fff;
      &:hover {
        border-color: #409eff;
        color: #409eff;
      }
    }
  }
}
// 确保页面整体层级正确
:deep(.el-card) {
  position: relative;
  z-index: 1;
}
// 修复可能的全局z-index冲突
:deep(.el-table__body-wrapper) {
  z-index: 1 !important;
}
:deep(.el-table__header-wrapper) {
  z-index: 1 !important;
}
// 数量和年限输入框样式
.quantity-input,
.duration-input {
  width: 100px !important;
  height: 35px !important;
  :deep(.el-input__wrapper) {
    padding: 0 8px !important;
  }
  :deep(.el-input__inner) {
    width: 30px !important;
    text-align: center !important;
    min-width: 30px !important;
    max-width: 30px !important;
  }
  :deep(.el-input-number__decrease),
  :deep(.el-input-number__increase) {
    width: 32px !important;
    height: 32px !important;
  }
}
// 使用更强的选择器确保样式生效
.suite-table .quantity-input,
.suite-table .duration-input {
  :deep(.el-input__inner) {
    width: 40px !important;
    text-align: center !important;
    min-width: 40px !important;
    max-width: 40px !important;
    box-sizing: border-box !important;
  }
}
// 全局样式覆盖,确保输入框内部宽度生效
:deep(.el-input-number.quantity-input .el-input__inner),
:deep(.el-input-number.duration-input .el-input__inner) {
  width: 40px !important;
  text-align: center !important;
  min-width: 40px !important;
  max-width: 40px !important;
  box-sizing: border-box !important;
}
</style>
src/views/tradeManage/buyer/index.vue
@@ -227,7 +227,7 @@
                            交易确认
                          </el-button>
                          <el-button
                            v-else-if="action.type === ActionType.EVALUATE"
                            v-else-if="action.type === ActionType.EVALUATE && row.parentOrder.isEvaluate === '未评价'"
                            type="primary"
                            link
                            size="small"
@@ -280,12 +280,10 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Search, Refresh } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import orderApi from '@/api/orderApi'
import productApi from '@/api/productApi'
import { useUserInfo } from '@/stores/modules/userInfo'
import ProductOrderStatusDialog from '@/views/productManage/productOrderStatusDialog/index.vue'
import { OrderWorkflowController, OrderStatus, ActionType, PageType, StatusMapper } from '@/utils/orderWorkflow'
import { queryUserDetail } from '@/api/userInfo'
@@ -582,7 +580,8 @@
      status: uiStatus,
      statusName: order.orderStatus || '',
      orderStatus: StatusMapper.toUIStatus(order.orderStatus), // 转换为标准状态枚举
      workFlowId: order.workflowId || ''
      workFlowId: order.workflowId || '',
      isEvaluate: order.isEvaluate || ''
    }
    const subOrders: any[] = Array.isArray(order?.orderDetails)
      ? order.orderDetails.map((d: any, i: number) => ({
src/views/tradeManage/confirm/index.vue
@@ -237,8 +237,11 @@
import { Document, User, Goods, List } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import orderApi from '@/api/orderApi'
import pointsApi from '@/api/pointsApi' // 新增积分API导入
import { useUserInfo } from '@/stores/modules/userInfo'
import createAxios from '@/utils/axios'
import productApi from '@/api/productApi'
import sysUserService from '@/api/sysUser'
const route = useRoute()
const router = useRouter()
@@ -299,6 +302,39 @@
    const statusName: string = data.orderStatus || ''
    const uiStatus = statusServerToUi[statusName] || 'INFO'
    // 根据产品id获取产品信息,更新头部展示
    try {
      if (data.productId) {
        const detailRes: any = await productApi.getProductById({ id: data.productId })
        if (detailRes?.code === 200 && detailRes.data) {
          // 用产品详情补全头信息
          data.productName = detailRes.data.name || data.productName
          data.providerName = detailRes.data.submissionUnit || data.providerName
          data.industry = detailRes.data.industrialChainName || data.industry
          data.productDesc = detailRes.data.describe || data.productDesc
          data.projectUnit = detailRes.data.importantAreaName || data.productDesc
          data.productType = detailRes.data.typeName || data.productDesc
        }
      }
    } catch (e) {
      // 忽略产品详情失败,不阻塞订单详情
    }
    // 获取用户信息
    try {
      const userRes: any = await sysUserService.getUserdetail({ userId: data.userId })
      if (userRes?.code === 200 && userRes.data) {
        // 用产品详情补全头信息
        data.unitName = userRes.data.unitName || data.unitName
        data.userName = userRes.data.name || data.userName
        data.userDept = userRes.data.departmentName || data.userDept
        data.userPhone = userRes.data.phone || data.userPhone
        data.userAccount = userRes.data.username || data.userAccount
      }
    }catch (e){
    }
    // 映射订单详情头部信息
    const head = {
      orderNo: data.orderId,
@@ -317,6 +353,7 @@
      projectUnit: data.projectUnit || '-',
      productType: data.productType || '-',
      productDesc: data.productDesc || '-',
      providerId: data.providerId || ''
    }
    // 明细项映射
@@ -401,6 +438,41 @@
      return
    }
    // 检查订单是否涉及积分扣减
    const hasPointsDeduction = detail.items && detail.items.some((item: any) => Number(item.pricePoint || 0) > 0)
    // if (hasPointsDeduction) {
    //   // 计算需要扣减的积分总额
    //   const totalPointsToDeduct = detail.items.reduce((sum: number, item: any) => {
    //     return sum + (Number(item.pricePoint || 0) * Number(item.quantity || 0))
    //   }, 0)
    //
    //   if (totalPointsToDeduct > 0) {
    //     // 获取当前用户积分余额
    //     try {
    //       const userPointsRes = await pointsApi.getUserPoints(userId)
    //       if (userPointsRes.code !== 200 || !userPointsRes.data) {
    //         ElMessage.error('获取积分余额失败')
    //         return
    //       }
    //
    //       const currentPoints = userPointsRes.data.balance || 0
    //
    //       if (currentPoints < totalPointsToDeduct) {
    //         ElMessage.error(`积分余额不足!当前积分:${currentPoints.toLocaleString()},需要积分:${totalPointsToDeduct.toLocaleString()}`)
    //         return
    //       }
    //
    //       // 积分余额充足,继续执行
    //       console.log(`积分余额检查通过:当前${currentPoints},需要${totalPointsToDeduct}`)
    //     } catch (error) {
    //       console.error('获取积分余额失败:', error)
    //       ElMessage.error('获取积分余额失败,请重试')
    //       return
    //     }
    //   }
    // }
    // 确认操作
    await ElMessageBox.confirm('确定要确认交易?', '确认操作', {
      confirmButtonText: '确定',
@@ -408,8 +480,46 @@
      type: 'warning'
    })
    // 更新订单状态进入下一个状态
    // 如果涉及积分扣减,先进行积分扣减和流水记录
    if (hasPointsDeduction) {
      const totalPointsToDeduct = detail.items.reduce((sum: number, item: any) => {
        return sum + (Number(item.pricePoint || 0) * Number(item.quantity || 0))
      }, 0)
      if (totalPointsToDeduct > 0) {
        try {
          // 获取用户单位ID
          const unitId = userStore.getUserInfo?.unitId || userStore.getUserInfo?.unitId || ''
          // 获取当前订购的产品名称作为备注(使用产品名称,不是套件名称)
          const productNames = detail.productName || '订单交易扣减积分'
          // 先执行积分扣减
          const deductRes: any = await pointsApi.deductPointsByFlow(
            userId.toString(),
            unitId,
            totalPointsToDeduct,
            orderId,
            productNames || '订单交易扣减积分',  // 使用产品名称作为备注
            '积分交易',  // 数据类别
             detail.providerId
          )
          if (!deductRes || deductRes.code !== 200) {
            ElMessage.error(deductRes?.msg || deductRes?.message || '积分扣减失败,交易确认终止')
            return
          }
          console.log(`积分扣减成功:${totalPointsToDeduct}`)
        } catch (error) {
          console.error('积分扣减失败:', error)
          ElMessage.error('积分扣减失败,交易确认终止')
          return
        }
      }
    }
    // 积分扣减成功后,更新订单状态进入下一个状态
    await orderApi.updateOrderStatusToNext(orderId)
    ElMessage.success('交易确认成功')
    router.back()    
  } catch (error) {
src/views/tradeManage/evaluate/index.vue
@@ -862,8 +862,8 @@
    })
    if (result && result.code === 200) {
      // 更新订单状态进入下一个状态
      await orderApi.updateOrderStatusToNext(orderId)
      // 更新订单评价状态为已评价
      await orderApi.updateOrderStatusToIsEvaluate(orderId)
      ElMessage.success('评价提交成功')
      router.back()    
      evaluationLoading.value = false
src/views_back/layout/index.vue
@@ -118,7 +118,7 @@
.content-wrapper {
  width: 100%;
  height: calc(100vh - 70px);
  height: calc(100vh);
  display: flex;
}
.content-wrapperall{