|
|
|
|
@ -343,6 +343,39 @@ const escapeRegExp = (string: string) => {
|
|
|
|
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 获取关键字段(key 包含"名称"、"规格"、"价"、"金额") |
|
|
|
|
* @param item - ExcelRowData 对象 |
|
|
|
|
* @returns 关键字段列表 |
|
|
|
|
*/ |
|
|
|
|
const getKeyFields = (item: ExcelRowData) => { |
|
|
|
|
const keyNames = ['名称', '规格', '价', '金额']; |
|
|
|
|
try { |
|
|
|
|
const rowObj = JSON.parse(item.rowData); |
|
|
|
|
const keyword = searchKeyword.value.toLowerCase().trim(); |
|
|
|
|
|
|
|
|
|
const highlightText = (text: string) => { |
|
|
|
|
if (!keyword) return text; |
|
|
|
|
const regex = new RegExp(`(${escapeRegExp(keyword)})`, 'gi'); |
|
|
|
|
return text.replace(regex, '<mark class="highlight">$1</mark>'); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return Object.entries(rowObj) |
|
|
|
|
.filter(([key]) => keyNames.some(k => key.includes(k))) |
|
|
|
|
.map(([key, value]) => { |
|
|
|
|
let displayValue = typeof value === 'object' |
|
|
|
|
? JSON.stringify(value) |
|
|
|
|
: String(value); |
|
|
|
|
return { |
|
|
|
|
key: highlightText(key), |
|
|
|
|
value: highlightText(displayValue) |
|
|
|
|
}; |
|
|
|
|
}); |
|
|
|
|
} catch { |
|
|
|
|
return []; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// ==================== 表格列表相关方法 ==================== |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -694,10 +727,10 @@ loadBatchList();
|
|
|
|
|
</div> |
|
|
|
|
</template> |
|
|
|
|
<template v-else> |
|
|
|
|
<!-- 数据列表 Tab:显示行数据预览 --> |
|
|
|
|
<!-- 数据列表 Tab:只显示关键字段 --> |
|
|
|
|
<div class="row-data-list"> |
|
|
|
|
<el-tooltip |
|
|
|
|
v-for="(field, index) in getCardPreview(item)" |
|
|
|
|
v-for="(field, index) in getKeyFields(item)" |
|
|
|
|
:key="index" |
|
|
|
|
:content="getRawText(field.value)" |
|
|
|
|
placement="bottom" |
|
|
|
|
@ -870,13 +903,27 @@ loadBatchList();
|
|
|
|
|
> |
|
|
|
|
<!-- 卡片头部 - 始终显示,点击可展开/折叠 --> |
|
|
|
|
<div class="data-card-header" @click="toggleCardExpand(item.id)"> |
|
|
|
|
<div class="data-source"> |
|
|
|
|
<el-icon class="source-icon"><Document /></el-icon> |
|
|
|
|
<span class="source-name clickable" @click.stop="handleFilePreview(item)">{{ item.fileName }}</span> |
|
|
|
|
<el-tag size="small" type="info">{{ item.sheetName }}</el-tag> |
|
|
|
|
</div> |
|
|
|
|
<div class="header-right"> |
|
|
|
|
<!-- <span class="row-number">第 {{ item.rowIndex }} 行</span>--> |
|
|
|
|
<!-- 序号显示 --> |
|
|
|
|
<div class="serial-number"> |
|
|
|
|
{{ (searchResultPagination.current - 1) * searchResultPagination.size + index + 1 }} |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<!-- 显示关键字段预览(折叠时) --> |
|
|
|
|
<div class="key-fields-preview" v-if="!isCardExpanded(item.id)"> |
|
|
|
|
<el-tag |
|
|
|
|
v-for="(field, idx) in getKeyFields(item).slice(0, 3)" |
|
|
|
|
:key="idx" |
|
|
|
|
size="small" |
|
|
|
|
type="success" |
|
|
|
|
class="key-field-tag" |
|
|
|
|
> |
|
|
|
|
{{ field.key.replace(/<[^>]*>/g, '') }}:{{ field.value.replace(/<[^>]*>/g, '') }} |
|
|
|
|
</el-tag> |
|
|
|
|
<span class="fields-count" v-if="getKeyFields(item).length > 3"> |
|
|
|
|
+{{ getKeyFields(item).length - 3 }}项 |
|
|
|
|
</span> |
|
|
|
|
</div> |
|
|
|
|
<el-button |
|
|
|
|
link |
|
|
|
|
size="small" |
|
|
|
|
@ -915,10 +962,16 @@ loadBatchList();
|
|
|
|
|
|
|
|
|
|
<!-- 卡片底部 - 始终显示 --> |
|
|
|
|
<div class="data-card-footer"> |
|
|
|
|
<!-- 数据源信息 --> |
|
|
|
|
<div class="data-source-info"> |
|
|
|
|
<el-icon><Document /></el-icon> |
|
|
|
|
<span class="source-name clickable" @click.stop="handleFilePreview(item)">{{ item.fileName }}</span> |
|
|
|
|
<el-tag size="small" type="info">{{ item.sheetName }}</el-tag> |
|
|
|
|
<span class="batch-name" v-if="item.batchName"> |
|
|
|
|
<el-icon><Folder /></el-icon> |
|
|
|
|
{{ item.batchName }} |
|
|
|
|
</span> |
|
|
|
|
</div> |
|
|
|
|
<el-button type="primary" link size="small" @click.stop="viewDataDetail(item)"> |
|
|
|
|
查看详情 |
|
|
|
|
<el-icon><View /></el-icon> |
|
|
|
|
@ -1221,9 +1274,8 @@ loadBatchList();
|
|
|
|
|
|
|
|
|
|
.data-card-header { |
|
|
|
|
display: flex; |
|
|
|
|
justify-content: space-between; |
|
|
|
|
align-items: center; |
|
|
|
|
margin-bottom: 14px; |
|
|
|
|
gap: 16px; |
|
|
|
|
padding-bottom: 12px; |
|
|
|
|
border-bottom: 2px solid #f0f2f5; |
|
|
|
|
|
|
|
|
|
@ -1345,6 +1397,29 @@ loadBatchList();
|
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
gap: 12px; |
|
|
|
|
flex-shrink: 0; |
|
|
|
|
|
|
|
|
|
.key-fields-preview { |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
gap: 12px; |
|
|
|
|
|
|
|
|
|
.key-field-tag { |
|
|
|
|
flex-shrink: 0; |
|
|
|
|
max-width: 500px; |
|
|
|
|
font-size: 14px; |
|
|
|
|
overflow: hidden; |
|
|
|
|
text-overflow: ellipsis; |
|
|
|
|
white-space: nowrap; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.fields-count { |
|
|
|
|
flex-shrink: 0; |
|
|
|
|
font-size: 13px; |
|
|
|
|
color: #909399; |
|
|
|
|
white-space: nowrap; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.row-number { |
|
|
|
|
font-size: 13px; |
|
|
|
|
@ -1355,7 +1430,19 @@ loadBatchList();
|
|
|
|
|
font-weight: 500; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.serial-number { |
|
|
|
|
flex-shrink: 0; |
|
|
|
|
font-size: 14px; |
|
|
|
|
font-weight: 600; |
|
|
|
|
color: #409eff; |
|
|
|
|
background: #ecf5ff; |
|
|
|
|
padding: 4px 10px; |
|
|
|
|
border-radius: 8px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.expand-btn { |
|
|
|
|
flex-shrink: 0; |
|
|
|
|
|
|
|
|
|
.el-icon { |
|
|
|
|
margin-left: 4px; |
|
|
|
|
transition: transform 0.3s ease; |
|
|
|
|
@ -1618,4 +1705,40 @@ loadBatchList();
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.data-card-footer { |
|
|
|
|
display: flex; |
|
|
|
|
justify-content: space-between; |
|
|
|
|
align-items: center; |
|
|
|
|
padding-top: 12px; |
|
|
|
|
border-top: 2px solid #f0f2f5; |
|
|
|
|
|
|
|
|
|
.data-source-info { |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
gap: 8px; |
|
|
|
|
font-size: 13px; |
|
|
|
|
color: #909399; |
|
|
|
|
flex: 1; |
|
|
|
|
min-width: 0; |
|
|
|
|
overflow: hidden; |
|
|
|
|
|
|
|
|
|
.source-name { |
|
|
|
|
color: var(--primary-color); |
|
|
|
|
cursor: pointer; |
|
|
|
|
|
|
|
|
|
&:hover { |
|
|
|
|
text-decoration: underline; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.batch-name { |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
gap: 4px; |
|
|
|
|
color: #909399; |
|
|
|
|
white-space: nowrap; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
</style> |
|
|
|
|
|