@@ -413,29 +413,33 @@
:key = "idx"
class = "selected-item-card"
>
<!-- Bug # 384 修复 : 项目卡片头部 , 可展开 / 收起 -- >
<!-- Bug # 384 修复 + # 426 修复 : 项目卡片头部 , 可展开 / 收起 -- >
< div class = "card-header" @click ="toggleItemExpand(item)" >
< el -tag v-if = "item.isPackage || item.packageName" size="small" type="warning" style="margin-right: 4px; flex-shrink: 0;" > 套餐 < / el -tag >
< span class = "card-name" > { { item . name } } < / span >
< span class = "card-price" > ¥ { { item . price } } < / span >
<!-- 展开图标 -- >
< el-icon : class = "[' expand-icon', { expanded: item.expanded }] " >
< ArrowDown v-if = "!item.expanded" / >
< ArrowUp v-if = "item.expanded" / >
<!-- 展开/ 收起 图标 -- >
< el-icon class = "expand-icon" : class = " { expanded: item.expanded }" >
< ArrowRight / >
< / el-icon >
<!-- 删除按钮 -- >
< el-button link type = "danger" size = "small" @click.stop ="handleRemoveItem(idx, item)" >
< el -icon > < Close / > < / el-icon >
< / el-button >
< / div >
<!-- Bug # 428 修复 : 展开后显示套餐明细或检查方法 -- >
< div v-if = "item.expanded" >
<!-- Bug # 428 修复 + # 426 修复 : 展开后显示套餐明细或检查方法 -- >
< div v-show = "item.expanded" class="expanded-content" >
< ! - - 显示套餐明细 - - >
< div v-if = "item.packageDetails && item.packageDetails.length > 0" class="package-details-list" >
< div v-if = "(item.isPackage || item.packageName) && item.packageDetails && item.packageDetails.length > 0" class="package-details-list" >
< div class = "detail-row" v-for = "detail in item.packageDetails" :key="detail.id" >
< span class = "detail-name" > { { detail . name } } < / span >
< span class = "detail-info" > 数量 : { { detail . quantity } } 单价 : ¥ { { detail . price } } < / span >
< / div >
< / div >
<!-- 套餐明细加载中 -- >
< div v-else-if = "(item.isPackage || item.packageName) && item.packageDetailsLoading" class="package-loading-hint" >
加载中...
< / div >
< ! - - 显示检查方法 - - >
< div v-else-if = "item.methods && item.methods.length > 0" class="method-list" >
< div v-for = "method in item.methods" :key="method.id" class="method-option" >
@@ -473,7 +477,7 @@
< script setup >
import { ref , reactive , computed , watch , onMounted , nextTick } from 'vue' ;
import { ElMessage , ElMessageBox } from 'element-plus' ;
import { Printer , Delete , ArrowDown , ArrowUp , Close } from '@element-plus/icons-vue' ;
import { Printer , Delete , ArrowDown , ArrowUp , Close , ArrowRight } from '@element-plus/icons-vue' ;
import useUserStore from '@/store/modules/user' ;
import request from '@/utils/request' ;
import { listCheckMethod , searchCheckMethod , listCheckPackage } from '@/api/system/checkType' ;
@@ -592,11 +596,13 @@ async function loadPackageDetails(row, treeNode, resolve) {
}
}
// #428修复: 为已选择项目加载套餐明细( 通过packageId或packageName查询)
// #428修复 + #426修复 : 为已选择项目加载套餐明细( 通过packageId或packageName查询)
async function loadPackageDetailsForItem ( item ) {
if ( ! item . isPackage || ( ! item . packageId && ! item . packageName ) ) {
// 只要有 packageName 就认为是套餐,不强制要求 isPackage 或 packageId
if ( ! item . packageName && ! item . packageId ) {
return ;
}
item . packageDetailsLoading = true ;
try {
let packageId = item . packageId ;
if ( ! packageId && item . packageName ) {
@@ -612,6 +618,10 @@ async function loadPackageDetailsForItem(item) {
}
packageId = packages [ 0 ] . id ;
}
if ( ! packageId ) {
item . packageDetails = [ ] ;
return ;
}
const res = await request ( {
url : ` /system/package/ ${ packageId } /details ` ,
method : 'get'
@@ -630,6 +640,8 @@ async function loadPackageDetailsForItem(item) {
} catch ( err ) {
console . error ( '加载套餐明细失败:' , err ) ;
item . packageDetails = [ ] ;
} finally {
item . packageDetailsLoading = false ;
}
}
const detailTableRef = ref ( null ) ;
@@ -1136,17 +1148,19 @@ function handleRowClick(row) {
selectedItems . value = [ ] ;
activeDetailTab . value = 'applyForm' ;
request ( { url : ` /exam/apply/ ${ row . applyNo } ` , method : 'get' } ) . then ( async res => {
const resp = res . data || res ;
// Bug #408修复: items 在 AjaxResult 顶层(res.items / resp.items),不在 ExamApply 对象内
// 防御性提取:优先取顶层 items, 兼容嵌套在 resp.data. items 的情况
let rawItems = res . items || resp . items ;
if ( ! rawItems && resp . data && typeof resp . data === 'object' ) {
rawItems = resp . data . items ;
}
rawItems = rawItems || [ ] ;
const d = resp . data || resp ;
if ( d ) Object . assign ( form , d ) ;
if ( Array . isArray ( rawItems ) && rawItems . length > 0 ) {
// 响应结构判定: Axios拦截器对 code===200 返回 res.data( AjaxResult体) ,
// 但某些情况下可能返回完整 Axios 响应 {data: AjaxResult}。
// 用 res.code 判定是否已是 AjaxResult 体,避免二次解包导致 items 丢失。
const isAjaxResult = res && typeof res === 'object' && res . code !== undefined ;
const ajaxBody = isAjaxResult ? res : ( res . data || res ) ;
// items 在 AjaxResult 顶层, data 字段是 ExamApply 实体
const rawItems = Array . isArray ( ajaxBody . items ) ? ajaxBody . items : [ ] ;
const detailData = ajaxBody . data || { } ;
if ( detailData && typeof detailData === 'object' ) Object . assign ( form , detailData ) ;
if ( rawItems . length > 0 ) {
try {
// 为每个项目加载检查方法
const itemsWithMethods = await Promise . all ( rawItems . map ( async m => {
@@ -1394,11 +1408,11 @@ async function handleItemSelect(checked, item, cat) {
// Bug #382 修复:移除自动切换页签逻辑,保持当前页签状态
}
// Bug #384修复: 展开/收起项目卡片
// Bug #384修复 + #426修复 : 展开/收起项目卡片
async function toggleItemExpand ( item ) {
item . expanded = ! item . expanded ;
// 如果是展开且该项目是套餐,加载套餐明细
if ( item . expanded && item . isPackage && ( ! item . packageDetails || item . packageDetails . length === 0 ) ) {
// 如果是展开且该项目是套餐(通过 isPackage 或 packageName 判断) ,加载套餐明细
if ( item . expanded && ( item . isPackage || item . packageName ) && ( ! item . packageDetails || item . packageDetails . length === 0 ) && ! item . packageDetailsLoading ) {
await loadPackageDetailsForItem ( item ) ;
}
}
@@ -1810,10 +1824,24 @@ defineExpose({ getList });
font - size : 12 px ;
color : # 909399 ;
transition : transform 0.2 s ;
transform : rotate ( 0 deg ) ;
}
. expand - icon . expanded {
transform : rotate ( 18 0deg ) ;
transform : rotate ( 9 0deg ) ;
}
/* Bug #426修复: 展开内容容器 */
. expanded - content {
overflow : hidden ;
}
/* Bug #426修复: 套餐明细加载提示 */
. package - loading - hint {
padding : 8 px 10 px ;
font - size : 11 px ;
color : # c0c4cc ;
text - align : center ;
}
/* Bug #428修复: 套餐明细列表样式 */