seatonwan9
2025-08-28 bfbb1ea3c6bb2dd7db064fb189290a1bfcf6c9cd
提交源码
3个文件已添加
17个文件已修改
1481 ■■■■ 已修改文件
.env.development 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cartApi.ts 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/orderApi.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productApi.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/userInfo.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.ts 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/transactionManage.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/modules/userInfo.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/orderWorkflow.ts 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/loginT/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pointsManage/personal/index.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/price/index.vue 104 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/product/index.vue 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/productPriceViewer/index.vue 298 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/buyer/index.vue 219 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/detail/index.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/seller/index.vue 195 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tradeManage/track/index.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.development
src/App.vue
@@ -29,13 +29,7 @@
  })
}
provide('reload', reload)
// 临时初始化:写入 userId 与 unitId,便于本地联调后端下单接口
if (!userStore.getUserId) {
  userStore.userId = '1'
}
if (!userStore.getUnitId) {
  userStore.unitId = '1'
}
setInterval(() => {
// 这个判断代表登录成功后才开始写入时间
  if(userStore.getAdminToken) {
src/api/cartApi.ts
@@ -5,7 +5,7 @@
const cartApi = {
  // 添加商品到购物车
  addToCart(data: any, userId: number, unitId: number): ApiPromise {
  addToCart(data: any, userId: string, unitId: string): ApiPromise {
    return createAxios({
      url: `${baseUrl}/add`,
      method: 'post',
@@ -15,7 +15,7 @@
  },
  // 从购物车移除商品
  removeFromCart(userId: number, unitId: number, pricingId: number): ApiPromise {
  removeFromCart(userId: string, unitId: string, pricingId: string): ApiPromise {
    return createAxios({
      url: `${baseUrl}/remove`,
      method: 'delete',
@@ -24,7 +24,7 @@
  },
  // 更新购物车商品数量
  updateCartItem(userId: number, unitId: number, pricingId: number, quantity: number): ApiPromise {
  updateCartItem(userId: string, unitId: string, pricingId: number, quantity: number): ApiPromise {
    return createAxios({
      url: `${baseUrl}/update`,
      method: 'put',
@@ -33,7 +33,7 @@
  },
  // 获取购物车信息
  getCartInfo(userId: number, unitId: number): ApiPromise {
  getCartInfo(userId: string, unitId: string): ApiPromise {
    return createAxios({
      url: `${baseUrl}/info`,
      method: 'get',
@@ -42,7 +42,7 @@
  },
  // 获取购物车商品列表
  getCartItems(userId: number, unitId: number): ApiPromise {
  getCartItems(userId: string, unitId: string): ApiPromise {
    return createAxios({
      url: `${baseUrl}/items`,
      method: 'get',
@@ -51,7 +51,7 @@
  },
  // 清空购物车
  clearCart(userId: number, unitId: number): ApiPromise {
  clearCart(userId: string, unitId: string): ApiPromise {
    return createAxios({
      url: `${baseUrl}/clear`,
      method: 'delete',
src/api/orderApi.ts
@@ -29,9 +29,23 @@
      data
    }) as ApiPromise
  },
  getBuyerOrderPageWithProductConditions(data: any): ApiPromise {
    return createAxios({
      url: `${url}/buyer/page/with-product-conditions`,
      method: 'post',
      data
    }) as ApiPromise
  },
  getSellerOrderPage(data: any): ApiPromise {
    return createAxios({
      url: `${url}/seller/page`,
      method: 'post',
      data
    }) as ApiPromise
  },
  getSellerOrderPageWithProductConditions(data: any): ApiPromise {
    return createAxios({
      url: `${url}/seller/page/with-product-conditions`,
      method: 'post',
      data
    }) as ApiPromise
@@ -128,6 +142,24 @@
        orderId
      }
    }) as ApiPromise
  },
  // 启动并完成审批流程(外部工作流服务)
  startWorkflowAndComplete(data: { processdefId: string; userid: string; businessKey: string }): ApiPromise {
    return createAxios({
      url: `/test/app/workflow/startProcessAndComplete`,
      method: 'post',
      data
    }) as ApiPromise
  },
  // 更新订单的工作流ID
  updateWorkflowId(orderId: string, workflowId: string): ApiPromise {
    return createAxios({
      url: `${url}/workflow/update`,
      method: 'post',
      params: { orderId, workflowId }
    }) as ApiPromise
  }
}
src/api/productApi.ts
New file
@@ -0,0 +1,32 @@
import createAxios from '@/utils/axios'
let url = '/test/report/product/'
const productApi = {
    // 获取产品列表
    getProductList(data: any): ApiPromise {
        return createAxios({
            url: `${url}accessApplyList`,
            method: 'POST',
            data: data,
        }) as ApiPromise
    },
    // 根据ID获取产品详情
    getProductById(data: any): ApiPromise {
        return createAxios({
            url: `${url}get`,
            method: 'post',
            data: data,
        }) as ApiPromise
    },
   getCategoryByParent(data: any):ApiPromise{
    return createAxios({
        url: `${url}categoryByParent`,
        method: 'post',
        data: data,
    }) as ApiPromise
   }
}
export default productApi
src/api/userInfo.ts
@@ -1,4 +1,5 @@
import createAxios from '@/utils/axios'
let url = '/test'
// 修改密码
export function updatePassw(data: object = {}): ApiPromise {
@@ -14,7 +15,7 @@
// 查看个人信息
export function queryUserDetail(data: object = {}): ApiPromise {
    return createAxios({
        url: `/admin/common/userDetail`,
        url: `${url}/admin/common/userDetail`,
        headers: {
            'Content-Type': 'application/json;charset=UTF-8'
        },
src/router/index.ts
@@ -12,6 +12,7 @@
import { useUserInfo } from '@/stores/modules/userInfo'
import { useCommonInfo } from '@/stores/modules/common' // 状态管理器
import { useNavTabs } from '@/stores/modules/navTabs' // 标签导航
import { queryUserDetail } from '@/api/userInfo'
const router = createRouter({
  history: createWebHistory('/manage/'),
@@ -20,7 +21,7 @@
    return { top: 0 }
  },
})
router.beforeEach((to, from, next) => {
router.beforeEach(async (to, from, next) => {
  console.log("to",to)
  const userStore = useUserInfo()
  // * 在跳转路由之前,清除所有的请求
@@ -41,11 +42,17 @@
    }
    // 如果没有token,设置一个默认token并继续
    if (!userStore.getAdminToken) {
      const localUserInfo = localStorage.getItem('userInfo')
      if (localUserInfo) {
          const userInfo = JSON.parse(localUserInfo)
          if (userInfo.adminToken) {
      const obj: any = {
        adminToken: 'dev-token',
            adminToken: userInfo.adminToken,
        screenToken: '',
      }
      userStore.updateUserInfo(obj)
        }
      }
    }
    next()
    return
@@ -58,12 +65,17 @@
  } else {
    if (to.query.token) {
      const token = to.query.token as string
      localStorage.setItem('lastRecordTime', new Date().getTime())
      localStorage.setItem('lastRecordTime', new Date().getTime().toString())
      const obj: any = {
        adminToken: token,
        screenToken: '',
      }
      userStore.updateUserInfo(obj)
      const localUserInfo = localStorage.getItem('userInfo')
      if (localUserInfo) {
        const userInfo = JSON.parse(localUserInfo)
        userStore.updateUserDetail(userInfo)
      }
      // chrome
      document.body.scrollTop = 0
      // firefox
@@ -72,9 +84,43 @@
      window.pageYOffset = 0
      next()
    } else if (!userStore.getAdminToken) {
      // 如果没有,则跳至登录页面
      next({ path: '/loginT' })
      // 从 localStorage 中获取 userInfo 的 adminToken
      const localUserInfo = localStorage.getItem('userInfo')
      if (localUserInfo) {
        try {
          const userInfo = JSON.parse(localUserInfo)
          if (userInfo.adminToken) {
            // 如果获取到 adminToken,则更新 userStore
            userStore.updateUserInfo({
              adminToken: userInfo.adminToken,
              screenToken: userInfo.screenToken || '',
              fullUnitName: userInfo.fullUnitName || '',
              empCode: userInfo.empCode || '',
              menuList: userInfo.menuList || []
            })
            // 同时更新用户详细信息
            userStore.updateUserDetail(userInfo)
            next()
    } else {
            // 如果没有 adminToken,则用户未登录
            next({ path: '/loginT' })
          }
        } catch (error) {
          console.error('解析 localStorage 中的 userInfo 失败:', error)
          // 解析失败,认为用户未登录
          next({ path: '/loginT' })
        }
      } else {
        // 如果没有 userInfo,则用户未登录
        next({ path: '/loginT' })
      }
    } else {
      // 获取用户信息
      const localUserInfo = localStorage.getItem('userInfo')
      if (localUserInfo) {
        const userInfo = JSON.parse(localUserInfo)
        userStore.updateUserDetail(userInfo)
      }
      next()
    }
  }
src/router/modules/transactionManage.ts
@@ -12,6 +12,15 @@
      },
    },
    {
      path: 'track/:id',
      name: 'tradeTrack',
      component: () => import('@/views/tradeManage/track/index.vue'),
      meta: {
        title: '订单审批轨迹',
        keepAlive: false,
      },
    },
    {
      path: 'seller',
      name: 'tradeSellerCenter',
      component: () => import('@/views/tradeManage/seller/index.vue'),
src/stores/modules/userInfo.ts
@@ -3,7 +3,6 @@
import { defineStore } from 'pinia'
import { UserInfo } from '@/stores/interface'
import router from '@/router/index'
import { updateUserDetail } from '@/api/userInfo'
export const useUserInfo = defineStore('userInfo', {
  state: (): UserInfo => {
src/utils/orderWorkflow.ts
New file
@@ -0,0 +1,258 @@
/**
 * 订单工作流程控制器
 * 管理订单状态流转和操作权限
 */
// 订单状态枚举(按照流程顺序)
export enum OrderStatus {
  WAIT_UPLOAD = '待上传文件',      // 1
  WAIT_AUTHORIZE = '待授权',       // 2
  WAIT_CONFIRM = '待交易确认',     // 3
  COMPLETED = '已完成',           // 4
  EVALUATED = '已评价'            // 5 (最终状态)
}
// 操作类型枚举
export enum ActionType {
  VIEW = '查看',
  TRACK = '追踪',
  UPLOAD_FILE = '提交文件',
  AUTHORIZE = '授权',
  CONFIRM_TRADE = '交易确认',
  EVALUATE = '评价',
  CANCEL_ORDER = '取消订单'
}
// 页面类型枚举
export enum PageType {
  TRADE_APPROVAL = 'tradeApproval',    // 交易审核
  BUYER_CENTER = 'buyerCenter',        // 买家中心
  SELLER_CENTER = 'sellerCenter'       // 卖家中心
}
// 操作配置接口
interface ActionConfig {
  type: ActionType
  routeName?: string
  params?: Record<string, any>
}
// 各页面在不同状态下的操作配置
const PAGE_ACTION_CONFIG: Record<PageType, Record<OrderStatus, ActionConfig[]>> = {
  [PageType.TRADE_APPROVAL]: {
    [OrderStatus.WAIT_UPLOAD]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.WAIT_AUTHORIZE]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK },
      { type: ActionType.AUTHORIZE, routeName: 'tradeApproval' }
    ],
    [OrderStatus.WAIT_CONFIRM]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.COMPLETED]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.EVALUATED]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ]
  },
  [PageType.BUYER_CENTER]: {
    [OrderStatus.WAIT_UPLOAD]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK },
      { type: ActionType.UPLOAD_FILE, routeName: 'tradeOrderUpload' },
      { type: ActionType.CANCEL_ORDER }
    ],
    [OrderStatus.WAIT_AUTHORIZE]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK },
      { type: ActionType.CANCEL_ORDER }
    ],
    [OrderStatus.WAIT_CONFIRM]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK },
      { type: ActionType.CONFIRM_TRADE, routeName: 'tradeOrderConfirm' },
      { type: ActionType.CANCEL_ORDER }
    ],
    [OrderStatus.COMPLETED]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK },
      { type: ActionType.EVALUATE, routeName: 'tradeOrderEvaluate' }
    ],
    [OrderStatus.EVALUATED]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ]
  },
  [PageType.SELLER_CENTER]: {
    [OrderStatus.WAIT_UPLOAD]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.WAIT_AUTHORIZE]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.WAIT_CONFIRM]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.COMPLETED]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ],
    [OrderStatus.EVALUATED]: [
      { type: ActionType.VIEW, routeName: 'tradeOrderDetail' },
      { type: ActionType.TRACK }
    ]
  }
}
// 状态流转映射(当前状态 -> 下一个状态)
const STATUS_FLOW_MAP: Record<OrderStatus, OrderStatus | null> = {
  [OrderStatus.WAIT_UPLOAD]: OrderStatus.WAIT_AUTHORIZE,
  [OrderStatus.WAIT_AUTHORIZE]: OrderStatus.WAIT_CONFIRM,
  [OrderStatus.WAIT_CONFIRM]: OrderStatus.COMPLETED,
  [OrderStatus.COMPLETED]: OrderStatus.EVALUATED,
  [OrderStatus.EVALUATED]: null // 最终状态,无下一状态
}
/**
 * 订单工作流程控制器类
 */
export class OrderWorkflowController {
  /**
   * 获取指定页面和状态下的可用操作列表
   * @param pageType 页面类型
   * @param currentStatus 当前订单状态
   * @returns 操作配置列表
   */
  static getAvailableActions(pageType: PageType, currentStatus: OrderStatus): ActionConfig[] {
    const pageConfig = PAGE_ACTION_CONFIG[pageType]
    if (!pageConfig) {
      console.warn(`未找到页面类型 ${pageType} 的配置`)
      return []
    }
    const statusConfig = pageConfig[currentStatus]
    if (!statusConfig) {
      console.warn(`未找到状态 ${currentStatus} 在页面 ${pageType} 中的配置`)
      return []
    }
    return statusConfig
  }
  /**
   * 判断是否可以执行指定操作
   * @param pageType 页面类型
   * @param currentStatus 当前订单状态
   * @param actionType 操作类型
   * @returns 是否可以执行
   */
  static canExecuteAction(pageType: PageType, currentStatus: OrderStatus, actionType: ActionType): boolean {
    const actions = this.getAvailableActions(pageType, currentStatus)
    return actions.some(action => action.type === actionType)
  }
  /**
   * 获取下一个状态
   * @param currentStatus 当前状态
   * @returns 下一个状态,如果已是最终状态则返回null
   */
  static getNextStatus(currentStatus: OrderStatus): OrderStatus | null {
    return STATUS_FLOW_MAP[currentStatus] || null
  }
  /**
   * 判断当前状态是否为最终状态
   * @param currentStatus 当前状态
   * @returns 是否为最终状态
   */
  static isFinalStatus(currentStatus: OrderStatus): boolean {
    return currentStatus === OrderStatus.EVALUATED
  }
  /**
   * 验证状态流转是否合法
   * @param fromStatus 起始状态
   * @param toStatus 目标状态
   * @returns 是否为合法的状态流转
   */
  static validateStatusTransition(fromStatus: OrderStatus, toStatus: OrderStatus): boolean {
    const expectedNextStatus = this.getNextStatus(fromStatus)
    return expectedNextStatus === toStatus
  }
  /**
   * 获取所有状态的顺序列表
   * @returns 状态列表
   */
  static getAllStatusInOrder(): OrderStatus[] {
    return [
      OrderStatus.WAIT_UPLOAD,
      OrderStatus.WAIT_AUTHORIZE,
      OrderStatus.WAIT_CONFIRM,
      OrderStatus.COMPLETED,
      OrderStatus.EVALUATED
    ]
  }
  /**
   * 根据产品套件判断初始状态
   * @param orderDetails 订单详情列表
   * @returns 初始状态
   */
  static determineInitialStatus(orderDetails: any[]): OrderStatus {
    // 检查是否有价格类型为"协议"的套件
    const hasAgreementPrice = orderDetails.some(detail => {
      const priceType = String(detail.priceType || '').toLowerCase()
      return priceType.includes('协议') || priceType.includes('agreement')
    })
    // 如果有协议价格,从"待上传文件"开始,否则从"待授权"开始
    return hasAgreementPrice ? OrderStatus.WAIT_UPLOAD : OrderStatus.WAIT_AUTHORIZE
  }
}
// 状态映射工具函数(用于前后端状态转换)
export const StatusMapper = {
  /**
   * 前端枚举状态转后端中文状态
   */
  toServerStatus: (uiStatus: string): string => {
    // 直接返回中文状态,因为枚举值本身就是中文
    return uiStatus
  },
  /**
   * 后端中文状态转前端枚举状态
   */
  toUIStatus: (serverStatus: string): OrderStatus => {
    // 标准化状态名称
    const normalizedStatus = serverStatus?.trim()
    switch (normalizedStatus) {
      case '待上传文件':
        return OrderStatus.WAIT_UPLOAD
      case '待授权':
        return OrderStatus.WAIT_AUTHORIZE
      case '待交易确认':
        return OrderStatus.WAIT_CONFIRM
      case '已完成':
        return OrderStatus.COMPLETED
      case '已评价':
        return OrderStatus.EVALUATED
      default:
        console.warn(`未知的订单状态: ${serverStatus},默认返回待授权状态`)
        return OrderStatus.WAIT_AUTHORIZE
    }
  }
}
src/views/loginT/index.vue
@@ -111,7 +111,7 @@
<script lang="ts" setup>
import { reactive, ref, toRefs, onMounted } from 'vue'
import { login } from '@/api/login'
import { FormInstance, FormRules } from 'element-plus'
import { ElMessage, FormInstance, FormRules } from 'element-plus'
import router from '@/router'
import { useUserInfo } from '@/stores/modules/userInfo'
import { getAssetsImages } from '@/utils/common'
@@ -165,7 +165,7 @@
      // 正常登录接口调用
      login(state.userInfo).then((res: any) => {
        if (res.code == 200) {
          localStorage.setItem('lastRecordTime', new Date().getTime())
          localStorage.setItem('lastRecordTime', new Date().getTime().toString())
          // 将缓存进行缓存或者状态管理器中
          userStore.removeMenuList()
          userStore.updateUserInfo(res.data)
src/views/pointsManage/personal/index.vue
@@ -157,6 +157,11 @@
} from '@element-plus/icons-vue'
import pointsApi from '@/api/pointsApi'
import type { PointsStats, PointsFlow, PointsQueryParams } from '@/types/points'
import { useUserInfo } from '@/stores/modules/userInfo'
import { queryUserDetail } from '@/api/userInfo'
// 获取用户信息 store
const userStore = useUserInfo()
// 积分统计
const stats = ref<PointsStats>({
@@ -168,7 +173,7 @@
// 查询参数
const queryParams = reactive<PointsQueryParams>({
  userId: '1',
  userId: '',
  dataCategory: '',
  dataType: '',
  pageNum: 1,
@@ -208,7 +213,13 @@
// 获取积分统计
const getPointsStats = async () => {
  try {
    const userId = 1;
    const userId = userStore.getUserId || userStore.getUserInfo?.userId
    if (!userId) {
      console.error('无法获取用户ID')
      return
    }
    // 更新查询参数中的 userId
    queryParams.userId = userId.toString()
    const res = await pointsApi.getPersonalPointsStats(userId)
    if (res.code === 200 && res.data) {
      stats.value = res.data
@@ -233,6 +244,13 @@
// 获取积分流水
const getPointsFlow = async () => {
  try {
    const userId = userStore.getUserId || userStore.getUserInfo?.userId
    if (!userId) {
      console.error('无法获取用户ID')
      return
    }
    // 更新查询参数中的 userId
    queryParams.userId = userId.toString()
    const res = await pointsApi.getPersonalPointsFlow(queryParams)
    if (res.code === 200) {
      flowList.value = res.data.list || []
@@ -300,7 +318,26 @@
  ElMessage.info('关闭全部功能')
}
onMounted(() => {
onMounted(async () => {
  // 页面挂载时,检查 userStore 是否已有用户信息
  let userId = userStore.getUserId || userStore.getUserInfo?.userId
  if (!userId) {
    try {
      const res: any = await queryUserDetail()
      if (res?.code === 200 && res.data) {
        userStore.updateUserDetail(res.data)
        userId = res.data.userId || res.data.id
      } else {
        ElMessage.error(res?.msg || '无法获取用户信息,请先登录')
        return
      }
    } catch (e) {
      console.error('获取用户详情失败:', e)
      ElMessage.error('获取用户信息失败,请稍后重试')
      return
    }
  }
  getPointsStats()
  getPointsFlow()
  getCategoryList()
src/views/productManage/price/index.vue
@@ -19,14 +19,14 @@
      <el-descriptions v-if="productDetail" :column="2" border>
        <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="提报单位" label-width="10%">{{ productDetail.submissionUnit }}</el-descriptions-item>
        <el-descriptions-item label="提报人" label-width="10%">{{ productDetail.createBy }}</el-descriptions-item>
        <el-descriptions-item label="行业领域" label-width="10%">{{ productDetail.industrialChainName }}</el-descriptions-item>
        <el-descriptions-item label="单位工程" label-width="10%">{{ productDetail.importantAreaName }}</el-descriptions-item>
        <el-descriptions-item label="产业阶段" label-width="10%">{{ productDetail.businessProcessName }}</el-descriptions-item>
        <el-descriptions-item label="产品类型"label-width="10%">{{ productDetail.typeName }}</el-descriptions-item>
        <el-descriptions-item label="产品简介" :span="3">
          <div class="intro">{{ productDetail.description }}</div>
          <div class="intro">{{ productDetail.describe }}</div>
        </el-descriptions-item>
      </el-descriptions>
      <el-empty v-else description="未找到该产品的详情" />
@@ -239,18 +239,19 @@
import { Goods, Lock, Unlock } from '@element-plus/icons-vue'
import ProductPriceViewer from '@/views/productManage/productPriceViewer/index.vue'
import productPricingApi from '@/api/productPricingApi'
import productApi from '@/api/productApi'
interface ProductDetail {
  id: string
  name: string
  submitUnit: string
  submitter: string
  industry: string
  projectUnit: string
  industryStage: string
  productType: string
  description: string
  submissionUnit: string
  createBy: string
  industrialChainName: string
  importantAreaName: string
  businessProcessName: string
  typeName: string
  describe: string
  shelfStatus?: '待上架' | '已上架' | '已下架'
}
@@ -297,32 +298,32 @@
})
// 模拟产品详情数据源
const mockProductMap: Record<string, ProductDetail> = {
  '1': {
    id: '1',
    name: '数字化产品A',
    submitUnit: '中交一公局',
    submitter: '张三',
    industry: '交通基础设施',
    projectUnit: '某高速公路工程',
    industryStage: '应用阶段',
    productType: '软件/平台',
    description: '本产品定位为以建设期BIM数字资产作为数字底盘,结合项目运营维保需求的实时性、交互性、便捷性的三维可视化运维管理系统。系统提供项目数字化、智能化运维管理功能,能够解决建筑运行维护管理中的实际问题,实现信息快速整合与查询、信息有效共享与传递,提升项目综合管理与维护水平。',
    shelfStatus: '待上架'
  },
  '2': {
    id: '2',
    name: '数字化产品B',
    submitUnit: '中交二航局',
    submitter: '李四',
    industry: '市政工程',
    projectUnit: '智慧管廊项目',
    industryStage: '研发阶段',
    productType: '硬件/传感',
    description: '面向城市管廊监测的传感设备与采集网关,支持边缘计算与远程运维。',
    shelfStatus: '已上架'
  }
}
// const mockProductMap: Record<string, ProductDetail> = {
//   '1': {
//     id: '1',
//     name: '数字化产品A',
//     submitUnit: '中交一公局',
//     submitter: '张三',
//     industry: '交通基础设施',
//     projectUnit: '某高速公路工程',
//     industryStage: '应用阶段',
//     productType: '软件/平台',
//     description: '本产品定位为以建设期BIM数字资产作为数字底盘,结合项目运营维保需求的实时性、交互性、便捷性的三维可视化运维管理系统。系统提供项目数字化、智能化运维管理功能,能够解决建筑运行维护管理中的实际问题,实现信息快速整合与查询、信息有效共享与传递,提升项目综合管理与维护水平。',
//     shelfStatus: '待上架'
//   },
//   '2': {
//     id: '2',
//     name: '数字化产品B',
//     submitUnit: '中交二航局',
//     submitter: '李四',
//     industry: '市政工程',
//     projectUnit: '智慧管廊项目',
//     industryStage: '研发阶段',
//     productType: '硬件/传感',
//     description: '面向城市管廊监测的传感设备与采集网关,支持边缘计算与远程运维。',
//     shelfStatus: '已上架'
//   }
// }
const loading = ref(false)
const priceList = ref<any[]>([])
@@ -503,14 +504,17 @@
  }
  loading.value = true
  try {
    productDetail.value = mockProductMap[productId] || null
    // 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 || '获取产品详情失败')
    // }
    // productDetail.value = mockProductMap[productId] || null
    const data = {
      id: productId
    }
    const detailRes: any = await productApi.getProductById(data)
    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
@@ -638,7 +642,7 @@
  
  return {
    id: row.id,
    productId: Number(row.productId),
    productId: row.productId,
    productName: productDetail.value?.name,
    suiteName: row.productSuite,
    salesForm: mapSalesFormToCN(row.salesForm),
@@ -789,7 +793,7 @@
   
    return {
      id: isEditMode.value && formData.id  ? formData.id : undefined,
      productId: Number(currentProductId.value),
      productId: currentProductId.value,
      productName: productDetail.value?.name,
      suiteName: formData.productSuite,
      salesForm: mapSalesFormToCN(formData.salesForm),
src/views/productManage/product/index.vue
@@ -24,9 +24,9 @@
      >
        <el-table-column prop="id" label="产品ID" width="120" />
        <el-table-column prop="name" label="产品名称" min-width="200" />
        <el-table-column prop="productType" label="产品类型" width="120" />
        <el-table-column prop="industry" label="行业领域" width="150" />
        <el-table-column prop="submitUnit" label="提报单位" width="150" />
        <el-table-column prop="typeName" label="产品类型" width="120" />
        <el-table-column prop="importantAreaName" label="行业领域" width="150" />
        <el-table-column prop="submissionUnit" label="提报单位" width="150" />
        <el-table-column prop="shelfStatus" label="上架状态" width="100">
          <template #default="{ row }">
            <el-tag :type="getStatusType(row.shelfStatus)" size="small">
@@ -56,11 +56,11 @@
    </el-card>
    <!-- 产品价格查看弹窗 -->
    <ProductPriceViewer
    <!-- <ProductPriceViewer
      v-model="priceViewerVisible"
      :product-id="currentProductId"
      @order="handleOrder"
    />
    /> -->
  </div>
</template>
@@ -69,6 +69,7 @@
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import ProductPriceViewer from '@/views/productManage/productPriceViewer/index.vue'
import productApi from '@/api/productApi'
interface ProductItem {
  id: string
@@ -91,57 +92,6 @@
const priceViewerVisible = ref(false)
const currentProductId = ref('')
// 模拟产品数据
const mockProductList: ProductItem[] = [
  {
    id: '1',
    name: '数字化产品A',
    productType: '软件/平台',
    industry: '交通基础设施',
    submitUnit: '中交一公局',
    submitter: '张三',
    projectUnit: '某高速公路工程',
    industryStage: '应用阶段',
    description: '本产品定位为以建设期BIM数字资产作为数字底盘,结合项目运营维保需求的实时性、交互性、便捷性的三维可视化运维管理系统。系统提供项目数字化、智能化运维管理功能,能够解决建筑运行维护管理中的实际问题,实现信息快速整合与查询、信息有效共享与传递,提升项目综合管理与维护水平。',
    shelfStatus: '待上架'
  },
  {
    id: '2',
    name: '数字化产品B',
    productType: '硬件/传感',
    industry: '市政工程',
    submitUnit: '中交二航局',
    submitter: '李四',
    projectUnit: '智慧管廊项目',
    industryStage: '研发阶段',
    description: '面向城市管廊监测的传感设备与采集网关,支持边缘计算与远程运维。',
    shelfStatus: '已上架'
  },
  {
    id: '3',
    name: '数字化产品C',
    productType: '软件/平台',
    industry: '建筑工程',
    submitUnit: '中交三航局',
    submitter: '王五',
    projectUnit: '智慧建筑项目',
    industryStage: '应用阶段',
    description: '基于BIM技术的建筑工程管理平台,提供设计、施工、运维全生命周期管理。',
    shelfStatus: '已下架'
  },
  {
    id: '10004',
    name: '数字化产品D',
    productType: '硬件/传感',
    industry: '水利工程',
    submitUnit: '中交四航局',
    submitter: '赵六',
    projectUnit: '智慧水利项目',
    industryStage: '研发阶段',
    description: '水利工程监测设备与数据采集系统,支持实时监测和预警。',
    shelfStatus: '已上架'
  }
]
// 获取状态类型
const getStatusType = (status: string) => {
@@ -162,8 +112,16 @@
  loading.value = true
  try {
    // 模拟API调用
    await new Promise(resolve => setTimeout(resolve, 500))
    productList.value = mockProductList
    // await new Promise(resolve => setTimeout(resolve, 500))
    let data = {"name":"","industrialChainId":"","importantAreaId":"","importantAreaIdList":[],"typeId":"","typeChildId":[],"startDate":"","endDate":"","page":{"current":1,"size":10,"total":3}}
    const res: any = await productApi.getProductList(data)
    if (res?.code === 200) {
      productList.value = res.data.records || []
    } else {
      productList.value = []
      ElMessage.error(res?.msg || '获取产品列表失败')
    }
    // productList.value = mockProductList
  } catch (error) {
    console.error('加载产品列表失败:', error)
    ElMessage.error('加载产品列表失败')
@@ -187,8 +145,12 @@
// 查看定价
const handleViewPricing = (row: ProductItem) => {
  currentProductId.value = row.id
  priceViewerVisible.value = true
  // currentProductId.value = row.id
  // priceViewerVisible.value = true
  router.push({
    path: '/product/priceViewer',
    query: { productId: row.id}
  })
}
// 处理订购
src/views/productManage/productPriceViewer/index.vue
@@ -1,12 +1,5 @@
<template>
  <el-dialog
    v-model="visible"
    :title="dialogTitle"
    :before-close="handleClose"
    destroy-on-close
    class="product-price-dialog"
    :width="dialogWidth"
  >
   <div>
    <div class="price-viewer-container" v-loading="loading">
      <!-- 价格对比表格 -->
      <div class="pricing-table-container" v-if="showPricePanel && priceList.length > 0">
@@ -174,19 +167,19 @@
              <span class="label">产品名称:</span><span class="value strong">{{ productHeader.name }}</span>
            </div>
            <div class="grid-item">
              <span class="label">行业板块:</span><span class="value">{{ productHeader.industry }}</span>
              <span class="label">行业板块:</span><span class="value">{{ productHeader.industrialChainName }}</span>
            </div>
            <div class="grid-item">
              <span class="label">提供者:</span><span class="value">{{ productHeader.provider }}</span>
              <span class="label">提供者:</span><span class="value">{{ productHeader.submissionUnit }}</span>
            </div>
            <div class="grid-item">
              <span class="label">用户姓名:</span><span class="value">{{ userInfo.name }}</span>
            </div>
            <div class="grid-item">
              <span class="label">单位:</span><span class="value">{{ userInfo.unit }}</span>
              <span class="label">单位:</span><span class="value">{{ userInfo.unitName }}</span>
            </div>
            <div class="grid-item">
              <span class="label">部门:</span><span class="value">{{ userInfo.department }}</span>
              <span class="label">部门:</span><span class="value">{{ userInfo.departmentName }}</span>
            </div>
          </div>
        </el-card>
@@ -338,32 +331,29 @@
        <el-empty description="暂无价格信息" />
      </div>
    </div>
    <template #footer v-if="!showOrderStatus">
      <span class="dialog-footer">
        <template v-if="showPricePanel">
        <el-button @click="handleClose">关闭</el-button>
    <div class="footer" v-if="showPricePanel">
        <el-button type="primary" @click="handleOrder">立即订购</el-button>
        </template>
        <template v-else>
    </div>
    <div class="footer" v-else>
          <el-button @click="returnPricePanel">返回价格对比</el-button>
          <el-button type="primary" @click="submitOrder">提交申请</el-button>
        </template>
      </span>
    </template>
  </el-dialog>
    </div>
  </div>
  <!-- 产品订购对话框移除,改为内嵌展示 -->
</template>
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import { ref, watch, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router'
import productPricingApi from '@/api/productPricingApi'
import cartApi from '@/api/cartApi'
import orderApi from '@/api/orderApi'
import { ShoppingCart, Coin, Money } from '@element-plus/icons-vue'
import { useUserInfo } from '@/stores/modules/userInfo'
import { queryUserDetail } from '@/api/userInfo'
import productApi from '@/api/productApi'
const route = useRoute()
@@ -384,36 +374,11 @@
  enableStatus: 'ENABLED' | 'DISABLED'
}
interface Props {
  modelValue: boolean
  productId?: string
  width?: string
}
interface Emits {
  (e: 'update:modelValue', value: boolean): void
  (e: 'order', selectedItems: PriceItem[]): void
}
// 兼容 path 参数 productId 与 query 参数 id/productId
const currentProductId = computed<string | undefined>(() => {
  return (route.params.productId as string) || (route.query.productId as string) || (route.query.id as string)
})
const props = withDefaults(defineProps<Props>(), {
  modelValue: false,
  productId: '',
  width: '90%'
})
const emit = defineEmits<Emits>()
const visible = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})
const loading = ref(false)
const activeTab = ref('enterprise')
@@ -424,8 +389,32 @@
const showPricePanel = ref(true)
const showOrderStatus = ref(false)
// 模拟用户信息(实际应从用户状态获取)
const currentUserId = ref(1)
const currentUnitId = ref(1)
const userStore = useUserInfo()
let currentUserId = computed(() => userStore.getUserId || userStore.getUserInfo?.userId)
const currentUnitId = computed(() => userStore.getUnitId || userStore.getUserInfo?.unitId || '')
onMounted(async () => {
  if (!currentUserId.value) {
    try {
      const res: any = await queryUserDetail()
      if (res?.code === 200 && res.data) {
        userStore.updateUserDetail(res.data)
        currentUserId = res.data.userId || res.data.id
        userInfo.value = res.data
      } else {
        ElMessage.error(res?.msg || '无法获取用户信息,请先登录')
        return
      }
    } catch (e) {
      console.error('获取用户详情失败:', e)
      ElMessage.error('获取用户信息失败,请稍后重试')
      return
    }
  }else{
    userInfo.value = userStore.getUserInfos
  }
})
type PriceTypeKey = 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'
interface OrderSuite extends PriceItem {
  selected: boolean
@@ -513,14 +502,15 @@
// 产品信息/用户信息(静态)
const productHeader = ref({
  name: '中交方远智能实测实量管理系统',
  industry: '公路,市政,建筑',
  provider: '中交建筑集团第一工程有限公司'
  name: '',
  industrialChainName: '',
  submissionUnit: '',
  createUserId: ''
})
const userInfo = ref({
  name: '张静',
  unit: '信科集团',
  department: '门户系统临时组'
  name: '',
  departmentName: '',
  unitName: ''
})
// 全选
@@ -551,7 +541,7 @@
  const suite = orderSuites.value[index]
  if (!suite) return
  try {
    await removeFromCart(suite.id)
    await removeFromCart(String(suite.id))
    orderSuites.value.splice(index, 1)
  } catch (e) {
  }
@@ -563,8 +553,6 @@
  ElMessage.success('订购申请提交成功!')
  // 清空选择状态
  selectedSuiteIds.value.clear()
  // 关闭价格查看器
  visible.value = false
}
// 弹窗标题
@@ -572,10 +560,6 @@
  return `【${productName.value}】产品定价`
})
// 弹窗宽度
const dialogWidth = computed(() => {
  return props.width
})
// 按客户对象分组的价格数据
const groupedPriceData = computed(() => {
@@ -701,31 +685,33 @@
  } finally {
    loading.value = false
  }
  //获取产品详情信息
  try {
    // productDetail.value = mockProductMap[productId] || null
    const data = {
      id: productId
    }
    const detailRes: any = await productApi.getProductById(data)
    if (detailRes?.code === 200) {
      productHeader.value = detailRes.data || { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
    } else {
      productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
      ElMessage.error(detailRes?.msg || '获取产品详情失败')
    }
  } catch (e) {
    productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
    ElMessage.error('获取产品详情失败')
  } finally {
    loading.value = false
  }
}
// 监听产品ID变化
watch(() => props.productId, (newProductId) => {
  if (newProductId && visible.value) {
watch(currentProductId, (newProductId) => {
  if (newProductId) {
    fetchProductData(newProductId)
  }
})
// 监听弹窗显示状态
watch(() => visible.value, (newVisible) => {
  if (newVisible && props.productId) {
    fetchProductData(props.productId)
  }
})
// 关闭弹窗
const handleClose = () => {
  visible.value = false
  showOrderPanel.value = false
  showOrderStatus.value = false
  showPricePanel.value = true
  orderSuites.value = []
  selectedSuiteIds.value.clear()
}
},{ immediate: true })
// 套件选择处理
const handleSuiteSelect = (suiteId: number, checked: any) => {
@@ -740,7 +726,7 @@
  } else {
    selectedSuiteIds.value.delete(suiteId)
    // 从购物车移除
    removeFromCart(suiteId)
    removeFromCart(String(suiteId))
  }
}
@@ -789,19 +775,26 @@
    ElMessage.error('下单Token获取失败,请返回重试')
    return
  }
  const hasPoints = items.some(item => item.priceType === 'POINTS')
  const hasAGREEMENT = items.some(item => item.priceType === 'AGREEMENT')
  let paymentType = ''
  if(hasPoints){
     paymentType = '积分'
  }else{
     paymentType = '协议'
  }
  // 组装创建订单参数(CreateOrderDTO)
  const payload = {
    userId: currentUserId.value,
    unitId: currentUnitId.value,
    productName: productHeader.value.name,
    providerName: productHeader.value.provider,
    providerId: 3,
    paymentType: '积分',
    providerName: productHeader.value.submissionUnit,
    providerId: productHeader.value.createUserId,
    paymentType: paymentType,
    buyerRemarks: '',
    items: items.map(it => ({
      pricingId: it.id,
      productId: Number(it.productId),
      productId: it.productId,
      suiteName: getProductSuiteText(it.productSuite),
      salesForm: getSalesFormText(it.salesForm),
      customerType: getCustomerObjectText(it.customerObject),
@@ -813,8 +806,8 @@
      quantity: it.quantity,
      duration: it.duration,
      totalPrice: undefined,
      providerId: 3,
      providerName: productHeader.value.provider,
      providerId: productHeader.value.createUserId,
      providerName: productHeader.value.submissionUnit,
      remarks: ''
    }))
  }
@@ -827,11 +820,28 @@
      orderStatus.value = {
        id: data.orderId || '—',
        productName: data.productName || productHeader.value.name,
        provider: data.providerName || productHeader.value.provider,
        provider: data.providerName || productHeader.value.submissionUnit,
        status: 'SUBMITTED',
        submitTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString(),
        applyTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString()
      }
      // 调用工作流接口发起审批流程,拿到流程实例ID后回写订单workflow_id
      try {
        // 根据是否包含协议明细,配置不同流程定义与业务Key(先用静态值占位)
        const processdefId = hasAGREEMENT ? 'Process_Agreement_Static' : 'Process_Points_Static'
        const businessKey = hasAGREEMENT ? 'agreement_biz_key' : 'points_biz_key'
        const wfRes: any = await orderApi.startWorkflowAndComplete({
          processdefId,
          userid: String(currentUserId.value || ''),
          businessKey
        })
        if (wfRes?.code === 200 && wfRes.data?.processinstId) {
          await orderApi.updateWorkflowId(data.orderId, wfRes.data.processinstId)
        }
      } catch (e) {
        console.warn('启动工作流失败或更新workflow_id失败', e)
      }
      // 清空购物车(后端 + 本地状态)
      try {
        const clearRes: any = await cartApi.clearCart(currentUserId.value, currentUnitId.value)
@@ -859,7 +869,7 @@
  try {
    const cartData = {
      pricingId: priceItem.id,
      productId: Number(priceItem.productId),
      productId: priceItem.productId,
      productName: productName.value,
      suiteName: getProductSuiteText(priceItem.productSuite),
      salesForm: getSalesFormText(priceItem.salesForm),
@@ -885,7 +895,7 @@
  }
}
const removeFromCart = async (pricingId: number) => {
const removeFromCart = async (pricingId: string) => {
  try {
    const res: any = await cartApi.removeFromCart(currentUserId.value, currentUnitId.value, pricingId)
    if (res?.code === 200) {
@@ -1084,29 +1094,65 @@
  .pricing-table-container {
    .pricing-tabs {
      :deep(.el-tabs__header) {
        margin-bottom: 5px;
        margin-bottom: 12px;
      }
      /* 居中 tabs 导航 */
      :deep(.el-tabs__nav-wrap) {
        display: flex;
        justify-content: center;
      }
      :deep(.el-tabs__nav) {
        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: bold;
        font-weight: 600;
        color: #fff;
        background: #3a7afe;
        box-shadow: 0 6px 16px rgba(58, 122, 254, 0.25);
      }
    }
    .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 {
        width: 100%;
        border-collapse: collapse;
        border: 1px solid #e4e7ed;
        border-collapse: separate;
        border-spacing: 0;
        background: #fff;
        border-radius: 10px;
        
        th, td {
          border: 1px solid #e4e7ed;
          padding: 6px 8px;
          border-bottom: 1px solid #eef2f7;
          border-right: 1px solid #f3f5f7;
          padding: 12px 14px;
          text-align: center;
          vertical-align: middle;
          font-size: 16px;
          font-size: 14px;
        }
        tr th:first-child, td:first-child{
          font-weight: 700;
@@ -1114,7 +1160,7 @@
        }
        
        th {
          background-color: #f5f7fa;
          background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
          font-weight: 600;
          color: #303133;
        }
@@ -1126,9 +1172,9 @@
        
        .sub-header {
          th {
            background-color: #fafafa;
            background-color: #f8fafc;
            font-weight: 500;
              font-size: 16px;
            font-size: 13px;
            color: #606266;
          }
        }
@@ -1141,26 +1187,34 @@
        }
        
        .feature-value {
          color: #606266;
          background: #fafafa; /* 行为灰色 */
          color: #303133;
          background: #fff;
        }
        tbody tr:hover td {
          background: #fafcff;
        }
        
        .order-methods {
          display: flex;
          flex-direction: column;
          gap: 4px;
          gap: 6px;
          align-items: center;
          
          .method-item {
            font-size: 16px;
            color: #409eff;
            font-size: 12px;
            color: #3a7afe;
            background: #f4f7ff;
            border: 1px solid #e3ebff;
            border-radius: 999px;
            padding: 2px 10px;
          }
        }
        
        .price-info {
          display: flex;
          flex-direction: column; /* 竖排 */
          gap: 8px;
          gap: 10px;
          align-items: center;
          
          .price-item {
@@ -1168,24 +1222,29 @@
            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{
              display: flex;
              gap: 6px;
            }
            .price-label {
              font-size: 16px;
              color: #606266;
              font-size: 12px;
              color: #909399;
            }
            
            .price-value {
              font-weight: 500;
              font-size: 20px;
              font-weight: 600;
              font-size: 18px;
              &.points {
                color: #e7900d;
              }
              
              &.currency {
                color: #e7900d;
                color: #10b981;
              }
              
              &.free {
@@ -1193,7 +1252,7 @@
              }
            }
            .price-icon.points { color: #e6a23c; }
            .price-icon.currency { color: #e7900d; }
            .price-icon.currency { color: #16a34a; }
          }
          
                     .add-checkbox {
@@ -1206,7 +1265,7 @@
           justify-content: center;
           align-items: center;
           padding: 8px 0;
           .add-cart-icon { color: #409eff; font-size:22px}
           .add-cart-icon { color: #3a7afe; font-size:22px}
         }
      }
    }
@@ -1348,4 +1407,5 @@
.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;}
</style>
src/views/tradeManage/buyer/index.vue
@@ -9,7 +9,7 @@
            <el-input v-model="query.productName" placeholder="请输入产品名称" clearable style="width: 100%" />
          </el-form-item>
          <el-form-item label="行业领域" class="col-25">
            <el-select v-model="query.industry" placeholder="请选择行业领域" clearable style="width: 100%">
            <el-select v-model="query.industry" placeholder="请选择行业领域" clearable style="width: 100%" @change="handleIndustryChange">
              <el-option v-for="item in industryOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
@@ -31,7 +31,7 @@
            <el-input v-model="query.orderNo" placeholder="请输入订单编号" clearable style="width: 100%" />
          </el-form-item>
          <el-form-item label="产品类型" class="col-25">
            <el-select v-model="query.productType" placeholder="请选择产品类型" clearable style="width: 100%">
            <el-select v-model="query.productType" placeholder="请选择产品类型" clearable style="width: 100%" @change="handleProductTypeChange">
              <el-option v-for="item in productTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
@@ -74,9 +74,7 @@
              <template #default="{ row }">
                <div v-if="row.isMainOrder" class="main-order-info">
                  <div class="order-header">
                    <div class="order-item status-item">
                      <el-tag :type="getStatusType(row.status)" size="small">{{ row.statusName }}</el-tag>
                    </div>
                    <div class="order-item">
                      <span class="label">申请时间:</span>
                      <span class="value">{{ row.applyTime }}</span>
@@ -88,6 +86,9 @@
                    <div class="order-item">
                      <span class="label">供应方:</span>
                      <span class="value">{{ row.supplySide }}</span>
                    </div>
                    <div class="order-item status-item">
                      <el-tag :type="getStatusType(row.status)" size="small">{{ row.statusName }}</el-tag>
                    </div>
                  </div>
                </div>
@@ -269,10 +270,10 @@
    </el-card>
    
    <!-- 订单状态对话框 -->
    <ProductOrderStatusDialog
    <!-- <ProductOrderStatusDialog
      v-model="orderStatusDialogVisible" 
      :order-id="currentOrderId" 
    />
    /> -->
  </div>
</template>
@@ -282,9 +283,11 @@
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'
const router = useRouter()
const userStore = useUserInfo()
@@ -299,35 +302,11 @@
  { label: '已评价', value: 'EVALUATED' },
]
// 行业领域选项
const industryOptions = [
  { label: '建筑工程', value: 'construction' },
  { label: '交通工程', value: 'transportation' },
  { label: '水利工程', value: 'water' },
  { label: '电力工程', value: 'power' },
]
// 单位工程选项
const unitProjectOptions = [
  { label: '土建工程', value: 'civil' },
  { label: '安装工程', value: 'installation' },
  { label: '装饰工程', value: 'decoration' },
]
// 产品类型选项
const productTypeOptions = [
  { label: '企业私有SaaS版许可', value: 'enterprise_private' },
  { label: '企业公有SaaS版许可', value: 'enterprise_public' },
  { label: '项目公有SaaS版许可', value: 'project_public' },
  { label: '个人公有SaaS版许可', value: 'personal_public' },
]
// 产品类型子级选项
const productSubTypeOptions = [
  { label: '基础版', value: 'basic' },
  { label: '标准版', value: 'standard' },
  { label: '高级版', value: 'premium' },
]
// 动态选项数据
const industryOptions = ref<any[]>([])
const unitProjectOptions = ref<any[]>([])
const productTypeOptions = ref<any[]>([])
const productSubTypeOptions = ref<any[]>([])
// 查询条件
const query = reactive({
@@ -380,6 +359,92 @@
  if (/(协议|agreement)/i.test(s)) return 'agreement'
  if (/(免费|free)/i.test(s)) return 'free'
  return 'currency'
}
// 获取行业领域选项
const getIndustryOptions = async () => {
  try {
    const res = await productApi.getCategoryByParent({ parentCode: 'business_direction' })
    if (res?.code === 200 && res.data) {
      industryOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取行业领域选项失败:', error)
  }
}
// 获取产品类型选项
const getProductTypeOptions = async () => {
  try {
    const res = await productApi.getCategoryByParent({ parentCode: 'product_type' })
    if (res?.code === 200 && res.data) {
      productTypeOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取产品类型选项失败:', error)
  }
}
// 根据行业领域获取单位工程选项
const getUnitProjectOptions = async (industryCode: string) => {
  if (!industryCode) {
    unitProjectOptions.value = []
    return
  }
  try {
    const res = await productApi.getCategoryByParent({ parentId: industryCode })
    if (res?.code === 200 && res.data) {
      unitProjectOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取单位工程选项失败:', error)
    unitProjectOptions.value = []
  }
}
// 根据产品类型获取产品子级选项
const getProductSubTypeOptions = async (productTypeCode: string) => {
  if (!productTypeCode) {
    productSubTypeOptions.value = []
    return
  }
  try {
    const res = await productApi.getCategoryByParent({ parentId: productTypeCode })
    if (res?.code === 200 && res.data) {
      productSubTypeOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取产品子级选项失败:', error)
    productSubTypeOptions.value = []
  }
}
// 处理行业领域变化
const handleIndustryChange = async (value: string) => {
  // 清空单位工程选择
  query.unitProject = ''
  // 获取对应的单位工程选项
  await getUnitProjectOptions(value)
}
// 处理产品类型变化
const handleProductTypeChange = async (value: string) => {
  // 清空产品子级选择
  query.productSubType = ''
  // 获取对应的产品子级选项
  await getProductSubTypeOptions(value)
}
// 获取状态类型
@@ -480,7 +545,7 @@
    pageSize: page.size,
    productName: query.productName || undefined,
    orderId: query.orderNo || undefined,
    userId: userStore.getUserId ? Number(userStore.getUserId) : undefined,
    userId: userStore.getUserId ? userStore.getUserId : undefined,
  }
  if (query.status) payload.orderStatus = statusUiToServer[query.status]
  if (Array.isArray(query.dateRange) && query.dateRange.length === 2) {
@@ -488,25 +553,23 @@
    payload.applyTimeEnd = query.dateRange[1]
  }
  const res = (await orderApi.getBuyerOrderPage(payload)) as any
  // 添加产品条件查询
  if (query.industry) payload.industryId = query.industry
  if (query.unitProject) payload.unitProjectId = query.unitProject
  if (query.productType) payload.productTypeId = query.productType
  if (query.productSubType) payload.productSubTypeId = query.productSubType
  // 根据是否有产品条件选择不同的API
  const hasProductConditions = query.industry || query.unitProject || query.productType || query.productSubType
  const apiMethod = hasProductConditions ? orderApi.getBuyerOrderPageWithProductConditions : orderApi.getBuyerOrderPage
  const res = (await apiMethod(payload)) as any
  const pageData = res?.data
  const list: any[] = Array.isArray(pageData?.list) ? pageData.list : []
  page.total = Number(pageData?.total || 0)
  // 并发获取每个订单的详情(用于构造子订单行)
  const detailsArr = await Promise.all(
    list.map(async (order: any) => {
      try {
        const detailRes = (await orderApi.getOrderDetail(order.orderId)) as any
        return detailRes?.data
      } catch (e) {
        return null
      }
    })
  )
  const flatData: any[] = []
  list.forEach((order: any, idx: number) => {
  list.forEach((order: any) => {
    const uiStatus = statusServerToUi[order.orderStatus] || 'WAIT_UPLOAD'
    const mainRow: any = {
      id: order.orderId,
@@ -518,11 +581,10 @@
      status: uiStatus,
      statusName: order.orderStatus || '',
      orderStatus: StatusMapper.toUIStatus(order.orderStatus), // 转换为标准状态枚举
      workFlowId: order.workflowId || ''
    }
    const detail = detailsArr[idx]
    const subOrders: any[] = Array.isArray(detail?.orderDetails)
      ? detail.orderDetails.map((d: any, i: number) => ({
    const subOrders: any[] = Array.isArray(order?.orderDetails)
      ? order.orderDetails.map((d: any, i: number) => ({
          id: `${order.orderId}-${i + 1}`,
          isMainOrder: false,
          productName: order.productName || '',
@@ -560,6 +622,9 @@
    status: '',
    dateRange: [],
  })
  // 清空动态选项
  unitProjectOptions.value = []
  productSubTypeOptions.value = []
  page.current = 1
  handleSearch()
}
@@ -569,7 +634,13 @@
const toUpload = (row: any) => router.push({ name: 'tradeOrderUpload', params: { id: row.id } })
const toConfirm = (row: any) => router.push({ name: 'tradeOrderConfirm', params: { id: row.id } })
const toEvaluate = (row: any) => router.push({ name: 'tradeOrderEvaluate', params: { id: row.id } })
const toTrack = (row: any) => router.push({ name: 'tradeTrack', params: { id: row.id } })
const toTrack = (row: any) => {
  if (!row.workFlowId) {
    ElMessage.warning('该订单暂无工作流信息')
    return
  }
  router.push({ name: 'tradeTrack', params: { id: row.workFlowId } })
}
// 追踪订单 - 显示订单状态对话框
const showOrderTrack = (row: any) => {
@@ -590,7 +661,8 @@
      toDetail(order)
      break
    case ActionType.TRACK:
      showOrderTrack(order)
      // showOrderTrack(order)
      toTrack(order)
      break
    case ActionType.UPLOAD_FILE:
      toUpload(order)
@@ -635,7 +707,33 @@
  }
}
onMounted(handleSearch)
onMounted(async ()=>{
  // 获取用户信息
  if (!userStore.getUserId) {
    try {
      const res: any = await queryUserDetail()
      if (res?.code === 200 && res.data) {
        userStore.updateUserDetail(res.data)
      } else {
        ElMessage.error(res?.msg || '无法获取用户信息,请先登录')
        return
      }
    } catch (e) {
      console.error('获取用户详情失败:', e)
      ElMessage.error('获取用户信息失败,请稍后重试')
      return
    }
  }
  // 获取初始选项数据
  await Promise.all([
    getIndustryOptions(),
    getProductTypeOptions()
  ])
  // 执行搜索
  handleSearch()
})
</script>
<style scoped lang="scss">
@@ -779,12 +877,15 @@
    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/tradeManage/detail/index.vue
@@ -226,11 +226,11 @@
     </el-card>
    <!-- 审批追踪 -->
    <el-card class="mt15" shadow="never" v-if="detail.records?.length">
    <el-card class="mt15" shadow="never">
      <div class="title">审批追踪</div>
      
      <!-- 标签页 -->
      <el-tabs v-model="activeTab" class="approval-tabs">
      <!-- <el-tabs v-model="activeTab" class="approval-tabs">
        <el-tab-pane label="审批记录" name="records">
          <el-table
            :data="detail.records"
@@ -279,13 +279,18 @@
            </el-table-column>
          </el-table>
        </el-tab-pane>
      </el-tabs>
      </el-tabs> -->
      
      <!-- 外部系统审批轨迹 iframe -->
      <div class="iframe-wrap" v-if="workflowIframeUrl">
        <iframe :src="workflowIframeUrl" class="workflow-iframe" referrerpolicy="no-referrer"></iframe>
      </div>
    </el-card>
      <!-- 返回按钮 -->
      <div class="action-buttons">
        <el-button @click="goBack">返回</el-button>
      </div>
    </el-card>
  </div>
</template>
@@ -296,12 +301,22 @@
import { ElMessage } from 'element-plus'
import orderApi from '@/api/orderApi'
import createAxios from '@/utils/axios'
import productApi from '@/api/productApi'
import { useUserInfo } from '@/stores/modules/userInfo'
const hostUrl =  import.meta.env.VITE_AXIOS_BASE_URL
const route = useRoute()
const router = useRouter()
const detail = reactive<any>({ items: [], records: [], nodes: [] })
const orderTableWrapRef = ref<HTMLElement | null>(null)
const activeTab = ref('records')
const userStore = useUserInfo()
// 外部审批轨迹iframe地址
const workflowIframeUrl = computed(() => {
  const pid = (detail.workflowId || route.query.processinstId || '').toString().trim()
  if (!pid) return ''
  return `${hostUrl}/activity/history?processinstId=${encodeURIComponent(pid)}&token=${userStore.getAdminToken}`
})
const labelStyle = { width: '180px', maxWidth: '180px' }
const contentStyle = { width: 'calc(50% - 180px)' }
@@ -379,6 +394,26 @@
    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) {
      // 忽略产品详情失败,不阻塞订单详情
    }
    // 获取用户信息
    // 映射订单详情头部信息
    const head = {
      orderNo: data.orderId,
@@ -430,6 +465,7 @@
      cashTotal: cashTotalNum.toLocaleString(),
      records: [],
      nodes: [],
      workflowId: data.workflowId || data.processinstId || ''
    })
    // 初始化交易信息备注数据
@@ -1006,6 +1042,10 @@
  text-align: left !important;
}
/* 外部审批轨迹 iframe 样式 */
.iframe-wrap { margin-top: 10px; }
.workflow-iframe { width: 100%; height: 500px; border: none; border-radius: 6px; }
/* 文件操作按钮样式 */
.file-actions {
  display: flex;
src/views/tradeManage/seller/index.vue
@@ -9,7 +9,7 @@
            <el-input v-model="query.productName" placeholder="请输入产品名称" clearable style="width: 100%" />
          </el-form-item>
          <el-form-item label="行业领域" class="col-25">
            <el-select v-model="query.industry" placeholder="请选择行业领域" clearable style="width: 100%">
            <el-select v-model="query.industry" placeholder="请选择行业领域" clearable style="width: 100%" @change="handleIndustryChange">
              <el-option v-for="item in industryOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
@@ -19,7 +19,7 @@
            </el-select>
          </el-form-item>
          <el-form-item label="产品类型" class="col-30">
            <el-select v-model="query.productType" placeholder="请选择产品类型" clearable style="width: 100%">
            <el-select v-model="query.productType" placeholder="请选择产品类型" clearable style="width: 100%" @change="handleProductTypeChange">
              <el-option v-for="item in productTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
          </el-form-item>
@@ -198,7 +198,7 @@
                          >
                            查看
                          </el-button>
                          <el-button
                          <!-- <el-button
                            v-else-if="action.type === ActionType.TRACK"
                            type="primary" 
                            link 
@@ -206,7 +206,7 @@
                            @click="handleAction(action, row.parentOrder)"
                          >
                            追踪
                          </el-button>
                          </el-button> -->
                        </template>
                      </div>
                    </div>
@@ -246,9 +246,11 @@
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'
const router = useRouter()
const userStore = useUserInfo()
@@ -263,35 +265,11 @@
  { label: '已评价', value: 'EVALUATED' },
]
// 行业领域选项
const industryOptions = [
  { label: '建筑工程', value: 'construction' },
  { label: '交通工程', value: 'transportation' },
  { label: '水利工程', value: 'water' },
  { label: '电力工程', value: 'power' },
]
// 单位工程选项
const unitProjectOptions = [
  { label: '土建工程', value: 'civil' },
  { label: '安装工程', value: 'installation' },
  { label: '装饰工程', value: 'decoration' },
]
// 产品类型选项
const productTypeOptions = [
  { label: '企业私有SaaS版许可', value: 'enterprise_private' },
  { label: '企业公有SaaS版许可', value: 'enterprise_public' },
  { label: '项目公有SaaS版许可', value: 'project_public' },
  { label: '个人公有SaaS版许可', value: 'personal_public' },
]
// 产品类型子级选项
const productSubTypeOptions = [
  { label: '基础版', value: 'basic' },
  { label: '标准版', value: 'standard' },
  { label: '高级版', value: 'premium' },
]
// 动态选项数据
const industryOptions = ref<any[]>([])
const unitProjectOptions = ref<any[]>([])
const productTypeOptions = ref<any[]>([])
const productSubTypeOptions = ref<any[]>([])
// 查询条件
const query = reactive({
@@ -344,6 +322,92 @@
  if (/(协议|agreement)/i.test(s)) return 'agreement'
  if (/(免费|free)/i.test(s)) return 'free'
  return 'currency'
}
// 获取行业领域选项
const getIndustryOptions = async () => {
  try {
    const res = await productApi.getCategoryByParent({ parentCode: 'business_direction' })
    if (res?.code === 200 && res.data) {
      industryOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取行业领域选项失败:', error)
  }
}
// 获取产品类型选项
const getProductTypeOptions = async () => {
  try {
    const res = await productApi.getCategoryByParent({ parentCode: 'product_type' })
    if (res?.code === 200 && res.data) {
      productTypeOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取产品类型选项失败:', error)
  }
}
// 根据行业领域获取单位工程选项
const getUnitProjectOptions = async (industryCode: string) => {
  if (!industryCode) {
    unitProjectOptions.value = []
    return
  }
  try {
    const res = await productApi.getCategoryByParent({ parentId: industryCode })
    if (res?.code === 200 && res.data) {
      unitProjectOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取单位工程选项失败:', error)
    unitProjectOptions.value = []
  }
}
// 根据产品类型获取产品子级选项
const getProductSubTypeOptions = async (productTypeCode: string) => {
  if (!productTypeCode) {
    productSubTypeOptions.value = []
    return
  }
  try {
    const res = await productApi.getCategoryByParent({ parentId: productTypeCode })
    if (res?.code === 200 && res.data) {
      productSubTypeOptions.value = res.data.map((item: any) => ({
        label: item.name,
        value: item.id
      }))
    }
  } catch (error) {
    console.error('获取产品子级选项失败:', error)
    productSubTypeOptions.value = []
  }
}
// 处理行业领域变化
const handleIndustryChange = async (value: string) => {
  // 清空单位工程选择
  query.unitProject = ''
  // 获取对应的单位工程选项
  await getUnitProjectOptions(value)
}
// 处理产品类型变化
const handleProductTypeChange = async (value: string) => {
  // 清空产品子级选择
  query.productSubType = ''
  // 获取对应的产品子级选项
  await getProductSubTypeOptions(value)
}
@@ -444,7 +508,7 @@
  try {
    // 获取用户ID作为providerId,如果没有则使用默认值
    const userId = userStore.getUserId
    const providerId = userId ? Number(userId) : 1 // 使用默认值1作为临时解决方案
    const providerId = userId ? userId : '' // 使用默认值1作为临时解决方案
    const payload: any = {
      pageNum: page.current,
@@ -459,25 +523,23 @@
      payload.applyTimeEnd = query.dateRange[1]
    }
    const res = (await orderApi.getSellerOrderPage(payload)) as any
    // 添加产品条件查询
    if (query.industry) payload.industryId = query.industry
    if (query.unitProject) payload.unitProjectId = query.unitProject
    if (query.productType) payload.productTypeId = query.productType
    if (query.productSubType) payload.productSubTypeId = query.productSubType
    // 根据是否有产品条件选择不同的API
    const hasProductConditions = query.industry || query.unitProject || query.productType || query.productSubType
    const apiMethod = hasProductConditions ? orderApi.getSellerOrderPageWithProductConditions : orderApi.getSellerOrderPage
    const res = (await apiMethod(payload)) as any
    const pageData = res?.data
    const list: any[] = Array.isArray(pageData?.list) ? pageData.list : []
    page.total = Number(pageData?.total || 0)
    // 并发获取每个订单的详情(用于构造子订单行)
    const detailsArr = await Promise.all(
      list.map(async (order: any) => {
        try {
          const detailRes = (await orderApi.getOrderDetail(order.orderId)) as any
          return detailRes?.data
        } catch (e) {
          return null
        }
      })
    )
    const flatData: any[] = []
    list.forEach((order: any, idx: number) => {
    list.forEach((order: any) => {
      const uiStatus = statusServerToUi[order.orderStatus] || 'WAIT_UPLOAD'
      const mainRow: any = {
        id: order.orderId,
@@ -489,11 +551,11 @@
        status: uiStatus,
        statusName: order.orderStatus || '',
        orderStatus: StatusMapper.toUIStatus(order.orderStatus), // 转换为标准状态枚举
        workFlowId: order.workflowId || ''
      }
      const detail = detailsArr[idx]
      const subOrders: any[] = Array.isArray(detail?.orderDetails)
        ? detail.orderDetails.map((d: any, i: number) => ({
      const subOrders: any[] = Array.isArray(order?.orderDetails)
        ? order.orderDetails.map((d: any, i: number) => ({
            id: `${order.orderId}-${i + 1}`,
            isMainOrder: false,
            productName: order.productName || '',
@@ -537,6 +599,9 @@
    status: '',
    dateRange: [],
  })
  // 清空动态选项
  unitProjectOptions.value = []
  productSubTypeOptions.value = []
  page.current = 1
  handleSearch()
}
@@ -570,7 +635,33 @@
// 路由跳转
const toDetail = (row: any) => router.push({ name: 'tradeOrderDetail', params: { id: row.id } })
onMounted(handleSearch)
onMounted(async ()=>{
  // 获取用户信息
  if (!userStore.getUserId) {
    try {
      const res: any = await queryUserDetail()
      if (res?.code === 200 && res.data) {
        userStore.updateUserDetail(res.data)
      } else {
        ElMessage.error(res?.msg || '无法获取用户信息,请先登录')
        return
      }
    } catch (e) {
      console.error('获取用户详情失败:', e)
      ElMessage.error('获取用户信息失败,请稍后重试')
      return
    }
  }
  // 获取初始选项数据
  await Promise.all([
    getIndustryOptions(),
    getProductTypeOptions()
  ])
  // 执行搜索
  handleSearch()
})
</script>
<style scoped lang="scss">
src/views/tradeManage/track/index.vue
New file
@@ -0,0 +1,72 @@
<template>
  <div class="track-page">
    <el-card shadow="never">
      <div class="content">
        <div class="iframe-wrap" v-if="iframeUrl">
          <iframe :src="iframeUrl" class="workflow-iframe" referrerpolicy="no-referrer"></iframe>
        </div>
        <div v-else class="empty">暂无流程实例ID</div>
      </div>
    </el-card>
    <div class="action-buttons">
      <el-button @click="goBack">返回</el-button>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const processId = computed(() => {
  return route.query.processinstId || route.params.id || ''
})
const hostUrl =  import.meta.env.VITE_AXIOS_BASE_URL
const iframeUrl = computed(() => {
  const pid = processId.value
  if (pid && pid.toString().trim()) {
    return `${hostUrl}/activity/history?processinstId=${encodeURIComponent(pid.toString().trim())}`
  }
  return ''
})
const goBack = () => router.back()
</script>
<style scoped>
.track-page {
  padding: 20px;
}
.title {
  font-weight: 600;
  margin-bottom: 10px;
}
.content {
  margin-top: 10px;
}
.iframe-wrap {
  margin-top: 10px;
}
.workflow-iframe {
  width: 100%;
  height: 600px;
  border: none;
  border-radius: 6px;
}
.action-buttons {
  display: flex;
  justify-content: center;
  margin-top: 15px;
}
.empty {
  text-align: center;
  color: #909399;
  padding: 40px 0;
}
</style>
vite.config.ts
@@ -17,6 +17,11 @@
const viteConfig = ({ mode }: ConfigEnv): UserConfig => {
  let proxy: Record<string, string | ProxyOptions> = {}
  proxy = {
    '/api/test': {
      target: 'http://36.133.126.111:7099',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api\/test/, '/api'),
    },
    '/api': {
      // target: 'http://192.168.0.38:8088', // 李
      // target: 'http://10.88.211.191:8088', // 李