


列表页不允许出现页面级竖向滚动条,筛选区 + 表格必须在一屏内完整展示。
const filterFormRef = ref<HTMLElement>()
const tableHeight = ref(0)
function calcTableHeight() {
const filterH = filterFormRef.value?.offsetHeight ?? 0
// 根据实际布局减去 header、padding 等固定高度
tableHeight.value = window.innerHeight - filterH - OTHER_FIXED_HEIGHT
}
onMounted(() => {
calcTableHeight()
window.addEventListener('resize', calcTableHeight)
})
onUnmounted(() => {
window.removeEventListener('resize', calcTableHeight)
})
<el-table :data="tableData" :height="tableHeight">
<!-- columns -->
</el-table>
操作列使用 fixed="right",横向滚动时操作按钮始终可见。
<el-table-column label="操作" fixed="right" width="200">
<template #default="{ row }">
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
表格单元格长文本使用 show-overflow-tooltip,保持列宽整齐,悬浮可查看完整内容。
<el-table-column prop="remark" label="备注" show-overflow-tooltip />
所有弹窗必须设置 close-on-click-modal="false",防止用户误触遮罩层导致数据丢失。
<el-dialog
v-model="dialogVisible"
title="新增数据"
:close-on-click-modal="false"
width="600px"
>
<!-- form content -->
</el-dialog>
提交前调用 formRef.validate(),校验不通过不发起请求。
async function handleSubmit() {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
// 发起请求
}
弹窗每次打开必须重置表单数据和校验状态,避免上次数据残留。
function openDialog(row?: RowType) {
nextTick(() => {
formRef.value?.resetFields()
if (row) {
Object.assign(formData, row)
}
})
dialogVisible.value = true
}
弹窗内表单有修改时,关闭前弹出二次确认"是否放弃修改?"。
function handleDialogClose(done: () => void) {
if (isFormDirty.value) {
ElMessageBox.confirm('表单已修改,确定放弃当前编辑内容吗?', '提示', {
type: 'warning',
}).then(() => done()).catch(() => {})
} else {
done()
}
}
<el-dialog :before-close="handleDialogClose">
新增、完成等流程操作使用 throttle 防止多次点击生成重复数据。
方式一:throttle 节流
import { throttle } from 'lodash'
const handleSubmit = throttle(() => {
// 业务逻辑
}, 1000, { trailing: false })
方式二:$confirm + beforeClose 模式(推荐用于关键流程操作)
function handleConfirm() {
ElMessageBox.confirm(
'确认执行此操作?一旦确认将不可撤回',
'提示',
{
type: 'warning',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
instance.confirmButtonText = '执行中...'
submitApi().finally(() => {
setTimeout(() => {
instance.confirmButtonLoading = false
done()
}, 300)
})
} else {
done()
}
},
}
).then(() => {
getList()
ElMessage.success('操作成功')
}).catch(() => {})
}
提交/保存按钮在请求期间设为 loading,请求完成后恢复,给用户明确的视觉反馈。
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
确 定
</el-button>
const submitLoading = ref(false)
async function handleSubmit() {
submitLoading.value = true
try {
await submitApi(formData)
ElMessage.success('提交成功')
dialogVisible.value = false
getList()
} finally {
submitLoading.value = false
}
}
所有 el-select 加上 filterable 属性,当选项较多时允许用户键入关键字快速定位。
<el-select v-model="formData.type" filterable placeholder="请选择" clearable>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
输入框使用 .trim 修饰符或手动 trim(),避免空格导致查询/提交异常。
<!-- 方式一:v-model.trim -->
<el-input v-model.trim="queryParams.keyword" placeholder="请输入关键字" />
<!-- 方式二:@blur 时手动 trim -->
<el-input v-model="formData.name" @blur="formData.name = formData.name.trim()" />
筛选表单支持按 Enter 键直接触发查询,减少鼠标操作。
<el-form @submit.prevent="handleQuery">
<el-input
v-model.trim="queryParams.keyword"
placeholder="请输入关键字"
@keyup.enter="handleQuery"
clearable
/>
</el-form>
数字输入框限制输入类型,禁止输入非法字符。
<el-input
v-model="formData.amount"
placeholder="请输入金额"
@input="formData.amount = formData.amount.replace(/[^\d.]/g, '')"
/>
<!-- 或使用 el-input-number -->
<el-input-number v-model="formData.quantity" :min="0" :max="99999" :precision="0" />
日期选择器通过 disabled-date 限制可选范围,避免选择无效日期。
<el-date-picker
v-model="formData.date"
type="date"
placeholder="请选择日期"
:disabled-date="disabledDate"
/>
function disabledDate(time: Date) {
return time.getTime() > Date.now()
}
数据加载时必须显示 loading,避免空白页面让用户困惑。
<el-table :data="tableData" :height="tableHeight" v-loading="tableLoading">
<!-- columns -->
</el-table>
const tableLoading = ref(false)
async function getList() {
tableLoading.value = true
try {
const res = await fetchListApi(queryParams)
tableData.value = res.data.rows
total.value = res.data.total
} finally {
tableLoading.value = false
}
}
列表无数据时显示空状态组件,区分"加载中"与"确实没有数据"。
<el-table :data="tableData" :height="tableHeight" v-loading="tableLoading">
<!-- columns -->
<template #empty>
<el-empty description="暂无数据" />
</template>
</el-table>
增删改操作成功后统一使用 ElMessage.success 提示,让用户明确知道操作已完成。
ElMessage.success('新增成功')
ElMessage.success('修改成功')
ElMessage.success('删除成功')
删除、重置等不可逆操作必须使用 ElMessageBox.confirm 二次确认。
async function handleDelete(row: RowType) {
await ElMessageBox.confirm(
`确认删除「${row.name}」吗?删除后将无法恢复`,
'警告',
{ type: 'warning' }
)
await deleteApi(row.id)
ElMessage.success('删除成功')
getList()
}
编辑/删除后保持当前页码;新增后跳转第一页;删除当前页最后一条时自动回退上一页。
function afterEdit() {
getList() // 保持当前 queryParams.pageNum
}
function afterAdd() {
queryParams.pageNum = 1
getList()
}
function afterDelete() {
if (tableData.value.length === 1 && queryParams.pageNum > 1) {
queryParams.pageNum--
}
getList()
}
批量操作前必须校验是否已选中数据,未选中时给出提示。
function handleBatchDelete() {
if (selectedRows.value.length === 0) {
ElMessage.warning('请至少选择一条数据')
return
}
ElMessageBox.confirm(
`确认删除选中的 ${selectedRows.value.length} 条数据吗?`,
'警告',
{ type: 'warning' }
).then(async () => {
const ids = selectedRows.value.map(row => row.id)
await batchDeleteApi(ids)
ElMessage.success('批量删除成功')
getList()
}).catch(() => {})
}
|
# |
规范 |
关键配置 / API |
|---|---|---|
|
1 |
列表一屏显示 |
|
|
2 |
弹窗禁止遮罩关闭 |
|
|
3 |
流程操作防重复 |
|
|
4 |
下拉框可搜索 |
|
|
5 |
输入框去空格 |
|
|
6 |
回车查询 |
|
|
7 |
表格 Loading |
|
|
8 |
空状态提示 |
|
|
9 |
提交前校验 |
|
|
10 |
按钮 Loading |
|
|
11 |
关闭前确认 |
|
|
12 |
成功消息 |
|
|
13 |
删除二次确认 |
|
|
14 |
分页保持 |
条件判断 pageNum |
|
15 |
表单重置 |
|
|
16 |
数字输入限制 |
|
|
17 |
日期范围限制 |
|
|
18 |
操作列固定 |
|
|
19 |
长文本省略 |
|
|
20 |
批量操作校验 |
选中数量判断 |