Skip to content

예제 및 실전

이 문서는 Antdv Next Admin을 사용하여 일반적인 기능을 개발하는 방법을 실제 사례로 보여줍니다.

목차


CRUD 완전한 예제

시나리오

목록, 신규 추가, 편집, 삭제 기능을 포함한 완전한 사용자 관리 모듈을 구현합니다.

1. API 인터페이스 생성

typescript
// src/api/user.ts
import request from '@/utils/request'
import type { User, PaginationParams } from '@/types'

export const getUserList = (params: PaginationParams) => {
  return request.get('/api/users', { params })
}

export const createUser = (data: Partial<User>) => {
  return request.post('/api/users', data)
}

export const updateUser = (id: string, data: Partial<User>) => {
  return request.put(`/api/users/${id}`, data)
}

export const deleteUser = (id: string) => {
  return request.delete(`/api/users/${id}`)
}

2. Mock 데이터 생성

typescript
// mock/data/user.data.ts
import { faker } from '@faker-js/faker/locale/zh_CN'

export interface User {
  id: string
  username: string
  email: string
  phone: string
  status: 'active' | 'inactive'
  role: string
  createdAt: string
}

export const userList: User[] = Array.from({ length: 35 }, (_, i) => ({
  id: `user_${i + 1}`,
  username: faker.internet.userName(),
  email: faker.internet.email(),
  phone: faker.phone.number(),
  status: faker.helpers.arrayElement(['active', 'inactive']),
  role: faker.helpers.arrayElement(['admin', 'user', 'editor']),
  createdAt: faker.date.past().toISOString(),
}))
typescript
// mock/handlers/user.mock.ts
import { defineMock } from 'vite-plugin-mock-dev-server'
import { userList } from '../data/user.data'

export default defineMock([
  {
    url: '/api/users',
    method: 'GET',
    response: ({ query }) => {
      const { current = 1, pageSize = 10, username, status } = query
      
      let list = [...userList]
      if (username) {
        list = list.filter(u => u.username.includes(username))
      }
      if (status) {
        list = list.filter(u => u.status === status)
      }
      
      const start = (current - 1) * pageSize
      const end = start + parseInt(pageSize)
      
      return {
        code: 200,
        data: {
          list: list.slice(start, end),
          total: list.length,
        },
      }
    },
  },
  // ... POST, PUT, DELETE 接口
])

3. 페이지 생성

vue
<!-- src/views/system/users/index.vue -->
<template>
  <div class="user-management">
    <ProTable
      ref="tableRef"
      :columns="columns"
      :request="fetchUserList"
      :toolbar="toolbar"
      :form-items="formItems"
      @form-submit="handleFormSubmit"
    >
      <template #toolbar-actions>
        <a-button type="primary" @click="handleCreate">
          <PlusOutlined />
          新增用户
        </a-button>
      </template>
    </ProTable>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { message } from 'antdv-next'
import { PlusOutlined } from '@antdv-next/icons'
import ProTable from '@/components/Pro/ProTable/index.vue'
import type { ProTableColumn, ProFormItem, ProTableRequest } from '@/types/pro'
import {
  getUserList,
  createUser,
  updateUser,
  deleteUser,
} from '@/api/user'

const tableRef = ref()

// 表格列配置
const columns: ProTableColumn[] = [
  {
    title: '用户名',
    dataIndex: 'username',
    search: true,
  },
  {
    title: '邮箱',
    dataIndex: 'email',
  },
  {
    title: '手机号',
    dataIndex: 'phone',
  },
  {
    title: '状态',
    dataIndex: 'status',
    valueType: 'tag',
    search: true,
    options: [
      { label: '启用', value: 'active', color: 'green' },
      { label: '禁用', value: 'inactive', color: 'red' },
    ],
  },
  {
    title: '角色',
    dataIndex: 'role',
    valueType: 'tag',
    options: [
      { label: '管理员', value: 'admin', color: 'blue' },
      { label: '编辑', value: 'editor', color: 'cyan' },
      { label: '用户', value: 'user' },
    ],
  },
  {
    title: '创建时间',
    dataIndex: 'createdAt',
    valueType: 'dateTime',
  },
  {
    title: '操作',
    dataIndex: 'actions',
    actions: [
      {
        label: '编辑',
        permission: 'user.edit',
        onClick: (record) => tableRef.value?.openEditModal(record),
      },
      {
        label: '删除',
        danger: true,
        permission: 'user.delete',
        confirm: '确认删除该用户?',
        onClick: (record) => handleDelete(record),
      },
    ],
  },
]

// 表单配置
const formItems: ProFormItem[] = [
  { name: 'username', label: '用户名', type: 'input', required: true },
  { name: 'email', label: '邮箱', type: 'input', rules: [{ type: 'email' }] },
  { name: 'phone', label: '手机号', type: 'input' },
  {
    name: 'status',
    label: '状态',
    type: 'select',
    required: true,
    options: [
      { label: '启用', value: 'active' },
      { label: '禁用', value: 'inactive' },
    ],
  },
  {
    name: 'role',
    label: '角色',
    type: 'select',
    required: true,
    options: [
      { label: '管理员', value: 'admin' },
      { label: '编辑', value: 'editor' },
      { label: '用户', value: 'user' },
    ],
  },
]

// 请求数据
const fetchUserList: ProTableRequest = async (params) => {
  const res = await getUserList(params)
  return {
    data: res.data.list,
    total: res.data.total,
    success: true,
  }
}

// 表单提交
const handleFormSubmit = async ({ values, record, isEdit }) => {
  try {
    if (isEdit) {
      await updateUser(record.id, values)
      message.success('更新成功')
    } else {
      await createUser(values)
      message.success('创建成功')
    }
    tableRef.value?.refresh()
  } catch (error) {
    message.error('操作失败')
  }
}

// 删除
const handleDelete = async (record: any) => {
  try {
    await deleteUser(record.id)
    message.success('删除成功')
    tableRef.value?.refresh()
  } catch (error) {
    message.error('删除失败')
  }
}

// 打开新增弹窗
const handleCreate = () => {
  tableRef.value?.openCreateModal()
}
</script>

폼 설계 패턴

단계별 폼

typescript
const stepFormItems: ProFormItem[][] = [
  // 第一步
  [
    { name: 'name', label: '项目名称', type: 'input', required: true },
    { name: 'type', label: '项目类型', type: 'select', required: true, options: [...] },
  ],
  // 第二步
  [
    { name: 'startDate', label: '开始日期', type: 'datePicker', required: true },
    { name: 'endDate', label: '结束日期', type: 'datePicker', required: true },
  ],
  // 第三步
  [
    { name: 'members', label: '成员', type: 'select', mode: 'multiple' },
    { name: 'description', label: '描述', type: 'textarea' },
  ],
]

동적 폼

선택에 따라 필드를 동적으로 표시:

typescript
const formItems: ProFormItem[] = [
  {
    name: 'type',
    label: '类型',
    type: 'select',
    options: [
      { label: '个人', value: 'personal' },
      { label: '企业', value: 'enterprise' },
    ],
  },
  {
    name: 'idCard',
    label: '身份证号',
    type: 'input',
    hidden: (values) => values.type !== 'personal',
  },
  {
    name: 'businessLicense',
    label: '营业执照',
    type: 'input',
    hidden: (values) => values.type !== 'enterprise',
  },
]

테이블 고급 사용법

커스텀 열 렌더링

typescript
const columns: ProTableColumn[] = [
  {
    title: '头像',
    dataIndex: 'avatar',
    render: (text) => h('img', { src: text, style: { width: '40px', borderRadius: '50%' } }),
  },
  {
    title: '进度',
    dataIndex: 'progress',
    render: (text) => h(Progress, { percent: text }),
  },
]

중첩 테이블

vue
<template>
  <ProTable :columns="columns" :request="request">
    <template #expand="{ record }">
      <ProTable
        :columns="detailColumns"
        :data-source="record.details"
        :pagination="false"
      />
    </template>
  </ProTable>
</template>

권한 제어 실전

버튼 레벨 권한

vue
<template>
  <div>
    <a-button v-permission="'user.create'">新增</a-button>
    <a-button v-permission="'user.export'">导出</a-button>
  </div>
</template>

페이지 레벨 권한

typescript
{
  path: '/system/users',
  meta: {
    requiredPermissions: ['user.view'],
  },
}

동적 권한 판정

typescript
const { can } = usePermission()

const actions = computed(() => {
  const list = []
  if (can('user.edit')) {
    list.push({ label: '编辑', onClick: handleEdit })
  }
  if (can('user.delete')) {
    list.push({ label: '删除', onClick: handleDelete, danger: true })
  }
  return list
})

다음 단계

MIT 라이선스에 따라 배포됨