refactor: remove React dependencies and streamline Vite configuration for Vue application

feat/vapor
tangjinzhou 2025-07-27 15:30:36 +08:00
parent c49a9aa6c3
commit 312f82a5b6
12 changed files with 250 additions and 59 deletions

View File

@ -20,8 +20,6 @@
"@wdns/vue-code-block": "^2.3.3",
"clsx": "^2.1.1",
"cookies": "^0.9.1",
"react": "18",
"react-dom": "18",
"uuid": "^10.0.0",
"vue": "^3.4.34",
"vue-router": "^4.4.0"
@ -35,9 +33,6 @@
"@tailwindcss/vite": "^4.1.3",
"@types/cookies": "^0.9.0",
"@types/node": "^20.0.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"@vitejs/plugin-vue": "^5.1.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^4.1.3",

View File

@ -0,0 +1,29 @@
<template>
<div class="flex h-screen" :class="pageClass">
<TheNavbar v-if="!hideNavbar" :items="navs"></TheNavbar>
<div class="flex-1 justify-center px-4 py-16" :class="contentClass">
<RouterView></RouterView>
</div>
</div>
</template>
<script lang="ts" setup>
import { provideLayoutOptions } from '@/composables/layout'
import { computed, ref } from 'vue'
import TheNavbar from './TheNavbar.vue'
const props = defineProps<{
navs: { name: string; path: string }[]
hideNavbar?: boolean
hideBreadcrumbs?: boolean
}>()
const pageClass = ref<string>()
const contentClass = ref<string>()
provideLayoutOptions({
pageClass,
contentClass,
hideNavbar: computed(() => props.hideNavbar),
hideBreadcrumbs: computed(() => props.hideBreadcrumbs),
})
</script>

View File

@ -0,0 +1,25 @@
<template>
<div class="flex flex-wrap gap-8 px-8">
<RouterLink v-for="item in items" :key="item.path" :to="item.path">
<div
class="shadow-xs relative flex w-72 flex-col rounded-lg bg-primary capitalize text-primary-content transition-all hover:scale-105 hover:shadow-xl"
>
<div className="flex-col gap-2 flex flex-auto p-4 text-sm items-center text-center">
<h2 className="font-semibold flex items-center gap-2 text-xl mb-1">
{{ item.name }}
</h2>
<div className="flex flex-wrap items-start gap-2 justify-end">
<ArrowRightIcon class="size-5" />
</div>
</div>
</div>
</RouterLink>
</div>
</template>
<script lang="ts" setup>
import { ArrowRightIcon } from '@heroicons/vue/20/solid'
defineProps<{
items: { name: string; path: string }[]
}>()
</script>

View File

@ -0,0 +1,29 @@
<template>
<div class="max-w-full select-none overflow-x-auto px-4 py-2 text-sm">
<ul class="flex min-h-min items-center whitespace-nowrap capitalize">
<template v-for="(item, i) in items" :key="item.path">
<li class="flex items-center">
<template v-if="i > 0">
<span
class="ml-2 mr-3 block size-1.5 rotate-45 transform border-r-[1px] border-t-[1px] border-base-content/70 bg-transparent"
></span>
</template>
<template v-if="item.path !== route.path">
<RouterLink class="flex items-center hover:underline" :to="item.path">
{{ item.name }}
</RouterLink>
</template>
<template v-else>
<span class="text-base-content/70">{{ item.name }}</span>
</template>
</li>
</template>
</ul>
</div>
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router'
defineProps<{ items: { name: string; path: string }[] }>()
const route = useRoute()
</script>

View File

@ -0,0 +1,44 @@
<template>
<div class="bg-base-100/90 text-base-content border-base-content/10 border-r">
<div class="flex min-h-16 w-full items-center p-2">
<div class="justify-start">
<div class="group relative inline-block">
<ul
tabindex="0"
class="bg-base-100 z-[1] mt-3 flex w-52 origin-top scale-95 flex-col flex-wrap rounded-lg p-2 text-sm capitalize"
>
<li v-for="item in items" :key="item.name">
<RouterLink
:aria-disabled="item.path === route.path"
:to="item.path"
@click.stop="$event.currentTarget.blur()"
class="hover:bg-base-content/10 flex cursor-pointer flex-col rounded-lg px-3 py-2 transition duration-200"
>
{{ item.name }}
</RouterLink>
<ul v-if="item.children">
<li v-for="child in item.children" :key="child.name">
<RouterLink
:to="child.path"
@click.stop="$event.currentTarget.blur()"
class="hover:bg-base-content/10 flex cursor-pointer flex-col rounded-lg px-3 py-2 text-xs opacity-80 transition duration-200"
>
{{ child.name }}
</RouterLink>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router'
defineProps<{
items: { name: string; path: string; children?: { name: string; path: string }[] }[]
}>()
const route = useRoute()
</script>

View File

@ -0,0 +1,22 @@
import { inject, InjectionKey, provide, Ref } from 'vue'
export interface LayoutOptions {
pageClass: Ref<string | undefined>
contentClass: Ref<string | undefined>
hideNavbar: Ref<boolean>
hideBreadcrumbs: Ref<boolean>
}
const LayoutOptionsToken: InjectionKey<LayoutOptions> = Symbol()
export function provideLayoutOptions(options: LayoutOptions) {
provide(LayoutOptionsToken, options)
}
export function injectLayoutOptions() {
const options = inject(LayoutOptionsToken)
if (!options) {
throw new Error('"injectLayoutOptions" must be called inside pages')
}
return options
}

View File

@ -0,0 +1,9 @@
<template>
<div class="flex flex-col gap-2">
<button class="btn btn-primary">Primary Button</button>
<button class="btn btn-secondary">Default Button</button>
<button class="btn btn-error">Dashed Button</button>
<button class="btn btn-link">Text Button</button>
<button class="btn btn-link">Link Button</button>
</div>
</template>

View File

@ -0,0 +1,12 @@
<template>
<div class="flex flex-col gap-2">
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Default</button>
<button class="btn btn-error">Danger</button>
<button class="btn btn-link">Link</button>
<button class="btn btn-ghost">Ghost</button>
<button class="btn btn-link">Link</button>
<button class="btn btn-link">Link</button>
<button class="btn btn-link">Link</button>
</div>
</template>

View File

@ -1,4 +1,65 @@
import { RouteRecordRaw } from 'vue-router'
import { RouteRecordRaw, RouterView } from 'vue-router'
import BasicLayout from './components/BasicLayout.vue'
import { Fragment, h } from 'vue'
const items = import.meta.glob('./pages/*/index.ts', { import: 'default', eager: true })
export default Object.values(items) as RouteRecordRaw[]
// /pages/button/basic.vue
const items = import.meta.glob('./pages/*/*.vue', { import: 'default', eager: true })
const categoryRoutes: Record<string, RouteRecordRaw[]> = {}
Object.keys(items).forEach(path => {
const route = path.replace('./pages/', '').replace('.vue', '')
const [category, demo] = route.split('/')
if (!categoryRoutes[category]) {
categoryRoutes[category] = []
}
categoryRoutes[category].push({
path: demo,
component: items[path],
})
})
const routes: RouteRecordRaw[] = Object.entries(categoryRoutes).map(([category, children]) => {
const renderComponents = () =>
h(
'div',
children.map(child => h(child.component)),
)
renderComponents.displayName = 'renderComponents'
return {
path: `/${category}`,
component: RouterView,
children: [
...children,
{
path: ':demo*',
component: renderComponents,
},
],
}
})
const navs = Object.keys(categoryRoutes).map(category => ({
name: category,
path: `/${category}`,
children: categoryRoutes[category].map(child => ({
name: child.path,
path: `/${category}/${child.path}`,
})),
}))
routes.push({
path: '/:pathMatch(.*)*',
component: h('div', 'demo not found'),
})
export default [
{
path: '/',
component: BasicLayout,
children: routes,
props: {
navs,
},
},
]

View File

@ -0,0 +1,11 @@
{
"extends": "@ant-design-vue/typescript-config/tsconfig.vue.json",
"include": ["src/**/*.ts", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }],
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
"~/*": ["./assets/*"]
}
}
}

View File

@ -0,0 +1,4 @@
{
"extends": "@ant-design-vue/typescript-config/tsconfig.node.json",
"include": ["vite.config.*"]
}

View File

@ -1,24 +1,11 @@
import tailwindcss from '@tailwindcss/vite'
import react from '@vitejs/plugin-react'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path'
import { defineConfig, Plugin } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
appType: 'mpa',
plugins: [
vue(),
react(),
tailwindcss(),
],
build: {
rollupOptions: {
input: {
main: resolve(__dirname, './index.html'),
},
},
},
plugins: [vue(), tailwindcss()],
server: {
watch: {
ignored: ['!**/node_modules/@ant-design-vue/**'],
@ -31,40 +18,3 @@ export default defineConfig({
},
},
})
interface ImportmapOptions {
imports: Record<string, string>
}
function importmapPlugin(options: ImportmapOptions): Plugin {
return {
name: 'vite-plugin-importmap',
apply: 'build',
config() {
return {
build: {
rollupOptions: {
external: Object.keys(options.imports),
},
},
}
},
transformIndexHtml: {
order: 'pre',
handler(html) {
return {
html,
tags: [
{
tag: 'script',
attrs: {
type: 'importmap',
},
children: JSON.stringify(options),
injectTo: 'head-prepend',
},
],
}
},
},
}
}