Commit eb5cd26b authored by Data Governance Dev's avatar Data Governance Dev

feat(web3): 分页器改 el-pagination + 字段配置抽屉联动真字段

1. ResultTable:自定义分页器换成 el-pagination(带 ... 折叠中间页)

   pageSizes 默认 [10, 25, 50, 100, 200]、defaultPageSize=10

   加 visibleFields prop + allColumns 优先按它过滤(FieldConfigDrawer 联动)

   表格下方加 padding 让分页器不贴表格

2. DataQualityView:字段配置抽屉改从 resultFields(后端 /start 返回的真字段)取

   visibleFields 默认按 show_default=true 重置勾选;用户手动保存后写入

3. TDZ 修复:fields/visibleFields/watch 块挪到 resultFields ref 声明之后

   computed + watch immediate 在 setup 立即跑;const ref 在它前面会 TDZ 报错

   顺手清掉不用的 fieldsByType / violationRows import
parent a4a064dd
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* row[col.key] 单元格的显示值 * row[col.key] 单元格的显示值
* row.errorCells?: [key1, key2] 哪些单元格高亮+tooltip * row.errorCells?: [key1, key2] 哪些单元格高亮+tooltip
* row.issues?: [{field, desc}] 备注列要展示的所有错误(每行末尾「备注」列展开) * row.issues?: [{field, desc}] 备注列要展示的所有错误(每行末尾「备注」列展开)
- props.pageSizes / defaultPageSize 分页每页条数(默认 [5, 10, 20, 50],默认 5) - 分页:2026-08-21 改用 el-pagination,pageSizes 默认 [25, 50, 100, 200],中间页用 pager 自带 ... 折叠
- 无「操作」列(plan §3 决策;后续如要加,应在父组件再套一层,不要塞回通用组件) - 无「操作」列(plan §3 决策;后续如要加,应在父组件再套一层,不要塞回通用组件)
单元格错误展示: 单元格错误展示:
...@@ -106,30 +106,19 @@ ...@@ -106,30 +106,19 @@
</table> </table>
</div> </div>
<!-- 分页:含每页条数选择 --> <!-- 分页:el-pagination 自带「...」省略中间页码(pager 默认行为) -->
<div class="pagination"> <el-pagination
<div class="pagination-info"> v-if="rows.length > 0"
显示第 {{ rangeStart }}-{{ rangeEnd }} 条 / 共 <span class="highlight">{{ rows.length }}</span> 条 v-model:current-page="currentPage"
</div> v-model:page-size="pageSize"
<div class="pagination-controls"> :page-sizes="pageSizes"
<select :total="rows.length"
class="select-input page-size-select" layout="total, sizes, prev, pager, next, jumper"
v-model.number="pageSize" background
@change="onPageSizeChange" small
> class="result-table__pagination"
<option v-for="s in pageSizes" :key="s" :value="s">{{ s }} 条/页</option> @current-change="onPageChange"
</select> />
<button class="page-btn" :disabled="currentPage === 1" @click="currentPage--">‹</button>
<button
v-for="p in totalPages"
:key="p"
class="page-btn"
:class="{ active: currentPage === p }"
@click="currentPage = p"
>{{ p }}</button>
<button class="page-btn" :disabled="currentPage === totalPages" @click="currentPage++">›</button>
</div>
</div>
</div> </div>
</template> </template>
...@@ -139,22 +128,34 @@ import { computed, ref, watch } from 'vue' ...@@ -139,22 +128,34 @@ import { computed, ref, watch } from 'vue'
const props = defineProps({ const props = defineProps({
columns: { type: Array, required: true }, // [{ key, cn, en, rules, ruleList? }] columns: { type: Array, required: true }, // [{ key, cn, en, rules, ruleList? }]
rows: { type: Array, required: true }, // 每行:{ errorCells?, issues?, ...业务字段 } rows: { type: Array, required: true }, // 每行:{ errorCells?, issues?, ...业务字段 }
pageSizes: { type: Array, default: () => [5, 10, 20, 50] }, // 2026-08-21:参考 web2 IdCardTable.vue 的 el-pagination 配置
defaultPageSize: { type: Number, default: 5 }, pageSizes: { type: Array, default: () => [10, 25, 50, 100, 200] },
defaultPageSize: { type: Number, default: 10 },
// 行 key:默认用 index;父组件可传 (row, i) => string 走更稳定的 key(如 row.id) // 行 key:默认用 index;父组件可传 (row, i) => string 走更稳定的 key(如 row.id)
rowKey: { type: Function, default: (_row, i) => i }, rowKey: { type: Function, default: (_row, i) => i },
// 分页 append 模式(2026-08-21):默认 false(rows 整体替换 → length 变化回到第 1 页) // 分页 append 模式(2026-08-21):默认 false(rows 整体替换 → length 变化回到第 1 页)
// 设为 true 时 rows.length 变化不重置页码 —— 用于「分页拉取 + 增量追加」场景 // 设为 true 时 rows.length 变化不重置页码 —— 用于「分页拉取 + 增量追加」场景
appendMode: { type: Boolean, default: false }, appendMode: { type: Boolean, default: false },
// 2026-08-21:可见字段列表(FieldConfigDrawer 出来的)。
// null 时回退到「按 column.showDefault 过滤」(保持向后兼容 + 老调用方不传)。
visibleFields: { type: Array, default: null },
}) })
const emit = defineEmits(['page-change']) const emit = defineEmits(['page-change'])
// 业务列 + 自动追加的「备注」列 // 业务列 + 自动追加的「备注」列
// 任务配置里「默认展示」未勾选的列(showDefault === false)→ 不进结果表 // 优先级:
// 兜底:`showDefault !== false` —— 未传 showDefault 的列(mock / 老调用方)全部展示,向后兼容 // 1. props.visibleFields 非空 → 严格按它过滤(FieldConfigDrawer 出来的用户选择)
// 2. props.visibleFields 为空/null → 回退到「按 column.showDefault 过滤」(向后兼容)
// 兜底:`showDefault !== false` —— 未传 showDefault 的列(mock / 老调用方)全部展示
// rows 为空时(无任务 / 查询 0 行)也不追加备注列 —— 没数据时备注列只会显示「—」占位,没意义 // rows 为空时(无任务 / 查询 0 行)也不追加备注列 —— 没数据时备注列只会显示「—」占位,没意义
const allColumns = computed(() => { const allColumns = computed(() => {
const cols = props.columns.filter((c) => c.showDefault !== false) let cols = props.columns.filter((c) => c.key !== '__reason')
if (Array.isArray(props.visibleFields) && props.visibleFields.length > 0) {
const visibleSet = new Set(props.visibleFields)
cols = cols.filter((c) => visibleSet.has(c.key))
} else {
cols = cols.filter((c) => c.showDefault !== false)
}
if (props.rows.length > 0) { if (props.rows.length > 0) {
cols.push({ key: '__reason', cn: '备注' }) cols.push({ key: '__reason', cn: '备注' })
} }
...@@ -170,8 +171,6 @@ const pagedRows = computed(() => { ...@@ -170,8 +171,6 @@ const pagedRows = computed(() => {
const start = (currentPage.value - 1) * pageSize.value const start = (currentPage.value - 1) * pageSize.value
return props.rows.slice(start, start + pageSize.value) return props.rows.slice(start, start + pageSize.value)
}) })
const rangeStart = computed(() => props.rows.length === 0 ? 0 : (currentPage.value - 1) * pageSize.value + 1)
const rangeEnd = computed(() => Math.min(currentPage.value * pageSize.value, props.rows.length))
// rows 长度变化 → 回到第 1 页(避免翻页中途被切走,停在空页) // rows 长度变化 → 回到第 1 页(避免翻页中途被切走,停在空页)
// 2026-08-21:append 模式下不重置 —— 新页到达时用户已经看过的页别被强制拉回首屏 // 2026-08-21:append 模式下不重置 —— 新页到达时用户已经看过的页别被强制拉回首屏
...@@ -179,9 +178,8 @@ watch(() => props.rows.length, () => { ...@@ -179,9 +178,8 @@ watch(() => props.rows.length, () => {
if (!props.appendMode) currentPage.value = 1 if (!props.appendMode) currentPage.value = 1
}) })
function onPageSizeChange() { function onPageChange(page) {
currentPage.value = 1 emit('page-change', { page, pageSize: pageSize.value })
emit('page-change', { page: currentPage.value, pageSize: pageSize.value })
} }
// ── 单元格渲染辅助 ── // ── 单元格渲染辅助 ──
...@@ -234,11 +232,16 @@ function ruleHint(col) { ...@@ -234,11 +232,16 @@ function ruleHint(col) {
gap: 4px; gap: 4px;
cursor: help; cursor: help;
} }
.page-size-select { /* el-pagination 居中 + 顶部间距(让分页器跟表格拉开距离) */
margin-right: 12px; .result-table__pagination {
width: auto; display: flex;
padding: 4px 8px; justify-content: center;
font-size: 13px; margin-top: 28px;
margin-bottom: 16px;
}
/* 表格底部留白(防止最后一行贴到分页器 / 卡片底) */
.table-scroll {
padding-bottom: 8px;
} }
/* 「备注」列固定在右侧 —— 数据列多了时只滚左侧,备注常驻 */ /* 「备注」列固定在右侧 —— 数据列多了时只滚左侧,备注常驻 */
......
...@@ -81,8 +81,7 @@ ...@@ -81,8 +81,7 @@
<ResultTable <ResultTable
:columns="resultFields" :columns="resultFields"
:rows="rows" :rows="rows"
:page-sizes="[5, 10, 20, 50]" :visible-fields="visibleFields"
:default-page-size="5"
:append-mode="true" :append-mode="true"
:row-key="rowKeyFn" :row-key="rowKeyFn"
@page-change="onPageChange" @page-change="onPageChange"
...@@ -102,7 +101,7 @@ ...@@ -102,7 +101,7 @@
import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue' import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { fieldsByType, violationRows } from '@/data/mockData' // violationRows 暂留:mock 表头;等真接通后用 resultFields // mockData 已不引用:表头改为 resultFields(真任务字段),violationRows 同样由 /start 返回
import { listTasks } from '@/api/tasks' import { listTasks } from '@/api/tasks'
import { startQuery, fetchPage, cancelQuery } from '@/api/queries' import { startQuery, fetchPage, cancelQuery } from '@/api/queries'
import FieldConfigDrawer from '@/components/FieldConfigDrawer.vue' import FieldConfigDrawer from '@/components/FieldConfigDrawer.vue'
...@@ -124,7 +123,7 @@ const TYPE_TO_GROUP = { ...@@ -124,7 +123,7 @@ const TYPE_TO_GROUP = {
} }
// 表格字段 mock(仅在没查询前用,避免页面初次加载看起来空;查询后会用 resultFields 覆盖) // 表格字段 mock(仅在没查询前用,避免页面初次加载看起来空;查询后会用 resultFields 覆盖)
const fields = computed(() => fieldsByType[type.value] || []) // 注:现在 fields 由下方「字段展示开关」段改用 resultFields,跟真任务字段联动
// ── 任务下拉 ── // ── 任务下拉 ──
const taskOptions = ref([]) const taskOptions = ref([])
...@@ -158,10 +157,6 @@ async function loadTasksForCurrentType() { ...@@ -158,10 +157,6 @@ async function loadTasksForCurrentType() {
watch(type, loadTasksForCurrentType, { immediate: true }) watch(type, loadTasksForCurrentType, { immediate: true })
onMounted(loadTasksForCurrentType) onMounted(loadTasksForCurrentType)
// 字段展示开关(默认全开)—— 暂未应用
const visibleFields = ref(fields.value.map((f) => f.key))
watch(fields, (fs) => { visibleFields.value = fs.map((f) => f.key) })
// ── 分页查询 orchestrator(2026-08-21 重构) ── // ── 分页查询 orchestrator(2026-08-21 重构) ──
const PAGE_CONCURRENCY = 3 // 并发 /page 数;> 3 可能压垮 DB const PAGE_CONCURRENCY = 3 // 并发 /page 数;> 3 可能压垮 DB
const PAGE_SIZE = 500 // 每页行数,与后端默认对齐 const PAGE_SIZE = 500 // 每页行数,与后端默认对齐
...@@ -180,6 +175,23 @@ const queryError = ref('') ...@@ -180,6 +175,23 @@ const queryError = ref('')
const rows = ref([]) // 喂给 ResultTable 的行 const rows = ref([]) // 喂给 ResultTable 的行
const resultFields = ref([]) const resultFields = ref([])
// 字段展示开关 —— 跟 resultFields 联动
// 「字段配置」抽屉列出全部 + 默认勾上 show_default=true 的列;
// 用户手动改勾选后写入 visibleFields,ResultTable 按它过滤(优先于 show_default)
// —— 必须在 resultFields 声明之后定义,否则 TDZ 报错(computed/watch 会立即执行)
const fields = computed(() => resultFields.value)
const visibleFields = ref([])
watch(
resultFields,
(rf) => {
// 任务切换 / 新查询完成时,按任务的 show_default 默认勾选
visibleFields.value = (rf || [])
.filter((f) => f.showDefault !== false)
.map((f) => f.key)
},
{ immediate: true },
)
// 行 key:用后端注入的全局行号(append 模式下保证 Vue DOM 复用正确) // 行 key:用后端注入的全局行号(append 模式下保证 Vue DOM 复用正确)
const rowKeyFn = (row) => row.__row_index const rowKeyFn = (row) => row.__row_index
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment