Files
vibe_coding/.cursor/skills/vue-page/references/page-templates.md
2026-03-05 21:27:11 +08:00

2.4 KiB
Raw Blame History

Vue Page — 页面模板与 Store

主流程见 SKILL.md本文档为列表页/详情页/路由/Pinia/Provider 的完整实现。

⚠️ 双前端区分:本文件中使用 el-* 组件的模板仅适用于管理端 (Case-Database-Frontend-admin/)。 用户端 (Case-Database-Frontend-user/) 使用 Headless UI + Tailwind CSS禁止引入 Element Plus

列表页模板

<script setup>
import { useTable } from '@/hooks/useTable'
document.title = '{{PageTitle}} - {{AppName}}'
const { loading, dataList, pagination, loadData } = useTable((params) => {{resource}}Api.list(params))
const searchForm = reactive({ keyword: '', status: '' })
function handleSearch() { pagination.current = 1; loadData() }
onMounted(() => loadData())
</script>
<template>
  <div class="p-4 space-y-4">
    <el-card><el-form :model="searchForm" inline>...</el-form></el-card>
    <el-card>
      <template #header><span>{{PageTitle}}</span><el-button @click="handleCreate">新增</el-button></template>
      <el-table v-loading="loading" :data="dataList" border stripe>...</el-table>
      <el-pagination v-model:current-page="pagination.current" ... @current-change="loadData" />
    </el-card>
  </div>
</template>

详情页模板

使用 useRoute/useRouterfetchDetail 加载数据el-skeleton 加载态el-page-header + el-descriptions。

路由配置

const routes = [
  { path: '/{{module}}/{{route-path}}', name: '{{RouteName}}', component: () => import('@/views/...'), meta: { title, requiresAuth: true, permission } },
  { path: '/{{module}}/{{route-path}}/:id', name: '{{RouteName}}Detail', ... }
]

Pinia Store — List 与 Detail 分离

ListItem 类型:轻量,排除大文本/复杂对象/大数组。Detail 类型完整字段。Storelist (array)、detailMap (Record<id, Detail>)、loadingDetailIds、getDetail(id)、isDetailLoading(id)、fetchList、fetchDetail有缓存则返回、invalidateDetail、invalidateAll。

页面级 Provider 模式

当 ≥3 个子组件共享状态时provider.vue provide 状态+actionskeys.ts 定义 PAGE_KEY + usePageContext子组件 inject。文件结构index.vue、provider.vue、keys.ts、components/SearchBar/DataTable/BatchActions、composables/usePageData。

动态路由 (RBAC)

loadDynamicRoutesmenuStore.fetchMenus()router.addRoute('layout', { path, name, component: () => import(...), meta })。