Установка UI
Установка клиентской части — последовательность: подключить приватный npm-реестр, поставить зависимости, создать конфигурацию Vite и точку входа, скачать шрифт локально, прописать стили и локализацию, собрать проект.
Настройте npm
Добавьте в .npmrc проекта instance-level-адрес реестра — он отдаёт все пакеты scope @frame (в т.ч. опциональные модули @frame/permissions, @frame/notes, @frame/action-events):
@frame:registry=https://git.qubeek.io/api/v4/packages/npm/
//git.qubeek.io/api/v4/packages/npm/:_authToken=<ваш-токен>
.npmrc в .gitignore — файл содержит секрет.auth.json). Выдаётся службой поддержки.Пропишите зависимости
Замените содержимое package.json:
{
"scripts": {
"build": "vite build",
"dev": "vite",
"dev:frame": "vite --config vite-frame.config.js",
"build:frame": "vite build --config vite-frame.config.js"
},
"devDependencies": {
"@frame/vue": "^0.0.13",
"@tailwindcss/vite": "^4.1.4",
"@types/node": "^24.1.0",
"@vitejs/plugin-vue": "^6.0.5",
"chokidar": "^4.0.3",
"laravel-vite-plugin": "^2.1.0",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.4",
"typescript": "^5.8.3",
"vite": "^7.1.11",
"vue": "^3.5.13",
"vue-tsc": "^3.0.4"
}
}
Создайте конфигурацию Vite
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import tailwindcss from '@tailwindcss/vite'
import vue from '@vitejs/plugin-vue'
const group_chunks = [
'vue-forms',
'vue-table',
'vue-charts',
'd3',
'tres',
'date-fns',
'vueuse',
'lodash-es',
'dicebear',
'lottie',
'tabler',
'tanstack',
'simplewebauthn',
]
export default defineConfig({
allowedHosts: true,
server: {
cors: {
origin: /https?:\/\/([A-Za-z0-9-.]+)?(localhost|\.test|\.lan)(?::\d+)?$/,
},
},
plugins: [
vue({
template: {
transformAssetUrls: {
includeAbsolute: false,
},
},
}),
tailwindcss(),
laravel({
input: ['resources/js/frame.ts'],
buildDirectory: 'frame',
refresh: true,
}),
],
build: {
rollupOptions: {
output: {
manualChunks: (id) => {
let outputId = null
group_chunks.map((i) => {
if (id.includes(i)) {
outputId = i
}
})
if (outputId) {
return outputId
}
},
},
},
},
})
Создайте точку входа
import { createApp } from 'vue'
// @ts-ignore
import { Frame, FramePlugin } from '@frame/vue'
import '../css/frame.css'
import locale from '../../lang/ru.json'
const app = createApp(Frame)
app.use(FramePlugin, {
path: '/',
endpoint: '/api/frame',
appName: 'My Application',
appLogoPath: '/images/logo.png',
i18n: locale,
})
app.mount('#app')
Полный перечень опций FramePlugin и их PHP-эквивалентов — в разделе Конфигурация приложения.
Скачайте шрифт локально
По умолчанию Frame использует Onest. Чтобы избавиться от зависимости от Google Fonts, положите variable-версию рядом с приложением в public/fonts/onest/:
mkdir -p public/fonts/onest && cd public/fonts/onest
curl -sfL "https://fonts.gstatic.com/s/onest/v9/gNMKW3F-SZuj7xmS-HYoEoeyxMI.woff2" -o onest-cyrillic-ext.woff2
curl -sfL "https://fonts.gstatic.com/s/onest/v9/gNMKW3F-SZuj7xmb-HYoEoeyxMI.woff2" -o onest-cyrillic.woff2
curl -sfL "https://fonts.gstatic.com/s/onest/v9/gNMKW3F-SZuj7xmR-HYoEoeyxMI.woff2" -o onest-latin-ext.woff2
curl -sfL "https://fonts.gstatic.com/s/onest/v9/gNMKW3F-SZuj7xmf-HYoEoey.woff2" -o onest-latin.woff2
v9) variable-шрифта из Google Fonts. Актуальный список можно получить запросом CSS с современным User-Agent:curl -A "Mozilla/5.0 ... Chrome" "https://fonts.googleapis.com/css2?family=Onest:wght@100..900&display=swap"
Добавьте стили
@import 'tailwindcss' source(none);
@import '../../node_modules/@frame/core/styles/theme.css';
@import '../../node_modules/@frame/vue/dist/vue.css';
@config '../../node_modules/@frame/core/dist/tailwind.config.js';
@source '../../node_modules/@frame/core';
@source '../../node_modules/@frame/vue';
@source '../../node_modules/@frame/vue-table';
@source '../../node_modules/@frame/vue-charts';
@source '../../node_modules/@frame/vue-forms';
@source '../../node_modules/@frame/vue-uploader';
@source '../js';
@font-face {
font-family: 'Onest';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/onest/onest-cyrillic-ext.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
font-family: 'Onest';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/onest/onest-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Onest';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/onest/onest-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Onest';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/onest/onest-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
:root {
--font-family: 'Onest', ui-sans-serif, system-ui, sans-serif;
}
html,
body {
font-family: var(--font-family), sans-serif;
}
@source '../js' подхватывает ваши кастомные компоненты для Tailwind. Чтобы подключить свой шрифт — положите woff2-файлы в public/fonts/<family>/ и замените блоки @font-face. Для смены бренд-цвета и тёмной темы см. Стилизация темы и Дизайн-токены.Настройте локализацию
Скопируйте базовый файл переводов из вендора:
cp vendor/frame/frame/packages/laravel/resources/lang/ru.json lang/ru.json
При подключении модулей (frame/permissions, frame/notes, frame/action-events) слейте их переводы в объект i18n:
import locale from '../../lang/ru.json'
import permissionsLocale from '../../vendor/frame/permissions/resources/lang/ru.json'
app.use(FramePlugin, {
i18n: { ...locale, ...permissionsLocale },
// ...
})
Соберите проект
Удалите node_modules и package-lock.json, затем переустановите зависимости и соберите:
rm -rf node_modules package-lock.json && npm install --legacy-peer-deps
npm run build:frame
--legacy-peer-deps нужен, потому что @tailwindcss/vite отстаёт от последних версий vite по peerDependencies. После выравнивания апстримом флаг можно убрать.Кастомизация
Название, логотип, локаль и пути задаются через FramePlugin (JS) или через Frame\Core\Config\Config в сервис-провайдере (PHP). PHP имеет приоритет над JS. Подробно — в разделе Конфигурация приложения.
Что дальше
- Конфигурация приложения
- Модули: роли и права, журнал событий
- Стилизация темы и Дизайн-токены
- Переопределение полей — регистрация кастомных полей через
FieldRegistry