1. 核心架构设计
- API 层:纯粹的请求定义。
- Hook 层 (核心):管理所有状态(List, Loading, Submitting)和逻辑(CRUD 请求)。
- 列表页 (容器):调度中心,负责触发 Hook 中的方法。
-
弹窗页 (展示):纯 UI 组件,通过
emit传递数据。
2. API 定义 (src/api/user.js)
export const UserAPI = {
fetchList: () => Promise.resolve([{ id: 1, name: 'Gemini', role: 'Admin' }]),
create: (data) => Promise.resolve({ success: true }),
update: (id, data) => Promise.resolve({ success: true }),
delete: (id) => Promise.resolve({ success: true })
}
3. 业务逻辑 Hook (src/hooks/useUserManagement.js)
所有的 API 调用和状态都在这里! 弹窗和列表页都共享这个 Hook。
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { UserAPI } from '@/api/user'
export function useUserManagement() {
const list = ref([])
const loading = ref(false)
const submitting = ref(false) // 新增:全局提交状态
// 1. 获取列表
const loadList = async () => {
loading.value = true
try {
list.value = await UserAPI.fetchList()
} finally {
loading.value = false
}
}
// 2. 统一保存入口 (新增或编辑)
const saveUser = async (id, formData) => {
submitting.value = true
try {
if (id) {
await UserAPI.update(id, formData)
} else {
await UserAPI.create(formData)
}
ElMessage.success('保存成功')
await loadList() // 自动刷新列表
return true
} catch (error) {
return false
} finally {
submitting.value = false
}
}
// 3. 删除
const removeUser = async (id) => {
try {
await ElMessageBox.confirm('确定删除吗?', '警告', { type: 'warning' })
await UserAPI.delete(id)
ElMessage.success('删除成功')
await loadList()
} catch (e) {}
}
return { list, loading, submitting, loadList, saveUser, removeUser }
}
4. 纯 UI 弹窗组件 (src/views/user/UserEditDialog.vue)
变化:不直接引用 UserAPI,而是通过 emit('save') 发送信号。
<template>
<el-dialog v-model="visible" :title="isEdit ? '编辑用户' : '新增用户'" width="450px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户名" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" class="w-full">
<el-option label="管理员" value="Admin" />
<el-option label="普通用户" value="User" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" :loading="loading" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useCloned } from '@vueuse/core'
defineProps({ loading: Boolean })
const emit = defineEmits(['save'])
const visible = ref(false)
const formRef = ref(null)
const sourceData = ref({ name: '', role: 'User' })
const { cloned: form, sync } = useCloned(sourceData, { manual: true })
const isEdit = computed(() => !!sourceData.value.id)
const rules = { name: [{ required: true, message: '必填', trigger: 'blur' }] }
const open = (row) => {
sourceData.value = row ? { ...row } : { name: '', role: 'User' }
sync()
visible.value = true
}
const close = () => { visible.value = false }
const handleConfirm = async () => {
await formRef.value.validate()
// 将数据传给父组件处理
emit('save', { id: sourceData.value.id, data: form.value })
}
defineExpose({ open, close })
</script>
5. 用户列表主页面 (src/views/user/index.vue)
变化:负责业务逻辑的调度。
<template>
<div class="p-6">
<div class="flex justify-between mb-4">
<h2 class="text-lg font-bold">用户管理</h2>
<el-button type="primary" @click="dialogRef.open()">新增</el-button>
</div>
<el-table :data="list" v-loading="loading" border>
<el-table-column prop="name" label="姓名" />
<el-table-column label="操作">
<template #default="{ row }">
<el-button link type="primary" @click="dialogRef.open(row)">编辑</el-button>
<el-button link type="danger" @click="removeUser(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<UserEditDialog
ref="dialogRef"
:loading="submitting"
@save="onSave"
/>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useUserManagement } from '@/hooks/useUserManagement'
import UserEditDialog from './UserEditDialog.vue'
const { list, loading, submitting, loadList, saveUser, removeUser } = useUserManagement()
const dialogRef = ref(null)
// 处理保存逻辑
const onSave = async ({ id, data }) => {
const isOk = await saveUser(id, data)
if (isOk) dialogRef.value.close()
}
onMounted(loadList)
</script>
总结:
-
逻辑高度集中:所有跟
User相关的增删改查动作都在useUserManagement.js里。如果 API 地址变了或业务规则变了(比如保存前要弹两次确认),你只需要改 Hook。 -
组件极致精简:
UserEditDialog.vue现在是一个纯粹的表单组件。你可以把它直接拿去给其他页面用,只需要在外部监听@save即可。 -
状态同步成本低:因为
saveUser内部执行了await loadList(),所以页面不需要手动去处理“保存后如何刷新表格”的琐事,状态自动同步。
Top comments (0)