Browse Source

feat: 去掉type

gemercheung 7 months ago
parent
commit
e05a838239

+ 4 - 2
packages/backend/src/modules/article/article.entity.ts

@@ -15,10 +15,12 @@ import { Category } from '../category/category.entity';
 import { User } from '../user/user.entity';
 import { User } from '../user/user.entity';
 import { ArticleTranslation } from './article.entity.tranlation';
 import { ArticleTranslation } from './article.entity.tranlation';
 import { TranslatableEntity, Translation } from 'typeorm-translatable';
 import { TranslatableEntity, Translation } from 'typeorm-translatable';
+import { Transform } from 'class-transformer';
+
 @Entity()
 @Entity()
 export class Article extends TranslatableEntity<ArticleTranslation> {
 export class Article extends TranslatableEntity<ArticleTranslation> {
-  @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true })
-  id?: number;
+  @PrimaryGeneratedColumn()
+  id: number;
 
 
   @Column({ unique: false, default: '', length: 200 })
   @Column({ unique: false, default: '', length: 200 })
   title?: string;
   title?: string;

+ 0 - 1
packages/backend/src/modules/web/dto/create-web.dto.ts

@@ -1 +0,0 @@
-export class CreateWebDto {}

+ 6 - 0
packages/backend/src/modules/web/dto/query-web.dto.ts

@@ -0,0 +1,6 @@
+
+export class QueryPublicCategoryDto {
+
+
+
+}

+ 0 - 4
packages/backend/src/modules/web/dto/update-web.dto.ts

@@ -1,4 +0,0 @@
-import { PartialType } from '@nestjs/swagger';
-import { CreateWebDto } from './create-web.dto';
-
-export class UpdateWebDto extends PartialType(CreateWebDto) {}

+ 3 - 13
packages/backend/src/modules/web/web.controller.ts

@@ -7,18 +7,8 @@ import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
 export class WebController {
 export class WebController {
   constructor(private readonly webService: WebService) {}
   constructor(private readonly webService: WebService) {}
 
 
-  @Get()
-  findAll() {
-    return this.webService.findAll();
-  }
-
-  @Get('category/tree')
-  getCategories(@Param('category') category: string) {
-    return this.webService.findAll();
-  }
-
-  @Get(':id')
-  findOne(@Param('id') id: string) {
-    return this.webService.findOne(+id);
+  @Get('menu')
+  getMenus() {
+    return this.webService.findMenuTree();
   }
   }
 }
 }

+ 4 - 0
packages/backend/src/modules/web/web.module.ts

@@ -1,8 +1,12 @@
 import { Module } from '@nestjs/common';
 import { Module } from '@nestjs/common';
 import { WebService } from './web.service';
 import { WebService } from './web.service';
 import { WebController } from './web.controller';
 import { WebController } from './web.controller';
+import { Category } from '@/modules/category/category.entity';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { Menu } from '@/modules/menu/menu.entity';
 
 
 @Module({
 @Module({
+  imports: [TypeOrmModule.forFeature([Menu, Category])],
   controllers: [WebController],
   controllers: [WebController],
   providers: [WebService],
   providers: [WebService],
 })
 })

+ 23 - 18
packages/backend/src/modules/web/web.service.ts

@@ -1,26 +1,31 @@
 import { Injectable } from '@nestjs/common';
 import { Injectable } from '@nestjs/common';
-import { CreateWebDto } from './dto/create-web.dto';
-import { UpdateWebDto } from './dto/update-web.dto';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Like, Repository } from 'typeorm';
+import { Menu } from '@/modules/menu/menu.entity';
 
 
 @Injectable()
 @Injectable()
 export class WebService {
 export class WebService {
-  create(createWebDto: CreateWebDto) {
-    return 'This action adds a new web';
+  constructor(
+    @InjectRepository(Menu)
+    private menuRepo: Repository<Menu>,
+  ) {
   }
   }
 
 
-  findAll() {
-    return `This action returns all web`;
-  }
-
-  findOne(id: number) {
-    return `This action returns a #${id} web`;
-  }
-
-  update(id: number, updateWebDto: UpdateWebDto) {
-    return `This action updates a #${id} web`;
-  }
-
-  remove(id: number) {
-    return `This action removes a #${id} web`;
+  findMenuTree() {
+    return this.menuRepo.find({
+      where: {
+        enable: true,
+      },
+      relations: {
+        children: true,
+        category: true,
+      },
+      select: {
+        category: {
+          id: true,
+          title: true,
+        },
+      },
+    });
   }
   }
 }
 }

+ 23 - 23
packages/frontend/src/views/article/index.vue

@@ -2,7 +2,7 @@
   <CommonPage>
   <CommonPage>
     <template #action>
     <template #action>
       <NButton type="primary" @click="router.push('article/add')">
       <NButton type="primary" @click="router.push('article/add')">
-        <i class="i-material-symbols:add mr-4 text-18" />
+        <i class="i-material-symbols:add mr-4 text-18"/>
         新增文章
         新增文章
       </NButton>
       </NButton>
     </template>
     </template>
@@ -10,7 +10,7 @@
     <MeCrud ref="$table" v-model:query-items="queryItems" :scroll-x="1200" :columns="columns" :get-data="api.read">
     <MeCrud ref="$table" v-model:query-items="queryItems" :scroll-x="1200" :columns="columns" :get-data="api.read">
       <MeQueryItem label="标题" :label-width="50">
       <MeQueryItem label="标题" :label-width="50">
         <n-input v-model:value="queryItems.title" type="text" placeholder="请输入标题名" clearable>
         <n-input v-model:value="queryItems.title" type="text" placeholder="请输入标题名" clearable>
-          <template #password-visible-icon />
+          <template #password-visible-icon/>
         </n-input>
         </n-input>
       </MeQueryItem>
       </MeQueryItem>
       <MeQueryItem label="状态" :label-width="50">
       <MeQueryItem label="状态" :label-width="50">
@@ -26,13 +26,13 @@
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { MeCrud, MeQueryItem } from '@/components'
-import { useCrud } from '@/composables'
-import { formatDateTime } from '@/utils'
-import { NButton, NSwitch } from 'naive-ui'
+import {MeCrud, MeQueryItem} from '@/components'
+import {useCrud} from '@/composables'
+import {formatDateTime} from '@/utils'
+import {NButton, NSwitch} from 'naive-ui'
 import api from './api'
 import api from './api'
 
 
-defineOptions({ name: 'RoleMgt' })
+defineOptions({name: 'RoleMgt'})
 
 
 const router = useRouter()
 const router = useRouter()
 
 
@@ -44,24 +44,24 @@ onMounted(() => {
   $table.value?.handleSearch()
   $table.value?.handleSearch()
 })
 })
 
 
-const { handleDelete }
+const {handleDelete}
   = useCrud({
   = useCrud({
-    name: '文章',
-    doCreate: api.create,
-    doDelete: api.delete,
-    doUpdate: api.update,
-    initForm: { enable: true },
-    refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
-  })
+  name: '文章',
+  doCreate: api.create,
+  doDelete: api.delete,
+  doUpdate: api.update,
+  initForm: {enable: true},
+  refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
+})
 
 
 const columns = [
 const columns = [
-  { title: '标题名', key: 'title', width: '200' },
-  { title: '分类', key: 'category.title' },
+  {title: '标题名', key: 'title', width: '200'},
+  {title: '分类', key: 'category.title'},
   {
   {
     title: '内容',
     title: '内容',
     key: 'content',
     key: 'content',
     width: '400',
     width: '400',
-    render: row => h('div', htmlspecialchars(row.translations?.length ? row.translations[0].content : row.content)),
+    render: row => h('div', htmlspecialchars(row.translations?.length ? row.translations.find(i => i.locale === 'zh').content : row.content)),
   },
   },
   {
   {
     title: '创建时间',
     title: '创建时间',
@@ -107,7 +107,7 @@ const columns = [
           },
           },
           {
           {
             default: () => '编辑',
             default: () => '编辑',
-            icon: () => h('i', { class: 'i-material-symbols:edit-outline text-14' }),
+            icon: () => h('i', {class: 'i-material-symbols:edit-outline text-14'}),
           },
           },
         ),
         ),
 
 
@@ -122,7 +122,7 @@ const columns = [
           },
           },
           {
           {
             default: () => '删除',
             default: () => '删除',
-            icon: () => h('i', { class: 'i-material-symbols:delete-outline text-14' }),
+            icon: () => h('i', {class: 'i-material-symbols:delete-outline text-14'}),
           },
           },
         ),
         ),
       ]
       ]
@@ -133,16 +133,16 @@ const columns = [
 async function handleEnable(row) {
 async function handleEnable(row) {
   row.enableLoading = true
   row.enableLoading = true
   try {
   try {
-    await api.update({ id: row.id, enable: !row.enable })
+    await api.update({id: row.id, enable: !row.enable})
     row.enableLoading = false
     row.enableLoading = false
     $message.success('操作成功')
     $message.success('操作成功')
     $table.value?.handleSearch()
     $table.value?.handleSearch()
-  }
-  catch (error) {
+  } catch (error) {
     console.error(error)
     console.error(error)
     row.enableLoading = false
     row.enableLoading = false
   }
   }
 }
 }
+
 function htmlspecialchars(str) {
 function htmlspecialchars(str) {
   const div = document.createElement('div')
   const div = document.createElement('div')
   div.innerHTML = str
   div.innerHTML = str

+ 16 - 3
packages/frontend/src/views/menu/list.vue

@@ -73,7 +73,12 @@
         </n-form-item>
         </n-form-item>
 
 
         <n-form-item
         <n-form-item
-          label="文章链接" path="articleId"
+          label="文章链接" path="articleId" :rule="{
+            required: false,
+            type: 'number',
+            trigger: ['change', 'blur'],
+            message: '请输入文章链接',
+          }"
         >
         >
           <n-select
           <n-select
             v-model:value="modalForm.articleId" :options="allArticle" clearable filterable tag
             v-model:value="modalForm.articleId" :options="allArticle" clearable filterable tag
@@ -257,6 +262,7 @@ async function handleEnable(row) {
     row.enableLoading = false
     row.enableLoading = false
   }
   }
 }
 }
+
 watchEffect(() => {
 watchEffect(() => {
   if (userId) {
   if (userId) {
     modalForm.value.userId = userId
     modalForm.value.userId = userId
@@ -308,6 +314,7 @@ async function handleFormEdit(data = {}) {
   }
   }
   handleEdit(data)
   handleEdit(data)
 }
 }
+
 function handleCoverRemove() {
 function handleCoverRemove() {
   modalForm.value.cover = ''
   modalForm.value.cover = ''
   previewFileList.value = []
   previewFileList.value = []
@@ -333,7 +340,13 @@ function handleTopMenuEdit() {
 }
 }
 
 
 function getAllType() {
 function getAllType() {
-  categoryApi.getAll().then(({ data = [] }) => (allCategory.value = data.map(item => ({ label: item.title, value: item.id }))))
-  articleApi.getAll().then(({ data = [] }) => (allArticle.value = data.map(item => ({ label: item.title, value: item.id }))))
+  categoryApi.getAll().then(({ data = [] }) => (allCategory.value = data.map(item => ({
+    label: item.title,
+    value: +item.id,
+  }))))
+  articleApi.getAll().then(({ data = [] }) => (allArticle.value = data.map(item => ({
+    label: item.title,
+    value: +item.id,
+  }))))
 }
 }
 </script>
 </script>

+ 3 - 0
packages/web/.env

@@ -0,0 +1,3 @@
+VITE_PUBLIC_PATH = ""
+VITE_AXIOS_BASE_URL = '/api'  # 用于代理
+

+ 12 - 0
packages/web/.env.development

@@ -0,0 +1,12 @@
+# 是否使用Hash路由
+VITE_USE_HASH = 'true'
+
+# 资源公共路径,需要以 /开头和结尾
+VITE_PUBLIC_PATH = '/'
+
+# Axios 基础路径
+VITE_AXIOS_BASE_URL = '/api'  # 用于代理
+# VITE_AXIOS_BASE_URL = 'https://mock.apipark.cn/m1/3776410-0-default'  # apifox云端mock
+
+# 代理配置-target
+VITE_PROXY_TARGET = 'http://localhost:8085'

+ 3 - 2
packages/web/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@4dkankan/helperweb",
   "name": "@4dkankan/helperweb",
-  "description": " xxx",
+  "description": " web show",
   "type": "module",
   "type": "module",
   "version": "0.0.2",
   "version": "0.0.2",
   "private": true,
   "private": true,
@@ -26,7 +26,8 @@
     "@unocss/core": "^65.4.0",
     "@unocss/core": "^65.4.0",
     "@unocss/vite": "^65.4.0",
     "@unocss/vite": "^65.4.0",
     "@vicons/ionicons5": "^0.13.0",
     "@vicons/ionicons5": "^0.13.0",
-    "nativeui": "^0.0.0",
+    "axios": "^1.7.9",
+    "naive-ui": "^2.40.3",
     "unocss": "^0.65.1",
     "unocss": "^0.65.1",
     "vue": "^3.5.13",
     "vue": "^3.5.13",
     "vue-i18n": "^11.0.1",
     "vue-i18n": "^11.0.1",

+ 1 - 0
packages/web/src/api/index.ts

@@ -0,0 +1 @@
+export * from './menu'

+ 11 - 0
packages/web/src/api/menu.ts

@@ -0,0 +1,11 @@
+import { request } from '@/utils/http'
+import type { ResultData } from '@/utils/http'
+
+export type MenuItem = {
+  id: number
+  title: string
+  cover: string
+  children: MenuItem[]
+  grid: number
+}
+export const getMenuList = (): Promise<ResultData<MenuItem[]>> => request.get('web/menu')

+ 59 - 1
packages/web/src/pages/index.vue

@@ -1,8 +1,66 @@
 <template>
 <template>
-  <div class="max-w-screen-xl content my-0 mx-auto">home</div>
+  <div class="max-w-screen-xl content my-0 mx-auto">
+    <div v-for="(item, index) in list" :key="index" class="m-y-[180px]">
+      <n-h1 class="text-center mb-[120px]">{{ item.title }}</n-h1>
+
+      <n-grid x-gap="100" y-gap="100" :cols="item.grid || 3">
+        <n-gi v-for="child of item.children" :key="child.id">
+          <div
+            class="show-item b-rd-3xl relative"
+            :class="{ [`grid-${item.grid}`]: true }"
+            :style="{ backgroundImage: `url(${child.cover})` }"
+          >
+            <div
+              class="w-full h-full flex flex-col absolute top-0 left-0 -mx-auto justify-end overflow-hidden"
+            >
+              <div
+                class="w-full h-[60px] title text-white text-size-2xl flex items-center p-x-10 bg-white bg-op-50"
+              >
+                {{ child.title }}
+              </div>
+            </div>
+          </div>
+          <!--          <n-card class="flex items-center" :title="child.title">-->
+          <!--            <template #cover>-->
+          <!--              <img :src="child.cover" alt="" object-fit="scale-down" />-->
+          <!--            </template>-->
+          <!--            {{ child.title }}-->
+          <!--          </n-card>-->
+        </n-gi>
+      </n-grid>
+    </div>
+  </div>
 </template>
 </template>
 
 
 <route lang="yaml">
 <route lang="yaml">
 meta:
 meta:
 title: About
 title: About
 </route>
 </route>
+
+<script setup lang="ts">
+import { NH1, NCard, NGrid, NGi } from 'naive-ui'
+import { getMenuList } from '@/api'
+import type { MenuItem } from '@/api'
+
+const list = ref<MenuItem[]>([])
+
+getMenuList().then((data) => {
+  if (data.data) {
+    list.value = data.data
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.show-item {
+  cursor: pointer;
+  background-repeat: no-repeat;
+  background-position: center bottom;
+  background-size: cover;
+
+  &.grid-2 {
+    width: 614px;
+    height: 346px;
+  }
+}
+</style>

+ 18 - 0
packages/web/src/pages/showdoc.vue

@@ -0,0 +1,18 @@
+<template>
+  <div class="max-w-screen-xl content my-0 mx-auto">
+    showdoc1
+    showdoc2
+  </div>
+</template>
+
+<route lang="yaml">
+meta:
+title: showdoc
+</route>
+
+<script setup lang="ts">
+
+
+</script>
+
+<style lang="scss" scoped></style>

+ 134 - 0
packages/web/src/utils/http/index.ts

@@ -0,0 +1,134 @@
+import axios from 'axios'
+import type { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
+import { useMessage } from 'naive-ui'
+// 数据返回的接口
+// 定义请求响应参数,不含data
+interface Result {
+  code: number
+  message: string
+}
+
+// 请求响应参数,包含data
+export interface ResultData<T = unknown> extends Result {
+  data?: T
+}
+
+const URL: string = import.meta.env.VITE_AXIOS_BASE_URL
+
+enum RequestEnums {
+  TIMEOUT = 20000,
+  OVERDUE = 600, // 登录失效
+  FAIL = 999, // 请求失败
+  SUCCESS = 200, // 请求成功
+}
+
+const config = {
+  // 默认地址
+  baseURL: URL as string,
+  // 设置超时时间
+  timeout: RequestEnums.TIMEOUT as number,
+  // 跨域时候允许携带凭证
+  withCredentials: true,
+}
+
+class RequestHttp {
+  // 定义成员变量并指定类型
+  service: AxiosInstance
+
+  public constructor(config: AxiosRequestConfig) {
+    // 实例化axios
+    this.service = axios.create(config)
+
+    /**
+     * 请求拦截器
+     * 客户端发送请求 -> [请求拦截器] -> 服务器
+     * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
+     */
+    this.service.interceptors.request.use(
+      (config: AxiosRequestConfig) => {
+        // const token = localStorage.getItem('token') || ''
+        return {
+          ...config,
+          headers: {
+            // 'x-access-token': token, // 请求头中携带token信息
+          },
+        }
+      },
+      (error: AxiosError) => {
+        // 请求报错
+        Promise.reject(error)
+      },
+    )
+
+    /**
+     * 响应拦截器
+     * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
+     */
+    this.service.interceptors.response.use(
+      (response: AxiosResponse) => {
+        const message = useMessage()
+        const { data } = response // 解构
+        if (data.code === RequestEnums.OVERDUE) {
+          // 登录信息失效,应跳转到登录页面,并清空本地的token
+          localStorage.setItem('token', '')
+          // router.replace({
+          //   path: '/login'
+          // })
+          return Promise.reject(data)
+        }
+        // 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
+        if (data.code && data.code !== RequestEnums.SUCCESS) {
+          message.error(data) // 此处也可以使用组件提示报错信息
+          return Promise.reject(data)
+        }
+        return data
+      },
+      (error: AxiosError) => {
+        const message = useMessage()
+        const { response } = error
+        if (response) {
+          this.handleCode(response.status)
+        }
+        if (!window.navigator.onLine) {
+          message.error('网络连接失败')
+          // 可以跳转到错误页面,也可以不做操作
+          // return router.replace({
+          //   path: '/404'
+          // });
+        }
+      },
+    )
+  }
+
+  handleCode(code: number): void {
+    const message = useMessage()
+    switch (code) {
+      case 401:
+        message.error('登录失败,请重新登录')
+        break
+      default:
+        message.error('请求失败')
+        break
+    }
+  }
+
+  // 常用方法封装
+  get<T>(url: string, params?: object): Promise<ResultData<T>> {
+    return this.service.get(url, { params })
+  }
+
+  post<T>(url: string, params?: object): Promise<ResultData<T>> {
+    return this.service.post(url, params)
+  }
+
+  put<T>(url: string, params?: object): Promise<ResultData<T>> {
+    return this.service.put(url, params)
+  }
+
+  delete<T>(url: string, params?: object): Promise<ResultData<T>> {
+    return this.service.delete(url, { params })
+  }
+}
+
+// 导出一个实例对象
+export const request = new RequestHttp(config)

+ 2 - 0
packages/web/src/utils/index.ts

@@ -0,0 +1,2 @@
+export * from './http'
+

+ 1 - 0
packages/web/typed-router.d.ts

@@ -20,5 +20,6 @@ declare module 'vue-router/auto-routes' {
   export interface RouteNamedMap {
   export interface RouteNamedMap {
     '/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
     '/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
     '/about': RouteRecordInfo<'/about', '/about', Record<never, never>, Record<never, never>>,
     '/about': RouteRecordInfo<'/about', '/about', Record<never, never>, Record<never, never>>,
+    '/showdoc': RouteRecordInfo<'/showdoc', '/showdoc', Record<never, never>, Record<never, never>>,
   }
   }
 }
 }

+ 54 - 26
packages/web/vite.config.ts

@@ -1,6 +1,6 @@
 import { fileURLToPath, URL } from 'node:url'
 import { fileURLToPath, URL } from 'node:url'
 
 
-import { defineConfig } from 'vite'
+import { defineConfig,loadEnv } from 'vite'
 import vue from '@vitejs/plugin-vue'
 import vue from '@vitejs/plugin-vue'
 import vueRouter from 'unplugin-vue-router/vite'
 import vueRouter from 'unplugin-vue-router/vite'
 import vueLayouts from 'vite-plugin-vue-layouts'
 import vueLayouts from 'vite-plugin-vue-layouts'
@@ -9,30 +9,58 @@ import autoImports from 'unplugin-auto-import/vite'
 import Unocss from 'unocss/vite'
 import Unocss from 'unocss/vite'
 import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
 import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
 import Components from 'unplugin-vue-components/vite'
 import Components from 'unplugin-vue-components/vite'
-export default defineConfig({
-  plugins: [
-    vueRouter(),
-    vue(),
-    Unocss(),
-    vueLayouts(),
-    vueComponents(),
-    autoImports({
-      imports: ['vue', 'vue-router', 'vue-i18n', {
-        'naive-ui': [
-          'useDialog',
-          'useMessage',
-          'useNotification',
-          'useLoadingBar'
-        ]
-      }],
-    }),
-    Components({
-      resolvers: [NaiveUiResolver()]
-    })
-  ],
-  resolve: {
-    alias: {
-      '@': fileURLToPath(new URL('./src', import.meta.url)),
+
+export default defineConfig(({ mode }) => {
+
+  const viteEnv = loadEnv(mode, process.cwd())
+  const { VITE_PUBLIC_PATH, VITE_PROXY_TARGET } = viteEnv
+
+  return {
+    base: VITE_PUBLIC_PATH || '/',
+    plugins: [
+      vueRouter(),
+      vue(),
+      Unocss(),
+      vueLayouts(),
+      vueComponents(),
+      autoImports({
+        imports: ['vue', 'vue-router', 'vue-i18n', {
+          'naive-ui': [
+            'useDialog',
+            'useMessage',
+            'useNotification',
+            'useLoadingBar'
+          ]
+        }],
+      }),
+      Components({
+        resolvers: [NaiveUiResolver()]
+      })
+    ],
+    resolve: {
+      alias: {
+        '@': fileURLToPath(new URL('./src', import.meta.url)),
+      },
     },
     },
-  },
+    server: {
+      host: '0.0.0.0',
+      port: 3300,
+      open: false,
+      proxy: {
+        '/api': {
+          target: VITE_PROXY_TARGET,
+          changeOrigin: true,
+          rewrite: path => path.replace(/^\/api/, ''),
+          secure: false,
+          // configure: (proxy, options) => {
+          //   // 配置此项可在响应头中看到请求的真实地址
+          //   proxy.on('proxyRes', (proxyRes, req) => {
+          //     proxyRes.headers['x-real-url'] = new URL(req.url || '', options.target)?.href || ''
+          //   })
+          // },
+        },
+      },
+    },
+  }
 })
 })
+

+ 6 - 8
pnpm-lock.yaml

@@ -535,9 +535,12 @@ importers:
       '@vicons/ionicons5':
       '@vicons/ionicons5':
         specifier: ^0.13.0
         specifier: ^0.13.0
         version: 0.13.0
         version: 0.13.0
-      nativeui:
-        specifier: ^0.0.0
-        version: 0.0.0
+      axios:
+        specifier: ^1.7.9
+        version: 1.7.9(debug@4.4.0)
+      naive-ui:
+        specifier: ^2.40.3
+        version: 2.40.4(vue@3.5.13(typescript@5.7.3))
       unocss:
       unocss:
         specifier: ^0.65.1
         specifier: ^0.65.1
         version: 0.65.3(postcss@8.4.49)(rollup@4.29.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.83.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))
         version: 0.65.3(postcss@8.4.49)(rollup@4.29.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.83.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))
@@ -6432,9 +6435,6 @@ packages:
     engines: {node: ^18 || >=20}
     engines: {node: ^18 || >=20}
     hasBin: true
     hasBin: true
 
 
-  nativeui@0.0.0:
-    resolution: {integrity: sha512-IV+bxfIIWqK1CjHCcIvZJW7ISnnplCkD0272CQcnOMiLWlkiOWNlcDfUwWR/fU39KLGfLuRyJDYhX0b94FpX0g==}
-
   natural-compare@1.4.0:
   natural-compare@1.4.0:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
 
 
@@ -15737,8 +15737,6 @@ snapshots:
 
 
   nanoid@5.0.9: {}
   nanoid@5.0.9: {}
 
 
-  nativeui@0.0.0: {}
-
   natural-compare@1.4.0: {}
   natural-compare@1.4.0: {}
 
 
   natural-orderby@5.0.0: {}
   natural-orderby@5.0.0: {}