mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-04 21:37:11 -04:00
144 lines
3.3 KiB
Vue
144 lines
3.3 KiB
Vue
<script lang="ts" setup>
|
|
import { useRoute } from 'vue-router';
|
|
import { useHead } from '@vueuse/head';
|
|
import type { HeadObject } from '@vueuse/head';
|
|
import VueMarkdown from 'vue-markdown-render';
|
|
|
|
import BaseLayout from './base.layout.vue';
|
|
import FavoriteButton from '@/components/FavoriteButton.vue';
|
|
import type { Tool } from '@/tools/tools.types';
|
|
import { useTheme } from '@/ui/c-link/c-link.theme';
|
|
import { useThemeVars } from 'naive-ui';
|
|
|
|
const route = useRoute();
|
|
|
|
const head = computed<HeadObject>(() => ({
|
|
title: `${route.meta.name} - IT Tools`,
|
|
meta: [
|
|
{
|
|
name: 'description',
|
|
content: route.meta?.description as string,
|
|
},
|
|
{
|
|
name: 'keywords',
|
|
content: ((route.meta.keywords ?? []) as string[]).join(','),
|
|
},
|
|
],
|
|
}));
|
|
useHead(head);
|
|
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)));
|
|
const toolFooter = computed<string>(() => {
|
|
const createLink = (linkText: string, url: string) => {
|
|
return `[${linkText.replace('[', '\\[').replace(']', '\\]')}](${url.replace('(', '%28').replace(')', '%29')})`;
|
|
};
|
|
let footer = t(`tools.${i18nKey.value}.footer`, String(route.meta.footer));
|
|
if (footer === 'undefined') {
|
|
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();
|
|
});
|
|
const themeVars = useThemeVars();
|
|
</script>
|
|
|
|
<template>
|
|
<BaseLayout>
|
|
<div class="tool-layout">
|
|
<div class="tool-header">
|
|
<div flex flex-nowrap items-center justify-between>
|
|
<n-h1>
|
|
{{ toolTitle }}
|
|
</n-h1>
|
|
|
|
<div>
|
|
<FavoriteButton :tool="{ name: route.meta.name } as Tool" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="separator" />
|
|
|
|
<div class="description">
|
|
{{ toolDescription }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tool-content">
|
|
<slot />
|
|
</div>
|
|
|
|
<div class="tool-footer">
|
|
<VueMarkdown :source="toolFooter" />
|
|
</div>
|
|
</BaseLayout>
|
|
</template>
|
|
|
|
<style lang="less" scoped>
|
|
.tool-content {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
flex-wrap: wrap;
|
|
gap: 16px;
|
|
overflow-x: auto;
|
|
|
|
::v-deep(& > *) {
|
|
flex: 0 1 600px;
|
|
min-width:0;
|
|
}
|
|
}
|
|
|
|
.tool-layout {
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
box-sizing: border-box;
|
|
|
|
.tool-header {
|
|
padding: 40px 0;
|
|
width: 100%;
|
|
|
|
.n-h1 {
|
|
opacity: 0.9;
|
|
font-size: 40px;
|
|
font-weight: 400;
|
|
margin: 0;
|
|
line-height: 1;
|
|
}
|
|
|
|
.separator {
|
|
width: 200px;
|
|
height: 2px;
|
|
background: rgb(161, 161, 161);
|
|
opacity: 0.2;
|
|
|
|
margin: 10px 0;
|
|
}
|
|
|
|
.description {
|
|
margin: 0;
|
|
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
}
|
|
.tool-footer {
|
|
opacity: 0.7;
|
|
font-size: 12px;
|
|
text-align: center;
|
|
|
|
::v-deep(a) {
|
|
color: v-bind('themeVars.textColor1');
|
|
font-style: italic;
|
|
}
|
|
}
|
|
</style>
|