aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.vue8
-rw-r--r--src/composable/copy.ts15
-rw-r--r--src/layouts/base.layout.vue88
-rw-r--r--src/layouts/index.ts2
-rw-r--r--src/layouts/tool.layout.vue68
-rw-r--r--src/main.ts2
-rw-r--r--src/plugins/naive.plugin.ts10
-rw-r--r--src/router.ts3
-rw-r--r--src/tools/Tool.ts7
-rw-r--r--src/tools/index.ts8
10 files changed, 192 insertions, 19 deletions
diff --git a/src/App.vue b/src/App.vue
index c7c24ef..abbb362 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -16,9 +16,11 @@ const layout = computed(() => route?.meta?.layout ?? layouts.base)
<template>
<n-config-provider>
<n-global-style />
- <component :is="layout">
- <router-view />
- </component>
+ <n-message-provider placement="bottom">
+ <component :is="layout">
+ <router-view />
+ </component>
+ </n-message-provider>
</n-config-provider>
</template>
diff --git a/src/composable/copy.ts b/src/composable/copy.ts
new file mode 100644
index 0000000..fb29be5
--- /dev/null
+++ b/src/composable/copy.ts
@@ -0,0 +1,15 @@
+import { useClipboard } from '@vueuse/core';
+import { useMessage } from 'naive-ui';
+import type { Ref } from 'vue';
+
+export function useCopy({ source, text = 'Copied to the clipboard' }: { source: Ref; text?: string }) {
+ const { copy } = useClipboard({ source });
+ const message = useMessage();
+
+ return {
+ async copy() {
+ await copy();
+ message.success(text);
+ },
+ };
+}
diff --git a/src/layouts/base.layout.vue b/src/layouts/base.layout.vue
index bfbe5da..f2c601b 100644
--- a/src/layouts/base.layout.vue
+++ b/src/layouts/base.layout.vue
@@ -1,19 +1,85 @@
-<script lang="ts">
-export default {
- name: 'base-layout'
-}
+<script lang="ts" setup>
+import { NIcon } from 'naive-ui';
+import { h, ref, type Component } from 'vue';
+import { RouterLink, useRoute } from 'vue-router';
+import { User } from '@vicons/tabler'
+import { tools } from '@/tools';
+
+const collapsed = ref(false)
+const activeKey = ref(null)
+const route = useRoute()
+
+const makeLabel = (text: string, to: string) => () => h(RouterLink, { to }, { default: () => text })
+const makeIcon = (icon: Component) => () => h(NIcon, null, { default: () => h(icon) })
+
+const menuOptions = tools.map(({ name, path, icon }) => ({
+ label: makeLabel(name, path),
+ key: name,
+ icon: makeIcon(icon)
+}))
</script>
<template>
- <div class="base-layout">
- <slot />
- </div>
+ <n-layout has-sider>
+ <n-layout-sider
+ bordered
+ collapse-mode="width"
+ :collapsed-width="64"
+ :width="240"
+ :collapsed="collapsed"
+ show-trigger
+ @collapse="collapsed = true"
+ @expand="collapsed = false"
+ >
+ <router-link
+ to="/"
+ style="text-decoration: none; color: grey; display: block; text-align: center; margin:25px 0; font-size: 25px;"
+ >
+ <strong>IT-Tools</strong>
+ </router-link>
+
+ <n-menu
+ :value="route.name"
+ class="menu"
+ :collapsed="collapsed"
+ :collapsed-width="64"
+ :collapsed-icon-size="22"
+ :options="menuOptions"
+ v-model:value="activeKey"
+ />
+ </n-layout-sider>
+ <n-layout class="content">
+ <div class="bar-wrapper">
+ <n-input />
+ <n-button secondary circle>
+ <n-icon size="large">
+ <user />
+ </n-icon>
+ </n-button>
+ </div>
+ <slot />
+ </n-layout>
+ </n-layout>
</template>
<style lang="less" scoped>
-.base-layout {
- width: 100%;
- min-height: 100vh;
- background-color: #f4f6fa;
+.bar-wrapper {
+ display: flex;
+ & > *:not(:first-child) {
+ margin-left: 15px;
+ }
+ & > :first-child {
+ flex-grow: 1;
+ }
+}
+
+.content {
+ background-color: #f1f5f9;
+ ::v-deep(.n-layout-scroll-container) {
+ padding: 26px;
+ }
+}
+.n-layout {
+ height: 100vh;
}
</style> \ No newline at end of file
diff --git a/src/layouts/index.ts b/src/layouts/index.ts
index 736164a..8f52b62 100644
--- a/src/layouts/index.ts
+++ b/src/layouts/index.ts
@@ -1,5 +1,7 @@
import BaseLayout from './base.layout.vue';
+import ToolLayout from './tool.layout.vue';
export const layouts = {
base: BaseLayout,
+ toolLayout: ToolLayout,
};
diff --git a/src/layouts/tool.layout.vue b/src/layouts/tool.layout.vue
new file mode 100644
index 0000000..01c8cc1
--- /dev/null
+++ b/src/layouts/tool.layout.vue
@@ -0,0 +1,68 @@
+<script lang="ts" setup>
+import { useRoute } from 'vue-router';
+import BaseLayout from './base.layout.vue';
+import { useHead } from '@vueuse/head'
+import type { HeadObject } from '@vueuse/head'
+import { reactive } from 'vue';
+
+const route = useRoute()
+
+const head = reactive<HeadObject>({
+ title: `${route.meta.name} - IT Tools`,
+ meta: [
+ {
+ name: 'description',
+ content: route.meta.description
+ },
+ {
+ name: 'keywords',
+ content: route.meta.keywords
+ }
+ ]
+})
+useHead(head)
+</script>
+
+<template>
+ <base-layout>
+ <div class="tool-layout">
+ <div class="tool-header">
+ <n-h1>{{ route.meta.name }}</n-h1>
+ <div class="separator" />
+ <div class="description">{{ route.meta.description }}</div>
+ </div>
+
+ <slot />
+ </div>
+ </base-layout>
+</template>
+
+<style lang="less" scoped>
+.tool-layout {
+ max-width: 700px;
+ margin: 0 auto;
+ box-sizing: border-box;
+
+ .tool-header {
+ padding: 40px 0;
+ .n-h1 {
+ opacity: 0.9;
+ font-size: 40px;
+ font-weight: 400;
+ margin: 0;
+ }
+ .separator {
+ width: 200px;
+ height: 2px;
+ background: rgb(161, 161, 161);
+
+ margin-bottom: 10px;
+ }
+ .description {
+ margin: 0;
+
+ opacity: 0.7;
+ }
+ }
+}
+</style> \ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 36230d2..7377786 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,6 @@
import { createApp } from 'vue';
import { createPinia } from 'pinia';
+import { createHead } from '@vueuse/head';
import { naive } from './plugins/naive.plugin';
@@ -9,6 +10,7 @@ import router from './router';
const app = createApp(App);
app.use(createPinia());
+app.use(createHead());
app.use(router);
app.use(naive);
diff --git a/src/plugins/naive.plugin.ts b/src/plugins/naive.plugin.ts
index 5d36387..50bb9b5 100644
--- a/src/plugins/naive.plugin.ts
+++ b/src/plugins/naive.plugin.ts
@@ -33,9 +33,19 @@ import {
NP,
NH2,
NDropdown,
+ NLayout,
+ NLayoutSider,
+ NMenu,
+ NMessageProvider,
+ NPageHeader,
} from 'naive-ui';
const components = [
+ NPageHeader,
+ NMessageProvider,
+ NLayout,
+ NLayoutSider,
+ NMenu,
NDropdown,
NH2,
NP,
diff --git a/src/router.ts b/src/router.ts
index 30565bb..00f39c8 100644
--- a/src/router.ts
+++ b/src/router.ts
@@ -1,3 +1,4 @@
+import { layouts } from './layouts/index';
import { createRouter, createWebHistory } from 'vue-router';
import HomePage from './pages/Home.page.vue';
import { tools } from './tools';
@@ -10,7 +11,7 @@ const router = createRouter({
name: 'home',
component: HomePage,
},
- ...Object.values(tools).flat(),
+ ...tools.map(({ path, name, component, ...config }) => ({ path, name, component, meta: { isTool: true, layout: layouts.toolLayout, name, ...config } })),
],
});
diff --git a/src/tools/Tool.ts b/src/tools/Tool.ts
index e452fdd..9ccd2d7 100644
--- a/src/tools/Tool.ts
+++ b/src/tools/Tool.ts
@@ -6,4 +6,11 @@ export interface ITool {
description: string;
keywords: string[];
component: () => Promise<Component>;
+ icon: Component;
+}
+
+export interface ToolCategory {
+ name: string;
+ icon: Component;
+ components: ITool[];
}
diff --git a/src/tools/index.ts b/src/tools/index.ts
index 0db7d63..1bcd931 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -1,5 +1,5 @@
-import { tool as tokenGenerator } from './token-generator';
+import type { ToolCategory } from './Tool';
-export const tools = {
- crypto: [tokenGenerator],
-};
+export const toolsByCategory: ToolCategory[] = [];
+
+export const tools = toolsByCategory.flatMap(({ components }) => components);