Compare commits

...
28 Commits
Author SHA1 Message Date
Hooray e057f01edb chore: release v6.1.0 2026-05-10 19:42:54 +08:00
Hooray b4b68d78ea chore: 更新依赖 2026-05-10 19:41:28 +08:00
Hooray 1b0dc3b947 refactor!: 因上游 vite-plugin-fake-server 插件问题,暂时移动 fake 文件目录 2026-05-10 19:40:33 +08:00
Hooray 244e6cb4fa fix: 修复登录页“记住我”无法勾选 2026-05-10 14:43:57 +08:00
Hooray ded93e90b7 chore: 更新依赖 2026-05-10 03:46:41 +08:00
Hooray 70b29edb6e feat: 扩展 FaInput 组件的 type 属性 2026-05-06 17:41:00 +08:00
Hooray fc2c89fa2d feat: 更新 FaDrawer 和 FaModal 组件,强制关闭自带遮罩 2026-04-30 21:47:22 +08:00
Hooray 5ba6d08df3 feat: 新增 FaCheckboxGrou 组件,并重构 FaCheckbox API 2026-04-29 16:15:10 +08:00
Hooray c0bc31f7f4 refactor!: 移除 FaRadioGroup 组件的 orientation 属性 2026-04-29 15:46:36 +08:00
Hooray 871dd5c56f docs: 统一组件文档格式 2026-04-29 00:57:54 +08:00
Hooray caedad8e72 style: 组件示例代码格式调整 2026-04-29 00:47:14 +08:00
Hooray bb3bd479b8 feat: 新增 FaRadioGroup 组件 2026-04-29 00:46:48 +08:00
Hooray 931b10c1f3 style: 调整标准模块列表分割线的边距样式 2026-04-26 17:38:40 +08:00
Hooray 5e2558ac05 style: FaPageMain 组件样式调整 2026-04-26 11:25:14 +08:00
HoorayandCopilot 81beda6e56 feat: 添加 FaSelect 组件的定位模式支持
Co-authored-by: Copilot <copilot@github.com>
2026-04-25 21:56:26 +08:00
HoorayandCopilot 1d86ef87d3 feat: 主题色调整
Co-authored-by: Copilot <copilot@github.com>
2026-04-23 23:57:18 +08:00
Hooray d0dc9275b3 refactor: 重命名组件和版权相关的导入及函数,以统一命名规范 2026-04-22 18:44:02 +08:00
Hooray 1ccbd6402b feat: 将生成图标脚本提取到公用子包 2026-04-20 17:45:52 +08:00
Hooray 84eb9b7a39 fix: 修复icon生成脚本错误 2026-04-20 16:31:49 +08:00
Hooray 5e023d9055 chore: 添加 Antdv Next 组件库友链 2026-04-20 16:09:50 +08:00
Hooray cf4a7eae7d fix: 修复 FaDrawer 和 FaModal 组件开启时警告错误 2026-04-19 21:48:44 +08:00
Hooray 42b7ab33c6 chore: 更新 README 中 logo 展示 2026-04-19 21:09:19 +08:00
Hooray 85eefe75bb refactor: 修改 @fantastic-admin/components 子包自动导入配置 2026-04-19 04:42:49 +08:00
Hooray 0bcea9d3c8 refactor: 迁移 composables 到 @fantastic-admin/composables 子包 2026-04-19 04:39:58 +08:00
Hooray 38e6abab21 feat: FaImageUpload 组件增加入场、出场、排序动画 2026-04-19 02:39:21 +08:00
Hooray 1c81115605 feat: FaImageUpload / FaFileUpload 增加粘贴上传功能 2026-04-18 14:04:12 +08:00
Hooray e55e94743f feat: 增强 dev 脚本匹配逻辑,支持以 'dev:' 开头的脚本名称 2026-04-17 22:23:03 +08:00
Hooray efb0886e10 chore: 更新readme 2026-04-17 17:02:42 +08:00
244 changed files with 4496 additions and 3859 deletions
+4 -55
View File
@@ -1,5 +1,5 @@
[中文](./readme.md) | **English**
<img src="https://fantastic-admin.hurui.me/logo.svg" align="right" width="80" height="80" alt="logo" />
<a href="https://fantastic-admin.hurui.me" target="_blank"><img src="https://fantastic-admin.hurui.me/logo.svg" align="right" height="80" alt="logo" /></a>
# Fantastic-admin
@@ -56,57 +56,6 @@ If you find Fantastic-admin useful, or you are already using it, please consider
## Ecosystem
<table>
<tr>
<th colspan="3" align="center">
<a href="https://hooray.github.io/fantastic-startkit/" target="_blank">Fantastic-startkit</a>
</th>
</tr>
<tr>
<th colspan="3" align="center">
A simple and easy-to-use Vue 3 project starter kit
</th>
</tr>
</table>
<table>
<tr>
<th colspan="3" align="center">
<a href="https://one-step-admin.hurui.me" target="_blank">One-step-admin</a>
</th>
</tr>
<tr>
<th colspan="3" align="center">
A Vue admin framework built to help you move faster than the rest
</th>
</tr>
<tr>
<td><img src="https://one-step-admin.hurui.me/preview1.png" alt="" /></td>
<td><img src="https://one-step-admin.hurui.me/preview2.png" alt="" /></td>
<td><img src="https://one-step-admin.hurui.me/preview3.png" alt="" /></td>
</tr>
<tr>
<td><img src="https://one-step-admin.hurui.me/preview4.png" alt="" /></td>
<td><img src="https://one-step-admin.hurui.me/preview5.png" alt="" /></td>
<td><img src="https://one-step-admin.hurui.me/preview6.png" alt="" /></td>
</tr>
</table>
<table>
<tr>
<th colspan="4" align="center">
<a href="https://fantastic-mobile.hurui.me" target="_blank">Fantastic-mobile</a>
</th>
</tr>
<tr>
<th colspan="4" align="center">
A distinctive mobile H5 framework
</th>
</tr>
<tr>
<td><img src="https://fantastic-mobile.hurui.me/preview1.png" alt="" /></td>
<td><img src="https://fantastic-mobile.hurui.me/preview2.png" alt="" /></td>
<td><img src="https://fantastic-mobile.hurui.me/preview3.png" alt="" /></td>
<td><img src="https://fantastic-mobile.hurui.me/preview4.png" alt="" /></td>
</tr>
</table>
- [`Fantastic-startkit`](https://hooray.github.io/fantastic-startkit/) - A simple and easy-to-use Vue 3 project starter kit
- [`Fantastic-mobile`](https://fantastic-mobile.hurui.me/) - Let your H5 project have a solid engineering base
- [`One-step-admin`](https://one-step-admin.hurui.me) - A Vue admin framework built to help you move faster than the rest
+1 -1
View File
@@ -1,5 +1,5 @@
**中文** | [English](./README.EN.md)
<img src="https://fantastic-admin.hurui.me/logo.svg" align="right" width="80" height="80" alt="logo" />
<a href="https://fantastic-admin.hurui.me" target="_blank"><img src="https://fantastic-admin.hurui.me/logo.svg" align="right" height="80" alt="logo" /></a>
# Fantastic-admin
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -49,9 +50,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -49,9 +50,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -49,9 +50,9 @@
},
"devDependencies": {
"@arco-design/web-vue": "catalog:arco-design-vue",
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -48,9 +49,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -49,9 +50,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
-108
View File
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -49,9 +50,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,11 +9,12 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@vee-validate/zod": "catalog:",
@@ -49,9 +50,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -66,7 +67,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
-108
View File
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
-46
View File
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
+1 -1
View File
@@ -87,7 +87,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
+4
View File
@@ -24,6 +24,7 @@ declare module 'vue' {
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -48,6 +49,7 @@ declare module 'vue' {
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
@@ -75,6 +77,7 @@ declare global {
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
@@ -99,6 +102,7 @@ declare global {
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
+10 -7
View File
@@ -1,7 +1,8 @@
import type { PluginOption } from 'vite'
import process from 'node:process'
import { FantasticAutoImports, FantasticComponentsResolver, FantasticComponentsType } from '@fantastic-admin/components/resolver'
import { createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import { ComponentsAutoImports as FantasticAdminComponentsAutoImports, ComponentsResolver as FantasticAdminComponentsResolver, ComponentsType as FantasticAdminComponentsType } from '@fantastic-admin/components/resolver'
import { ComposablesAutoImports as FantasticAdminComposablesAutoImports } from '@fantastic-admin/composables/resolver'
import { createCopyrightPlugins as createFantasticAdminCopyrightPlugins } from '@fantastic-admin/copyright'
import VitePluginSvgSpritemap from '@spiriit/vite-plugin-svg-spritemap'
import vueLegacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'
@@ -37,6 +38,7 @@ export default function createVitePlugins(mode: string, isBuild = false) {
launchEditor: viteEnv.VITE_LAUNCH_EDITOR,
}),
// https://github.com/yue1123/vite-plugin-env-parse
envParse({
dtsPath: 'src/types/env.d.ts',
}),
@@ -47,7 +49,8 @@ export default function createVitePlugins(mode: string, isBuild = false) {
'vue',
'vue-router',
'pinia',
FantasticAutoImports,
FantasticAdminComponentsAutoImports,
FantasticAdminComposablesAutoImports,
],
dts: './src/types/auto-imports.d.ts',
dirs: [
@@ -63,10 +66,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
],
dts: './src/types/components.d.ts',
resolvers: [
FantasticComponentsResolver(),
FantasticAdminComponentsResolver(),
],
types: [
FantasticComponentsType,
FantasticAdminComponentsType,
],
}),
@@ -78,9 +81,10 @@ export default function createVitePlugins(mode: string, isBuild = false) {
// https://github.com/condorheroblog/vite-plugin-fake-server
vitePluginFakeServer({
logger: !isBuild,
include: 'src/api/modules',
include: 'src/api/fake_modules',
enableProd: isBuild && viteEnv.VITE_BUILD_FAKE,
}),
// https://github.com/nonzzz/vite-plugin-compression
viteEnv.VITE_BUILD_COMPRESS && compression({
exclude: [/\.(br)$/, /\.(gz)$/],
@@ -105,7 +109,6 @@ export default function createVitePlugins(mode: string, isBuild = false) {
(() => {
const moduleId = 'virtual:fantastic-admin/turbo-console'
const resolvedModuleId = `\0${moduleId}`
return {
name: 'vite-plugin-turbo-console-bridge',
resolveId(id) {
+3 -3
View File
@@ -9,7 +9,7 @@
"serve": "http-server ./dist -o",
"serve:test": "http-server ./dist-test -o",
"svgo": "svgo -f src/assets/icons",
"generate:icons": "tsx ./scripts/generate.icons.ts",
"generate:icons": "fa-iconify-tools",
"lint": "vue-tsc -b"
},
"dependencies": {
@@ -17,6 +17,7 @@
"@bytemd/plugin-gfm": "catalog:",
"@bytemd/vue-next": "catalog:",
"@fantastic-admin/components": "workspace:*",
"@fantastic-admin/composables": "workspace:*",
"@fantastic-admin/settings": "workspace:*",
"@fantastic-admin/types": "workspace:*",
"@tinymce/tinymce-vue": "catalog:",
@@ -69,9 +70,9 @@
"zod": "catalog:"
},
"devDependencies": {
"@clack/prompts": "catalog:",
"@faker-js/faker": "catalog:",
"@fantastic-admin/copyright": "workspace:*",
"@fantastic-admin/iconify-tools": "workspace:*",
"@iconify/vue": "catalog:",
"@spiriit/vite-plugin-svg-spritemap": "catalog:",
"@types/nprogress": "catalog:",
@@ -87,7 +88,6 @@
"postcss-nested": "catalog:",
"sass-embedded": "catalog:",
"svgo": "catalog:",
"tsx": "catalog:",
"unplugin-auto-import": "catalog:",
"unplugin-turbo-console": "catalog:",
"unplugin-vue-components": "catalog:",
-108
View File
@@ -1,108 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import * as p from '@clack/prompts'
import { lookupCollection, lookupCollections } from '@iconify/json'
// 拿到全部图标集的原始数据
const raw = await lookupCollections()
let lastChoose = fs.readFileSync(path.resolve(process.cwd(), 'src/iconify/index.json'), 'utf-8')
lastChoose = JSON.parse(lastChoose)
// 取出可使用的图标集数据用于选择,并按名称排序
const collections = Object.entries(raw).map(([id, item]) => ({
...item,
id,
})).sort((a, b) => a.name.localeCompare(b.name))
p.intro('图标集生成工具')
/**
* 分别会在对应目录下生成以下文件,其中(1)(3)用于离线下载并安装图标,(2)用于图标选择器使用
* (1) src/iconify/index.json 记录用户交互信息
* (2) src/iconify/data.json 包含多个图标集数据,仅记录图标名
* (3) public/icons/*-raw.json 多个图标集的原始数据,独立存放,用于离线使用
*/
const answers = await p.group(
{
collections: () =>
p.multiselect({
message: '请选择需要生成的图标集',
options: collections.map(item => ({
label: item.name,
value: item.id,
hint: `${item.total} 个图标`,
})),
initialValues: lastChoose.collections,
}),
isOfflineUse: () =>
p.confirm({
message: '是否需要离线使用',
initialValue: false,
}),
},
{
onCancel: () => {
p.cancel('操作已取消')
process.exit(0)
},
},
)
const spinner = p.spinner()
spinner.start('正在生成图标集...')
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/index.json'),
{
collections: answers.collections,
isOfflineUse: answers.isOfflineUse,
},
)
const outputDir = path.resolve(process.cwd(), 'public/icons')
await fs.ensureDir(outputDir)
await fs.emptyDir(outputDir)
const collectionsMeta: object[] = []
for (const info of answers.collections) {
const setData = await lookupCollection(info)
collectionsMeta.push({
prefix: setData.prefix,
info: setData.info,
icons: Object.keys(setData.icons),
})
const offlineFilePath = path.join(outputDir, `${info}-raw.json`)
if (answers.isOfflineUse) {
await fs.writeJSON(offlineFilePath, setData)
}
}
await fs.writeJSON(
path.resolve(process.cwd(), 'src/iconify/data.json'),
collectionsMeta,
)
// 使用 spawn 运行 eslint
const eslint = spawn('eslint', ['src/iconify/data.json', 'src/iconify/index.json', '--cache', '--fix'], {
stdio: 'inherit',
shell: true,
})
eslint.on('error', (err) => {
p.log.error(`ESLint 执行失败: ${err.message}`)
process.exit(1)
})
eslint.on('close', (code) => {
if (code !== 0) {
p.log.error(`ESLint 执行失败,退出码: ${code}`)
process.exit(code)
}
spinner.stop('图标集生成完成!')
})
@@ -113,7 +113,7 @@ function testAccount(account: string) {
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
<FormItem>
<FormControl>
<FaCheckbox v-bind="componentField">
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
记住我
</FaCheckbox>
</FormControl>
@@ -1,46 +0,0 @@
export function usePagination() {
const pagination = ref({
page: 1,
size: 10,
total: 0,
sort: null as string | null,
order: null as string | null,
})
function getParams() {
return {
// page,size 和 from,limit 是分页的两种方式,方便兼容两种接口
page: pagination.value.page,
size: pagination.value.size,
from: (pagination.value.page - 1) * pagination.value.size,
limit: pagination.value.size,
...(
pagination.value.sort && pagination.value.order && {
sort: pagination.value.sort,
order: pagination.value.order,
}
),
}
}
async function onSizeChange(size: number) {
pagination.value.size = size
}
async function onCurrentChange(page: number) {
pagination.value.page = page
}
async function onSortChange(prop: string, order: string) {
pagination.value.sort = prop
pagination.value.order = order === 'ascending' ? 'asc' : 'desc'
}
return {
pagination,
getParams,
onSizeChange,
onCurrentChange,
onSortChange,
}
}
File diff suppressed because one or more lines are too long
@@ -53,7 +53,7 @@ function handleCopy() {
'columns-2': appSettingsStore.mode === 'pc',
}"
>
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="主题" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
颜色方案
@@ -83,7 +83,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.theme.colorAmblyopia" />
</div>
</FaPageMain>
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain v-if="appSettingsStore.mode === 'pc'" title="导航菜单" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="menu-mode">
<FaTooltip text="侧边栏模式 (有主导航菜单)" :delay="500">
<FaButton variant="outline" class="mode mode-side" :class="{ active: appSettingsStore.settings.menu.mode === 'side' }" @click="appSettingsStore.settings.menu.mode = 'side'">
@@ -145,7 +145,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.menu.hotkeys" :disabled="appSettingsStore.settings.menu.mode === 'single'" />
</div>
</FaPageMain>
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="顶栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
标签栏
@@ -175,7 +175,7 @@ function handleCopy() {
</FaButtonGroup>
</div>
</FaPageMain>
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="标签栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
显示图标
@@ -189,7 +189,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.tabbar.hotkeys" />
</div>
</FaPageMain>
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="工具栏" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div v-if="appSettingsStore.mode === 'pc'" class="setting-item">
<div class="label">
<FaIcon name="i-ic:twotone-double-arrow" />
@@ -237,7 +237,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.toolbar.colorScheme" />
</div>
</FaPageMain>
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="页面" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="setting-item">
<div class="label">
载入进度条
@@ -248,7 +248,7 @@ function handleCopy() {
<FaSwitch v-model="appSettingsStore.settings.page.progress" />
</div>
</FaPageMain>
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="pb-0 border-none font-bold" main-class="space-y-4">
<FaPageMain title="应用" class="m-0 mb-4 break-inside-avoid light:border-none" title-class="font-bold" main-class="space-y-4">
<div class="p-4 pb-4 pt-14 border rounded-lg relative space-y-4">
<div class="font-bold px-4 py-2 border-b border-e rounded-rb-lg inset-s-0 inset-t-0 absolute">
账号
@@ -53,6 +53,14 @@ const routes: RouteRecordRaw = {
title: '复选框',
},
},
{
path: 'checkbox_group',
name: 'componentExampleCheckboxGroup',
component: () => import('@/views/component_example/checkbox_group/index.vue'),
meta: {
title: '复选框组',
},
},
{
path: 'collapsible',
name: 'componentExampleCollapsible',
@@ -222,6 +230,14 @@ const routes: RouteRecordRaw = {
title: '进度条',
},
},
{
path: 'radio_group',
name: 'componentExampleRadioGroup',
component: () => import('@/views/component_example/radio_group/index.vue'),
meta: {
title: '单选组',
},
},
{
path: 'scroll_area',
name: 'componentExampleScrollArea',
@@ -95,6 +95,16 @@ const routes: RouteRecordRaw[] = [
link: 'https://github.com/mineadmin/mineadmin',
},
},
{
path: 'antdv-next',
redirect: '',
name: 'recommandAntdvNext',
meta: {
title: 'Antdv Next 组件库',
icon: 'https://www.antdv-next.com/antdv-next.svg',
link: 'https://www.antdv-next.com/',
},
},
],
},
]
+1 -1
View File
@@ -88,7 +88,7 @@ declare global {
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const usePagination: typeof import('../composables/pagination').usePagination
const usePagination: typeof import('@fantastic-admin/composables').usePagination
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
Vendored Executable → Regular
+5 -77
View File
@@ -19,82 +19,46 @@ declare module 'vue' {
AppNotAllowed: typeof import('./../components/AppNotAllowed/index.vue')['default']
AppNotSupportedMobile: typeof import('./../components/AppNotSupportedMobile/index.vue')['default']
AppSystemInfo: typeof import('./../components/AppSystemInfo/index.vue')['default']
FaAnimatedBeam: typeof import('@fantastic-admin/components')['FaAnimatedBeam']
FaAnimatedCountTo: typeof import('@fantastic-admin/components')['FaAnimatedCountTo']
FaAnimatedCountToGroup: typeof import('@fantastic-admin/components')['FaAnimatedCountToGroup']
FaAvatar: typeof import('@fantastic-admin/components')['FaAvatar']
FaBlurReveal: typeof import('@fantastic-admin/components')['FaBlurReveal']
FaBorderBeam: typeof import('@fantastic-admin/components')['FaBorderBeam']
FaButton: typeof import('@fantastic-admin/components')['FaButton']
FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
FaCard: typeof import('@fantastic-admin/components')['FaCard']
FaCarousel: typeof import('@fantastic-admin/components')['FaCarousel']
FaCascader: typeof import('@fantastic-admin/components')['FaCascader']
FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
FaCode: typeof import('@fantastic-admin/components')['FaCode']
FaCodePreview: typeof import('@fantastic-admin/components')['FaCodePreview']
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaCountTo: typeof import('@fantastic-admin/components')['FaCountTo']
FaDigitalCard: typeof import('@fantastic-admin/components')['FaDigitalCard']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
FaEmpty: typeof import('@fantastic-admin/components')['FaEmpty']
FaFileUpload: typeof import('@fantastic-admin/components')['FaFileUpload']
FaFixedBar: typeof import('@fantastic-admin/components')['FaFixedBar']
FaFlipCard: typeof import('@fantastic-admin/components')['FaFlipCard']
FaFlipWords: typeof import('@fantastic-admin/components')['FaFlipWords']
FaGlowyCard: typeof import('@fantastic-admin/components')['FaGlowyCard']
FaGlowyCardWrapper: typeof import('@fantastic-admin/components')['FaGlowyCardWrapper']
FaGradientButton: typeof import('@fantastic-admin/components')['FaGradientButton']
FaHoverCard: typeof import('@fantastic-admin/components')['FaHoverCard']
FaIcon: typeof import('@fantastic-admin/components')['FaIcon']
FaIconPicker: typeof import('@fantastic-admin/components')['FaIconPicker']
FaImagePreview: typeof import('@fantastic-admin/components')['FaImagePreview']
FaImageUpload: typeof import('@fantastic-admin/components')['FaImageUpload']
FaInput: typeof import('@fantastic-admin/components')['FaInput']
FaInputOTP: typeof import('@fantastic-admin/components')['FaInputOTP']
FaInteractiveButton: typeof import('@fantastic-admin/components')['FaInteractiveButton']
FaKbd: typeof import('@fantastic-admin/components')['FaKbd']
FaKbdGroup: typeof import('@fantastic-admin/components')['FaKbdGroup']
FaLabel: typeof import('@fantastic-admin/components')['FaLabel']
FaLayoutContainer: typeof import('@fantastic-admin/components')['FaLayoutContainer']
FaLinkPreview: typeof import('@fantastic-admin/components')['FaLinkPreview']
FaLoading: typeof import('@fantastic-admin/components')['FaLoading']
FaMarquee: typeof import('@fantastic-admin/components')['FaMarquee']
FaModal: typeof import('@fantastic-admin/components')['FaModal']
FaMultiStepLoader: typeof import('@fantastic-admin/components')['FaMultiStepLoader']
FaNumberField: typeof import('@fantastic-admin/components')['FaNumberField']
FaPageHeader: typeof import('@fantastic-admin/components')['FaPageHeader']
FaPageMain: typeof import('@fantastic-admin/components')['FaPageMain']
FaPagination: typeof import('@fantastic-admin/components')['FaPagination']
FaParticlesBg: typeof import('@fantastic-admin/components')['FaParticlesBg']
FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
FaPatternBg: typeof import('@fantastic-admin/components')['FaPatternBg']
FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
FaQrcode: typeof import('@fantastic-admin/components')['FaQrcode']
FaScratchOff: typeof import('@fantastic-admin/components')['FaScratchOff']
FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
FaScrollingText: typeof import('@fantastic-admin/components')['FaScrollingText']
FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
FaSlider: typeof import('@fantastic-admin/components')['FaSlider']
FaSmoothSwipe: typeof import('@fantastic-admin/components')['FaSmoothSwipe']
FaSparklesText: typeof import('@fantastic-admin/components')['FaSparklesText']
FaSparkline: typeof import('@fantastic-admin/components')['FaSparkline']
FaSpotlightCard: typeof import('@fantastic-admin/components')['FaSpotlightCard']
FaStorageBox: typeof import('@fantastic-admin/components')['FaStorageBox']
FaSwitch: typeof import('@fantastic-admin/components')['FaSwitch']
FaTabs: typeof import('@fantastic-admin/components')['FaTabs']
FaTextarea: typeof import('@fantastic-admin/components')['FaTextarea']
FaTextHighlight: typeof import('@fantastic-admin/components')['FaTextHighlight']
FaTimeAgo: typeof import('@fantastic-admin/components')['FaTimeAgo']
FaTimeline: typeof import('@fantastic-admin/components')['FaTimeline']
FaToast: typeof import('@fantastic-admin/components')['FaToast']
FaTooltip: typeof import('@fantastic-admin/components')['FaTooltip']
FaTree: typeof import('@fantastic-admin/components')['FaTree']
FaTrend: typeof import('@fantastic-admin/components')['FaTrend']
}
}
@@ -108,81 +72,45 @@ declare global {
const AppNotAllowed: typeof import('./../components/AppNotAllowed/index.vue')['default']
const AppNotSupportedMobile: typeof import('./../components/AppNotSupportedMobile/index.vue')['default']
const AppSystemInfo: typeof import('./../components/AppSystemInfo/index.vue')['default']
const FaAnimatedBeam: typeof import('@fantastic-admin/components')['FaAnimatedBeam']
const FaAnimatedCountTo: typeof import('@fantastic-admin/components')['FaAnimatedCountTo']
const FaAnimatedCountToGroup: typeof import('@fantastic-admin/components')['FaAnimatedCountToGroup']
const FaAvatar: typeof import('@fantastic-admin/components')['FaAvatar']
const FaBlurReveal: typeof import('@fantastic-admin/components')['FaBlurReveal']
const FaBorderBeam: typeof import('@fantastic-admin/components')['FaBorderBeam']
const FaButton: typeof import('@fantastic-admin/components')['FaButton']
const FaButtonGroup: typeof import('@fantastic-admin/components')['FaButtonGroup']
const FaCard: typeof import('@fantastic-admin/components')['FaCard']
const FaCarousel: typeof import('@fantastic-admin/components')['FaCarousel']
const FaCascader: typeof import('@fantastic-admin/components')['FaCascader']
const FaCheckbox: typeof import('@fantastic-admin/components')['FaCheckbox']
const FaCode: typeof import('@fantastic-admin/components')['FaCode']
const FaCodePreview: typeof import('@fantastic-admin/components')['FaCodePreview']
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaCountTo: typeof import('@fantastic-admin/components')['FaCountTo']
const FaDigitalCard: typeof import('@fantastic-admin/components')['FaDigitalCard']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
const FaEmpty: typeof import('@fantastic-admin/components')['FaEmpty']
const FaFileUpload: typeof import('@fantastic-admin/components')['FaFileUpload']
const FaFixedBar: typeof import('@fantastic-admin/components')['FaFixedBar']
const FaFlipCard: typeof import('@fantastic-admin/components')['FaFlipCard']
const FaFlipWords: typeof import('@fantastic-admin/components')['FaFlipWords']
const FaGlowyCard: typeof import('@fantastic-admin/components')['FaGlowyCard']
const FaGlowyCardWrapper: typeof import('@fantastic-admin/components')['FaGlowyCardWrapper']
const FaGradientButton: typeof import('@fantastic-admin/components')['FaGradientButton']
const FaHoverCard: typeof import('@fantastic-admin/components')['FaHoverCard']
const FaIcon: typeof import('@fantastic-admin/components')['FaIcon']
const FaIconPicker: typeof import('@fantastic-admin/components')['FaIconPicker']
const FaImagePreview: typeof import('@fantastic-admin/components')['FaImagePreview']
const FaImageUpload: typeof import('@fantastic-admin/components')['FaImageUpload']
const FaInput: typeof import('@fantastic-admin/components')['FaInput']
const FaInputOTP: typeof import('@fantastic-admin/components')['FaInputOTP']
const FaInteractiveButton: typeof import('@fantastic-admin/components')['FaInteractiveButton']
const FaKbd: typeof import('@fantastic-admin/components')['FaKbd']
const FaKbdGroup: typeof import('@fantastic-admin/components')['FaKbdGroup']
const FaLabel: typeof import('@fantastic-admin/components')['FaLabel']
const FaLayoutContainer: typeof import('@fantastic-admin/components')['FaLayoutContainer']
const FaLinkPreview: typeof import('@fantastic-admin/components')['FaLinkPreview']
const FaLoading: typeof import('@fantastic-admin/components')['FaLoading']
const FaMarquee: typeof import('@fantastic-admin/components')['FaMarquee']
const FaModal: typeof import('@fantastic-admin/components')['FaModal']
const FaMultiStepLoader: typeof import('@fantastic-admin/components')['FaMultiStepLoader']
const FaNumberField: typeof import('@fantastic-admin/components')['FaNumberField']
const FaPageHeader: typeof import('@fantastic-admin/components')['FaPageHeader']
const FaPageMain: typeof import('@fantastic-admin/components')['FaPageMain']
const FaPagination: typeof import('@fantastic-admin/components')['FaPagination']
const FaParticlesBg: typeof import('@fantastic-admin/components')['FaParticlesBg']
const FaPasswordStrength: typeof import('@fantastic-admin/components')['FaPasswordStrength']
const FaPatternBg: typeof import('@fantastic-admin/components')['FaPatternBg']
const FaPopover: typeof import('@fantastic-admin/components')['FaPopover']
const FaProgress: typeof import('@fantastic-admin/components')['FaProgress']
const FaQrcode: typeof import('@fantastic-admin/components')['FaQrcode']
const FaScratchOff: typeof import('@fantastic-admin/components')['FaScratchOff']
const FaRadioGroup: typeof import('@fantastic-admin/components')['FaRadioGroup']
const FaScrollArea: typeof import('@fantastic-admin/components')['FaScrollArea']
const FaScrollingText: typeof import('@fantastic-admin/components')['FaScrollingText']
const FaSearchBar: typeof import('@fantastic-admin/components')['FaSearchBar']
const FaSelect: typeof import('@fantastic-admin/components')['FaSelect']
const FaSlider: typeof import('@fantastic-admin/components')['FaSlider']
const FaSmoothSwipe: typeof import('@fantastic-admin/components')['FaSmoothSwipe']
const FaSparklesText: typeof import('@fantastic-admin/components')['FaSparklesText']
const FaSparkline: typeof import('@fantastic-admin/components')['FaSparkline']
const FaSpotlightCard: typeof import('@fantastic-admin/components')['FaSpotlightCard']
const FaStorageBox: typeof import('@fantastic-admin/components')['FaStorageBox']
const FaSwitch: typeof import('@fantastic-admin/components')['FaSwitch']
const FaTabs: typeof import('@fantastic-admin/components')['FaTabs']
const FaTextarea: typeof import('@fantastic-admin/components')['FaTextarea']
const FaTextHighlight: typeof import('@fantastic-admin/components')['FaTextHighlight']
const FaTimeAgo: typeof import('@fantastic-admin/components')['FaTimeAgo']
const FaTimeline: typeof import('@fantastic-admin/components')['FaTimeline']
const FaToast: typeof import('@fantastic-admin/components')['FaToast']
const FaTooltip: typeof import('@fantastic-admin/components')['FaTooltip']
const FaTree: typeof import('@fantastic-admin/components')['FaTree']
const FaTrend: typeof import('@fantastic-admin/components')['FaTrend']
}
}
@@ -5,10 +5,8 @@ import Demo1 from './_demo1.vue'
<template>
<div>
<FaPageHeader title="头像" description="FaAvatar" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
</div>
</template>
@@ -5,10 +5,8 @@ import Demo1 from './_demo1.vue'
<template>
<div>
<FaPageHeader title="按钮" description="FaButton" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
</div>
</template>
@@ -9,30 +9,20 @@ import Demo5 from './_demo5.vue'
<template>
<div>
<FaPageHeader title="按钮组" description="FaButtonGroup" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
<FaPageMain title="垂直" main-class="p-0">
<div class="p-4">
<Demo2 />
</div>
<FaPageMain title="垂直" main-class="p-4">
<Demo2 />
</FaPageMain>
<FaPageMain title="分割线" main-class="p-0">
<div class="p-4">
<Demo3 />
</div>
<FaPageMain title="分割线" main-class="p-4">
<Demo3 />
</FaPageMain>
<FaPageMain title="嵌套" main-class="p-0">
<div class="p-4">
<Demo4 />
</div>
<FaPageMain title="嵌套" main-class="p-4">
<Demo4 />
</FaPageMain>
<FaPageMain title="与其他组件组合" main-class="p-0">
<div class="p-4">
<Demo5 />
</div>
<FaPageMain title="与其他组件组合" main-class="p-4">
<Demo5 />
</FaPageMain>
</div>
</template>
@@ -5,10 +5,8 @@ import Demo1 from './_demo1.vue'
<template>
<div>
<FaPageHeader title="卡片" description="FaCard" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
</div>
</template>
+1 -1
View File
@@ -5,7 +5,7 @@ const checked = ref(false)
<template>
<div class="flex gap-4">
<FaCheckbox v-model="checked">
Checkbox
同意协议
</FaCheckbox>
</div>
</template>
+1 -1
View File
@@ -4,6 +4,6 @@ const checked = ref<'indeterminate' | boolean>('indeterminate')
<template>
<FaCheckbox v-model="checked">
Checkbox
部分选中
</FaCheckbox>
</template>
+4 -8
View File
@@ -6,15 +6,11 @@ import Demo2 from './_demo2.vue'
<template>
<div>
<FaPageHeader title="复选框" description="FaCheckbox" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
<FaPageMain title="半选状态" main-class="p-0">
<div class="p-4">
<Demo2 />
</div>
<FaPageMain title="半选状态" main-class="p-4">
<Demo2 />
</FaPageMain>
</div>
</template>
@@ -0,0 +1,26 @@
<script setup lang="ts">
const value = ref(['music'])
const options = [
{ label: '阅读', value: 'reading' },
{ label: '音乐', value: 'music' },
{ label: '运动', value: 'sports' },
]
const currentText = computed(() =>
options
.filter(option => value.value.includes(option.value))
.map(option => option.label)
.join('、'),
)
</script>
<template>
<div class="gap-4 grid">
<FaCheckboxGroup v-model="value" :options="options" />
<FaCheckboxGroup v-model="value" :options="options" class="flex" />
<div class="text-sm text-muted-foreground">
当前值:{{ currentText }}
</div>
</div>
</template>
@@ -0,0 +1,38 @@
<script setup lang="ts">
const value = ref(['dashboard', 'report'])
const options = [
{
label: '看板订阅',
value: 'dashboard',
description: '每天 8:00 推送核心经营指标。',
},
{
label: '周报摘要',
value: 'report',
description: '每周一汇总关键数据变化。',
},
{
label: '系统告警',
value: 'alarm',
description: '异常波动时立即通知处理人。',
disabled: true,
},
]
const currentText = computed(() =>
options
.filter(option => value.value.includes(option.value))
.map(option => option.label)
.join('、'),
)
</script>
<template>
<div class="gap-4 grid">
<FaCheckboxGroup v-model="value" :options="options" />
<div class="text-sm text-muted-foreground">
当前值:{{ currentText }}
</div>
</div>
</template>
@@ -0,0 +1,68 @@
<script setup lang="ts">
const value = ref(['focus', 'balanced'])
const options = [
{
label: '专注模式',
value: 'focus',
description: '突出主任务,弱化辅助信息,适合录入和审批场景。',
},
{
label: '平衡模式',
value: 'balanced',
description: '信息密度与可读性保持平衡,适合作为默认配置。',
},
{
label: '高密度模式',
value: 'dense',
description: '在大屏中同时承载更多信息,适合运营与监控看板。',
},
]
const currentText = computed(() =>
options
.filter(option => value.value.includes(option.value))
.map(option => option.label)
.join('、'),
)
</script>
<template>
<FaCheckboxGroup
v-model="value"
:options="options"
class="gap-2 md:grid-cols-3"
option-class="rounded-xl border border-transparent px-1 py-1"
>
<template #option="{ option, checked, disabled }">
<div
class="px-4 py-3 border rounded-xl flex gap-3 w-full transition-colors items-start justify-between" :class="[
checked ? 'border-primary bg-primary/5' : 'border-border hover:border-primary/40',
disabled && 'opacity-60',
]"
>
<div class="gap-1 grid min-w-0">
<div class="text-sm font-medium truncate">
{{ option.label }}
</div>
<div
v-if="option.description"
class="text-xs text-muted-foreground leading-5"
>
{{ option.description }}
</div>
</div>
<span
class="text-xs font-medium shrink-0" :class="[
checked ? 'text-primary' : 'text-muted-foreground',
]"
>
{{ checked ? '已选中' : '可选择' }}
</span>
</div>
</template>
</FaCheckboxGroup>
<div class="text-sm text-muted-foreground">
当前模式:{{ currentText }}
</div>
</template>
@@ -0,0 +1,33 @@
<script setup lang="ts">
const value = ref(['dashboard'])
const options = [
{ label: '看板订阅', value: 'dashboard' },
{ label: '周报摘要', value: 'report' },
{ label: '系统告警', value: 'alarm' },
]
const currentText = computed(() =>
options
.filter(option => value.value.includes(option.value))
.map(option => option.label)
.join('、'),
)
</script>
<template>
<div class="gap-4 grid">
<FaCheckboxGroup
v-model="value"
:options="options"
:min="1"
:max="2"
/>
<div class="text-sm text-muted-foreground">
当前值:{{ currentText }}
</div>
<div class="text-xs text-muted-foreground leading-5">
已选数量小于等于 1 时,已选项会被禁用;已选数量大于等于 2 时,未选项会被禁用。
</div>
</div>
</template>
@@ -0,0 +1,24 @@
<script setup lang="ts">
import Demo1 from './_demo1.vue'
import Demo2 from './_demo2.vue'
import Demo3 from './_demo3.vue'
import Demo4 from './_demo4.vue'
</script>
<template>
<div>
<FaPageHeader title="复选框组" description="FaCheckboxGroup" />
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
<FaPageMain title="带描述和禁用态" main-class="p-4">
<Demo2 />
</FaPageMain>
<FaPageMain title="自定义选项内容" main-class="p-4">
<Demo3 />
</FaPageMain>
<FaPageMain title="最少 / 最多选择" main-class="p-4">
<Demo4 />
</FaPageMain>
</div>
</template>
@@ -6,15 +6,11 @@ import Demo2 from './_demo2.vue'
<template>
<div>
<FaPageHeader title="折叠面板" description="FaCollapsible" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
<FaPageMain title="手动控制" main-class="p-0">
<div class="p-4">
<Demo2 />
</div>
<FaPageMain title="手动控制" main-class="p-4">
<Demo2 />
</FaPageMain>
</div>
</template>
@@ -6,15 +6,11 @@ import Demo2 from './_demo2.vue'
<template>
<div>
<FaPageHeader title="右键菜单" description="FaContextMenu" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
<FaPageMain title="嵌套" main-class="p-0">
<div class="p-4">
<Demo2 />
</div>
<FaPageMain title="嵌套" main-class="p-4">
<Demo2 />
</FaPageMain>
</div>
</template>
@@ -5,10 +5,8 @@ import Demo1 from './_demo1.vue'
<template>
<div>
<FaPageHeader title="分割线" description="FaDivider" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
</div>
</template>
@@ -6,15 +6,11 @@ import Demo2 from './_demo2.vue'
<template>
<div>
<FaPageHeader title="抽屉" description="FaDrawer" />
<FaPageMain main-class="p-0">
<div class="p-4">
<Demo1 />
</div>
<FaPageMain main-class="p-4">
<Demo1 />
</FaPageMain>
<FaPageMain title="函数式调用" main-class="p-0">
<div class="p-4">
<Demo2 />
</div>
<FaPageMain title="函数式调用" main-class="p-4">
<Demo2 />
</FaPageMain>
</div>
</template>

Some files were not shown because too many files have changed in this diff Show More