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

feat(web3): 任务下拉改从实际数据库查

DataQualityView 顶部「任务名称」下拉原来读 mock 的 taskOptionsByType[type],
改成调后端 listTasks({group}) 按分组名筛真实任务。

**改动(web3/src/views/DataQualityView.vue)**
- TYPE_TO_GROUP 映射(id-card→身份证 等 6 个 slug → group.name)
- loadTasksForCurrentType():type 变时自动重拉,默认选第一项;切路由前选中的任务还在列表里就保留
- watch(type, loadTasksForCurrentType, { immediate: true }) + onMounted 兜底(SSR 兼容冗余)
- 下拉加 disabled「加载中… / 该分组暂无任务」选项
- 「查询」按钮 :disabled="!selectedTask";task-info-bar v-if="selectedTask"
- 删 taskOptionsByType mock 导入

**为什么硬编码映射**:左侧菜单 slug 是英文、DB 分组名是中文,中间没有强对应字段。
硬编码是最直白方案,避免后端维护冗余 slug 字段(plan §3 决策)。

**API 验证**
  GET /api/tasks?group=身份证 → 4 个任务
  GET /api/tasks?group=手机号 → 0 个(其他分组还没建任务,符合现状)

踩坑(详见 work-logs/2026-08-20.md 主题 30)
- 选项 key 用 t.id 不是 t.name(同名任务时 Vue 复用 DOM 可能出错)
- 空态:selectedTask 用 '' 而非 undefined,按钮 :disabled 兜底
- onMounted + immediate=true 看起来重复,留作 SSR 兼容保险
parent ce3693a6
......@@ -4,15 +4,21 @@
<div class="query-row">
<div class="query-item">
<span class="query-label"><span class="required">*</span>任务名称:</span>
<select class="select-input" v-model="selectedTask">
<option v-for="t in taskOptions" :key="t" :value="t">{{ t }}</option>
<select
class="select-input"
v-model="selectedTask"
:disabled="loadingTasks || taskOptions.length === 0"
>
<option v-if="loadingTasks" disabled value="">加载中…</option>
<option v-else-if="taskOptions.length === 0" disabled value="">该分组暂无任务</option>
<option v-for="t in taskOptions" :key="t.id" :value="t.name">{{ t.name }}</option>
</select>
<button class="btn btn-primary" @click="onQuery">查询</button>
<button class="btn btn-primary" :disabled="!selectedTask" @click="onQuery">查询</button>
</div>
</div>
</div>
<div class="task-info-bar">
<div class="task-info-bar" v-if="selectedTask">
<el-icon><InfoFilled /></el-icon>
<span>当前任务:<b>{{ selectedTask }}</b></span>
<span class="meta">最近检测:2026-08-19 09:00 · 共扫描 10,234 条 · 命中不合规 <b style="color:#f56c6c;">12</b> 条</span>
......@@ -49,10 +55,11 @@
</template>
<script setup>
import { computed, ref, watch } from 'vue'
import { computed, ref, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fieldsByType, taskOptionsByType, violationRows, fieldRuleSamples } from '@/data/mockData'
import { fieldsByType, violationRows, fieldRuleSamples } from '@/data/mockData'
import { listTasks } from '@/api/tasks'
import FieldConfigDrawer from '@/components/FieldConfigDrawer.vue'
import ResultTable from '@/components/ResultTable.vue'
......@@ -61,12 +68,56 @@ const route = useRoute()
// 当前数据类型(来自路由 param)
const type = computed(() => route.params.type || 'id-card')
// 表格字段:业务字段(组件会自动追加末尾的「备注」列)
// 路由 slug → 后端 task_group.name 的映射
// 左侧菜单的英文 slug 跟 DB 里分组的真实中文名不一致,前端硬维护一份
// (plan §3 决策:避免后端维护冗余 slug 字段)
const TYPE_TO_GROUP = {
'id-card': '身份证',
'mobile': '手机号',
'email': '邮箱',
'address': '地址信息',
'bank-card': '银行卡号',
'date': '日期格式',
}
// 表格字段:业务字段(mock,组件会自动追加末尾的「备注」列)
// 注意:当前还是 mock,等任务真正跑起来后再用 task.field_list
const fields = computed(() => fieldsByType[type.value] || [])
const taskOptions = computed(() => taskOptionsByType[type.value] || [])
const selectedTask = ref(taskOptions.value[0])
watch(taskOptions, (opts) => { selectedTask.value = opts[0] })
// ── 任务下拉:从后端 /api/tasks 拉当前 type 对应分组的全部任务 ──
const taskOptions = ref([]) // [{ id, name }]
const loadingTasks = ref(false)
const selectedTask = ref('') // 任务名(不是 id,因为后面 onQuery 要传名)
async function loadTasksForCurrentType() {
const groupName = TYPE_TO_GROUP[type.value]
if (!groupName) {
taskOptions.value = []
selectedTask.value = ''
return
}
loadingTasks.value = true
try {
const list = await listTasks({ group: groupName })
taskOptions.value = (list || []).map((t) => ({ id: t.id, name: t.name }))
// 默认选中第一项;如果之前选中的还在列表里就保留(避免切换路由后丢失选择)
if (selectedTask.value && taskOptions.value.some((t) => t.name === selectedTask.value)) {
// keep
} else {
selectedTask.value = taskOptions.value[0]?.name || ''
}
} catch (e) {
ElMessage.error(`加载任务列表失败:${e.message}`)
taskOptions.value = []
selectedTask.value = ''
} finally {
loadingTasks.value = false
}
}
// 路由 type 变 → 重拉;首次挂载也要拉
watch(type, loadTasksForCurrentType, { immediate: true })
onMounted(loadTasksForCurrentType) // 保险:immediate=true 已经会跑,这里防 SSR/edge case
// 字段展示开关(默认全开)—— 暂时未应用在通用表里,预留给后续做字段隐藏
const visibleFields = ref(fields.value.map((f) => f.key))
......@@ -80,7 +131,6 @@ function onQuery() {
ElMessage.success(`已查询:${selectedTask.value}`)
}
function onPageChange({ page, pageSize }) {
// 通用表已自己管分页;这里仅打点,后续要做「记忆翻页位置」之类可以挂这
console.debug('[DataQualityView] page change', { page, pageSize })
}
function editRow(row) {
......
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