Gi Admin Pro — это бесплатный шаблон для среднего и заднего фронтендов, основанный на Vue3, Vite, TypeScript, Arco Design Vue, Pinia, VueUse и других технологиях. Он использует последнюю стек технологий фронтенда, имеет богатые настройки тем, высокие стандарты кодирования и динамическое отображение данных на основе mock. Шаблон можно использовать сразу после установки, а также использовать для обучения и ссылок.
Значение префикса Gi: G — означает "глобальный", i — означает "моё".
Gi используется для определения префикса глобальных компонентов, таких как GiNavBar, GiTitle, GiLoading.
| ----------------- | ---------------------------------------------------------- | | gitee (Яньюй) | Gi Admin Pro адрес для просмотра | | github | Gi Admin Pro адрес для просмотра || | Логин | Пароль | | ------ | ----- | ------ | | Администратор | admin | 123456 | | Пользователь | user | 123456 |
Платформа | Адрес для просмотра | Адрес репозитория |
---|---|---|
gitee (маяньюнь) | Адрес для просмотра Gi Admin Pro | Адрес репозитория на Gitee |
github | Адрес для просмотра Gi Admin Pro | Адрес репозитория на Github |
npm install
npm run dev
npm run build
1. Prettier - Code formatter
2. Vue - Official
3. Vue 3 Snippets
```## Внимание
```bash
В связи с обновлением до Vite3, согласно официальным требованиям, версия Node.js должна быть 14.18.0 или выше
Примечание: Теперь обновлено до Vite5.x, требования к версии Node.js см. на официальном сайте
Официальный сайт Vite: https://cn.vitejs.dev/
Lin
Почему установка зависимостей не проходит?
Проверьте версию Node.js
, лучше использовать оригинальное зеркало npm
Восстановите зеркало
npm config set registry https://registry.npmjs.org/
Почему выбран компонентный набор Arco, а не Element Plus?
Сравнение Element Plus и Arco design
Почему глобальные компоненты используют префикс Gi?
Глобальные компоненты настроены для импорта по мере необходимости, использование префикса позволяет легко отличать их от локальных компонентов
Почему компоненты используют названия с заглавной буквы (PascalCase)?
В данном проекте имена файлов .vue
и их использование в шаблонах используются с заглавной буквой (PascalCase)
См. Vue2 официальный сайт - стилистика: https://v2.cn.vuejs.org/v2/style-guide/
Именование компонентов: Имена файлов для однофайловых компонентов должны быть либо всегда с заглавной буквы (PascalCase), либо всегда с дефисом (kebab-case)
Другие преимущества: удобство поиска (компоненты с дефисом (kebab-case) менее удобны для поиска)Почему рекомендуется использовать дефисы для имен CSS классов (kebab-case)?
В большинстве крупных сайтов используется именно такой способ названия классов, а не такой: .my-class
.
Возникают проблемы с отображением страницы?
Страница должна содержать один корневой элемент!!!
Управление правами в Vue3: сортировка и форматирование маршрутов
Используется библиотека xe-utils для упрощения обработки данных.
Страница не кэшируется?
Проверьте, что страница настроена с name
, и что имя соответствует данным.
defineOptions({ name: 'AboutIndex' })
{
path: '/about/index',
name: 'AboutIndex', // Проверьте, соответствует ли имя
component: () => import('@/views/about/index.vue')
}
Обычно, количество строк в одном .vue файле не должно превышать 400
. Если превышено, рекомендуется разбить компонент на несколько.
<script setup lang="ts">
// Обычно для ссылочных типов используется const, а для примитивных типов - let
const arr = []
const obj = {}
const fn = () => {
console.log('123')
}
let num = 10
let str = 'abc'
let flag = false
// В Vue3 ref и reactive возвращают ссылочные типы
const loading = ref(false)
const person = reactive({ name: '张三', age: 20 })
</script>
``````vue
<script setup lang="ts">
const loading = ref(false) // Загрузка данных
const visible = ref(false) // Видимость
const disabled = ref(true) // Заблокировано
const showAddModal = ref(false) // Видимость модального окна добавления
const showAddDrawer = ref(false) // Видимость вкладки добавления
// Или видимость диалогового окна
const isShowDialog = ref<boolean>(false)
const isLogin = ref(false) // Вход
const isVIP = ref(false) // VIP-статус
``````markdown
# Несоответствующие объектные массивы
Добавьте "s" к словам, следующим за буквами.
```javascript
const ids = []
const selectedIds = []
const activedKeys = []
const nums = [3, 5, 6]
const strs = ['aaa', 'bbb', 'ccc']
const getData = () => {
const arr = []
nums.forEach((item) => {
arr.push({ value: item })
})
}
const getUserList = async () => {
const res = await Api.getUserPage()
userList = res.data
}
const edit = () => {}
const onEdit = () => {}
const handleEdit = () => {}
const add = () => {}
const onAdd = () => {}
const handleAdd = () => {}
// Не рекомендуется, delete - ключевое слово в JS
const del = () => {}
const onDelete = () => {}
const handleDelete = () => {}
const remove = () => {}
const rename = () => {}
const onRename = () => {}
const handleRename = () => {}
const mulDelete = () => {}
const onMulDelete = () => {}
const handleMulDelete = () => {}
const search = () => {}
const back = () => {}
const confirm = () => {}
const ok = () => {}
const form = reactive({
name: '',
phone: '',
remark: ''
})
const userInfo = ref({}) // Информация пользователя
const tableData = ref([]) // Данные таблицы
const treeData = ref([]) // Данные дерева
// Для массивов объектов лучше добавить к названию List или Data
const companyList = ref([])
const checkedList = ref([])
const selectedList = ref([])
const addressList = ref([])
const userList = [
{ id: '01', name: '张三' },
{ id: '02', name: '李四' }
]
const tableData = []
const optionsList = [
{ label: '哈哈', value: 1 },
{ label: '嘻嘻', value: 2 }
]
```javascript
const cancel = () => {}
const open = () => {}
const close = () => {}
const save = () => {}
const getTableData = () => {}
const getTableList = () => {}
**Некоторые часто используемые префиксы**
| Префикс | Префикс + Имя | Значение |
| ------------- | ------------------------------- | ------------------------------- |
| get | getUserInfo | Получение информации о пользователе |
| del/delete | delUserInfo | Удаление информации о пользователе |
| update/add | updateUserInfo / addUserInfo | Обновление информации о пользователе / Добавление информации о пользователе |
| is | isTimeout | Проверка на превышение времени |
| has | hasUserInfo | Проверка наличия информации о пользователе |
| handle | handleLogin | Обработка входа |
| calc | calcAverageSpeed | Вычисление средней скорости |
**Некоторые общие сокращения**
```| Исходное слово | Сокращение |
| --------------- | ----------- |
| сообщение | msg |
| информация | info |
| кнопка | btn |
| фон | bg |
| ответ | res |
| запрос | req |
| изображение | img |
| утилита | util |
| свойство | prop |
| источник | src |
| логический тип | bool |
| ошибка | err |
| настройки | set |#### Названия, связанные с Vue
```vue
<script setup lang="ts">
const isEdit = ref(false)
``````vue
<script setup lang="ts">
// Не рекомендуется
const title = computed(() => {
return isEdit.value ? 'Редактировать' : 'Добавить'
})
// Рекомендуется, если можно записать в одну строку, то делайте это
const title = computed(() => (isEdit.value ? 'Редактировать' : 'Добавить'))
</script>
<script setup lang="ts">
// Для форм рекомендуется использовать название form (более краткое), не используйте formData, а также используйте reactive
const form = reactive({
name: '',
phone: ''
})
</script>
<script setup lang="ts">
// Если у вас много свойств
const getInitForm = () => ({
name: '',
phone: '',
email: '',
sex: 1,
age: ''
})
const form = reactive(getInitForm())
// Сбросить форму
const resetForm = () => {
for (const key in form) {
delete form[key]
}
Object.assign(form, getInitForm())
}
</script>
<script setup lang="ts">
import { useAppStore, useUserStore } from '@/stores'
import { useLoading } from '@/hooks'
// Правила названий для stores или hooks
const appStore = useAppStore()
const userStore = useUserStore()
const { loading, setLoading } = useLoading()
</script>
Постарайтесь использовать тернарные выражения
// До оптимизации
let isEdit = true
let title = ''
if (isEdit) {
title = 'Редактировать'
} else {
title = 'Добавить'
}
// После оптимизации
let title = isEdit ? 'Редактировать' : 'Добавить'
Используйте метод includes
// До оптимизации
if (type === 1 || type === 2 || type === 3) {
}
// После оптимизации, такой способ можно использовать и в шаблонах Vue
if ([1, 2, 3].includes(type)) {
}
Используйте стрелочные функции для упрощения функций
// До оптимизации
function add(num1, num2) {
return num1 + num2
}
// После оптимизации
const add = (num1, num2) => num1 + num2
<script setup lang="ts">
// Цвет для прогресса пропорции, постарайтесь уменьшить количество if-else
const getProportionColor = (proportion: number) => {
if (proportion < 30) return 'опасность'
if (proportion < 60) return 'предупреждение'
return 'успех'
}
</script>
// До оптимизации
const status = 200
const message = ''
if (status === 200) {
message = 'Запрос успешен'
} else if (status === 404) {
message = 'Запрос неудачен'
} else if (status === 500) {
message = 'Ошибка сервера'
}
// После оптимизации
const status = 200
const messageMap = {
200: 'Запрос успешен',
404: 'Запрос неудачен',
500: 'Ошибка сервера'
}
const message = messageMap[status]
Если у функции больше двух параметров, рекомендуется оптимизировать
<script setup lang="ts">
function createUser(name, phone, age) {
console.log('Имя', name)
console.log('Телефон', phone)
console.log('Возраст', age)
}
// Такой способ использования имеет плохую читаемость, плохую расширяемость и трудно поддерживать
createUser('张三', '178****2828', 20)
function createUser2({ name, phone, age }) {
console.log('Имя', name)
console.log('Телефон', phone)
console.log('Возраст', age)
}
// Передача параметров в виде объекта более наглядна, легче расширять и поддерживать
createUser2({ name: '张三', phone: '178****2828', age: 20 })
</script>
Вариант 1
Если названия ваших API на сервере просты, вы можете использовать следующие правила названий (учтите правила названий типов в TypeScript):
Название должно состоять из: операции + модуль сервера + функциональность
```Префикс (операция) должен быть глаголом, например: add / update / delete / get / save
и т.д.
import type * as T from './type'
import http from '@/utils/http'
/** Получить список пользователей */
export function getUserList() {
return http.get<PageRes<T.UserItem[]>>('/user/list')
}
/** Получить детали пользователя */
export function getUserDetail() {
return http.get<T.UserDetail>('/user/detail')
}
/** Добавить пользователя */
export function addUser(data: any) {
return http.post<T.UserAddResult>('/user/add', data)
}
/** Редактировать пользователя */
export function updateUser(data: any) {
return http.post<T.UserUpdateResult>('/user/update', data)
}
/** Удалить пользователя */
export function deleteUser(data: { id: string }) {
return http.post<T.UserDeleteResult>('/user/delete', data)
}
Вышеуказанные правила названий обеспечивают, что названия API не будут конфликтовать, а добавление имени модуля позволяет быстро находить и легче поддерживать.
// @/apis/index.ts
export * from './user'
export * from './user/type'
Введение API для варианта 1
import { getUserList, addUser, type UserAddResult } from '@/apis'
Правила названий типов API для варианта 1
const url1 = '/user/list' // UserItem[] или UserListItem[]
const url2 = '/user/detail' // UserDetail или UserDetailResult
const url3 = '/role/list' // RoleItem[] или RoleListItem[]
const url4 = '/role/detail' // RoleDetail или RoleDetailResult
Вариант 2
Если названия ваших API на сервере не такие простые, вы можете использовать следующие правила названий (учтите правила названий типов в TypeScript)~~~ts import type * as T from './type' import http from '@/utils/http'
/** Получить список пользователей */ export function getUserList() { return http.get<PageRes<T.UserItem[]>>('/user/getUserList') }```md
/** Получить детали пользователя */
export function getUserDetail() {
return http.get<T.UserDetail>('/user/getUserdetail')
}
/** Добавить пользователя */
export function addUser(data: any) {
return http.post<T.AddUserResult>('/user/addUser', data)
}
/** Редактировать пользователя */
export function updateUser(data: any) {
return http.post<T.UpdateUserResult>('/user/updateUser', data)
}
/** Удалить пользователя */
export function deleteUser(data: { id: string }) {
return http.post<T.DeleteUserResult>('/user/deleteUser', data)
}
Вышеуказанные правила именования обеспечивают отсутствие конфликтов в именах API, а также позволяют быстро находить и удобно поддерживать код
Второй вариант импорта API
import { getUserList, addUser, type AddUserResult } from '@/apis/user' // Необходимо указывать конкретный модуль
import http from '@/utils/http'
import { prefix } from '../config'
import type * as T from './type'
/** Получить данные отдела */
export function getSystemDeptList() {
return http.get<PageRes<T.DeptItem[]>>(`${prefix}/system/dept/list`)
}
/** Получить данные пользователя */
export function getSystemUserList() {
return http.get<PageRes<T.UserItem[]>>(`${prefix}/system/user/list`)
}
/** Получить данные роли */
export function getSystemRoleList() {
return http.get<PageRes<T.RoleItem[]>>(`${prefix}/system/role/list`)
}
Не рекомендуется использовать следующий способ импорта типов, так как это менее удобно
import type { DeptItem, UserItem, RoleItem } from './type'
Вариант 1
Не требуется загрузка, не требуется вывод ошибок
<script setup lang="ts">
import { ref } from 'vue'
import { getUserList as getUserListApi, type UserItem } from '@/apis' // Для одинаковых имен можно использовать псевдоним
</script>
<script setup lang="ts">
import { ref } from 'vue'
import { getUserList as getUserListApi, type UserItem } from '@/apis' // одинаковые имена можно использовать псевдонимы
const loading = ref(false)
const userList = ref<UserItem[]>([])
const getUserList = async () => {
try {
loading.value = true
const res = await getUserListApi()
console.log('Если асинхронная операция завершится успешно, будет выведено это сообщение, в противном случае - нет')
userList.value = res.data
} catch (error) {
console.log('Если асинхронная операция завершится неудачно, будет выведено это сообщение')
// поскольку в обёртке axios уже есть обработка ошибок с выводом уведомления
// здесь нет необходимости использовать Message.error(error)
} finally {
console.log('Если асинхронная операция завершится успешно или неудачно, будет выведено это сообщение')
loading.value = false // можно использовать для управления загрузкой
}
}
</script>
```**Вариант 3**
Ситуация, требующая загрузки, но не вывода ошибок (обработка ошибок не требуется)
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { getUserList as getUserListApi, type UserItem } from '@/apis' // одинаковые имена можно использовать псевдонимы
const loading = ref(false)
const userList = ref<UserItem[]>([])
const getUserList = async () => {
try {
loading.value = true
const res = await getUserListApi()
console.log('Если асинхронная операция завершится успешно, будет выведено это сообщение, в противном случае - нет')
userList.value = res.data
} finally {
console.log('Если асинхронная операция завершится успешно или неудачно, будет выведено это сообщение')
loading.value = false // можно использовать для управления загрузкой
}
}
</script>
// catch можно опустить
```
#### Пример использования регулярных выражений
Путь к файлу: @/utils/regexp.ts
```ts
/** @desc Регулярное выражение для номера телефона */
export const Phone = /^1[3-9]\d{9}$/
/** @desc Регулярное выражение для электронной почты */
export const Email = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
/** @desc Регулярное выражение для шестизначного кода подтверждения */
export const Code_6 = /^\d{6}$/
/** @desc Регулярное выражение для четырёхзначного кода подтверждения */
export const Code_4 = /^\d{4}$/
/** @desc Регулярное выражение для шестнадцатеричного цвета #333 #8c8c8c */
export const ColorRegex = /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
/** @desc Регулярное выражение для кириллицы */
export const OnlyCh = /^[\u4e00-\u9fa5]+$/gi
```
```vue
/** @desc Регулярное выражение — только английский */
export const OnlyEn = /^[a-zA-Z]*$/
``````markdown
/** @desc Входящая регистрация — пароль от 6 до 16 символов (буквы, цифры) */
export const Password = /^[a-zA-Z0-9]{6,16}$/
```
Использование
```vue
<script lang="ts" setup>
import { reactive } from 'vue'
import { Message } from '@arco-design/web-vue'
// Рекомендуемый способ импорта регулярных выражений
import * as Regexp from '@/utils/regexp'
const form = reactive({
name: '',
phone: ''
})
const submit = () => {
if (!Regexp.Phone.test(form.phone)) {
return Message.warning('Введите правильный формат номера телефона')
}
}
</script>
```
Шаблон страницы с CSS классами, использующими дефис (-) в качестве разделителя
```vue
<template>
<div class="detail">
<h3 class="title">Заголовок</h3>
<section class="table-box">
<table></table>
</section>
</div>
</template>
```
#### Глобальные компоненты — правила названия
Название компонента: **Имя файла должно быть либо всегда с заглавной буквы (PascalCase), либо с дефисом (kebab-case)**
Для справки см. Vue2 официальный сайт — руководство по стилю: https://v2.ru.vuejs.org/v2/style-guide/
```
GiTitle.vue
GiThemeBtn.vue
GiSvgIcon.vue
```
#### Локальные компоненты — правила названия
Название компонента: **Имя файла должно быть либо всегда с заглавной буквы (PascalCase), либо с дефисом (kebab-case)**
Для справки см. Vue2 официальный сайт — руководство по стилю: https://v2.ru.vuejs.org/v2/style-guide/
```
Pane1.vue
Pane2.vue
PaneQuota1.vue
PaneQuota2.vue
Step1.vue
Step2.vue
AddModal.vue
EditDrawer.vue
DetailModal.vue
```
#### Правила названия папок (используются дефисы -)
1. Имена файлов рекомендуется составлять только из строчных букв, без использования заглавных букв
```2. При длинных названиях рекомендуется использовать дефис (-) для разделения
```
home/index.vue
user/index.vue
user-detail/index.vue
```
#### Общие правила для компонентов модальных окон Modal и панелей сайдбаров Drawer
```vue
<template>
<a-modal v-model:visible="visible" :title="title" @ok="confirm">
<!-- Содержимое -->
</a-modal>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
const visible = ref(false)
const detailId = ref('')
const isEdit = computed(() => !!detailId.value) // Определяет, является ли это режимом редактирования или добавления
const title = computed(() => (isEdit.value ? 'Редактирование' : 'Добавление'))
const add = () => {
detailId.value = ''
visible.value = true
}
const edit = (id: string) => {
detailId.value = id
visible.value = true
// getDetail() отображение данных
}
defineExpose({ add, edit })
const confirm = () => {
console.log('Нажата кнопка подтверждения')
}
</script>
```
Использование
**`Использование пользовательского компонента в шаблоне: используйте прописные буквы для начала именования в стиле camelCase, двойной клик для копирования, что облегчает поиск`**
```vue
<template>
<EditModal ref="EditModalRef"></EditModal>
</template>
<script setup lang="ts">
import EditModal from './EditModal.vue'
const EditModalRef = ref<InstanceType<typeof EditModal>>()
// Добавление
const onAdd = () => {
EditModalRef.value?.add()
}
// Редактирование
const onEdit = (item: PersonItem) => {
EditModalRef.value?.edit(item.id)
}
</script>
```
#### Пример использования GiForm (обновлено 25.10.2024: использование более мощного Grid-размещения)GiForm — это компонент формы на основе JSON, который позволяет быстро создавать формы с помощью JSON-конфигурации.
Основной пример
<img src="https://gitee.com/lin0716/gi-image/raw/master/form/GiForm-code.png" />
<img src="https://gitee.com/lin0716/gi-image/raw/master/form/form1.png" />
<img src="https://gitee.com/lin0716/gi-image/raw/master/form/form2.png" />
**Новая документация****Конфигурация columns**
```| Свойство | Описание |
| ----------------- | ------------------------------------------------------------ |
| type | Тип элемента формы, например input, select, date-picker и т.д. |
| label | Метка элемента формы (label a-form-item) |
| field | Поле элемента формы (field a-form-item) |
| span | Ширина элемента формы, наследуемая от a-grid-item |
| gridItemProps | Все свойства props, наследуемые от a-grid-item |
| formItemProps | Все свойства props, наследуемые от a-form-item |
| props | Все свойства props, наследуемые от компонентов a-input, a-select и т.д., настраиваемые в зависимости от типа элемента формы |
| rules | Правила валидации элемента формы |
| hide | Динамическое скрытие (form) => boolean, возвращает логическое значение, form — это привязка v-model к форме |
| show | Динамическое отображение (form) => boolean, возвращает логическое значение, form — это привязка v-model к форме |
| disabled | Динамическое отключение (form) => boolean, возвращает логическое значение, form — это привязка v-model к форме |**
Использование функции Modal для реализации более эффективных диалоговых окон форм**<img src="https://gitee.com/lin0716/gi-image/raw/master/form/GiForm-modal.png" />
#### Пример использования GiTable
**GiTable наследует все атрибуты и конфигурации a-table, но имеет несколько дополнительных слотов и prop-атрибутов, подробнее в исходном коде**
<img src="https://gitee.com/lin0716/gi-image/raw/master/table/GiTable-code.png" />
<img src="https://gitee.com/lin0716/gi-image/raw/master/table/GiTable-demo.gif" />
#### Структура каталога Hooks
<img src="https://gitee.com/lin0716/gi-image/raw/master/hooks-catalog.png" />
**По умолчанию в hooks хранятся общие hooks, не связанные с запросами к API**
```vue
<script setup lang="ts">
import { useLoading } from '@/hooks'
const { loading, setLoading } = useLoading()
</script>
```
**В hooks/app хранятся общие hooks для запросов к API**
/hooks/app/useDept.ts
```typescript
import { ref } from 'vue'
import { getSystemDeptList } from '@/apis'
import type { DeptItem } from '@/apis'
/** Модуль отделов */
export function useDept() {
const loading = ref(false)
const deptList = ref<DeptItem[]>([])
const getDeptList = async () => {
try {
loading.value = true
const res = await getSystemDeptList()
deptList.value = res.data.records
} catch (error) {
} finally {
loading.value = false
}
}
return { deptList, getDeptList, loading }
}
```
Использование
```vue
<script setup lang="ts">
import { useDept } from '@/hooks/app'
const { deptList, getDeptList, loading: deptLoading } = useDept()
getDeptList() // Рекомендуется вызывать методы hooks на странице, так как это более прямой подход (не вызывать внутри hooks)
</script>
```
#### Использование таблицы с помощью TSX
<img src="https://gitee.com/лн0716/gi-image/raw/master/table/tsx-table.png" />
#### Использование usePagination(hooks)
Путь к файлу: @/hooks/modules/usePagination.ts
**Старая версия:**```ts
import { ref } from 'vue'
type Callback = () => void
type Options = {
defaultPageSize: number
}
export default function usePagination(callback: Callback, options: Options = { defaultPageSize: 10 }) {
const current = ref(1)
const pageSize = ref(options.defaultPageSize)
const total = ref(0)
function changeCurrent(size: number) {
current.value = size
callback && callback()
}
function changePageSize(size: number) {
current.value = 1
pageSize.value = size
callback && callback()
}
``````js
function setTotal(value: number) {
total.value = value
}
const pagination = computed(() => {
return {
showPageSize: true,
// ...другие настройки
total: total.value,
current: current.value,
pageSize: pageSize.value,
onChange: changeCurrent,
onPageSizeChange: changePageSize
}
})
return {
current,
pageSize,
total,
pagination,
changeCurrent,
changePageSize,
setTotal
}
}
```
**`Вышеуказанное решение устарело`**. Новое решение представлено ниже
**Улучшенная версия (совместима с предыдущей версией):**
```js
import { reactive, toRefs } from 'vue'
import type { PaginationProps } from '@arco-design/web-vue'
type Callback = () => void
type Options = {
defaultPageSize: number
}
export default function usePagination(callback: Callback, options: Options = { defaultPageSize: 10 }) {
const pagination = reactive({
showPageSize: true,
current: 1,
pageSize: options.defaultPageSize,
total: 0,
onChange: (size: number) => {
pagination.current = size
callback && callback()
},
onPageSizeChange: (size: number) => {
pagination.current = 1
pagination.pageSize = size
callback && callback()
}
})
const changeCurrent = pagination.onChange
const changePageSize = pagination.onPageSizeChange
function setTotal(value: number) {
pagination.total = value
}
const { current, pageSize, total } = toRefs(pagination)
return {
current,
pageSize,
total,
pagination,
changeCurrent,
changePageSize,
setTotal
}
}
```Способ использования 1
```vue
<template>
<!-- ... -->
<div class="table-box">
<a-table
row-key="id"
:columns="columns"
:data="tableData"
:pagination="{
showPageSize: true,
total: total,
current: current,
pageSize: pageSize
}"
@page-change="changeCurrent"
@page-size-change="changePageSize"
>
</a-table>
</div>
</template>
<script setup lang="ts">
import { usePagination } from '@/hooks'
const { current, pageSize, total, changeCurrent, changePageSize, setTotal } = usePagination(() => {
getTableData()
})
// Начать с первой страницы
changeCurrent(1)
</script>
```
Способ использования 2 (улучшенная версия, меньше кода)
```vue
<template>
<!-- ... -->
<div class="table-box">
<a-table row-key="id" :columns="columns" :data="tableData" :pagination="pagination"> </a-table>
</div>
</template>
<script setup lang="ts">
import { usePagination } from '@/hooks'
const { pagination, setTotal } = usePagination(() => {
getTableData()
})
// Начинаем поиск с первой страницы
pagination.onChange(1)
// Поиск
const search = () => {
pagination.onChange(1)
}
const search2 = () => {
pagination.current = 1
getTableData()
}
</script>
```
Примечание:
```vue
<script setup lang="ts">
import { usePagination } from '@/hooks'
const { pagination, setTotal } = usePagination(() => {
getTableData()
})
const form = reactive({
name: '',
status: ''
})
const getTableData = async () => {
const res = await getData({ ...form, page: pagination.current, size: pagination.pageSize })
}
</script>
``````vue
<template>
<div>
<a-pagination v-bind="pagination" />
</div>
</template>
<script setup lang="ts">
import { usePagination } from '@/hooks'
const { pagination, setTotal } = usePagination(() => {
getTableData()
})
const form = reactive({
name: '',
status: ''
})
const getTableData = async () => {
const res = await getData({ ...form, page: pagination.current, size: pagination.pageSize })
}
</script>
```
#### Использование useTable(hooks)
<img src="https://gitee.com/lin0716/gi-image/raw/master/table/useTable.png" />
Использование
<img src="https://gitee.com/lin0716/gi-image/raw/master/table/useTable-code.png" />
**Подсказка**
При использовании useTable не нужно передавать тип, tableData автоматически будет выводить тип по входным параметрам.<img src="https://gitee.com/lin0716/gi-image/raw/master/table/useTable-code2.png" />
**Последнее уведомление**
В новой версии `useTable` переменная `selectKeys` была заменена на `selectedKeys`, а также добавлены новые функции. Для получения более подробной информации обратитесь к исходному коду.
#### Использование useResetReactive(hooks)
Цель: Иногда требуется сбросить данные формы, и этот hook значительно упрощает этот процесс.
Код: `useResetReactive.ts`
```js
import { reactive } from 'vue'
import { cloneDeep } from 'lodash-es'
export function useResetReactive<T extends object>(value: T) {
const getInitValue = () => cloneDeep(value)
const state = reactive(getInitValue())
const reset = () => {
Object.keys(state).forEach((key) => delete state[key])
Object.assign(state, getInitValue())
}
}
``` return [state, reset] as const
}
```
**Пример использования**
```js
import { useResetReactive } from '@/hooks'
const [form, resetForm] = useResetReactive({
id: '',
name: '',
phone: '',
status: false
})
// Сбросить данные формы
resetForm()
```
**Внимание**
Почему метод `resetForm` должен содержать следующий код?
```js
for (const key in form) {
delete form[key]
}
```
Например, в редактирующем модальном окне при нажатии на кнопку "Редактировать" данные формы заполняются по ID. Иногда для удобства данные из деталей просто присваиваются к форме, что приводит к появлению избыточных свойств при сбросе формы. Пример:
```js
const form = { name: '' }
const detail = { name: '张三', status: 1 }
Object.assign(form, detail)
console.log(form) // { name: '张三', status: 1 }
// Если просто сбросить
Object.assign(form, { name: '' })
console.log(form) // { name: '', status: 1 }
// Метод `resetForm` удаляет все свойства формы, чтобы избежать отправки избыточных данных на сервер.
```
#### Использование модальных окон с TSX
##### Метод 1
tool.tsx
<img src="https://gitee.com/lin0716/gi-image/raw/master/modal/tsx-modal2.png" />
Использование
<img src="https://gitee.com/lin0716/gi-image/raw/master/modal/tsx-modal-use.png" />
##### Метод 2
AddUserForm.vue
<img src="https://gitee.com/lin0716/gi-image/raw/master/modal/tsx-modal1.png" />
Использование
<img src="https://gitee.com/lin0716/gi-image/raw/master/modal/tsx-modal-use.png" />
##### Метод 3
<img src="https://gitee.com/lin0716/gi-image/raw/master/modal/tsx-modal3-1.png" />
#### Советы по использованию компонентов
Где возможно, используйте компоненты для реализации структуры страницы. Используйте компонент **Row** для флекс-разметки.```vue
<template>
<a-row justify="space-between" align="center"> </a-row>
</template>
```
Для создания интервалов между кнопками рекомендуется использовать компонент **Space**
```vue
<template>
<a-space :size="10">
<a-button>Вернуться</a-button>
<a-button type="primary">Отправить</a-button>
</a-space>
</template>
```
Использование компонента **TypographyText** для текста с состоянием
```vue
<template>
<a-typography-text>Основной текст</a-typography-text>
<a-typography-text type="secondary">Второстепенный текст</a-typography-text>
<a-typography-text type="primary">Текст основного цвета</a-typography-text>
<a-typography-text type="primary">Отправлено</a-typography-text>
<a-typography-text type="success">Успешно одобрено</a-typography-text>
<a-typography-text type="warning">Не отправлено</a-typography-text>
<a-typography-text type="danger">Не одобрено</a-typography-text>
</template>
```
Сценарии использования компонента **Link**
```vue
<template>
<a-table>
<a-table-column title="Действия" :width="150" fixed="right">
<template #cell="{ record }">
<a-space>
<a-link :hoverable="false">Редактировать</a-link>
<a-link :hoverable="false">Редактировать</a-link>
<a-link :hoverable="false">Удалить</a-link>
</a-space>
</template>
</a-table-column>
</a-table>
</template>
```
#### Нормы именования CSS
Рекомендуется использовать строчные буквы, а несколько слов соединять дефисом (на большинстве веб-сайтов, включая Жэньгу, Майгу и другие, используется этот стандарт)
Или использовать нормы именования **BEM**
```css
// Рекомендуется
.header
.footer
.main
.content
.container
.page
.detail
.pane-left
.pane-right
.list
.list-item
// Не рекомендуется
.header
.footer
.main
.content
.container
.page
.detail
.pane-left
.pane-right
.list
.list-item
```**Нормы именования BEM**
```html
<div class="article">
<div class="article__body">
<button class="article__button--primary"></button>
<button class="article__button--success"></button>
</div>
</div>
```
```less
.article {
max-width: 1200px;
&__body {
padding: 20px;
}
&__button {
padding: 5px 8px;
&--primary {
background: blue;
}
&--success {
background: green;
}
}
}
```
#### Глобальные классы CSS - правила именования
**Используйте подчеркивание _, чтобы было удобно копировать**
```scss
.gi_line_1 .gi_line_2 .gi_margin .gi_box
```
```scss
// Путь к файлу: @/styles/global.scss
.gi_line_1 {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
```
```scss
.gi_line_2 {
-webkit-line-clamp: 2;
}
.gi_line_3 {
-webkit-line-clamp: 3;
}
.gi_line_4 {
-webkit-line-clamp: 4;
}
.gi_line_5 {
-webkit-line-clamp: 5;
}
.gi_line_2,
.gi_line_3,
.gi_line_4,
.gi_line_5 {
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box; // упругий контейнер
-webkit-box-orient: vertical; // задает ориентацию элементов контейнера
}
.gi_padding {
padding: $padding;
}
.gi_margin {
margin: $margin;
}
.gi_relative {
position: relative;
}
.gi_absolute {
position: absolute;
}
.gi_rotate_90deg {
transform: rotate(90deg);
}
.gi_rotate_-90deg {
transform: rotate(-90deg);
}
.gi_rotate_180deg {
transform: rotate(180deg);
}
.gi_rotate_-180deg {
transform: rotate(-180deg);
}
.gi_mt {
margin-top: $margin;
}
.gi_mb {
margin-bottom: $margin;
}
.gi_ml {
margin-left: $margin;
}
.gi_mr {
margin-right: $margin;
}
.gi_mx {
margin: 0 $margin;
}
.gi_my {
margin: $margin 0;
}
.gi_m0 {
margin: 0;
}
.gi_pt {
padding-top: $margin;
}
.gi_pb {
padding-bottom: $margin;
}
.gi_pl {
padding-left: $margin;
}
.gi_pr {
padding-right: $margin;
}
.gi_px {
padding: 0 $padding;
}
.gi_py {
padding: $padding 0;
}
.gi_p0 {
padding: 0;
}
// сценарий использования, когда содержимое страницы выходит за пределы высоты, происходит автоматическое прокручивание
.gi_page {
flex: 1;
padding: $margin;
box-sizing: border-box;
overflow-y: auto;
}// универсальный контейнер
.gi_box {
background: var(--color-bg-1);
border-radius: $radius-box;
overflow: hidden;
}
```
#### Глобальные переменные SCSS — правила именования
```scss
$color-theme: rgb(var(--primary-6)); // тематический цвет
$color-primary: rgb(var(--primary-6)); // основной цвет
$color-success: rgb(var(--success- Yöntem 6));
$color-warning: rgb(var(--warning-6));
$color-danger: rgb(var(--danger-6));
$color-info: rgb(var(--gray-6));
$title-color: xxx; // устаревший, трудоемкий для запоминания
$text-color: xxx; // устаревший
$text-sub-color: xxx; // устаревший
$text-sup-color: xxx; // устаревший
// заимствованы правила именования Arco Design
$color-text-1: var(--color-text-1); // цвет текста для заголовков и основного текста
$color-text-2: var(--color-text-2); // цвет текста для глобальных текстовых элементов
$color-text-3: var(--color-text-3); // цвет текста для второстепенных текстовых элементов
$color-text-4: var(--color-text-4); // цвет текста для вспомогательных текстовых элементов
```$margin: 16px; // отступы между элементами
$padding: 16px; // отступы между содержимым и границами элемента
Пример:
Позиция 1: Использование глобальной переменной SCSS $margin
Позиция 2: Использование глобальной переменной SCSS $padding
Рекомендуется использовать глобальные переменные SCSS при разработке, что повышает эффективность и улучшает командную работу.
<img src="https://gitee.com/lin0716/gi-image/raw/master/gap.png" />
#### Названия CSS классов
```css
Предыдущий prev
Следующий next
Текущий current
Показать show
Скрыть hide
Открыть open
Закрыть close
```
Внесены исправления в текст, убраны лишние пробелы и исправлены ошибки в описании цветов.Выбранный selected
Активный активный
По умолчанию по умолчанию
Переключить переключить
Отключенный отключенный
Опасный опасный
Основной основной
Успешный успешный
Уведомление уведомление
Предупреждение предупреждение
Ошибка ошибка
Большой большой
Маленький маленький
Очень маленький очень маленький
```
```css
Документ doc
Заголовок header(hd)
Основной контент body
Подвал footer(ft)
Основной блок main
Сайдбар side
Контейнер box/container
```
```css
Список list
Элемент списка item
Таблица table
Форма form
Ссылка link
Заголовок caption/heading/title
Меню menu
Группа group
Строка bar
Контент content
Результат result
```
```css
Кнопка button(btn)
Выпадающее меню dropdown
Инструментальная панель toolbar
Пагинация page
Миниатюра thumbnail
Предупреждение alert
Прогресс progress
Навигационная панель navbar
Навигация nav
Поднавигация subnav
Хлебные крошки breadcrumb(crumb)
Метка label
Бейдж badge
Большой экран jumbotron
Панель panel
Впадина well
Вкладка tab
Подсказка tooltip
Попап popover
Карусель carousel
Аккордион collapse
Фиксация affix
```
```css
Бренд brand
Логотип logo
Дополнительный элемент addon
Авторские права copyright
Регистрация regist(reg)
Вход login
Поиск search
Топ-тема hot
Помощь help
Информация info
Подсказка tips
Переключатель toggle
Новости news
Реклама advertise(ad)
Топ-лист top
Загрузка download
```
```css
Левое выравнивание fl
Правое выравнивание fr
Очистка выравнивания clear
```#### Другие нормы
Для справки можно обратиться к стилю Vue2: https://v2.ru.vuejs.org/v2/style-guide/ , некоторые из которых можно использовать в качестве примера.
Также можно обратиться к исходному коду **Gi Admin Pro**, если у вас есть предложения по улучшению норм, вы можете связаться с автором.
## Связанные с Vue
<a href="https://ru.vuejs.org/" target="_blank">Официальный сайт Vue 3</a>
<a href="https://router.vuejs.org/ru/" target="_blank">Vue-Router</a>
<a href="https://ru.vitejs.dev/" target="_blank">Vite</a>
<a href="https://pinia.web3doc.top/" target="_blank">Pinia</a>
## Рекомендуемые плагины
<a href="https://arco.design/vue/component/button" target="_blank">Компонентная библиотека Arco Design</a>
<a href="https://dayjs.fenxianglu.cn/" target="_blank">Day.js - минималистичная JavaScript библиотека для парсинга, проверки, манипуляции и отображения дат и времени в современных браузерах (2К размер)</a>
<a href="https://www.lodashjs.com/" target="_blank">Lodash - модульная, производительная и последовательная JavaScript библиотека утилит</a>
<a href="https://vxetable.cn/xe-utils/#/" target="_blank">Xe-utils - JavaScript библиотека функций и утилит</a>
<a href="https://vueuse.org/" target="_blank">VueUse - библиотека Vue 3 Hooks</a>
<a href="https://next.attojs.com/" target="_blank">VueRequest - библиотека запросов для Vue</a>
<a href="https://mirari.cc/v-viewer/" target="_blank">V-Viewer - Vue компонент для просмотра изображений на основе viewer.js, поддерживает вращение, масштабирование и другие действия</a>
<a href="https://www.npmjs.com/package/vue-color-kit" target="_blank">Vue-Color-Kit - Vue 3 компонент выбора цвета</a><a href="https://vxetable.cn/#/table/start/install" target="_blank">Vxe-Table</a>
**Другое**
<a href="https://vcalendar.io/" target="_blank">VCalendar - компонент календаря</a>
<a href="https://antoniandre.github.io/vue-cal/" target="_blank">Vue Cal - компонент календаря</a>
<a href="https://alfred-skyblue.github.io/vue-draggable-plus/" target="_blank">VueDraggablePlus - компонент перетаскивания для Vue 2 и Vue 3</a>
## Рекомендуемые книги
<a href="https://vue3.chengpeiquan.com/" target="_blank">Введение в Vue и практические примеры</a>
<a href="https://jkchao.github.io/typescript-book-chinese/" target="_blank">Глубокое понимание TypeScript</a>
<a href="https://vue3js.cn/interview/" target="_blank">Библиотека фронтенд-разработчика</a>
<a href="https://lhammer.cn/You-need-to-know-css/#/translucent-borders" target="_blank">CSS-тактики, которые должен знать веб-разработчик</a>
<a href="https://es6.ruanyifeng.com/#README" target="_blank">Руань Ифэнг ES6</a>
<a href="https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html" target="_blank">Руань Ифэнг flex-разметка</a>
## Сборник открытых проектов
<a href="https://vue-admin.cn/admin" target="_blank">Vue3 сборник открытых проектов</a>
<a href="https://react-admin.cn/admin/" target="_blank">React сборник открытых проектов</a>
## Инструменты
<a href="https://c.runoob.com/" target="_blank">Инструменты для начинающих</a>
<a href="https://carbon.now.sh/?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=material&wt=none&l=auto&width=680&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false" target="_blank">Код в виде изображения</a>
<a href="http://xiets.gitee.io/json-to-any-web/" target="_blank">JSON-TO-ANY преобразование JSON в типы TypeScript</a>
<a href="http://49.234.61.19/tool/cssTriangle" target="_blank">Онлайн-генератор стилей треугольников</a><a href="https://go.itab.link/" target="_blank">iTab</a>
## Поддержка
<a href="https://www.aeoliancloud.com/cart/goods.htm?id=14" target="_blank">Феникс облако (T3 + прямое подключение к серверам по всей стране, среднее время отклика 20 мс) - перейти к просмотру</a>
<img style="width:360px" src="https://gitee.com/lin0716/gi-image/raw/master/adv1.png" />
<img style="width:360px" src="https://gitee.com/lin0716/gi-image/raw/master/sponsor.jpg" />
## Донат
<img style="width:300px" src="https://gitee.com/lin0716/gi-image/raw/master/alipay.jpg" />
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )