feat: 添加路由loading
parent
22b57e9fa7
commit
3f72b304f5
2
.env
2
.env
|
|
@ -2,7 +2,7 @@
|
|||
# 应用配置
|
||||
# =====================================================================================
|
||||
# 网站标题
|
||||
VITE_TITLE = 绝弹管理后台
|
||||
VITE_TITLE = Appnify
|
||||
# 网站副标题
|
||||
VITE_SUBTITLE = 快速开发web应用的模板工具
|
||||
# API接口前缀:参见 axios 的 baseURL
|
||||
|
|
|
|||
|
|
@ -30,13 +30,4 @@
|
|||
const router = useRouter();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"title": "404",
|
||||
"icon": "icon-park-outline-home"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
<style scoped></style>
|
||||
|
|
@ -132,7 +132,7 @@ export const FormModal = defineComponent({
|
|||
content = (
|
||||
<Button type="primary">
|
||||
{{
|
||||
default: () => (typeof props.trigger === "string" ? props.trigger : "添加"),
|
||||
default: () => (typeof props.trigger === "string" ? props.trigger : "新增"),
|
||||
icon: () => <i class="icon-park-outline-add" />,
|
||||
}}
|
||||
</Button>
|
||||
|
|
@ -144,7 +144,7 @@ export const FormModal = defineComponent({
|
|||
if (typeof props.trigger === "object") {
|
||||
content = (
|
||||
<Button type="primary" {...omit(props.trigger, "text")}>
|
||||
{props.trigger?.text || "添加"}
|
||||
{props.trigger?.text || "新增"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Modal } from "@arco-design/web-vue";
|
||||
import { assign } from "lodash-es";
|
||||
import { assign, merge } from "lodash-es";
|
||||
import { reactive } from "vue";
|
||||
import { useForm } from "./use-form";
|
||||
import { FormModalProps } from "./form-modal";
|
||||
|
|
@ -7,6 +7,7 @@ import { FormModalProps } from "./form-modal";
|
|||
const defaults: Partial<InstanceType<typeof Modal>> = {
|
||||
width: 1080,
|
||||
titleAlign: "start",
|
||||
closable: false
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -18,5 +19,5 @@ export const useFormModal = (options: FormModalProps): FormModalProps & { model:
|
|||
|
||||
const form = useForm({ model, items });
|
||||
|
||||
return reactive(assign({ modalProps: { ...defaults } }, { ...options, ...form }));
|
||||
return reactive(merge({ modalProps: { ...defaults } }, { ...options, ...form }));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -167,11 +167,11 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
|
|||
if (item.extend) {
|
||||
const createItem = createItems.find((i) => i.field === item.extend);
|
||||
if (createItem) {
|
||||
searchItems.push(merge({ itemProps: { hideLabel: true } }, createItem, item));
|
||||
searchItems.push(merge({}, createItem, item));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
searchItems.push(merge({ itemProps: { hideLabel: true } }, item));
|
||||
searchItems.push(merge({}, item));
|
||||
}
|
||||
searchItems.push(config.searchItemSubmit);
|
||||
options.search.items = searchItems;
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
export * from "./gender";
|
||||
export * from "./defineConstants";
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export default defineComponent({
|
|||
render() {
|
||||
return (
|
||||
<a-menu
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
style={{ width: "100%" }}
|
||||
breakpoint="xl"
|
||||
selectedKeys={this.selectedKeys}
|
||||
autoOpenSelected={true}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,26 @@
|
|||
<img src="/favicon.ico" alt="" width="22" height="22" class="" />
|
||||
<h1 class="relative text-lg font-semibold leading-[19px] dark:text-white m-0 p-0">
|
||||
{{ appStore.title }}
|
||||
<span v-if="isDev" class="absolute -right-14 -top-1 text-xs font-normal text-blue-500 bg-blue-50 px-2 rounded-full">开发版</span>
|
||||
<span
|
||||
v-if="isDev"
|
||||
class="absolute -right-14 -top-1 text-xs font-normal text-brand-500 bg-brand-50 px-1.5 rounded-full"
|
||||
>
|
||||
开发版
|
||||
</span>
|
||||
</h1>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<a-dropdown v-if="isDev" trigger="hover">
|
||||
<a-button shape="round">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-api"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption>接口文档</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-tooltip v-for="btn in buttons" :key="btn.icon" :content="btn.tooltip">
|
||||
<a-button shape="round" @click="btn.onClick">
|
||||
<template #icon>
|
||||
|
|
@ -53,9 +68,9 @@
|
|||
:hide-trigger="false"
|
||||
@collapse="onCollapse"
|
||||
>
|
||||
<div class="">
|
||||
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-hidden pt-2">
|
||||
<Menu />
|
||||
</div>
|
||||
</a-scrollbar>
|
||||
</a-layout-sider>
|
||||
<a-layout class="layout-content flex-1">
|
||||
<a-layout-header class="h-8 bg-white border-b border-slate-200 dark:bg-slate-800 dark:border-slate-700">
|
||||
|
|
@ -64,9 +79,14 @@
|
|||
</div>
|
||||
</a-layout-header>
|
||||
<a-layout-content class="overflow-x-auto">
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component"></component>
|
||||
</router-view>
|
||||
<a-spin :loading="appStore.pageLoding" tip="正在加载中,请稍等..." class="block h-full w-full">
|
||||
<template #icon>
|
||||
<IconSync></IconSync>
|
||||
</template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component"></component>
|
||||
</router-view>
|
||||
</a-spin>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
|
|
@ -77,6 +97,7 @@
|
|||
import { useAppStore, useUserStore } from "@/store";
|
||||
import { Message } from "@arco-design/web-vue";
|
||||
import Menu from "./components/menu.vue";
|
||||
import { IconSync } from "@arco-design/web-vue/es/icon";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
|
|
@ -84,23 +105,30 @@ const isCollapsed = ref(false);
|
|||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const themeConfig = ref({ visible: false });
|
||||
const isDev = import.meta.env.DEV
|
||||
const isDev = import.meta.env.DEV;
|
||||
|
||||
const onCollapse = (val: boolean) => {
|
||||
isCollapsed.value = val;
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
icon: "icon-park-outline-remind",
|
||||
tooltip: "通知",
|
||||
onClick: () => {
|
||||
Message.info("暂无通知");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "icon-park-outline-moon",
|
||||
tooltip: "点击切换主题色",
|
||||
tooltip: "切换主题色",
|
||||
onClick: () => {
|
||||
appStore.toggleDark();
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "icon-park-outline-config",
|
||||
tooltip: "点击打开设置",
|
||||
tooltip: "打开设置",
|
||||
onClick: () => {
|
||||
themeConfig.value.visible = true;
|
||||
},
|
||||
|
|
@ -112,7 +140,7 @@ const userButtons = [
|
|||
icon: "icon-park-outline-config",
|
||||
text: "个人设置",
|
||||
onClick: () => {
|
||||
router.push('/my')
|
||||
router.push("/my");
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { api } from "@/api";
|
||||
import { dayjs } from "@/libs";
|
||||
import { dayjs } from "@/libs/dayjs";
|
||||
import { useAppStore, useUserStore } from "@/store";
|
||||
import { FieldRule, Form, Message, Modal } from "@arco-design/web-vue";
|
||||
import { reactive } from "vue";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,21 @@
|
|||
<template>
|
||||
<bread-page class="">
|
||||
<a-card title="菜单权限">
|
||||
<a-tree :data="items" :field-names="{ title: 'title' }" checkable></a-tree>
|
||||
<template #title>
|
||||
菜单权限
|
||||
<a-link>展开</a-link>
|
||||
</template>
|
||||
<template #extra>
|
||||
<a-checkbox>全部选择</a-checkbox>
|
||||
</template>
|
||||
<a-tree :data="items" :block-node="true" :field-names="{ title: 'title' }" checkable :default-expand-all="true">
|
||||
<template #extra="nodeData">
|
||||
<div class="flex-1 flex justify-end px-1">
|
||||
<a-tag v-if="nodeData.children" color="orange">菜单</a-tag>
|
||||
<a-tag v-else color="green">页面</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
</a-tree>
|
||||
</a-card>
|
||||
</bread-page>
|
||||
</template>
|
||||
|
|
@ -52,7 +66,13 @@ const onItemChange = (item: any, menu: any) => {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style lang="less">
|
||||
.arco-tree-node {
|
||||
&:hover {
|
||||
background: rgb(var(--primary-1));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@
|
|||
<template #default>
|
||||
<div class="flex justify-between items-end gap-4">
|
||||
<div class="">
|
||||
<span class="text-base font-semibold text-gray-900">媒体素材</span>
|
||||
<div class="mt-1 text-gray-400">
|
||||
用户上传的图片、视频、音频等素材,可用于文章、图文、视频等内容的编辑。
|
||||
</div>
|
||||
<span class="text-lg font-bold text-gray-900">媒体素材</span>
|
||||
<div class="mt-1 text-gray-400">用户上传的图片、视频、音频等素材,可用于文章、图文、视频等内容的编辑。</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400">
|
||||
<a-button type="primary">
|
||||
|
|
@ -21,16 +19,14 @@
|
|||
<template #header>
|
||||
<div class="flex gap-2 items-center justify-between text-sm bg-[#fbfbfc] px-5 py-2">
|
||||
<div class="flex gap-4 my-1.5">
|
||||
<ACheckbox>
|
||||
全选
|
||||
</ACheckbox>
|
||||
<ACheckbox> 全选 </ACheckbox>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 text-gray-500">
|
||||
<div class="flex items-center text-gray-500">
|
||||
<ADropdown>
|
||||
<span class="cursor-pointer hover:text-gray-900">
|
||||
<a-button type="text">
|
||||
上传者
|
||||
<i class="icon-park-outline-down"></i>
|
||||
</span>
|
||||
<i class="icon-park-outline-down ml-1"></i>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<ADoption class="!hover:bg-transparent !px-0 flex">
|
||||
<div class="border-b border-gray-200 w-full pb-1">
|
||||
|
|
@ -48,10 +44,10 @@
|
|||
</template>
|
||||
</ADropdown>
|
||||
<ADropdown>
|
||||
<span class="cursor-pointer hover:text-gray-900">
|
||||
<a-button type="text">
|
||||
排序:默认
|
||||
<i class="icon-park-outline-down"></i>
|
||||
</span>
|
||||
<i class="icon-park-outline-down ml-1"></i>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<ADoption>
|
||||
<template #icon>
|
||||
|
|
@ -60,37 +56,30 @@
|
|||
<div class="w-48">默认</div>
|
||||
</ADoption>
|
||||
<ADoption>
|
||||
<template #icon>
|
||||
</template>
|
||||
<template #icon> </template>
|
||||
按创建时间升序
|
||||
</ADoption>
|
||||
<ADoption>
|
||||
按创建时间降序
|
||||
</ADoption>
|
||||
<ADoption>
|
||||
按文件大小升序
|
||||
</ADoption>
|
||||
<ADoption>
|
||||
按文件大小降序
|
||||
</ADoption>
|
||||
<ADoption> 按创建时间降序 </ADoption>
|
||||
<ADoption> 按文件大小升序 </ADoption>
|
||||
<ADoption> 按文件大小降序 </ADoption>
|
||||
</template>
|
||||
</ADropdown>
|
||||
<div class="space-x-1">
|
||||
<span
|
||||
class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer text-gray-400 hover:text-gray-700 bg-slate-200 text-slate-700"
|
||||
>
|
||||
<i class="icon-park-outline-list"></i>
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
>
|
||||
<i class="icon-park-outline-insert-table"></i>
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex p-1 hover:bg-slate-200 rounded cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
>
|
||||
<i class="icon-park-outline-refresh"></i>
|
||||
</span>
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-list"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-insert-table"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-refresh"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -99,7 +88,12 @@
|
|||
<AListItemMeta title="测试图片.png" description="image/png 1.2MB">
|
||||
<template #avatar>
|
||||
<ACheckbox class="mr-3"></ACheckbox>
|
||||
<AImage :src="`https://picsum.photos/200/300?${Math.random()}`" height="32" width="48" class="bg-slate-50">
|
||||
<AImage
|
||||
:src="`https://picsum.photos/200/300?${Math.random()}`"
|
||||
height="32"
|
||||
width="48"
|
||||
class="bg-slate-50"
|
||||
>
|
||||
</AImage>
|
||||
</template>
|
||||
<template #title>
|
||||
|
|
@ -110,30 +104,34 @@
|
|||
</template>
|
||||
</AListItemMeta>
|
||||
<template #actions>
|
||||
<span class="text-xs text-gray-400">
|
||||
<i class="icon-park-outline-user !w-[14px] !h-[14px]"></i>
|
||||
绝弹
|
||||
</span>
|
||||
<span class="text-xs text-gray-400">2023-08-17 17:00:01</span>
|
||||
<ADropdown @select="onRowActionsSelect" position="br">
|
||||
<span class="inline-flex p-1 hover:bg-slate-100 text-brand-500 rounded cursor-pointer">
|
||||
<i class="icon-park-outline-more"></i>
|
||||
<div class="flex items-center gap-6">
|
||||
<span class="text-xs text-gray-400">
|
||||
<i class="icon-park-outline-user !w-[14px] !h-[14px]"></i>
|
||||
绝弹
|
||||
</span>
|
||||
<template #content>
|
||||
<ADoption value="detail">
|
||||
<span class="text-xs text-gray-400">2023-08-17 17:00:01</span>
|
||||
<ADropdown @select="onRowActionsSelect" position="br">
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-repair"></i>
|
||||
<i class="icon-park-outline-more"></i>
|
||||
</template>
|
||||
<div>详情</div>
|
||||
</ADoption>
|
||||
<ADoption value="delete" class="!text-red-500 !hover-bg-red-50">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-delete"></i>
|
||||
</template>
|
||||
删除
|
||||
</ADoption>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<ADoption value="detail">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-repair"></i>
|
||||
</template>
|
||||
<div>详情</div>
|
||||
</ADoption>
|
||||
<ADoption value="delete" class="!text-red-500 !hover-bg-red-50">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-delete"></i>
|
||||
</template>
|
||||
删除
|
||||
</ADoption>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</template>
|
||||
</AListItem>
|
||||
</AList>
|
||||
|
|
@ -169,15 +167,14 @@ const onRowActionsSelect = () => {
|
|||
padding: 0;
|
||||
}
|
||||
}
|
||||
// .arco-dropdown-list {
|
||||
// padding: 0 4px;
|
||||
// .arco-dropdown-option {
|
||||
// border-radius: 4px;
|
||||
// }
|
||||
// }
|
||||
.arco-list-medium .arco-list-content-wrapper .arco-list-content > .arco-list-item {
|
||||
padding: 4px 20px;
|
||||
}
|
||||
button.arco-btn-text,
|
||||
.arco-btn-text[type="button"],
|
||||
.arco-btn-text[type="submit"] {
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="json">
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ const form = useForm({
|
|||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"sort": 20101,
|
||||
"sort": 10001,
|
||||
"title": "首页111",
|
||||
"icon": "icon-park-outline-home"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,14 +151,9 @@ const table = useTable({
|
|||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"sort": 10301,
|
||||
"sort": 10001,
|
||||
"title": "首页",
|
||||
"icon": "icon-park-outline-home"
|
||||
},
|
||||
"parentMeta": {
|
||||
"title": "总览",
|
||||
"sort": 10000,
|
||||
"icon": "icon-park-outline-home"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
<div>
|
||||
<div class="bg-white px-4 pt-2">
|
||||
<bread-crumb></bread-crumb>
|
||||
<div class="flex justify-between items-end gap-4 bg-white px-1 py-4">
|
||||
<div class="flex justify-between items-end gap-4 bg-white px-1 py-3">
|
||||
<div>
|
||||
<div class="text-lg font-semibold">新增文章</div>
|
||||
<div class="text-gray-400 mt-1">新增的文章需审核才能展现</div>
|
||||
<div class="text-gray-400 mt-1.5">新增的文章需审核才能展现</div>
|
||||
</div>
|
||||
<div>
|
||||
<a-button class="mr-2">保存为草稿</a-button>
|
||||
|
|
@ -40,12 +40,7 @@
|
|||
</a-checkbox-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="封面图">
|
||||
<div class="h-24 rounded w-full flex items-center justify-center text-gray-500 bg-gray-100">
|
||||
从素材库中选择...
|
||||
</div>
|
||||
<template #help>
|
||||
推荐使用 600x400 的图片
|
||||
</template>
|
||||
<a-upload draggable></a-upload>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,25 +21,25 @@
|
|||
<a-form-item label="文件名">
|
||||
<a-input v-model="model.filename" placeholder="请输入"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="导出类型">
|
||||
<a-form-item label="文件类型">
|
||||
<div class="grid gap-2">
|
||||
<div
|
||||
v-for="item in exportTypes"
|
||||
@click="model.exportType = item.name"
|
||||
class="w-full flex justify-between items-center gap-4 rounded py-2 px-4 border cursor-pointer border-slate-200"
|
||||
class="w-full flex justify-between items-center gap-4 rounded py-2 px-4 cursor-pointer bg-[var(--color-fill-2)] hover:bg-[var(--color-fill-3)]"
|
||||
:class="{
|
||||
'!border-brand-500': model.exportType === item.name,
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center gap-2 rounded">
|
||||
<div class="h-10 w-10 flex items-center justify-center rounded-full bg-brand-50">
|
||||
<div class="">
|
||||
<i :class="item.icon" class="text-2xl text-brand-500"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-slate-900">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div class="text-slate-400 text-xs">
|
||||
<div class="text-slate-500 text-xs">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<a-modal title="导入文件" :visible="false" title-align="start">
|
||||
<a-modal title="导入类型" :visible="false" title-align="start">
|
||||
<a-alert> 请按照 <a-link>上传模板</a-link> 中的格式进行填写,上传文件后系统将自动导入数据 </a-alert>
|
||||
<a-upload draggable class="mt-4"></a-upload>
|
||||
</a-modal>
|
||||
|
|
@ -185,26 +185,26 @@ const exportTypes = [
|
|||
name: "excel",
|
||||
icon: "icon-park-outline-file-excel",
|
||||
label: "Excel格式",
|
||||
description: "后缀: .xlsx, 可使用 office excel 2003 及以上版本打开",
|
||||
description: "导出为 .xlsx 文件",
|
||||
},
|
||||
{
|
||||
name: "csv",
|
||||
icon: "icon-park-outline-file-code",
|
||||
label: "CSV格式",
|
||||
description: "后缀: .csv, 可使用 excel 或 记事本等工具打开",
|
||||
description: "导出为 .csv 文件",
|
||||
},
|
||||
{
|
||||
name: "text",
|
||||
icon: "icon-park-outline-file-text",
|
||||
label: "TEXT格式",
|
||||
description: "后缀: .txt, 可使用 记事本 或 其他文本编辑器打开",
|
||||
description: "导出为 .txt 文件",
|
||||
},
|
||||
];
|
||||
|
||||
const model = reactive({
|
||||
visible: false,
|
||||
exportType: "excel",
|
||||
filename: dayjs().format("导出文件YYYYMMDDHHmmss"),
|
||||
filename: dayjs().format("文件YYYYMMDDHHmmss"),
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<script setup lang="tsx">
|
||||
import { api } from "@/api";
|
||||
import { Table, useTable } from "@/components";
|
||||
import { dayjs } from "@/libs";
|
||||
import { dayjs } from "@/libs/dayjs";
|
||||
import { Avatar, Button } from "@arco-design/web-vue";
|
||||
|
||||
const table = useTable({
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<script setup lang="tsx">
|
||||
import { api } from "@/api";
|
||||
import { Table, useTable } from "@/components";
|
||||
import { dayjs } from "@/libs";
|
||||
import { dayjs } from "@/libs/dayjs";
|
||||
|
||||
const table = useTable({
|
||||
data: async (model, paging) => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<script setup lang="tsx">
|
||||
import { api } from "@/api";
|
||||
import { Table, useTable } from "@/components";
|
||||
import { dayjs } from "@/libs";
|
||||
import { dayjs } from "@/libs/dayjs";
|
||||
import { Avatar } from "@arco-design/web-vue";
|
||||
|
||||
const table = useTable({
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
import { NProgress } from "@/libs/nprogress";
|
||||
import { NavigationGuardWithThis, NavigationHookAfter } from "vue-router";
|
||||
|
||||
const before: NavigationGuardWithThis<undefined> = function () {
|
||||
NProgress.start();
|
||||
};
|
||||
|
||||
const after: NavigationHookAfter = function () {
|
||||
NProgress.done();
|
||||
};
|
||||
|
||||
export const nprogressGuard = {
|
||||
before,
|
||||
after,
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { NProgress } from "@/libs/nprogress";
|
||||
import { useAppStore } from "@/store";
|
||||
import { NavigationGuardWithThis, NavigationHookAfter } from "vue-router";
|
||||
|
||||
const routeMap = new Map<string, boolean>();
|
||||
|
||||
const before: NavigationGuardWithThis<undefined> = function (to) {
|
||||
NProgress.start();
|
||||
if (routeMap.get(to.fullPath)) {
|
||||
return true;
|
||||
}
|
||||
const appStore = useAppStore();
|
||||
appStore.setPageLoading(true);
|
||||
};
|
||||
|
||||
const after: NavigationHookAfter = function (to) {
|
||||
NProgress.done();
|
||||
if (routeMap.get(to.fullPath)) {
|
||||
return;
|
||||
}
|
||||
const appStore = useAppStore();
|
||||
setTimeout(() => {
|
||||
appStore.setPageLoading(false);
|
||||
routeMap.set(to.fullPath, true);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
export const progressGuard = {
|
||||
before,
|
||||
after,
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import { authGuard } from "../guards/guard-auth";
|
||||
import { nprogressGuard } from "../guards/guard-nprogress";
|
||||
import { progressGuard } from "../guards/guard-progress";
|
||||
import { titleGuard } from "../guards/guard-title";
|
||||
import { routes } from "../routes";
|
||||
import { api } from "@/api";
|
||||
|
|
@ -16,8 +16,8 @@ export const router = createRouter({
|
|||
],
|
||||
});
|
||||
|
||||
router.beforeEach(nprogressGuard.before);
|
||||
router.afterEach(nprogressGuard.after);
|
||||
router.beforeEach(progressGuard.before);
|
||||
router.afterEach(progressGuard.after);
|
||||
router.beforeEach(authGuard);
|
||||
router.afterEach(titleGuard);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,27 +4,21 @@ import { RouteRecordRaw } from "vue-router";
|
|||
const APP_ROUTE_NAME = "_layout";
|
||||
|
||||
/**
|
||||
* 转换一维路由为二维路由,以 _ 开头的路由为顶级路由,其余为应用路由
|
||||
* 转换一维路由为二维路由
|
||||
* @description 以 _ 开头的路由为顶级路由,其余为应用路由
|
||||
*/
|
||||
const transformRoutes = (routes: RouteRecordRaw[]) => {
|
||||
const topRoutes: RouteRecordRaw[] = [];
|
||||
const appRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
routes.forEach((route) => {
|
||||
for (const route of routes) {
|
||||
if ((route.name as string)?.startsWith("_")) {
|
||||
route.path = route.path.replace("_", "");
|
||||
topRoutes.push(route);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
// route.component = defineAsyncComponent({
|
||||
// loader: route.component as any,
|
||||
// loadingComponent: () => h("div", null, "loading"),
|
||||
// errorComponent: () => h("div", null, "error"),
|
||||
// delay: 200,
|
||||
// timeout: 3000,
|
||||
// });
|
||||
appRoutes.push(route);
|
||||
});
|
||||
}
|
||||
|
||||
const appRoute = routes.find((i) => i.name === APP_ROUTE_NAME);
|
||||
if (appRoute) {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,22 @@ import { defineStore } from "pinia";
|
|||
export const useAppStore = defineStore({
|
||||
id: "app",
|
||||
state: () => ({
|
||||
/**
|
||||
* 是否为暗模式
|
||||
*/
|
||||
isDarkMode: false,
|
||||
/**
|
||||
* 站点标题
|
||||
*/
|
||||
title: import.meta.env.VITE_TITLE,
|
||||
/**
|
||||
* 站点副标题
|
||||
*/
|
||||
subtitle: import.meta.env.VITE_SUBTITLE,
|
||||
/**
|
||||
* 页面是否加载中,用于路由首次加载
|
||||
*/
|
||||
pageLoding: false,
|
||||
}),
|
||||
actions: {
|
||||
/**
|
||||
|
|
@ -30,6 +43,12 @@ export const useAppStore = defineStore({
|
|||
document.body.classList.add("dark");
|
||||
this.isDarkMode = true;
|
||||
},
|
||||
/**
|
||||
* 设置页面加载loading
|
||||
*/
|
||||
setPageLoading(loading: boolean) {
|
||||
this.pageLoding = loading;
|
||||
}
|
||||
},
|
||||
persist: true,
|
||||
persist: !import.meta.env.DEV,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import url('@arco-design/web-vue/es/index.less');
|
||||
@import url("@arco-design/web-vue/es/index.less");
|
||||
|
||||
// @blue-6: #09f;
|
||||
|
||||
|
|
@ -13,9 +13,16 @@ body {
|
|||
background-color: transparent;
|
||||
}
|
||||
.arco-menu-group-title {
|
||||
font-size: 13px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.arco-menu-vertical .arco-menu-item,
|
||||
.arco-menu-vertical .arco-menu-group-title,
|
||||
.arco-menu-vertical .arco-menu-pop-header,
|
||||
.arco-menu-vertical .arco-menu-inline-header {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
.arco-menu {
|
||||
.arco-menu-item {
|
||||
&:hover {
|
||||
|
|
@ -26,6 +33,7 @@ body {
|
|||
}
|
||||
}
|
||||
.arco-menu-inner {
|
||||
padding: 0;
|
||||
.arco-menu-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
|
@ -78,7 +86,8 @@ body {
|
|||
}
|
||||
.arco-menu {
|
||||
.arco-menu-item {
|
||||
&.arco-menu-selected, &:hover {
|
||||
&.arco-menu-selected,
|
||||
&:hover {
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ declare module '@vue/runtime-core' {
|
|||
AImage: typeof import('@arco-design/web-vue')['Image']
|
||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
|
||||
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
||||
|
|
@ -40,7 +41,9 @@ declare module '@vue/runtime-core' {
|
|||
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
||||
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
||||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
||||
ATabs: typeof import('@arco-design/web-vue')['Tabs']
|
||||
|
|
|
|||
Loading…
Reference in New Issue