2022-04-04 00:24:45 +02:00
|
|
|
<script lang="ts" setup>
|
|
|
|
import { useRoute } from 'vue-router';
|
2022-04-22 23:31:40 +02:00
|
|
|
import { useHead } from '@vueuse/head';
|
|
|
|
import type { HeadObject } from '@vueuse/head';
|
2024-06-25 09:10:28 +02:00
|
|
|
import VueMarkdown from 'vue-markdown-render';
|
2023-06-10 17:14:50 +02:00
|
|
|
|
2023-05-28 23:13:24 +02:00
|
|
|
import BaseLayout from './base.layout.vue';
|
2022-12-17 15:33:52 +01:00
|
|
|
import FavoriteButton from '@/components/FavoriteButton.vue';
|
|
|
|
import type { Tool } from '@/tools/tools.types';
|
2022-04-04 00:24:45 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
const route = useRoute();
|
2022-04-04 00:24:45 +02:00
|
|
|
|
2022-06-02 00:58:31 +02:00
|
|
|
const head = computed<HeadObject>(() => ({
|
2022-04-22 23:31:40 +02:00
|
|
|
title: `${route.meta.name} - IT Tools`,
|
|
|
|
meta: [
|
|
|
|
{
|
|
|
|
name: 'description',
|
2022-12-17 15:33:52 +01:00
|
|
|
content: route.meta?.description as string,
|
2022-04-22 23:31:40 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'keywords',
|
2022-12-17 15:33:52 +01:00
|
|
|
content: ((route.meta.keywords ?? []) as string[]).join(','),
|
2022-04-22 23:31:40 +02:00
|
|
|
},
|
|
|
|
],
|
2022-06-02 00:58:31 +02:00
|
|
|
}));
|
2022-04-22 23:31:40 +02:00
|
|
|
useHead(head);
|
2023-06-19 23:14:44 +02:00
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
|
|
const i18nKey = computed<string>(() => route.path.trim().replace('/', ''));
|
|
|
|
const toolTitle = computed<string>(() => t(`tools.${i18nKey.value}.title`, String(route.meta.name)));
|
|
|
|
const toolDescription = computed<string>(() => t(`tools.${i18nKey.value}.description`, String(route.meta.description)));
|
2024-06-25 09:10:28 +02:00
|
|
|
const toolFooter = computed<string>(() => {
|
|
|
|
const createLink = (linkText: string, url: string) => {
|
|
|
|
return `[${linkText.replace('[', '\\[').replace(']', '\\]')}](${url.replace('(', '%28').replace(')', '%29')})`;
|
|
|
|
};
|
|
|
|
const footer = t(`tools.${i18nKey.value}.footer`, String(route.meta.footer));
|
|
|
|
const npmPackages = (route.meta.npmPackages as string[] || [])
|
|
|
|
.map(
|
|
|
|
packageName => createLink(
|
|
|
|
packageName,
|
|
|
|
packageName.includes('://') ? packageName : `https://www.npmjs.com/package/${packageName}`),
|
|
|
|
);
|
|
|
|
return ((npmPackages.length > 0 ? `Made with ${npmPackages.join(', ')}\n` : '') + footer).trim();
|
|
|
|
});
|
2022-04-04 00:24:45 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2023-05-28 23:13:24 +02:00
|
|
|
<BaseLayout>
|
2022-04-15 23:10:47 +02:00
|
|
|
<div class="tool-layout">
|
|
|
|
<div class="tool-header">
|
2023-05-27 17:36:15 +02:00
|
|
|
<div flex flex-nowrap items-center justify-between>
|
2022-12-17 15:33:52 +01:00
|
|
|
<n-h1>
|
2023-06-19 23:14:44 +02:00
|
|
|
{{ toolTitle }}
|
2022-12-17 15:33:52 +01:00
|
|
|
</n-h1>
|
|
|
|
|
|
|
|
<div>
|
2023-05-28 23:13:24 +02:00
|
|
|
<FavoriteButton :tool="{ name: route.meta.name } as Tool" />
|
2022-12-17 15:33:52 +01:00
|
|
|
</div>
|
2023-05-27 17:36:15 +02:00
|
|
|
</div>
|
2022-06-01 23:52:21 +02:00
|
|
|
|
2022-04-15 23:10:47 +02:00
|
|
|
<div class="separator" />
|
2022-12-17 15:33:52 +01:00
|
|
|
|
2022-04-15 23:10:47 +02:00
|
|
|
<div class="description">
|
2023-06-19 23:14:44 +02:00
|
|
|
{{ toolDescription }}
|
2022-04-04 00:24:45 +02:00
|
|
|
</div>
|
2022-04-15 23:10:47 +02:00
|
|
|
</div>
|
2022-04-24 23:09:12 +02:00
|
|
|
</div>
|
2022-04-15 23:10:47 +02:00
|
|
|
|
2022-04-24 23:09:12 +02:00
|
|
|
<div class="tool-content">
|
2022-04-15 23:10:47 +02:00
|
|
|
<slot />
|
|
|
|
</div>
|
2024-06-25 09:10:28 +02:00
|
|
|
|
|
|
|
<div class="tool-footer">
|
|
|
|
<VueMarkdown :source="toolFooter" />
|
|
|
|
</div>
|
2023-05-28 23:13:24 +02:00
|
|
|
</BaseLayout>
|
2022-04-04 00:24:45 +02:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
2022-04-24 23:09:12 +02:00
|
|
|
.tool-content {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: row;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: flex-start;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
gap: 16px;
|
2024-06-25 09:10:28 +02:00
|
|
|
overflow-x: auto;
|
2022-04-24 23:09:12 +02:00
|
|
|
|
|
|
|
::v-deep(& > *) {
|
|
|
|
flex: 0 1 600px;
|
2024-06-25 09:10:28 +02:00
|
|
|
min-width:0;
|
2022-04-24 23:09:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-04 00:24:45 +02:00
|
|
|
.tool-layout {
|
2022-04-24 23:09:12 +02:00
|
|
|
max-width: 600px;
|
2022-04-22 23:31:40 +02:00
|
|
|
margin: 0 auto;
|
|
|
|
box-sizing: border-box;
|
2022-04-04 00:24:45 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
.tool-header {
|
|
|
|
padding: 40px 0;
|
2022-04-24 23:09:12 +02:00
|
|
|
width: 100%;
|
2022-04-15 12:21:09 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
.n-h1 {
|
|
|
|
opacity: 0.9;
|
|
|
|
font-size: 40px;
|
|
|
|
font-weight: 400;
|
|
|
|
margin: 0;
|
|
|
|
line-height: 1;
|
|
|
|
}
|
2022-04-15 12:21:09 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
.separator {
|
|
|
|
width: 200px;
|
|
|
|
height: 2px;
|
|
|
|
background: rgb(161, 161, 161);
|
2022-12-17 15:33:52 +01:00
|
|
|
opacity: 0.2;
|
2022-04-04 00:24:45 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
margin: 10px 0;
|
|
|
|
}
|
2022-04-15 12:21:09 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
.description {
|
|
|
|
margin: 0;
|
2022-04-04 00:24:45 +02:00
|
|
|
|
2022-04-22 23:31:40 +02:00
|
|
|
opacity: 0.7;
|
2022-04-04 00:24:45 +02:00
|
|
|
}
|
2022-04-22 23:31:40 +02:00
|
|
|
}
|
2024-06-25 09:10:28 +02:00
|
|
|
.tool-footer {
|
|
|
|
opacity: 0.7;
|
|
|
|
font-size: 12px;
|
|
|
|
}
|
2022-04-04 00:24:45 +02:00
|
|
|
}
|
2022-04-22 23:31:40 +02:00
|
|
|
</style>
|