#DRj#<_Fbdj~g(!Z`!p
z*4!)Ye`XJ~JqYPrXXb{EY)-npKl{+`z~yb$Dw2|X@W#A`9^Op0=FPsCLp|~0o^q?&n@gYMlP?MoMi}Q~k(>Jr@=a
zfj=cxhL0}E%Q5uD99RDm+Uefg&rWLYoRB<~1yAL-c6M6itCu|A{5m+fGOSZvj7?C<
zZy7gzeld1e&7|TPS!%hpz3X-i6UtZH6!O|CMIf-Kx=K+9zC04PW`OF@J6G8W=O^i=
za3P4pVd~MHG6cSul>e}Svmj})+zOP91e|wziSHIBOh-JKpHyj1Te%E2h{?dl?ycAR
zJ`8;NL!KNOO7w
diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue
new file mode 100644
index 0000000..0f054dd
--- /dev/null
+++ b/src/components/Pagination/index.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue
new file mode 100644
index 0000000..803f435
--- /dev/null
+++ b/src/components/SvgIcon/index.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/SwitchDark/index.vue b/src/components/SwitchDark/index.vue
new file mode 100644
index 0000000..c0fe263
--- /dev/null
+++ b/src/components/SwitchDark/index.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts
new file mode 100644
index 0000000..3c1525a
--- /dev/null
+++ b/src/hooks/useTheme.ts
@@ -0,0 +1,26 @@
+import { computed } from 'vue';
+import { useSettingStore } from '@/store/modules/setting';
+
+/**
+ * @description: 全局主题设置
+ */
+export const useTheme = () => {
+ // 获取全局状态管理仓库中系统设置状态
+ const settingStore = useSettingStore();
+ const isDark = computed(() => settingStore.isDark);
+
+ // 切换暗黑模式
+ const switchDark = () => {
+ const body = document.documentElement;
+
+ if (isDark.value) {
+ body.setAttribute('class', 'dark');
+ } else {
+ body.setAttribute('class', '');
+ }
+ };
+
+ return {
+ switchDark,
+ };
+};
diff --git a/src/layouts/Footer/index.vue b/src/layouts/Footer/index.vue
new file mode 100644
index 0000000..127d483
--- /dev/null
+++ b/src/layouts/Footer/index.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/layouts/Header/index.vue b/src/layouts/Header/index.vue
new file mode 100644
index 0000000..08e64bd
--- /dev/null
+++ b/src/layouts/Header/index.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
diff --git a/src/layouts/Main/index.vue b/src/layouts/Main/index.vue
new file mode 100644
index 0000000..28ec7bc
--- /dev/null
+++ b/src/layouts/Main/index.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/Sidebar/components/SubMenu.vue b/src/layouts/Sidebar/components/SubMenu.vue
new file mode 100644
index 0000000..cba106a
--- /dev/null
+++ b/src/layouts/Sidebar/components/SubMenu.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+ {{ subItem.meta?.title }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ subItem.meta?.title }}
+
+
+
+
+
+
+
+
diff --git a/src/layouts/Sidebar/index.vue b/src/layouts/Sidebar/index.vue
new file mode 100644
index 0000000..8a1779e
--- /dev/null
+++ b/src/layouts/Sidebar/index.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
diff --git a/src/layouts/index.vue b/src/layouts/index.vue
new file mode 100644
index 0000000..a299316
--- /dev/null
+++ b/src/layouts/index.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..0432413
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,29 @@
+import { createApp } from 'vue';
+import setupElementPlus from '@/plugins/element'; // 导入element plus
+import setupSvgIcons from '@/plugins/icons'; // 导入svg图标组件
+import { setupRouter } from '@/router';
+import { setupStore } from '@/store';
+import App from './App.vue';
+import './styles/index.scss'; // 全局css
+import 'virtual:svg-icons-register'; // svg-icons注册导入
+
+function bootstrap() {
+ // 创建实例
+ const app = createApp(App);
+
+ // 配置 store
+ setupStore(app);
+
+ // 配置路由
+ setupRouter(app);
+
+ // 导入svg图标
+ setupSvgIcons(app);
+
+ // 导入element和全局注册element 图标
+ setupElementPlus(app);
+
+ app.mount('#app');
+}
+
+bootstrap();
diff --git a/src/plugins/element.ts b/src/plugins/element.ts
new file mode 100644
index 0000000..5f25488
--- /dev/null
+++ b/src/plugins/element.ts
@@ -0,0 +1,16 @@
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'; // 统一导入el-icon图标
+import ElementPlus from 'element-plus';
+import { App } from 'vue';
+import 'element-plus/dist/index.css';
+import 'element-plus/theme-chalk/dark/css-vars.css'; // 暗黑主题
+
+/**
+ * 导入element和全局注册element 图标
+ */
+export default (app: App) => {
+ app.use(ElementPlus);
+ // 统一注册el-icon图标
+ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+ app.component(key, component);
+ }
+};
diff --git a/src/plugins/icons.ts b/src/plugins/icons.ts
new file mode 100644
index 0000000..dc09d07
--- /dev/null
+++ b/src/plugins/icons.ts
@@ -0,0 +1,6 @@
+import { App } from 'vue';
+import SvgIcon from '@/components/SvgIcon/index.vue'; // svg图标组件
+
+export default (app: App) => {
+ app.component('svg-icon', SvgIcon);
+};
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..48a215c
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,53 @@
+import { App } from 'vue';
+import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
+import Layout from '@/layouts/index.vue';
+import boatRoutes from './modules/boat';
+import historyRoutes from './modules/history';
+import manifestRoutes from './modules/manifest';
+import sendRoutes from './modules/send';
+import subscribeRoutes from './modules/subscribe';
+
+const routes: Array = [
+ {
+ path: '/',
+ name: 'layout',
+ component: Layout,
+ redirect: '/home',
+ children: [
+ {
+ path: '/home',
+ name: 'home',
+ component: () => import('@/views/Home/index.vue'),
+ meta: { title: 'home', affix: true },
+ },
+ {
+ path: '/notify',
+ name: 'notify',
+ component: () => import('@/views/Notify/index.vue'),
+ meta: { title: 'notify', affix: true },
+ },
+ {
+ path: '/resource',
+ name: 'resource',
+ component: () => import('@/views/Resource/index.vue'),
+ meta: { title: 'resource', affix: true },
+ },
+ ],
+ },
+ ...boatRoutes,
+ ...manifestRoutes,
+ ...sendRoutes,
+ ...subscribeRoutes,
+ ...historyRoutes,
+];
+
+// 创建一个可以被 Vue 应用程序使用的路由实例
+export const router = createRouter({
+ history: createWebHashHistory(),
+ routes,
+});
+
+// 配置路由器
+export function setupRouter(app: App) {
+ app.use(router);
+}
diff --git a/src/router/modules/boat.ts b/src/router/modules/boat.ts
new file mode 100644
index 0000000..d2111e6
--- /dev/null
+++ b/src/router/modules/boat.ts
@@ -0,0 +1,52 @@
+import { RouteRecordRaw } from 'vue-router';
+import Layout from '@/layouts/index.vue';
+
+/**
+ * 船舶路由
+ */
+const boatRoutes: Array = [
+ {
+ path: '/boat',
+ component: Layout,
+ redirect: '/boat/info',
+ meta: {
+ title: '船舶信息',
+ icon: 'BoatInfo',
+ native: false,
+ },
+ children: [
+ {
+ path: '/boat/info',
+ name: 'boatInfo',
+ component: () => import('@/views/Boat/Info/index.vue'),
+ meta: {
+ title: '船期信息',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ {
+ path: '/boat/query',
+ name: 'boatQuery',
+ component: () => import('@/views/Boat/Query/index.vue'),
+ meta: {
+ title: '船图查询',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ {
+ path: '/boat/track',
+ name: 'boatTrack',
+ component: () => import('@/views/Boat/Track/index.vue'),
+ meta: {
+ title: '船位跟踪',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ ],
+ },
+];
+
+export default boatRoutes;
diff --git a/src/router/modules/history.ts b/src/router/modules/history.ts
new file mode 100644
index 0000000..9863229
--- /dev/null
+++ b/src/router/modules/history.ts
@@ -0,0 +1,42 @@
+import { RouteRecordRaw } from 'vue-router';
+import Layout from '@/layouts/index.vue';
+
+/**
+ * 历史数据路由
+ */
+const historyRoutes: Array = [
+ {
+ path: '/history',
+ component: Layout,
+ redirect: '/history/mine',
+ meta: {
+ title: '历史数据',
+ icon: 'History',
+ native: false,
+ },
+ children: [
+ {
+ path: '/history/mine',
+ name: 'Mine',
+ component: () => import('@/views/History/Mine/index.vue'),
+ meta: {
+ title: '我发布的',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ {
+ path: '/history/receive',
+ name: 'Receive',
+ component: () => import('@/views/History/Receive/index.vue'),
+ meta: {
+ title: '我收到的',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ ],
+ },
+];
+
+export default historyRoutes;
diff --git a/src/router/modules/manifest.ts b/src/router/modules/manifest.ts
new file mode 100644
index 0000000..23a29db
--- /dev/null
+++ b/src/router/modules/manifest.ts
@@ -0,0 +1,53 @@
+import { RouteRecordRaw } from 'vue-router';
+import Layout from '@/layouts/index.vue';
+
+/**
+ * 舱单路由
+ */
+const manifestRoutes: Array = [
+ {
+ path: '/manifest',
+ component: Layout,
+ redirect: '/manifest/manage',
+ meta: {
+ title: '舱单信息',
+ icon: 'ManifestInfo',
+ native: false,
+ },
+ children: [
+ {
+ path: '/manifest/manage',
+ name: 'manifestManage',
+ component: () => import('@/views/Manifest/Manage/index.vue'),
+ meta: {
+ title: '舱单信息',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+
+ {
+ path: '/manifest/detail',
+ name: 'manifestDetail',
+ component: () => import('@/views/Manifest/Detail/index.vue'),
+ meta: {
+ title: '提单明细',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ {
+ path: '/manifest/stowage',
+ name: 'manifestStowage',
+ component: () => import('@/views/Manifest/Stowage/index.vue'),
+ meta: {
+ title: '积载图',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ ],
+ },
+];
+
+export default manifestRoutes;
diff --git a/src/router/modules/send.ts b/src/router/modules/send.ts
new file mode 100644
index 0000000..ac58182
--- /dev/null
+++ b/src/router/modules/send.ts
@@ -0,0 +1,23 @@
+import { RouteRecordRaw } from 'vue-router';
+import Layout from '@/layouts/index.vue';
+
+/**
+ * 发布信息路由
+ */
+const sendRoutes: Array = [
+ {
+ path: '/send',
+ component: Layout,
+ redirect: '/send/index',
+ children: [
+ {
+ path: '/send/index',
+ name: 'send',
+ component: () => import('@/views/Send/index.vue'),
+ meta: { title: '发布信息', icon: 'SendInfo', native: false },
+ },
+ ],
+ },
+];
+
+export default sendRoutes;
diff --git a/src/router/modules/subscribe.ts b/src/router/modules/subscribe.ts
new file mode 100644
index 0000000..c095fd4
--- /dev/null
+++ b/src/router/modules/subscribe.ts
@@ -0,0 +1,42 @@
+import { RouteRecordRaw } from 'vue-router';
+import Layout from '@/layouts/index.vue';
+
+/**
+ * 订阅信息路由
+ */
+const subscribeRoutes: Array = [
+ {
+ path: '/subscribe',
+ component: Layout,
+ redirect: '/subscribe/mine',
+ meta: {
+ title: '订阅信息',
+ icon: 'SubscribeInfo',
+ native: false,
+ },
+ children: [
+ {
+ path: '/subscribe/mine',
+ name: 'Mine',
+ component: () => import('@/views/Subscribe/Mine/index.vue'),
+ meta: {
+ title: '我的订阅',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ {
+ path: '/subscribe/receive',
+ name: 'Receive',
+ component: () => import('@/views/Subscribe/Receive/index.vue'),
+ meta: {
+ title: '收到订阅',
+ icon: 'Menu',
+ native: true,
+ },
+ },
+ ],
+ },
+];
+
+export default subscribeRoutes;
diff --git a/src/router/permission.ts b/src/router/permission.ts
new file mode 100644
index 0000000..80a7bec
--- /dev/null
+++ b/src/router/permission.ts
@@ -0,0 +1,76 @@
+import NProgress from 'nprogress'; // 进度条
+import { router } from '@/router';
+import 'nprogress/nprogress.css'; // 进度条样式
+
+// 进度条配置
+NProgress.configure({ showSpinner: false });
+
+// 白名单-不需要权限验证
+const whiteList = ['/login'];
+
+/**
+ * 路由前置守卫
+ * @param to 要到哪里去
+ * @param from 从哪里来
+ * @param next 是否要去
+ */
+router.beforeEach(async (to, from, next) => {
+ // 进度条开始
+ NProgress.start();
+
+ // 设置标题
+ if (typeof to.meta.title === 'string') {
+ document.title = to.meta.title || import.meta.env.VITE_APP_TITLE;
+ }
+
+ // 获取用户信息 store
+ // const userStore = useUserStore();
+ // 获取用户是否登录状态,确定用户是否已登录过,存在Token
+ // const hasToken = userStore.token;
+
+ if (false) {
+ // 用户登录
+ if (to.path === '/login') {
+ // 如果已登录,重定向到主页
+ next({ path: '/' });
+ } else {
+ // 获取用户权限 store
+ // const permissionStore = usePermissionStore();
+ // 确定用户是否已通过getInfo获得其权限角色
+ // const hasRoles = userStore.roles && userStore.roles.length > 0;
+ try {
+ // // 路由添加进去了没有及时更新 需要重新进去一次拦截
+ // if (!permissionStore.routes.length) {
+ // // 获取权限列表进行接口访问 因为这里页面要切换权限
+ // const accessRoutes = await permissionStore.generateRoutes(userStore.roles);
+
+ // // 动态添加访问路由表
+ // accessRoutes.forEach((item) => router.addRoute(item));
+
+ // // 这里相当于push到一个页面 不在进入路由拦截
+ // next({ ...to, replace: true });
+ // } else {
+ // // 如果不传参数就会重新执行路由拦截,重新进到这里
+ // next();
+ // }
+ next();
+ } catch (error) {
+ next(`/login?redirect=${to.path}`);
+ }
+ }
+ } else {
+ // 用户未登录
+ if (whiteList.indexOf(to.path) !== -1) {
+ // 在免登录白名单中,直接进入
+ next();
+ } else {
+ // 没有访问权限的其他页面将重定向到登录页面
+ next(`/login?redirect=${to.path}`);
+ }
+ }
+});
+
+router.afterEach(() => {
+ // 进度条结束
+ NProgress.done();
+});
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..674738d
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,12 @@
+import { createPinia } from 'pinia';
+import { App } from 'vue';
+
+// 创建pinia实例
+const store = createPinia();
+
+// 配置pinia
+export function setupStore(app: App) {
+ app.use(store);
+}
+
+export { store };
diff --git a/src/store/modules/setting.ts b/src/store/modules/setting.ts
new file mode 100644
index 0000000..586e895
--- /dev/null
+++ b/src/store/modules/setting.ts
@@ -0,0 +1,23 @@
+import { defineStore } from 'pinia';
+
+interface settingsStateType {
+ isDark: boolean;
+}
+
+export const useSettingStore = defineStore({
+ // id: 必须的,在所有 Store 中唯一
+ id: 'settingState',
+
+ // state: 返回对象的函数
+ state: (): settingsStateType => ({
+ isDark: false, // 深色模式 切换暗黑模式
+ }),
+
+ // actions: 可以同步 也可以异步
+ actions: {
+ // 设置暗黑模式
+ setThemeDark(value: boolean) {
+ this.isDark = value;
+ },
+ },
+});
diff --git a/src/styles/index.scss b/src/styles/index.scss
new file mode 100644
index 0000000..3efbde9
--- /dev/null
+++ b/src/styles/index.scss
@@ -0,0 +1,32 @@
+@import './reset.scss';
+@import './transition.scss';
+html,
+body {
+ height: 100%;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
+ Arial, sans-serif;
+}
+label {
+ font-weight: 700;
+}
+html {
+ box-sizing: border-box;
+ height: 100%;
+}
+body {
+ background: #f0f2f5;
+}
+#app {
+ height: 100%;
+}
+
+/* 自定义暗黑模式样式 */
+html.dark {
+ body {
+ color: #fff;
+ background-color: #222;
+ }
+}
diff --git a/src/styles/reset.scss b/src/styles/reset.scss
new file mode 100644
index 0000000..e7fc595
--- /dev/null
+++ b/src/styles/reset.scss
@@ -0,0 +1,126 @@
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ padding: 0;
+ margin: 0;
+ font: inherit;
+ font-size: 100%;
+ vertical-align: baseline;
+ border: 0;
+}
+
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+body {
+ padding: 0;
+ margin: 0;
+}
+ol,
+ul {
+ list-style: none;
+}
+blockquote,
+q {
+ quotes: none;
+}
+blockquote::before,
+blockquote::after,
+q::before,
+q::after {
+ content: '';
+ content: none;
+}
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
diff --git a/src/styles/transition.scss b/src/styles/transition.scss
new file mode 100644
index 0000000..628918a
--- /dev/null
+++ b/src/styles/transition.scss
@@ -0,0 +1,38 @@
+// 全局动画 css
+
+// logo动画
+.sidebarLogoFade-enter-active {
+ transition: opacity 2s;
+}
+.sidebarLogoFade-enter-from,
+.sidebarLogoFade-leave-to {
+ opacity: 0;
+}
+
+// 面包屑动画
+.breadcrumb-enter-active,
+.breadcrumb-leave-active {
+ transition: all 0.5s;
+}
+.breadcrumb-enter-from,
+.breadcrumb-leave-active {
+ opacity: 0;
+ transform: translateX(10px) skewX(-10deg);
+}
+.breadcrumb-leave-active {
+ position: absolute;
+}
+
+// 路由
+.fade-slide-leave-active,
+.fade-slide-enter-active {
+ transition: all 0.3s;
+}
+.fade-slide-enter-from {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+.fade-slide-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
diff --git a/src/utils/routers.ts b/src/utils/routers.ts
new file mode 100644
index 0000000..5785aa3
--- /dev/null
+++ b/src/utils/routers.ts
@@ -0,0 +1,86 @@
+import path from 'path-browserify';
+import { RouteRecordRaw } from 'vue-router';
+
+/**
+ * @description: 返回所有子路由
+ * @param {RouteRecordRaw[]} routes 路由数组
+ * @return 子路由数组
+ */
+const getChildrenRoutes = (routes: RouteRecordRaw[]) => {
+ const result: RouteRecordRaw[] = [];
+ routes.forEach((route) => {
+ if (route.children && route.children.length > 0) {
+ result.push(...route.children);
+ }
+ });
+ return result;
+};
+
+/**
+ * @description: 处理脱离层级的路由:某个一级路由为其他子路由,则剔除该一级路由,保留路由层级
+ * @param {RouteRecordRaw[]} routes
+ * @return 符合条件的路由
+ */
+export const filterRoutes = (routes: RouteRecordRaw[]) => {
+ const childrenRoutes = getChildrenRoutes(routes);
+ return routes.filter((route) => {
+ return !childrenRoutes.find((childrenRoute) => {
+ return childrenRoute.path === route.path;
+ });
+ });
+};
+
+/**
+ * @description: 判断数据是否为空值
+ * @param {any} data 数据
+ * @return 判断结果-是否为空
+ */
+const isNull = (data: any) => {
+ if (!data) return true;
+ if (JSON.stringify(data) === '{}') return true;
+ if (JSON.stringify(data) === '[]') return true;
+ return false;
+};
+
+/**
+ * @description: 根据routes数据,返回对应的menu规则数据
+ * @param {RouteRecordRaw[]} routes
+ * @param {string} basePath 基础路径
+ * @return 对应的menu规则数据
+ */
+export const generateMenus = (routes: RouteRecordRaw[], basePath = '') => {
+ const result: RouteRecordRaw[] = [];
+ // 不满足条件:meta && meta.title && meta.icon 的数据不应该存在
+ routes.forEach((item) => {
+ // 不存在children && meta ,直接返回
+ if (isNull(item.children) && isNull(item.meta)) return;
+ // 存在children,不存在meta,迭代 generateMenus
+ if (isNull(item.meta) && !isNull(item.children)) {
+ result.push(...generateMenus(item.children as RouteRecordRaw[])); // 断言children为Array类型
+ return;
+ }
+ // 不存在 children,存在meta || 存在children,也存在meta
+ // 因为最终的menu需要进行跳转,此时我们需要合并path
+ const routePath = path.resolve(basePath, item.path);
+ // 路由分离之后,可能存在同名父路由的情况
+ let route = result.find((item) => item.path === routePath);
+ // 当前路由尚未加入到result
+ if (!route) {
+ route = {
+ ...item,
+ path: routePath,
+ children: [],
+ };
+ // 路由icon和title存在
+ if (route.meta?.icon && route.meta.title) {
+ result.push(route);
+ }
+ }
+
+ // 存在children 和 meta
+ if (!isNull(item.children)) {
+ route.children?.push(...generateMenus(item.children as RouteRecordRaw[], route.path));
+ }
+ });
+ return result;
+};
diff --git a/src/views/Boat/Info/components/InfoTable.vue b/src/views/Boat/Info/components/InfoTable.vue
new file mode 100644
index 0000000..1bd6803
--- /dev/null
+++ b/src/views/Boat/Info/components/InfoTable.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/Boat/Info/index.vue b/src/views/Boat/Info/index.vue
new file mode 100644
index 0000000..c400b6b
--- /dev/null
+++ b/src/views/Boat/Info/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/Boat/Query/index.vue b/src/views/Boat/Query/index.vue
new file mode 100644
index 0000000..6038aa4
--- /dev/null
+++ b/src/views/Boat/Query/index.vue
@@ -0,0 +1,12 @@
+
+ 查询
+
+
+
+
+
diff --git a/src/views/Boat/Track/index.vue b/src/views/Boat/Track/index.vue
new file mode 100644
index 0000000..a13c33c
--- /dev/null
+++ b/src/views/Boat/Track/index.vue
@@ -0,0 +1,12 @@
+
+ 跟踪
+
+
+
+
+
diff --git a/src/views/History/Mine/index.vue b/src/views/History/Mine/index.vue
new file mode 100644
index 0000000..8bf2edb
--- /dev/null
+++ b/src/views/History/Mine/index.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/src/views/History/Receive/index.vue b/src/views/History/Receive/index.vue
new file mode 100644
index 0000000..1b87da3
--- /dev/null
+++ b/src/views/History/Receive/index.vue
@@ -0,0 +1,12 @@
+
+ 收到的订阅
+
+
+
+
+
diff --git a/src/views/Home/components/Card.vue b/src/views/Home/components/Card.vue
new file mode 100644
index 0000000..5cb2671
--- /dev/null
+++ b/src/views/Home/components/Card.vue
@@ -0,0 +1,52 @@
+
+
+
{{ cardData.title }}
+
{{ cardData.number }}
+
+
{{ cardData.totalTitle }}
+
{{ cardData.totalNumber }}
+
+
+
+
+
+
+
diff --git a/src/views/Home/components/HomeChart.vue b/src/views/Home/components/HomeChart.vue
new file mode 100644
index 0000000..c02767a
--- /dev/null
+++ b/src/views/Home/components/HomeChart.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/src/views/Home/index.vue b/src/views/Home/index.vue
new file mode 100644
index 0000000..94c0560
--- /dev/null
+++ b/src/views/Home/index.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/Manifest/Detail/index.vue b/src/views/Manifest/Detail/index.vue
new file mode 100644
index 0000000..33bbf3c
--- /dev/null
+++ b/src/views/Manifest/Detail/index.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/src/views/Manifest/Manage/index.vue b/src/views/Manifest/Manage/index.vue
new file mode 100644
index 0000000..a2b3542
--- /dev/null
+++ b/src/views/Manifest/Manage/index.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/src/views/Manifest/Stowage/index.vue b/src/views/Manifest/Stowage/index.vue
new file mode 100644
index 0000000..cb54f83
--- /dev/null
+++ b/src/views/Manifest/Stowage/index.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/src/views/Notify/index.vue b/src/views/Notify/index.vue
new file mode 100644
index 0000000..958d3a4
--- /dev/null
+++ b/src/views/Notify/index.vue
@@ -0,0 +1,91 @@
+
+
+
+
+ -
+
公告标题
+ 2022-01-01
+
+
+
+
+
平台公告
+
+
+
+
+ Content
+
+
+
+
+
+
+
+
diff --git a/src/views/Resource/index.vue b/src/views/Resource/index.vue
new file mode 100644
index 0000000..cd8745f
--- /dev/null
+++ b/src/views/Resource/index.vue
@@ -0,0 +1,12 @@
+
+ resource
+
+
+
+
+
diff --git a/src/views/Send/index.vue b/src/views/Send/index.vue
new file mode 100644
index 0000000..5dcff0c
--- /dev/null
+++ b/src/views/Send/index.vue
@@ -0,0 +1,12 @@
+
+ 发布信息
+
+
+
+
+
diff --git a/src/views/Subscribe/Mine/index.vue b/src/views/Subscribe/Mine/index.vue
new file mode 100644
index 0000000..8bf2edb
--- /dev/null
+++ b/src/views/Subscribe/Mine/index.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/src/views/Subscribe/Receive/index.vue b/src/views/Subscribe/Receive/index.vue
new file mode 100644
index 0000000..1b87da3
--- /dev/null
+++ b/src/views/Subscribe/Receive/index.vue
@@ -0,0 +1,12 @@
+
+ 收到的订阅
+
+
+
+
+
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..af36df3
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1,7 @@
+///
+
+declare module '*.vue' {
+ import type { DefineComponent } from 'vue';
+ const component: DefineComponent<{}, {}, any>;
+ export default component;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..67ce0b0
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "strict": true,
+ "jsx": "preserve",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "lib": ["ESNext", "DOM"],
+ "skipLibCheck": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..9d31e2a
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..590a4ae
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,28 @@
+import vue from '@vitejs/plugin-vue';
+import path from 'path';
+import { defineConfig } from 'vite';
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
+
+function resolve(dir: string) {
+ return path.join(__dirname, '.', dir);
+}
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ vue(),
+ // 使用 svg 图标
+ createSvgIconsPlugin({
+ // 指定需要缓存的图标文件夹
+ iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
+ // 指定symbolId格式
+ symbolId: 'icon-[dir]-[name]',
+ }),
+ ],
+ // 配置别名
+ resolve: {
+ alias: {
+ '@': resolve('src'), // 设置 `@` 指向 `src` 目录
+ },
+ },
+});