mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-23 15:51:01 +08:00
Add shadcn/ui tooltip UI && modify from the entire block being a button to just a button (the news publisher is also buttonm the tailwind tooltip is just for the news publisher plubin )
This commit is contained in:
parent
d7dfb2fb1d
commit
db0c0a3c25
2
bun.lock
2
bun.lock
@ -17,7 +17,7 @@
|
|||||||
"@sentry/nuxt": "^9",
|
"@sentry/nuxt": "^9",
|
||||||
"@tailwindcss/vite": "^4.1.5",
|
"@tailwindcss/vite": "^4.1.5",
|
||||||
"@uploadthing/nuxt": "^7.1.7",
|
"@uploadthing/nuxt": "^7.1.7",
|
||||||
"@vueuse/core": "^13.1.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"argon2": "^0.43.0",
|
"argon2": "^0.43.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ScanEyeIcon } from "lucide-vue-next";
|
||||||
import CheckKidUnfriendlyContent from "~/components/checks/checkKidUnfriendlyContent";
|
import CheckKidUnfriendlyContent from "~/components/checks/checkKidUnfriendlyContent";
|
||||||
const emit = defineEmits(["close", "min", "restore"]);
|
const emit = defineEmits(["close", "min", "restore"]);
|
||||||
const staticid = computed(() => props.staticid);
|
const staticid = computed(() => props.staticid);
|
||||||
@ -35,9 +36,9 @@ const updateContent = async (url: string, tabAction: boolean) => {
|
|||||||
|
|
||||||
const isPrimary = (url: string, defaultAction: boolean) => {
|
const isPrimary = (url: string, defaultAction: boolean) => {
|
||||||
if (primary.value === url) {
|
if (primary.value === url) {
|
||||||
return "text-sky-600 text-bold";
|
return true;
|
||||||
}
|
}
|
||||||
return "text-black";
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const openNews = (url: string) => {
|
const openNews = (url: string) => {
|
||||||
@ -72,12 +73,12 @@ watch(
|
|||||||
);
|
);
|
||||||
const findRel = (title: string) => {
|
const findRel = (title: string) => {
|
||||||
return tf(title);
|
return tf(title);
|
||||||
}
|
};
|
||||||
|
|
||||||
const tf = (text: string) => {
|
const tf = (text: string) => {
|
||||||
const words = text.toLowerCase().split('');
|
const words = text.toLowerCase().split("");
|
||||||
// const words = text.toLowerCase().match(/[\u4e00-\u9fff]|[a-zA-Z0-9]+/g) || [];
|
// const words = text.toLowerCase().match(/[\u4e00-\u9fff]|[a-zA-Z0-9]+/g) || [];
|
||||||
|
|
||||||
const freqMap = new Map();
|
const freqMap = new Map();
|
||||||
|
|
||||||
for (const word of words) {
|
for (const word of words) {
|
||||||
@ -92,7 +93,7 @@ const tf = (text: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return tfVector;
|
return tfVector;
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="justify-center align-center text-center">
|
<div class="justify-center align-center text-center">
|
||||||
@ -104,8 +105,9 @@ const tf = (text: string) => {
|
|||||||
<button
|
<button
|
||||||
v-for="item in tabs"
|
v-for="item in tabs"
|
||||||
@click="updateContent(item.url, true)"
|
@click="updateContent(item.url, true)"
|
||||||
:class="isPrimary(item.url, true)"
|
:class="isPrimary(item.url, true) ? 'text-sky-600 text-bold' : 'text-black'"
|
||||||
class=""
|
class=""
|
||||||
|
:disabled="isPrimary(item.url, true)"
|
||||||
>
|
>
|
||||||
<span>{{ item.text }}</span>
|
<span>{{ item.text }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -129,39 +131,45 @@ const tf = (text: string) => {
|
|||||||
:key="item.id"
|
:key="item.id"
|
||||||
:class="item.contentType !== 'GENERAL' && 'hidden'"
|
:class="item.contentType !== 'GENERAL' && 'hidden'"
|
||||||
>
|
>
|
||||||
<button @click="openNews(item.url.hash)">
|
<div class="p-2 bg-gray-200 rounded m-1 p-1">
|
||||||
<div class="p-2 bg-gray-200 rounded m-1 p-1">
|
<h1
|
||||||
<h1
|
class="text-2xl text-bold"
|
||||||
class="text-2xl text-bold"
|
:class="getCheckResult(item.title) ? 'text-red-600' : ''"
|
||||||
:class="getCheckResult(item.title) ? 'text-red-600' : ''"
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</h1>
|
||||||
|
<p class="m-0 text-gray-600">
|
||||||
|
<button >{{ item.publisher }}</button> --
|
||||||
|
{{
|
||||||
|
new Date(item.publishTimeUnix).toLocaleString("zh-TW", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div class="justify-center align-center text-center flex flex-row p-1">
|
||||||
|
<button
|
||||||
|
@click="openNews(item.url.hash)"
|
||||||
|
class="flex flex-row p-1 bg-sky-300/50 hover:bg-sky-400/50 shadow-lg backdrop-blur-sm rounded transition-all duration-200"
|
||||||
>
|
>
|
||||||
{{ item.title }}
|
<ScanEyeIcon class="w-6 h-6 p-1" /><span>觀看文章</span>
|
||||||
</h1>
|
</button>
|
||||||
<p class="m-0 text-gray-600">
|
</div>
|
||||||
{{ item.publisher }} --
|
<div>
|
||||||
{{
|
<h3 class="text-lg">類似文章</h3>
|
||||||
new Date(item.publishTimeUnix).toLocaleString("zh-TW", {
|
<div>{{ findRel(item.title) }}</div>
|
||||||
year: "numeric",
|
<!--<div v-for="item in findRel(item.title)">
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: false,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<h3 class="text-lg">類似文章</h3>
|
|
||||||
<div>{{ findRel(item.title) }}</div>
|
|
||||||
<!--<div v-for="item in findRel(item.title)">
|
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>-->
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
<!--<p :class="getCheckResult(item.title) ? 'hidden' : ''">
|
<!--<p :class="getCheckResult(item.title) ? 'hidden' : ''">
|
||||||
{{ item.shortDescription }}
|
{{ item.shortDescription }}
|
||||||
</p>-->
|
</p>-->
|
||||||
</div>
|
</div>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
14
components/ui/tooltip/Tooltip.vue
Normal file
14
components/ui/tooltip/Tooltip.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { TooltipRoot, type TooltipRootEmits, type TooltipRootProps, useForwardPropsEmits } from 'reka-ui'
|
||||||
|
|
||||||
|
const props = defineProps<TooltipRootProps>()
|
||||||
|
const emits = defineEmits<TooltipRootEmits>()
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(props, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipRoot v-bind="forwarded">
|
||||||
|
<slot />
|
||||||
|
</TooltipRoot>
|
||||||
|
</template>
|
28
components/ui/tooltip/TooltipContent.vue
Normal file
28
components/ui/tooltip/TooltipContent.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
|
import { TooltipContent, type TooltipContentEmits, type TooltipContentProps, TooltipPortal, useForwardPropsEmits } from 'reka-ui'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<TooltipContentProps & { class?: HTMLAttributes['class'] }>(), {
|
||||||
|
sideOffset: 4,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<TooltipContentEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, 'class')
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipPortal>
|
||||||
|
<TooltipContent v-bind="{ ...forwarded, ...$attrs }" :class="cn('z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)">
|
||||||
|
<slot />
|
||||||
|
</TooltipContent>
|
||||||
|
</TooltipPortal>
|
||||||
|
</template>
|
11
components/ui/tooltip/TooltipProvider.vue
Normal file
11
components/ui/tooltip/TooltipProvider.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { TooltipProvider, type TooltipProviderProps } from 'reka-ui'
|
||||||
|
|
||||||
|
const props = defineProps<TooltipProviderProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipProvider v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</TooltipProvider>
|
||||||
|
</template>
|
11
components/ui/tooltip/TooltipTrigger.vue
Normal file
11
components/ui/tooltip/TooltipTrigger.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { TooltipTrigger, type TooltipTriggerProps } from 'reka-ui'
|
||||||
|
|
||||||
|
const props = defineProps<TooltipTriggerProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipTrigger v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</TooltipTrigger>
|
||||||
|
</template>
|
4
components/ui/tooltip/index.ts
Normal file
4
components/ui/tooltip/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as Tooltip } from './Tooltip.vue'
|
||||||
|
export { default as TooltipContent } from './TooltipContent.vue'
|
||||||
|
export { default as TooltipProvider } from './TooltipProvider.vue'
|
||||||
|
export { default as TooltipTrigger } from './TooltipTrigger.vue'
|
@ -74,7 +74,7 @@
|
|||||||
"aboutNewsOrg": "關於這個新聞來源",
|
"aboutNewsOrg": "關於這個新聞來源",
|
||||||
"newsview": "新聞"
|
"newsview": "新聞"
|
||||||
},
|
},
|
||||||
"tools":{
|
"tools": {
|
||||||
"title": "工具",
|
"title": "工具",
|
||||||
"name": {
|
"name": {
|
||||||
"checkweirdkeywords": "檢查偏色情標體",
|
"checkweirdkeywords": "檢查偏色情標體",
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
"@sentry/nuxt": "^9",
|
"@sentry/nuxt": "^9",
|
||||||
"@tailwindcss/vite": "^4.1.5",
|
"@tailwindcss/vite": "^4.1.5",
|
||||||
"@uploadthing/nuxt": "^7.1.7",
|
"@uploadthing/nuxt": "^7.1.7",
|
||||||
"@vueuse/core": "^13.1.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"argon2": "^0.43.0",
|
"argon2": "^0.43.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
const localePath = useLocalePath();
|
const localePath = useLocalePath();
|
||||||
// Import Icons
|
// Import Icons
|
||||||
import { SearchXIcon, CircleSlash2Icon } from "lucide-vue-next";
|
import { SearchXIcon, CircleSlash2Icon } from "lucide-vue-next";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
// Array
|
// Array
|
||||||
const tools = [
|
const tools = [
|
||||||
{
|
{
|
||||||
@ -19,8 +19,8 @@ const tools = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: `${t("tools.title")}`
|
title: `${t("tools.title")}`,
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
export default defineEventHandler((event) => {
|
export default defineEventHandler((event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
const toolCall = query.tool;
|
const toolCall = query.tool;
|
||||||
const forwardCall = query.forward;
|
const forwardCall = query.forward;
|
||||||
if (toolCall) {
|
if (toolCall) {
|
||||||
const buildUrl = "/desktop?openapp=" + toolCall;
|
const buildUrl = "/desktop?openapp=" + toolCall;
|
||||||
return sendRedirect(event, buildUrl, 302);
|
return sendRedirect(event, buildUrl, 302);
|
||||||
}
|
}
|
||||||
if (forwardCall) {
|
if (forwardCall) {
|
||||||
const buildUrl = "/" + forwardCall;
|
const buildUrl = "/" + forwardCall;
|
||||||
return sendRedirect(event, buildUrl, 302);
|
return sendRedirect(event, buildUrl, 302);
|
||||||
}
|
}
|
||||||
return sendRedirect(event, "/", 302)
|
return sendRedirect(event, "/", 302);
|
||||||
})
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user