feat: 优化路由加载

master
绝弹 2023-12-17 23:16:59 +08:00
parent 43487136fb
commit 943ec54aed
28 changed files with 309 additions and 209 deletions

2
.env
View File

@ -30,3 +30,5 @@ VITE_PROXY = https://appnify.app.juetan.cn/
VITE_OPENAPI = http://127.0.0.1:3030/openapi.json VITE_OPENAPI = http://127.0.0.1:3030/openapi.json
# 文件后缀 说明设为dev时会优先加载index.dev.vue文件否则回退至index.vue文件 # 文件后缀 说明设为dev时会优先加载index.dev.vue文件否则回退至index.vue文件
VITE_EXTENSION = dev VITE_EXTENSION = dev
# 是否以离线模式启动,跳过登录
VITE_OFFLINE = true

View File

@ -1,9 +1,9 @@
import { Button, ButtonInstance, FormInstance, Message, Modal } from '@arco-design/web-vue'; import { Button, ButtonInstance, FormInstance, Message, Modal } from '@arco-design/web-vue';
import { useVModel } from '@vueuse/core';
import { InjectionKey, PropType, Ref } from 'vue'; import { InjectionKey, PropType, Ref } from 'vue';
import { getModel, setModel } from '../utils/useFormModel';
import { AnForm, AnFormInstance, AnFormSubmit } from './Form'; import { AnForm, AnFormInstance, AnFormSubmit } from './Form';
import { AnFormItemProps } from './FormItem'; import { AnFormItemProps } from './FormItem';
import { useVModel } from '@vueuse/core';
import { getModel, setModel } from '../utils/useFormModel';
export interface AnFormModalContext { export interface AnFormModalContext {
visible: Ref<boolean>; visible: Ref<boolean>;
@ -210,6 +210,8 @@ export const AnFormModal = defineComponent({
<Modal <Modal
titleAlign="start" titleAlign="start"
closable={false} closable={false}
maskAnimationName=""
modalAnimationName=""
{...this.modalProps} {...this.modalProps}
v-model:visible={this.visible} v-model:visible={this.visible}
class="an-form-modal" class="an-form-modal"

View File

@ -154,7 +154,7 @@ function useTableButtonColumn(column: TableButtonColumn & TableColumnData) {
} }
return ( return (
<> <>
{index !== 0 && <Divider direction="vertical" margin={2} />} {index !== 0 && <Divider direction="vertical" margin={4} />}
<Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}> <Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}>
{{ {{
default: () => item.text, default: () => item.text,

View File

@ -10,7 +10,7 @@
</div> </div>
<slot name="content"> <slot name="content">
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto" type="track"> <a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto" type="track">
<div class="m-4 p-4 bg-white rounded overflow-hidden"> <div class="m-4 bg-white rounded overflow-hidden" :class="{ 'p-4': contentPadding }">
<slot></slot> <slot></slot>
</div> </div>
</a-scrollbar> </a-scrollbar>
@ -19,7 +19,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import BreadCrumb from "./bread-crumb.vue"; import BreadCrumb from './bread-crumb.vue';
defineProps({
contentPadding: {
type: Boolean,
default: true,
},
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -1,5 +1,12 @@
<template> <template>
<a-modal v-model:visible="show" :fullscreen="true" :footer="false" class="ani-modal"> <a-modal
v-model:visible="show"
mask-animation-name=""
modal-animation-name=""
:fullscreen="true"
:footer="false"
class="ani-modal"
>
<div class="w-full h-full bg-slate-100 grid grid-rows-[auto_1fr] select-none"> <div class="w-full h-full bg-slate-100 grid grid-rows-[auto_1fr] select-none">
<div class="h-13 bg-white border-b border-slate-200 z-10"> <div class="h-13 bg-white border-b border-slate-200 z-10">
<EditorHeader <EditorHeader
@ -41,17 +48,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { delConfirm, sleep } from '@/utils';
import { Message } from '@arco-design/web-vue';
import { useVModel } from '@vueuse/core';
import { Block, ContextMenuItem, EditorKey, useEditor } from '../core'; import { Block, ContextMenuItem, EditorKey, useEditor } from '../core';
import ContextMenu from './ContextMenu.vue';
import EditorSetting from './EditorConfig.vue';
import EditorHeader from './EditorHeader.vue'; import EditorHeader from './EditorHeader.vue';
import EditorLeft from './EditorLeft.vue'; import EditorLeft from './EditorLeft.vue';
import EditorMain from './EditorMain.vue'; import EditorMain from './EditorMain.vue';
import EditorRight from './EditorRight.vue';
import EditorPreview from './EditorPreview.vue'; import EditorPreview from './EditorPreview.vue';
import EditorSetting from './EditorConfig.vue'; import EditorRight from './EditorRight.vue';
import ContextMenu from './ContextMenu.vue';
import { delConfirm, sleep } from '@/utils';
import { useVModel } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -85,7 +92,7 @@ const blockMenuItems: ContextMenuItem[] = [
await delConfirm({ await delConfirm({
content: '确定删除该组件吗?', content: '确定删除该组件吗?',
okText: '确定删除', okText: '确定删除',
}) });
if (blockMenu.block) { if (blockMenu.block) {
rmBlock(blockMenu.block); rmBlock(blockMenu.block);
} }

View File

@ -1,17 +1,17 @@
<template> <template>
<div class="h-full flex items-center justify-between pl-2 pr-4"> <div class="h-full flex items-center justify-between pl-2 pr-4">
<div class="text-base group"> <div class="text-base flex items-center group">
<a-link @click="emit('exit')"> <a-link @click="emit('exit')">
<template #icon> <template #icon>
<i class="icon-park-outline-left"></i> <i class="icon-park-outline-left"></i>
</template> </template>
返回 返回
</a-link> </a-link>
<a-divider :direction="'vertical'" :margin="4"></a-divider> <a-divider :direction="'vertical'" :margin="8"></a-divider>
<a-tag :color="container.id ? 'blue' : 'green'" class="mr-2 ml-1">
{{ container.id ? '修改' : '新增' }}
</a-tag>
<ani-texter v-model="container.title"></ani-texter> <ani-texter v-model="container.title"></ani-texter>
<!-- <a-tag :color="container.id ? 'blue' : 'green'" class="mr-2 ml-1">
{{ container.id ? '修改' : '新增' }}
</a-tag> -->
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<a-button @click="emit('preview')"> <a-button @click="emit('preview')">

View File

@ -1,10 +1,11 @@
<template> <template>
<span v-if="!descEditing"> <span v-if="!descEditing" class="inline-block leading-[32px] h-8">
{{ modelValue }} {{ modelValue }}
<i <a-button type="text" size="small" shape="round" class="!hidden !group-hover:inline-block !text-gray-500" @click="onDescEdit">
class="!hidden !group-hover:inline-block icon-park-outline-edit text-gray-400 hover:text-gray-700 ml-1 cursor-pointer" <template #icon>
@click="onDescEdit" <i class="icon-park-outline-edit"></i>
></i> </template>
</a-button>
</span> </span>
<span v-else class="inline-flex items-center"> <span v-else class="inline-flex items-center">
<a-input size="small" v-model="descContent" class="!w-96" v-bind="inputProps"></a-input> <a-input size="small" v-model="descContent" class="!w-96" v-bind="inputProps"></a-input>
@ -22,8 +23,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from "vue";
import { Input } from "@arco-design/web-vue"; import { Input } from "@arco-design/web-vue";
import { PropType } from "vue";
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {

View File

@ -25,29 +25,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from "vue-router"; import { useRouter } from 'vue-router';
import Image404 from "./image-404.svg?raw"; import Image404 from './image-404.svg?raw';
defineOptions({ name: "AllUncatchedPage" }); defineOptions({ name: 'AllUncatchedPage' });
const router = useRouter(); const router = useRouter();
</script> </script>
<style scoped> <style scoped></style>
.slide-in-bottom {
animation: slide-in-bottom 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
@keyframes slide-in-bottom {
0% {
transform: translateY(100px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
</style>
<route lang="json"> <route lang="json">
{ {

View File

@ -1,9 +1,9 @@
<script lang="tsx"> <script lang="tsx">
import { MenuItem } from "@/router"; import { MenuItem } from '@/router';
import { useMenuStore } from "@/store/menu"; import { useMenuStore } from '@/store/menu';
export default defineComponent({ export default defineComponent({
name: "LayoutMenu", name: 'LayoutMenu',
setup() { setup() {
const selectedKeys = ref<string[]>([]); const selectedKeys = ref<string[]>([]);
const route = useRoute(); const route = useRoute();
@ -13,21 +13,21 @@ export default defineComponent({
watch( watch(
() => route.path, () => route.path,
() => { () => {
selectedKeys.value = route.matched.map((i) => i.path); selectedKeys.value = route.matched.map(i => i.path);
}, },
{ immediate: true } { immediate: true }
); );
function goto(route: MenuItem) { function goto(route: MenuItem) {
if (route.external) { if (route.external) {
window.open(route.path, "_blank"); window.open(route.path, '_blank');
return; return;
} }
router.push(route.path); router.push(route.path);
} }
function renderItem(routes: MenuItem[]) { function renderItem(routes: MenuItem[]) {
return routes.map((route) => { return routes.map(route => {
const icon = route.icon ? () => <i class={route.icon} /> : null; const icon = route.icon ? () => <i class={route.icon} /> : null;
const node: any = route.children?.length ? ( const node: any = route.children?.length ? (
<> <>
@ -38,8 +38,13 @@ export default defineComponent({
</> </>
) : ( ) : (
<a-menu-item key={route.path} v-slots={{ icon }} onClick={() => goto(route)}> <a-menu-item key={route.path} v-slots={{ icon }} onClick={() => goto(route)}>
{route.title} <div class="flex items-center justify-between gap-2 pr-4">
{false && <span class="text-xs text-slate-400 ml-2">({route.sort})</span>} <div>{route.title}</div>
<div class="text-xs text-gray-400">
{/* <a-badge count={8}>8</a-badge> */}
{ route.only === 'dev' ? '仅开发' : null }
</div>
</div>
</a-menu-item> </a-menu-item>
); );
return node; return node;
@ -47,7 +52,7 @@ export default defineComponent({
} }
return () => ( return () => (
<a-menu style={{ width: "100%" }} selectedKeys={selectedKeys.value} autoOpenSelected={true} levelIndent={0}> <a-menu style={{ width: '100%' }} selectedKeys={selectedKeys.value} autoOpenSelected={true} levelIndent={0}>
{renderItem(menuStore.menus)} {renderItem(menuStore.menus)}
</a-menu> </a-menu>
); );

View File

@ -44,7 +44,7 @@
<a-divider :margin="4"></a-divider> <a-divider :margin="4"></a-divider>
<a-doption @click="logout"> <a-doption @click="logout">
<template #icon> <template #icon>
<i class="icon-park-outline-logout"></i> <i class="icon-park-outline-power"></i>
</template> </template>
退出 退出
</a-doption> </a-doption>
@ -75,7 +75,7 @@ const logout = async () => {
const { component: PasswordModal, open } = useFormModal({ const { component: PasswordModal, open } = useFormModal({
title: '修改密码', title: '修改密码',
trigger: false, trigger: false,
width: 452, width: 500,
items: [ items: [
{ {
field: 'password', field: 'password',
@ -84,6 +84,7 @@ const { component: PasswordModal, open } = useFormModal({
setterProps: { setterProps: {
placeholder: '请输入原密码', placeholder: '请输入原密码',
}, },
required: true,
}, },
{ {
field: 'password1', field: 'password1',
@ -92,14 +93,16 @@ const { component: PasswordModal, open } = useFormModal({
setterProps: { setterProps: {
placeholder: '请输入新密码', placeholder: '请输入新密码',
}, },
required: true,
}, },
{ {
field: 'password2', field: 'password2',
label: '确认密码', label: '确认密码',
setter: 'input', setter: 'input',
setterProps: { setterProps: {
placeholder: '请再次输入新密码', placeholder: '请再次输入新密码',
}, },
required: true,
}, },
], ],
}); });

View File

@ -4,12 +4,18 @@
class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700" class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700"
> >
<div class="h-13 flex items-center"> <div class="h-13 flex items-center">
<!-- <a-button size="small" @click="isCollapsed = !isCollapsed">
<template #icon>
<i class="icon-park-outline-hamburger-button text-base"></i>
</template>
</a-button> -->
<router-link to="/" class="px-2 flex items-center gap-2 text-slate-700"> <router-link to="/" class="px-2 flex items-center gap-2 text-slate-700">
<img src="/favicon.ico" alt="" width="24" height="24" class="" /> <img src="/favicon.ico" alt="" width="24" height="24" class="" />
<h1 class="relative text-[22px] leading-[22px] dark:text-white m-0 p-0 font-normal"> <h1 class="relative text-[18px] leading-[22px] dark:text-white m-0 p-0 font-normal">
{{ appStore.title }} {{ appStore.title }}
<span class="absolute -right-10 -top-1 font-normal text-xs text-gray-400"> v0.0.1 </span> <span class="absolute -right-10 -top-1 font-normal text-xs text-gray-400"> v0.0.1 </span>
</h1> </h1>
<!-- <span class="text-gray-400">{{ appStore.subtitle }}</span> -->
</router-link> </router-link>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@ -41,7 +47,7 @@
<a-layout-sider <a-layout-sider
class="h-full overflow-hidden dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700" class="h-full overflow-hidden dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700"
:width="224" :width="224"
:collapsed-width="52" :collapsed-width="49"
:collapsible="true" :collapsible="true"
:collapsed="isCollapsed" :collapsed="isCollapsed"
:hide-trigger="false" :hide-trigger="false"
@ -112,6 +118,13 @@ const buttons = [
window.open('https://github.com/appnify/starter-vue', '_blank'); window.open('https://github.com/appnify/starter-vue', '_blank');
}, },
}, },
{
icon: 'icon-park-outline-info',
tooltip: '关于',
onClick: () => {
window.open('https://github.com/appnify/starter-vue', '_blank');
},
},
]; ];
</script> </script>

View File

@ -85,7 +85,7 @@ const updateFileCategories = async () => {
loading.value = true; loading.value = true;
const res = await api.fileCategory.getFileCategorys({ size: 0 }); const res = await api.fileCategory.getFileCategorys({ size: 0 });
list.value = res.data.data ?? []; list.value = res.data.data ?? [];
list.value.length && emit('change', list.value[0]); list.value.length && emit('change', {});
} catch { } catch {
// nothing to do // nothing to do
} finally { } finally {

View File

@ -14,15 +14,15 @@
<a-modal v-model:visible="show" :footer="false"></a-modal> <a-modal v-model:visible="show" :footer="false"></a-modal>
</template> </template>
<template v-else> <template v-else>
<a-modal v-model:visible="show" title="预览" title-align="start" :closable="false" :width="420"> <a-modal v-model:visible="show" title="预览" title-align="start" mask-animation-name="" modal-animation-name="" :closable="false" :width="420">
抱歉此文件类型暂不支持预览! 抱歉此文件类型暂不支持预览!
</a-modal> </a-modal>
</template> </template>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from 'vue';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { PropType } from 'vue';
type FileType = 'text' | 'audio' | 'image' | 'video' | 'unknown'; type FileType = 'text' | 'audio' | 'image' | 'video' | 'unknown';

View File

@ -9,7 +9,9 @@
title="上传文件" title="上传文件"
title-align="start" title-align="start"
v-model:visible="visible" v-model:visible="visible"
:width="940" mask-animation-name=""
modal-animation-name=""
:width="960"
:mask-closable="false" :mask-closable="false"
:on-before-cancel="onBeforeCancel" :on-before-cancel="onBeforeCancel"
@close="onClose" @close="onClose"
@ -27,7 +29,12 @@
@error="onUploadError" @error="onUploadError"
> >
<template #upload-button> <template #upload-button>
<a-button type="primary"> 选择文件 </a-button> <a-button type="primary">
<template #icon>
<i class="icon-park-outline-upload-one"></i>
</template>
选择
</a-button>
</template> </template>
</a-upload> </a-upload>
<div class="flex-1 flex items-center text-gray-400"> <div class="flex-1 flex items-center text-gray-400">
@ -38,7 +45,7 @@
</div> </div>
</div> </div>
<div class="h-[424px] border-t border-b border-zinc-100 mt-3"> <div class="h-[424px] border-t border-b border-zinc-100 mt-4">
<ul v-if="fileList.length" class="overflow-hidden p-0 m-0"> <ul v-if="fileList.length" class="overflow-hidden p-0 m-0">
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto pr-[20px] divide-y"> <a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto pr-[20px] divide-y">
<li v-for="item in fileList" :key="item.uid" class="flex items-center gap-4 py-3"> <li v-for="item in fileList" :key="item.uid" class="flex items-center gap-4 py-3">
@ -99,7 +106,6 @@
</div> </div>
</div> </div>
<template #footer> <template #footer>
<div class="flex justify-between gap-2 items-center"> <div class="flex justify-between gap-2 items-center">
<div class="text-gray-400">已上传 {{ stat.doneCount }}/{{ fileList.length }} </div> <div class="text-gray-400">已上传 {{ stat.doneCount }}/{{ fileList.length }} </div>

View File

@ -22,8 +22,8 @@ import { useCreateColumn, useTable, useTableDelete, useUpdateColumn } from '@/co
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import numeral from 'numeral'; import numeral from 'numeral';
import AnGroup from './components/AnGroup.vue'; import AnGroup from './components/AnGroup.vue';
import AnUpload from './components/AnUpload.vue';
import AnPreview from './components/AnPreview.vue'; import AnPreview from './components/AnPreview.vue';
import AnUpload from './components/AnUpload.vue';
import { getIcon } from './components/util'; import { getIcon } from './components/util';
const current = ref<FileCategory>(); const current = ref<FileCategory>();
@ -111,10 +111,12 @@ const {
onClick: props => { onClick: props => {
window.open(props.record.path, '_blank'); window.open(props.record.path, '_blank');
}, },
icon: 'icon-park-outline-download',
}, },
{ {
type: 'modify', type: 'modify',
text: '修改', text: '修改',
icon: 'icon-park-outline-edit',
}, },
{ {
type: 'delete', type: 'delete',
@ -138,6 +140,41 @@ const {
categoryId: undefined, categoryId: undefined,
}, },
items: [ items: [
{
field: 'type',
label: '类型',
setter: 'select',
options: [
{
label: '视频',
value: 1,
},
{
label: '音频',
value: 2,
},
{
label: '图片',
value: 3,
},
{
label: '文本',
value: 4,
},
{
label: '其他',
value: 5,
},
],
setterProps: {
style: {
width: '100px',
},
triggerProps: {
autoFitPopupMinWidth: true
}
}
},
{ {
field: 'name', field: 'name',
label: '素材名称', label: '素材名称',

View File

@ -6,7 +6,7 @@
{ {
"only": "dev", "only": "dev",
"meta": { "meta": {
"sort": 20010, "sort": 120010,
"title": "开发相关", "title": "开发相关",
"icon": "icon-park-outline-home" "icon": "icon-park-outline-home"
} }

View File

@ -18,7 +18,7 @@
{ {
"only": "dev", "only": "dev",
"meta": { "meta": {
"sort": 20010, "sort": 120010,
"title": "接口文档", "title": "接口文档",
"icon": "icon-park-outline-api" "icon": "icon-park-outline-api"
} }

View File

@ -7,8 +7,8 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { api } from '@/api'; import { api } from '@/api';
import { TableColumnRender, useCreateColumn, useTable, useUpdateColumn } from '@/components/AnTable';
import { useFormModal } from '@/components/AnForm'; import { useFormModal } from '@/components/AnForm';
import { TableColumnRender, useCreateColumn, useTable, useUpdateColumn } from '@/components/AnTable';
defineOptions({ name: 'SystemUserPage' }); defineOptions({ name: 'SystemUserPage' });
@ -37,7 +37,7 @@ const usernameRender: TableColumnRender = ({ record }) => (
</a-avatar> </a-avatar>
<div class="w-full flex-1 overflow-hidden"> <div class="w-full flex-1 overflow-hidden">
<div> <div>
<span>{record.nickname}</span> <span class="cursor-pointer hover:text-brand-500">{record.nickname}</span>
<span class="text-gray-400 text-xs truncate ml-2">@{record.username}</span> <span class="text-gray-400 text-xs truncate ml-2">@{record.username}</span>
</div> </div>
<div class="w-full text-gray-400 space-x-4 text-xs"> <div class="w-full text-gray-400 space-x-4 text-xs">
@ -71,6 +71,7 @@ const { component: UserTable } = useTable({
title: '操作', title: '操作',
type: 'button', type: 'button',
width: 200, width: 200,
align: 'right',
buttons: [ buttons: [
{ {
text: '重置密码', text: '重置密码',

View File

@ -1,15 +1,15 @@
<template> <template>
<BreadPage> <BreadPage :content-padding="false">
<template #content> <section class="my-page bg-white">
<section class="my-page m-5 py-4 pr-4 h-full bg-white"> <a-tabs size="large">
<a-tabs direction="vertical">
<a-tab-pane key="1" title="基本设置"> <a-tab-pane key="1" title="基本设置">
<template #title> <a-form
<i class="icon-park-outline-config"></i> :model="user"
基本设置 :label-col-props="{ span: 3 }"
</template> label-align="left"
<a-form :model="user" layout="vertical"> class="px-6 divide-y divide-gray-100"
<a-form-item label="个人头像"> >
<a-form-item label="用户头像">
<a-avatar :size="64"> <a-avatar :size="64">
<img src="https://github.com/juetan.png" alt="" /> <img src="https://github.com/juetan.png" alt="" />
<template #trigger-icon> <template #trigger-icon>
@ -23,12 +23,13 @@
v-model="user.nickname" v-model="user.nickname"
placeholder="请输入" placeholder="请输入"
class="!w-[432px]" class="!w-[432px]"
allow-clear
:max-length="24" :max-length="24"
:show-word-limit="true" :show-word-limit="true"
></a-input> ></a-input>
<template #help> 用作系统内显示的名称可在后台修改 </template> <template #help> 用作系统内显示的名称可在后台修改 </template>
</a-form-item> </a-form-item>
<a-form-item label="个人描述"> <a-form-item label="用户描述">
<a-textarea <a-textarea
v-model="user.description" v-model="user.description"
placeholder="请输入" placeholder="请输入"
@ -47,15 +48,42 @@
<a-form-item label="出生日期"> <a-form-item label="出生日期">
<a-date-picker v-model="user.birth"></a-date-picker> <a-date-picker v-model="user.birth"></a-date-picker>
</a-form-item> </a-form-item>
<a-form-item>
<a-button type="primary">保存修改</a-button> <a-button type="primary">保存修改</a-button>
</a-form-item>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="5" title="消息推送"> <a-tab-pane key="5" title="消息推送">
<template #title> <a-form :model="user" :label-col-props="{ span: 3 }" label-align="left" class="px-6 divide-y divide-gray-100">
<i class="icon-park-outline-message"></i> <a-form-item label="站点LOGO">
消息通知 <a-avatar :size="64">
<img :src="appStore.logo" alt="" />
<template #trigger-icon>
<i class="icon-park-outline-edit"></i>
</template> </template>
<a-form :model="user" layout="vertical"> </a-avatar>
<template #help> 支持 5MB 以内大小, png jpg 格式的图片 </template>
</a-form-item>
<a-form-item label="站点名称">
<a-input
v-model="appStore.title"
placeholder="请输入"
class="!w-[432px]"
allow-clear
:max-length="24"
:show-word-limit="true"
></a-input>
<template #help> 用作系统内显示的名称可在后台修改 </template>
</a-form-item>
<a-form-item label="站点描述">
<a-textarea
v-model="appStore.subtitle"
placeholder="请输入"
class="!w-[432px] h-24"
:max-length="140"
:show-word-limit="true"
></a-textarea>
</a-form-item>
<a-form-item label="消息推送"> <a-form-item label="消息推送">
<div> <div>
<a-checkbox-group direction="vertical" v-model="user.msg"> <a-checkbox-group direction="vertical" v-model="user.msg">
@ -67,38 +95,7 @@
</div> </div>
<template elp> 用作系统内显示的名称可在后台修改 </template> <template elp> 用作系统内显示的名称可在后台修改 </template>
</a-form-item> </a-form-item>
<a-form-item label="邮箱账号"> <a-form-item label="默认主题">
<a-input v-model="user.email" placeholder="请输入" class="!w-[432px]"></a-input>
<template #help> 推荐使用主流邮箱, 例如: gmail.com, qq.com, 163.com等后缀 </template>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="8" title="账号密码">
<template #title>
<i class="icon-park-outline-lock"></i>
账号密码
</template>
<a-form :model="user" layout="vertical">
<a-form-item label="原密码">
<a-input placeholder="请输入原密码"></a-input>
</a-form-item>
<a-form-item label="新密码">
<a-input placeholder="请输入新密码"></a-input>
</a-form-item>
<a-form-item label="确认新密码">
<a-input placeholder="请再次输入新密码"></a-input>
</a-form-item>
<a-button type="primary">修改密码</a-button>
</a-form>
</a-tab-pane>
<a-tab-pane key="2" title="主题偏好">
<template #title>
<i class="icon-park-outline-theme"></i>
主题设置
</template>
<div class="flex-1">
<a-form :model="user" layout="vertical">
<a-form-item label="主题">
<div> <div>
<a-radio-group v-model="user.theme" direction="vertical"> <a-radio-group v-model="user.theme" direction="vertical">
<a-radio value="dark"> <a-radio value="dark">
@ -112,15 +109,27 @@
</a-radio-group> </a-radio-group>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item label="SMTP地址">
<a-input v-model="user.smtpIp" allow-clear placeholder="请输入" class="!w-[314px]"></a-input>
<span class="inline-block px-2">:</span>
<a-input-number v-model="user.smtpPort" :min="0" :max="65535" class="w-24!"></a-input-number>
<template #help> 推荐使用主流邮箱, 例如: gmail.com, qq.com, 163.com等后缀 </template>
</a-form-item>
<a-form-item label="SMTP账号">
<a-input v-model="user.smtpUser" placeholder="请输入" class="!w-[432px]"></a-input>
<template #help> 例如: gmail.com, qq.com, 163.com等后缀 </template>
</a-form-item>
<a-form-item label="SMTP密钥">
<a-input v-model="user.smtpPass" placeholder="请输入" class="!w-[432px]"></a-input>
<template #help> 测试 </template>
</a-form-item>
<a-form-item>
<a-button type="primary"> 保存修改 </a-button>
</a-form-item>
</a-form> </a-form>
</div>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="3" title="额外功能"> <a-tab-pane key="3" title="额外功能">
<template #title> <div class="flex-1 grid px-6">
<i class="icon-park-outline-shield"></i>
基本设置
</template>
<div class="flex-1 grid">
<div class="mb-3">功能列表</div> <div class="mb-3">功能列表</div>
<div v-for="i in 3" class="border-t py-4 flex justify-between items-center gap-4"> <div v-for="i in 3" class="border-t py-4 flex justify-between items-center gap-4">
<div class="flex gap-3 items-center"> <div class="flex gap-3 items-center">
@ -180,13 +189,15 @@
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
</section> </section>
</template>
</BreadPage> </BreadPage>
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
import { useAppStore } from '@/store';
import { reactive } from 'vue'; import { reactive } from 'vue';
const appStore = useAppStore()
const user = reactive({ const user = reactive({
nickname: '绝弹', nickname: '绝弹',
description: '选择在公开个人资料中显示私有项目的贡献,但不显示任何项目,仓库或组织信息', description: '选择在公开个人资料中显示私有项目的贡献,但不显示任何项目,仓库或组织信息',
@ -195,11 +206,21 @@ const user = reactive({
msg: [2], msg: [2],
gender: 1, gender: 1,
birth: '1988-12-18', birth: '1988-12-18',
smtpPort: 25,
smtpIp: '10.10.10.30',
smtpUser: '952222@163.com',
smtpPass: 'xxxxx',
}); });
</script> </script>
<style lang="less"> <style lang="less">
.my-page { .my-page {
.arco-form-item-label {
color: var(--color-text-3);
}
.arco-tabs-nav-type-line .arco-tabs-tab {
height: 52px;
}
.arco-form-item.arco-form-item-error, .arco-form-item.arco-form-item-error,
.arco-form-item.arco-form-item-has-help { .arco-form-item.arco-form-item-has-help {
margin-bottom: 20px; margin-bottom: 20px;
@ -220,6 +241,9 @@ const user = reactive({
.arco-radio-icon-hover { .arco-radio-icon-hover {
margin-top: 4px; margin-top: 4px;
} }
.arco-form-item:not(:first-child) {
padding: 20px 0 0;
}
} }
</style> </style>
@ -228,8 +252,7 @@ const user = reactive({
"meta": { "meta": {
"sort": 30401, "sort": 30401,
"title": "个人设置", "title": "个人设置",
"icon": "icon-park-outline-config", "icon": "icon-park-outline-config"
"auth": ["1"]
} }
} }
</route> </route>

View File

@ -1,4 +1,5 @@
import { api } from '@/api'; import { api } from '@/api';
import { env } from '@/config/env';
import { store, useUserStore } from '@/store'; import { store, useUserStore } from '@/store';
import { useMenuStore } from '@/store/menu'; import { useMenuStore } from '@/store/menu';
import { treeEach, treeFilter, treeFind } from '@/utils/listToTree'; import { treeEach, treeFilter, treeFind } from '@/utils/listToTree';
@ -7,7 +8,6 @@ import { Router } from 'vue-router';
import { menus } from '../menus'; import { menus } from '../menus';
import { APP_HOME_NAME } from '../routes/base'; import { APP_HOME_NAME } from '../routes/base';
import { APP_ROUTE_NAME, routes } from '../routes/page'; import { APP_ROUTE_NAME, routes } from '../routes/page';
import { env } from '@/config/env';
const WHITE_LIST = ['/:all(.*)*']; const WHITE_LIST = ['/:all(.*)*'];
const UNSIGNIN_LIST = ['/login']; const UNSIGNIN_LIST = ['/login'];
@ -27,7 +27,6 @@ export function useAuthGuard(router: Router) {
}; };
router.beforeEach(async function (to, from) { router.beforeEach(async function (to, from) {
console.log(to);
const userStore = useUserStore(store); const userStore = useUserStore(store);
const menuStore = useMenuStore(store); const menuStore = useMenuStore(store);

View File

@ -13,6 +13,7 @@ export interface MenuItem {
icon?: string; icon?: string;
external?: boolean; external?: boolean;
name?: string; name?: string;
only?: undefined | 'none' | 'dev';
keepAlive: boolean; keepAlive: boolean;
children?: MenuItem[]; children?: MenuItem[];
} }
@ -25,13 +26,13 @@ function routesToItems(routes: RouteRecordRaw[]): MenuItem[] {
const items: MenuItem[] = []; const items: MenuItem[] = [];
for (const route of routes) { for (const route of routes) {
const { meta = {}, parentMeta, path } = route as any; const { meta = {}, parentMeta, only, path } = route as any;
const { title, sort, icon, keepAlive = false, name } = meta; const { title, sort, icon, keepAlive = false, name } = meta;
let id = path; let id = path;
let paths = route.path.split('/'); let paths = route.path.split('/');
let parentId = paths.slice(0, -1).join('/'); let parentId = paths.slice(0, -1).join('/');
if (parentMeta) { if (parentMeta) {
const { title, icon, sort } = parentMeta; const { title, icon, sort, only } = parentMeta;
id = `${path}/index`; id = `${path}/index`;
parentId = path; parentId = path;
items.push({ items.push({
@ -39,6 +40,7 @@ function routesToItems(routes: RouteRecordRaw[]): MenuItem[] {
icon, icon,
sort, sort,
path, path,
only,
id: path, id: path,
keepAlive: false, keepAlive: false,
parentId: paths.slice(0, -1).join('/'), parentId: paths.slice(0, -1).join('/'),
@ -49,7 +51,7 @@ function routesToItems(routes: RouteRecordRaw[]): MenuItem[] {
parentId = p; parentId = p;
} }
} }
items.push({ id, title, parentId, path, icon, sort, keepAlive, name }); items.push({ id, title, parentId, path, icon, sort, only, keepAlive, name });
} }
return items; return items;

View File

@ -1,5 +1,8 @@
import generatedRoutes from 'virtual:generated-pages'; import generatedRoutes from 'virtual:generated-pages';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import { routes as vroutes } from 'vue-router/auto/routes';
console.log({vroutes, generatedRoutes});
export const TOP_ROUTE_PREF = '_'; export const TOP_ROUTE_PREF = '_';
export const APP_ROUTE_NAME = '_layout'; export const APP_ROUTE_NAME = '_layout';

View File

@ -6,6 +6,7 @@ export const useAppStore = defineStore({
state: (): AppStore => ({ state: (): AppStore => ({
isDarkMode: false, isDarkMode: false,
title: env.title, title: env.title,
logo: "/favicon.ico",
subtitle: env.subtitle, subtitle: env.subtitle,
pageLoding: false, pageLoding: false,
pageTags: [], pageTags: [],
@ -74,6 +75,7 @@ export const useAppStore = defineStore({
}); });
interface AppStore { interface AppStore {
logo: string;
/** /**
* *
*/ */

View File

@ -132,7 +132,7 @@ body {
margin-right: 0; margin-right: 0;
} }
.ani-form-modal .arco-modal-body { .an-form-modal .arco-modal-body {
padding-bottom: 8px; padding-bottom: 8px;
} }
} }

View File

@ -22,7 +22,6 @@ declare module '@vue/runtime-core' {
ADoption: typeof import('@arco-design/web-vue')['Doption'] ADoption: typeof import('@arco-design/web-vue')['Doption']
ADrawer: typeof import('@arco-design/web-vue')['Drawer'] ADrawer: typeof import('@arco-design/web-vue')['Drawer']
ADropdown: typeof import('@arco-design/web-vue')['Dropdown'] ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
ADropdownButton: typeof import('@arco-design/web-vue')['DropdownButton']
AEmpty: typeof import('@arco-design/web-vue')['Empty'] AEmpty: typeof import('@arco-design/web-vue')['Empty']
AForm: typeof import('@arco-design/web-vue')['Form'] AForm: typeof import('@arco-design/web-vue')['Form']
AFormItem: typeof import('@arco-design/web-vue')['FormItem'] AFormItem: typeof import('@arco-design/web-vue')['FormItem']
@ -43,7 +42,6 @@ declare module '@vue/runtime-core' {
AnEmpty: typeof import('./../components/AnEmpty/AnEmpty.vue')['default'] AnEmpty: typeof import('./../components/AnEmpty/AnEmpty.vue')['default']
AnForbidden: typeof import('./../components/AnForbidden/AnForbidden.vue')['default'] AnForbidden: typeof import('./../components/AnForbidden/AnForbidden.vue')['default']
AnToast: typeof import('./../components/AnToast/AnToast.vue')['default'] AnToast: typeof import('./../components/AnToast/AnToast.vue')['default']
AOption: typeof import('@arco-design/web-vue')['Option']
APagination: typeof import('@arco-design/web-vue')['Pagination'] APagination: typeof import('@arco-design/web-vue')['Pagination']
APopover: typeof import('@arco-design/web-vue')['Popover'] APopover: typeof import('@arco-design/web-vue')['Popover']
AProgress: typeof import('@arco-design/web-vue')['Progress'] AProgress: typeof import('@arco-design/web-vue')['Progress']
@ -73,7 +71,6 @@ declare module '@vue/runtime-core' {
EditorLeft: typeof import('./../components/editor/components/EditorLeft.vue')['default'] EditorLeft: typeof import('./../components/editor/components/EditorLeft.vue')['default']
EditorMain: typeof import('./../components/editor/components/EditorMain.vue')['default'] EditorMain: typeof import('./../components/editor/components/EditorMain.vue')['default']
EditorMainBlock: typeof import('./../components/editor/components/EditorMainBlock.vue')['default'] EditorMainBlock: typeof import('./../components/editor/components/EditorMainBlock.vue')['default']
EditorMainConfig: typeof import('./../components/editor/components/EditorMainConfig.vue')['default']
EditorMainHeader: typeof import('./../components/editor/components/EditorMainHeader.vue')['default'] EditorMainHeader: typeof import('./../components/editor/components/EditorMainHeader.vue')['default']
EditorPreview: typeof import('./../components/editor/components/EditorPreview.vue')['default'] EditorPreview: typeof import('./../components/editor/components/EditorPreview.vue')['default']
EditorRight: typeof import('./../components/editor/components/EditorRight.vue')['default'] EditorRight: typeof import('./../components/editor/components/EditorRight.vue')['default']
@ -84,13 +81,6 @@ declare module '@vue/runtime-core' {
InputTexter: typeof import('./../components/editor/components/InputTexter.vue')['default'] InputTexter: typeof import('./../components/editor/components/InputTexter.vue')['default']
Marquee: typeof import('./../components/editor/blocks/text/marquee.vue')['default'] Marquee: typeof import('./../components/editor/blocks/text/marquee.vue')['default']
Option: typeof import('./../components/editor/blocks/date/option.vue')['default'] Option: typeof import('./../components/editor/blocks/date/option.vue')['default']
PanelHeader: typeof import('./../components/editor/components/PanelHeader.vue')['default']
PanelLeft: typeof import('./../components/editor/components/PanelLeft.vue')['default']
PanelMain: typeof import('./../components/editor/components/PanelMain.vue')['default']
PanelMainBlock: typeof import('./../components/editor/components/PanelMainBlock.vue')['default']
PanelMainConfig: typeof import('./../components/editor/components/PanelMainConfig.vue')['default']
PanelMainHeader: typeof import('./../components/editor/components/PanelMainHeader.vue')['default']
PanelRight: typeof import('./../components/editor/components/PanelRight.vue')['default']
Render: typeof import('./../components/editor/blocks/date/render.vue')['default'] Render: typeof import('./../components/editor/blocks/date/render.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']

View File

@ -42,25 +42,24 @@ declare module 'vue-router/auto/routes' {
'/_layout/': RouteRecordInfo<'/_layout/', '/_layout', Record<never, never>, Record<never, never>>, '/_layout/': RouteRecordInfo<'/_layout/', '/_layout', Record<never, never>, Record<never, never>>,
'/_login/': RouteRecordInfo<'/_login/', '/_login', Record<never, never>, Record<never, never>>, '/_login/': RouteRecordInfo<'/_login/', '/_login', Record<never, never>, Record<never, never>>,
'/[..._all]/': RouteRecordInfo<'/[..._all]/', '/:_all(.*)', { _all: ParamValue<true> }, { _all: ParamValue<false> }>, '/[..._all]/': RouteRecordInfo<'/[..._all]/', '/:_all(.*)', { _all: ParamValue<true> }, { _all: ParamValue<false> }>,
'/dev/openapi': RouteRecordInfo<'/dev/openapi', '/dev/openapi', Record<never, never>, Record<never, never>>, '/content/': RouteRecordInfo<'/content/', '/content', Record<never, never>, Record<never, never>>,
'/home/demo': RouteRecordInfo<'/home/demo', '/home/demo', Record<never, never>, Record<never, never>>, '/content/category/': RouteRecordInfo<'/content/category/', '/content/category', Record<never, never>, Record<never, never>>,
'/home/home': RouteRecordInfo<'/home/home', '/home/home', Record<never, never>, Record<never, never>>, '/content/comment/': RouteRecordInfo<'/content/comment/', '/content/comment', Record<never, never>, Record<never, never>>,
'/home/test': RouteRecordInfo<'/home/test', '/home/test', Record<never, never>, Record<never, never>>, '/content/material/': RouteRecordInfo<'/content/material/', '/content/material', Record<never, never>, Record<never, never>>,
'/my/': RouteRecordInfo<'/my/', '/my', Record<never, never>, Record<never, never>>, '/content/post/': RouteRecordInfo<'/content/post/', '/content/post', Record<never, never>, Record<never, never>>,
'/my/dev': RouteRecordInfo<'/my/dev', '/my/dev', Record<never, never>, Record<never, never>>, '/dev/': RouteRecordInfo<'/dev/', '/dev', Record<never, never>, Record<never, never>>,
'/my/editor': RouteRecordInfo<'/my/editor', '/my/editor', Record<never, never>, Record<never, never>>, '/dev/editor/': RouteRecordInfo<'/dev/editor/', '/dev/editor', Record<never, never>, Record<never, never>>,
'/post/': RouteRecordInfo<'/post/', '/post', Record<never, never>, Record<never, never>>, '/dev/openapi/': RouteRecordInfo<'/dev/openapi/', '/dev/openapi', Record<never, never>, Record<never, never>>,
'/post/category/': RouteRecordInfo<'/post/category/', '/post/category', Record<never, never>, Record<never, never>>, '/home/': RouteRecordInfo<'/home/', '/home', Record<never, never>, Record<never, never>>,
'/post/comment/': RouteRecordInfo<'/post/comment/', '/post/comment', Record<never, never>, Record<never, never>>, '/log/': RouteRecordInfo<'/log/', '/log', Record<never, never>, Record<never, never>>,
'/post/media/': RouteRecordInfo<'/post/media/', '/post/media', Record<never, never>, Record<never, never>>, '/log/login/': RouteRecordInfo<'/log/login/', '/log/login', Record<never, never>, Record<never, never>>,
'/post/post/': RouteRecordInfo<'/post/post/', '/post/post', Record<never, never>, Record<never, never>>, '/log/operation/': RouteRecordInfo<'/log/operation/', '/log/operation', Record<never, never>, Record<never, never>>,
'/system/': RouteRecordInfo<'/system/', '/system', Record<never, never>, Record<never, never>>, '/system/': RouteRecordInfo<'/system/', '/system', Record<never, never>, Record<never, never>>,
'/system/login-log/': RouteRecordInfo<'/system/login-log/', '/system/login-log', Record<never, never>, Record<never, never>>, '/system/dict/': RouteRecordInfo<'/system/dict/', '/system/dict', Record<never, never>, Record<never, never>>,
'/system/menu/': RouteRecordInfo<'/system/menu/', '/system/menu', Record<never, never>, Record<never, never>>, '/system/menu/': RouteRecordInfo<'/system/menu/', '/system/menu', Record<never, never>, Record<never, never>>,
'/system/operation-log/': RouteRecordInfo<'/system/operation-log/', '/system/operation-log', Record<never, never>, Record<never, never>>,
'/system/permission/': RouteRecordInfo<'/system/permission/', '/system/permission', Record<never, never>, Record<never, never>>,
'/system/role/': RouteRecordInfo<'/system/role/', '/system/role', Record<never, never>, Record<never, never>>, '/system/role/': RouteRecordInfo<'/system/role/', '/system/role', Record<never, never>, Record<never, never>>,
'/system/user/': RouteRecordInfo<'/system/user/', '/system/user', Record<never, never>, Record<never, never>>, '/system/user/': RouteRecordInfo<'/system/user/', '/system/user', Record<never, never>, Record<never, never>>,
'/user/': RouteRecordInfo<'/user/', '/user', Record<never, never>, Record<never, never>>,
} }
} }

View File

@ -4,6 +4,8 @@ import { merge } from 'lodash-es';
export type DelOptions = string | Partial<Omit<ModalConfig, 'onOk' | 'onCancel'>>; export type DelOptions = string | Partial<Omit<ModalConfig, 'onOk' | 'onCancel'>>;
export const delOptions: ModalConfig = { export const delOptions: ModalConfig = {
maskAnimationName: '',
modalAnimationName: '',
title: '提示', title: '提示',
titleAlign: 'start', titleAlign: 'start',
width: 432, width: 432,

View File

@ -1,18 +1,19 @@
import Vue from "@vitejs/plugin-vue"; import Vue from '@vitejs/plugin-vue';
import VueJsx from "@vitejs/plugin-vue-jsx"; import VueJsx from '@vitejs/plugin-vue-jsx';
import { resolve } from "path"; import { resolve } from 'path';
import { visualizer } from "rollup-plugin-visualizer"; import { visualizer } from 'rollup-plugin-visualizer';
import { presetIcons, presetUno } from "unocss"; import { presetIcons, presetUno } from 'unocss';
import Unocss from "unocss/vite"; import Unocss from 'unocss/vite';
import AutoImport from "unplugin-auto-import/vite"; import AutoImport from 'unplugin-auto-import/vite';
import { ArcoResolver } from "unplugin-vue-components/resolvers"; import { ArcoResolver } from 'unplugin-vue-components/resolvers';
import AutoComponent from "unplugin-vue-components/vite"; import AutoComponent from 'unplugin-vue-components/vite';
import { defineConfig, loadEnv } from "vite"; import router from 'unplugin-vue-router/vite';
import Page from "vite-plugin-pages"; import { defineConfig, loadEnv } from 'vite';
import { arcoToUnoColor } from "./scripts/vite/color"; import Page from 'vite-plugin-pages';
import iconFile from "./scripts/vite/file.json"; import { arcoToUnoColor } from './scripts/vite/color';
import iconFmt from "./scripts/vite/fmt.json"; import iconFile from './scripts/vite/file.json';
import plugin from "./scripts/vite/plugin"; import iconFmt from './scripts/vite/fmt.json';
import plugin from './scripts/vite/plugin';
/** /**
* vite * vite
@ -20,12 +21,21 @@ import plugin from "./scripts/vite/plugin";
*/ */
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()); const env = loadEnv(mode, process.cwd());
const base = env.VITE_BASE ?? "/"; const base = env.VITE_BASE ?? '/';
const host = env.VITE_HOST ?? "0.0.0.0"; const host = env.VITE_HOST ?? '0.0.0.0';
const port = Number(env.VITE_PORT ?? 3020); const port = Number(env.VITE_PORT ?? 3020);
return { return {
base, base,
plugins: [ plugins: [
/**
*
* @see https://github.com/posva/unplugin-vue-router
*/
router({
dts: 'src/types/auto-router.d.ts',
exclude: ['**/components/*'],
}),
/** /**
* Vue 3 * Vue 3
* @see https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue * @see https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue
@ -47,9 +57,9 @@ export default defineConfig(({ mode }) => {
* @see https://github.com/antfu/unplugin-auto-import#readme * @see https://github.com/antfu/unplugin-auto-import#readme
*/ */
AutoImport({ AutoImport({
imports: ["vue", "vue-router"], dts: 'src/types/auto-import.d.ts',
imports: ['vue', 'vue-router'],
resolvers: [ArcoResolver()], resolvers: [ArcoResolver()],
dts: "./src/types/auto-import.d.ts",
}), }),
/** /**
@ -57,8 +67,8 @@ export default defineConfig(({ mode }) => {
* @see https://github.com/antfu/unplugin-vue-components * @see https://github.com/antfu/unplugin-vue-components
*/ */
AutoComponent({ AutoComponent({
dts: 'src/types/auto-component.d.ts',
resolvers: [ArcoResolver({ sideEffect: false })], resolvers: [ArcoResolver({ sideEffect: false })],
dts: "./src/types/auto-component.d.ts",
}), }),
/** /**
@ -66,13 +76,13 @@ export default defineConfig(({ mode }) => {
* @see https://github.com/hannoeru/vite-plugin-pages * @see https://github.com/hannoeru/vite-plugin-pages
*/ */
Page({ Page({
exclude: ["**/components/*", "**/*.*.*"], exclude: ['**/components/*', '**/*.*.*'],
importMode: "async", importMode: 'async',
onRoutesGenerated(routes) { onRoutesGenerated(routes) {
// if (env.DEV) { if (mode === 'development') {
// return routes; return routes.filter(route => route.only !== 'none');
// } }
return routes.filter((route) => route.only !== "dev"); return routes.filter(route => !['none', 'dev'].includes(route.only));
}, },
}), }),
@ -83,17 +93,17 @@ export default defineConfig(({ mode }) => {
Unocss({ Unocss({
theme: { theme: {
colors: { colors: {
brand: arcoToUnoColor("primary"), brand: arcoToUnoColor('primary'),
}, },
}, },
include: ["src/**/*.{vue,ts,tsx,css,scss,sass,less,styl}"], include: ['src/**/*.{vue,ts,tsx,css,scss,sass,less,styl}'],
presets: [ presets: [
presetUno(), presetUno(),
presetIcons({ presetIcons({
prefix: "", prefix: '',
collections: { collections: {
"icon-file": iconFile, 'icon-file': iconFile,
"icon-fmt": iconFmt, 'icon-fmt': iconFmt,
}, },
}), }),
], ],
@ -105,7 +115,7 @@ export default defineConfig(({ mode }) => {
*/ */
visualizer({ visualizer({
title: `构建统计 | ${env.VITE_SUBTITLE}`, title: `构建统计 | ${env.VITE_SUBTITLE}`,
filename: ".gitea/stat.html", filename: '.gitea/stat.html',
}), }),
/** /**
@ -117,8 +127,8 @@ export default defineConfig(({ mode }) => {
resolve: { resolve: {
alias: [ alias: [
{ {
find: "@", find: '@',
replacement: "/src", replacement: '/src',
}, },
], ],
}, },
@ -126,10 +136,10 @@ export default defineConfig(({ mode }) => {
host, host,
port, port,
proxy: { proxy: {
"/api": { '/api': {
target: env.VITE_PROXY, target: env.VITE_PROXY,
}, },
"/upload": { '/upload': {
target: env.VITE_PROXY, target: env.VITE_PROXY,
}, },
}, },
@ -139,8 +149,8 @@ export default defineConfig(({ mode }) => {
less: { less: {
javascriptEnabled: true, javascriptEnabled: true,
modifyVars: { modifyVars: {
hack: `true; @import (reference) "${resolve("src/styles/css-arco.less")}";`, hack: `true; @import (reference) "${resolve('src/styles/css-arco.less')}";`,
arcoblue: "#66f", arcoblue: '#66f',
}, },
}, },
}, },