1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/lin0716-gi-demo

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.md 59 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 23.04.2025 18:30 b3c91d6

Gi Admin Pro

star

лицензия

Краткое описание

Gi Admin Pro — это бесплатный шаблон для среднего и заднего фронтендов, основанный на Vue3, Vite, TypeScript, Arco Design Vue, Pinia, VueUse и других технологиях. Он использует последнюю стек технологий фронтенда, имеет богатые настройки тем, высокие стандарты кодирования и динамическое отображение данных на основе mock. Шаблон можно использовать сразу после установки, а также использовать для обучения и ссылок.

Значение префикса Gi: G — означает "глобальный", i — означает "моё".

Gi используется для определения префикса глобальных компонентов, таких как GiNavBar, GiTitle, GiLoading.

Основные характеристики

  • Последняя стек технологий: разработан с использованием передовых технологий фронтенда, таких как Vue3 / Vite, и эффективного менеджера пакетов npm.
  • TypeScript: язык программирования на уровне приложения JavaScript.
  • Темы: богатые настройки тем и тема темной темы.
  • Стандарты кодирования: богатые плагины стандартов и высокие стандарты кодирования.

Примеры| Платформа | Адрес для просмотра |

| ----------------- | ---------------------------------------------------------- | | 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

Установка плагинов VS Code

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 файле

Обычно, количество строк в одном .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>

Название API

Вариант 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' // Необходимо указывать конкретный модуль

Импорт типов для API

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'

Запись вызова API

Вариант 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 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/lin0716-gi-demo.git
git@api.gitlife.ru:oschina-mirror/lin0716-gi-demo.git
oschina-mirror
lin0716-gi-demo
lin0716-gi-demo
master