feat: add frontend
This commit is contained in:
3
frontend/src/.gitignore
vendored
3
frontend/src/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
|
||||
*.js
|
||||
*.vue.js
|
||||
@@ -1,11 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import Navigation from './components/Navigation.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Navigation />
|
||||
<Navigation :links="stNav.links" :setActive="stNav.setActive" />
|
||||
|
||||
<main class="p-4 mt-16 md:max-w-screen-md mx-auto">
|
||||
<RouterView />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Navigation from '@/components/Navigation.vue';
|
||||
|
||||
import { useNavigationStore } from "@/stores/navigation";
|
||||
|
||||
const stNav = useNavigationStore();
|
||||
</script>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<template>
|
||||
<div class="bg-slate-100 border rounded mb-4 p-2 md:mb-8 md:p-4">
|
||||
<div
|
||||
v-if="item.Content.length > 0"
|
||||
class="text-wrap font-sans"
|
||||
v-html="processedContent"
|
||||
></div>
|
||||
<div v-if="item.Content.length > 0" class="text-wrap font-sans" v-html="processedContent"></div>
|
||||
|
||||
<MediaGrid :medias="item.Media" :channel="item.ChannelID" />
|
||||
<div v-if="item.Media.length > 0" class="mt-2 md:mt-4">
|
||||
<div class="medias grid grid-cols-3 gap-2 md:gap-4">
|
||||
<template v-for="media in item.Media" :key="media.id">
|
||||
<MediaItem :media="media" :channel="item.ChannelID" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PostItem } from "@/types";
|
||||
import { defineComponent, computed, PropType } from "vue";
|
||||
import MediaGrid from "./MediaGrid.vue";
|
||||
<script>
|
||||
import { computed, defineComponent } from "vue";
|
||||
import MediaItem from "./MediaItem.vue";
|
||||
|
||||
function nl2br(str, is_xhtml) {
|
||||
var breakTag = is_xhtml || typeof is_xhtml === "undefined" ? "<br />" : "<br>";
|
||||
@@ -22,19 +23,16 @@ function nl2br(str, is_xhtml) {
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MediaGrid,
|
||||
MediaItem,
|
||||
},
|
||||
name: "ListItem",
|
||||
props: {
|
||||
item: {
|
||||
type: Object ,
|
||||
required: true,
|
||||
},
|
||||
item: { type: Object, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const processedContent = computed(() => {
|
||||
let content = props.item.Content.trim();
|
||||
return nl2br(content,false);
|
||||
return nl2br(content, false);
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="hideVideo"
|
||||
@click="hideVideo = false"
|
||||
class="bg-gray-500 min-h-full cursor-pointer flex justify-center align-center"
|
||||
>
|
||||
<div v-if="hideVideo" @click="hideVideo = false"
|
||||
class="bg-gray-500 min-h-full cursor-pointer flex justify-center align-center">
|
||||
<PlayIcon class="text-gray-400 w-1/2" />
|
||||
</div>
|
||||
<video v-else controls class="min-w-full min-h-full">
|
||||
@@ -11,10 +8,9 @@
|
||||
</video>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Media } from "@/types";
|
||||
<script>
|
||||
import { PlayIcon } from "@heroicons/vue/24/outline";
|
||||
import { defineComponent, PropType, ref } from "vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -23,10 +19,7 @@ export default defineComponent({
|
||||
name: "MediaDocument",
|
||||
props: {
|
||||
channel: { required: true },
|
||||
media: {
|
||||
type: Object as PropType<Media>,
|
||||
required: true,
|
||||
},
|
||||
media: { type: Object, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const videoSrc = () => {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<div v-if="medias.length > 0" class="mt-2 md:mt-4">
|
||||
<template v-if="medias.length == 1">
|
||||
<template v-for="media in medias" :key="media.id">
|
||||
<MediaItem :media="media" :channel="channel" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="medias.length % 2 == 0 && medias.length % 3 != 0">
|
||||
<div class="medias grid grid-cols-2 gap-2 md:gap-4">
|
||||
<template v-for="media in medias" :key="media.id">
|
||||
<MediaItem :media="media" :channel="channel" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="medias grid grid-cols-3 gap-2 md:gap-4">
|
||||
<template v-for="media in medias" :key="media.id">
|
||||
<MediaItem :media="media" :channel="channel" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Media } from "@/types";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
import MediaItem from "./MediaItem.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MediaItem,
|
||||
},
|
||||
name: "MediaGrid",
|
||||
props: {
|
||||
channel: { required: true },
|
||||
medias: {
|
||||
type: Object as PropType<Media[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -1,19 +1,13 @@
|
||||
<template>
|
||||
<div
|
||||
class="max-w-full overflow-hidden hide-scrollbar rounded-sm hover:shadow-lg aspect-3/4 bg-gray-500 min-h-full cursor-pointer flex justify-center align-center"
|
||||
>
|
||||
class="aspect-3/4 max-w-full overflow-hidden hide-scrollbar rounded-sm hover:shadow-lg bg-gray-500 min-h-full cursor-pointer flex justify-center align-center">
|
||||
<MediaPhoto v-if="media.photo?.length > 0" :media="media" :channel="channel" />
|
||||
<MediaDocument
|
||||
v-else-if="typeof media.document != undefined"
|
||||
:media="media"
|
||||
:channel="channel"
|
||||
/>
|
||||
<MediaDocument v-else-if="typeof media.document != undefined" :media="media" :channel="channel" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Media } from "@/types";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import MediaDocument from "./MediaDocument.vue";
|
||||
import MediaPhoto from "./MediaPhoto.vue";
|
||||
|
||||
@@ -25,10 +19,7 @@ export default defineComponent({
|
||||
name: "MediaItem",
|
||||
props: {
|
||||
channel: { required: true },
|
||||
media: {
|
||||
type: Object as PropType<Media>,
|
||||
required: true,
|
||||
},
|
||||
media: { type: Object, required: true },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
<template>
|
||||
<img
|
||||
:src="photoSrc()"
|
||||
class="max-w-full h-full max-h-full"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
@click="openPreview"
|
||||
/>
|
||||
<img :src="photoSrc()" class="max-w-full h-full max-h-full" loading="lazy" decoding="async" @click="openPreview" />
|
||||
<div v-if="isPreviewVisible" class="modal" @click="closePreview">
|
||||
<img :src="photoSrc()" alt="Preview" class="preview-image" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Media } from "@/types";
|
||||
import { defineComponent, nextTick, PropType, ref } from "vue";
|
||||
<script>
|
||||
import { defineComponent, nextTick, ref } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "MediaPhoto",
|
||||
props: {
|
||||
channel: { required: true },
|
||||
media: {
|
||||
type: Object as PropType<Media>,
|
||||
required: true,
|
||||
},
|
||||
media: { type: Object, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const photoSrc = () => {
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
<!-- This example requires Tailwind CSS v2.0+ -->
|
||||
<template>
|
||||
<Disclosure as="nav" class="bg-gray-800 fixed w-full top-0" v-slot="{ open }">
|
||||
<Disclosure as="nav" class="bg-gray-800 fixed w-full top-0">
|
||||
<nav class="md:max-w-screen-md mx-auto">
|
||||
<div class="flex items-center justify-between h-16 px-4">
|
||||
<div class="flex space-x-4">
|
||||
<RouterLink
|
||||
v-for="item in navigation"
|
||||
:key="item.name"
|
||||
:to="item.href"
|
||||
:class="[
|
||||
item.current
|
||||
? 'bg-gray-900 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
'px-3 py-2 rounded-md text-sm font-medium cursor-pointer',
|
||||
]"
|
||||
:aria-current="item.current ? 'page' : undefined"
|
||||
>{{ item.name }}</RouterLink
|
||||
>
|
||||
<RouterLink v-for="nav in links" :key="nav.name" :to="nav.href" @click="setActive(nav.name)" :class="[
|
||||
nav.current
|
||||
? 'bg-gray-900 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
'px-3 py-2 rounded-md text-sm font-medium cursor-pointer',
|
||||
]" :aria-current="nav.current ? 'page' : undefined">{{ nav.name }}</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -24,22 +16,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Disclosure } from "@headlessui/vue";
|
||||
import { RouterLink } from "vue-router";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
const navigation = [
|
||||
{ name: "Home", href: "/", current: false },
|
||||
{ name: "Favorites", href: "/favorites", current: false },
|
||||
];
|
||||
export default {
|
||||
components: {
|
||||
RouterLink,
|
||||
Disclosure,
|
||||
export default defineComponent({
|
||||
name: "Navigation",
|
||||
props: {
|
||||
links: { type: Array, required: true },
|
||||
setActive: { type: Function, required: true },
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
navigation,
|
||||
};
|
||||
},
|
||||
};
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
"photo": "https://picsum.photos/300/300",
|
||||
"msg_id": 26701,
|
||||
"asset_id": 6330272214070443931
|
||||
},
|
||||
{
|
||||
"photo": "https://picsum.photos/300/300",
|
||||
"msg_id": 26702,
|
||||
"asset_id": 6330272214070443932
|
||||
},
|
||||
{
|
||||
"photo": "https://picsum.photos/300/300",
|
||||
"msg_id": 26703,
|
||||
"asset_id": 6330272214070443933
|
||||
},
|
||||
{
|
||||
"photo": "https://picsum.photos/300/300",
|
||||
"msg_id": 26704,
|
||||
"asset_id": 6330272214070443934
|
||||
},
|
||||
{
|
||||
"msg_id": 26705,
|
||||
"asset_id": 6330272213614202883,
|
||||
"document": {
|
||||
"Ext": ".mp4",
|
||||
"Size": 2235348,
|
||||
"Video": {
|
||||
"Width": 576,
|
||||
"Height": 1280,
|
||||
"Duration": 25.147
|
||||
},
|
||||
"Filename": "",
|
||||
"MimeType": "video/mp4"
|
||||
}
|
||||
}
|
||||
]
|
||||
200
frontend/src/fixtures/channels.js
Normal file
200
frontend/src/fixtures/channels.js
Normal file
@@ -0,0 +1,200 @@
|
||||
export default [
|
||||
{
|
||||
"ID": 17,
|
||||
"UUID": 1811965783,
|
||||
"Username": "zhudayigebaipiao",
|
||||
"Title": "𝖥𝗋𝖾𝖾 𝖢𝗁𝖺𝗇𝗇𝖾𝗅🐸",
|
||||
"CreatedAt": "2024-09-12T10:26:09.874+08:00",
|
||||
"UpdatedAt": "2024-09-12T10:26:09.874+08:00",
|
||||
"Offset": 501,
|
||||
"MinID": 501,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"UUID": 1674292882,
|
||||
"Username": "Aliyun_4K_Movies",
|
||||
"Title": "阿里云盘4K影视",
|
||||
"CreatedAt": "2024-09-02T18:59:18.782+08:00",
|
||||
"UpdatedAt": "2024-09-02T18:59:18.782+08:00",
|
||||
"Offset": 27022,
|
||||
"MinID": 27022,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 19,
|
||||
"UUID": 1855542885,
|
||||
"Username": "meinvshouji",
|
||||
"Title": "美女收集器👗",
|
||||
"CreatedAt": "2024-09-13T08:52:07.72+08:00",
|
||||
"UpdatedAt": "2024-09-13T08:52:07.72+08:00",
|
||||
"Offset": 1339,
|
||||
"MinID": 1339,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 8,
|
||||
"UUID": 1117480027,
|
||||
"Username": "ruyoblog",
|
||||
"Title": "如有乐享",
|
||||
"CreatedAt": "2024-09-02T19:46:24.462+08:00",
|
||||
"UpdatedAt": "2024-09-02T19:46:24.462+08:00",
|
||||
"Offset": 4306,
|
||||
"MinID": 4306,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 4,
|
||||
"UUID": 1651435712,
|
||||
"Username": "abskoop",
|
||||
"Title": "ahhhhfs|A姐分享",
|
||||
"CreatedAt": "2024-09-02T19:10:15.728+08:00",
|
||||
"UpdatedAt": "2024-09-02T19:10:15.728+08:00",
|
||||
"Offset": 8628,
|
||||
"MinID": 8628,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 5,
|
||||
"UUID": 1469109660,
|
||||
"Username": "shareAliyun",
|
||||
"Title": "阿里云盘发布频道",
|
||||
"CreatedAt": "2024-09-02T19:13:23.865+08:00",
|
||||
"UpdatedAt": "2024-09-02T19:13:23.865+08:00",
|
||||
"Offset": 66151,
|
||||
"MinID": 66151,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 10,
|
||||
"UUID": 2081161978,
|
||||
"Username": "hackerNewsSummary007",
|
||||
"Title": "Hacker News 中文摘要",
|
||||
"CreatedAt": "2024-09-02T23:07:22.335+08:00",
|
||||
"UpdatedAt": "2024-09-02T23:07:22.335+08:00",
|
||||
"Offset": 1522,
|
||||
"MinID": 1522,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 15,
|
||||
"UUID": 1361351430,
|
||||
"Username": "chiguamaopao",
|
||||
"Title": "吃瓜冒泡吧",
|
||||
"CreatedAt": "2024-09-12T10:14:06.746+08:00",
|
||||
"UpdatedAt": "2024-09-12T10:14:06.746+08:00",
|
||||
"Offset": 11634,
|
||||
"MinID": 11634,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 11,
|
||||
"UUID": 1166415755,
|
||||
"Username": "buliang00",
|
||||
"Title": "不良林",
|
||||
"CreatedAt": "2024-09-02T23:09:25.094+08:00",
|
||||
"UpdatedAt": "2024-09-02T23:09:25.094+08:00",
|
||||
"Offset": 192,
|
||||
"MinID": 192,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 18,
|
||||
"UUID": 1443355998,
|
||||
"Username": "biubiubiuchat",
|
||||
"Title": "小岛电波",
|
||||
"CreatedAt": "2024-09-12T10:39:38.504+08:00",
|
||||
"UpdatedAt": "2024-09-12T10:39:38.504+08:00",
|
||||
"Offset": 948,
|
||||
"MinID": 948,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 20,
|
||||
"UUID": 1723117448,
|
||||
"Username": "yunying23",
|
||||
"Title": "自媒体运营秘籍",
|
||||
"CreatedAt": "2024-09-13T09:16:22.942+08:00",
|
||||
"UpdatedAt": "2024-09-13T09:16:22.942+08:00",
|
||||
"Offset": 3937,
|
||||
"MinID": 3937,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 14,
|
||||
"UUID": 2041671763,
|
||||
"Username": "cgblz",
|
||||
"Title": "吃瓜爆料站(苹果看不了👉🏾@cgbl8)",
|
||||
"CreatedAt": "2024-09-03T00:30:15.126+08:00",
|
||||
"UpdatedAt": "2024-09-03T00:30:15.126+08:00",
|
||||
"Offset": 26805,
|
||||
"MinID": 26805,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 16,
|
||||
"UUID": 1851814415,
|
||||
"Username": "plmmyyds",
|
||||
"Title": "妹子即正义😘",
|
||||
"CreatedAt": "2024-09-12T10:18:39.159+08:00",
|
||||
"UpdatedAt": "2024-09-12T10:18:39.159+08:00",
|
||||
"Offset": 9519,
|
||||
"MinID": 9519,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 13,
|
||||
"UUID": 2023304596,
|
||||
"Username": "",
|
||||
"Title": "爆料瓜田",
|
||||
"CreatedAt": "2024-09-02T23:34:26.949+08:00",
|
||||
"UpdatedAt": "2024-09-02T23:34:26.949+08:00",
|
||||
"Offset": 2874,
|
||||
"MinID": 2874,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 6,
|
||||
"UUID": 1604423588,
|
||||
"Username": "meizitu3",
|
||||
"Title": "朱颜别镜 | 妹子图 | 美女图",
|
||||
"CreatedAt": "2024-09-02T19:16:04.812+08:00",
|
||||
"UpdatedAt": "2024-09-02T19:16:04.812+08:00",
|
||||
"Offset": 7177,
|
||||
"MinID": 7177,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 12,
|
||||
"UUID": 1341930464,
|
||||
"Username": "woshadiao",
|
||||
"Title": "每日沙雕墙",
|
||||
"CreatedAt": "2024-09-02T23:18:11.31+08:00",
|
||||
"UpdatedAt": "2024-09-02T23:18:11.31+08:00",
|
||||
"Offset": 163088,
|
||||
"MinID": 163088,
|
||||
"ExportMedia": true
|
||||
},
|
||||
{
|
||||
"ID": 7,
|
||||
"UUID": 1320622866,
|
||||
"Username": "DNSPODT",
|
||||
"Title": "LoopDNS资讯播报",
|
||||
"CreatedAt": "2024-09-02T19:34:57.663+08:00",
|
||||
"UpdatedAt": "2024-09-02T19:34:57.663+08:00",
|
||||
"Offset": 5507,
|
||||
"MinID": 5507,
|
||||
"ExportMedia": false
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"UUID": 1762530683,
|
||||
"Username": "yunpanshare",
|
||||
"Title": "网盘资源收藏(夸克)",
|
||||
"CreatedAt": "2024-09-02T18:33:38.418+08:00",
|
||||
"UpdatedAt": "2024-09-02T18:33:38.418+08:00",
|
||||
"Offset": 71495,
|
||||
"MinID": 71495,
|
||||
"ExportMedia": false
|
||||
}
|
||||
]
|
||||
4
frontend/src/fixtures/index.js
Normal file
4
frontend/src/fixtures/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import channels from "./channels"
|
||||
import messages from "./messages"
|
||||
|
||||
export default { channels, messages }
|
||||
122
frontend/src/fixtures/messages.js
Normal file
122
frontend/src/fixtures/messages.js
Normal file
@@ -0,0 +1,122 @@
|
||||
export default [
|
||||
{
|
||||
"ID": 197357,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2903,
|
||||
"Content": "东湾 溜冰 千万不要别人的男朋友,你老公知道的下场有多么的恐怖 #打女人",
|
||||
"Media": "[{\"msg_id\": 2903, \"asset_id\": 6282933363250565939, \"document\": {\"Ext\": \".mp4\", \"Size\": 6459613, \"Video\": {\"Width\": 720, \"Height\": 1280, \"Duration\": 21.134}, \"Filename\": \"IMG_5451.MP4\", \"MimeType\": \"video/mp4\"}}]",
|
||||
"PublishedAt": "2024-09-20T18:01:06+08:00",
|
||||
"CreatedAt": "2024-09-20T18:06:37.863+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 197316,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2902,
|
||||
"Content": "#斑斑园区 #校长 #枫哥 #辉哥 #火枪\n\n推监狱生涯人情冷暖 \n\n老板:校长,枫哥 ,辉哥,克拉克班班园区,刚开始出事的时候还说会管,工资也不发,饭也不管了,这边也出不去,也无法回到国内,微信支付宝都被司法冻结,工资不发就算了,现在我们只想吃一口饱饭,能够正常的活下去,没有别的需求.\n\n联系公司的领导消息都是已读不回,过中秋节都是饿着肚子,真的让人寒心,这么大的老板几个人都管不起了吗?格局就这么小?每天都是饿着肚子睡觉,上厕所都是半个月上一次,上多了怕饿,我们也不奢求什么了,只求吃一口饱饭!!\n\n公司领导,校长,枫哥,辉哥,盘口主管火枪!\n\nPS:吃不饱了 ?好可怜\n\n------------------------------------\n⚡️ 查看: 凤凰娱乐已在爆料瓜田上押70000U保证金 🙏 点击\n\n群主担保: 000999c.com 放心娱乐\n\n☎️ 免费投稿爆料: @TT9533",
|
||||
"Media": "[{\"photo\": \"6294249687703731203.jpg\", \"msg_id\": 2902, \"asset_id\": 6294249687703731203}]",
|
||||
"PublishedAt": "2024-09-20T17:01:51+08:00",
|
||||
"CreatedAt": "2024-09-20T17:03:11.268+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196959,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2901,
|
||||
"Content": "#凤凰娱乐 ❗️ 巨额出款无忧❗️❗️\n东南亚最大线上博彩平台❤️ ❤️❤️❤️❤️\n\n\n\n⚡️ 查看: 凤凰娱乐已在爆料瓜田上押70000U保证金 🙏 点击\n\n凤凰娱乐大会员再创新高\n\n逆天之举:4月3号会员本金2600赢走58万 🙏\n史无前例:国内江苏某行业老板30天赢走568万 🙏\n赌神附体:6月7号PG麻将大爆91万提款 🙏\n再创新高:8月14号某盘口老板单笔提款163万 🙏\n怒杀狗庄:8月15号某盘口管理怒提203万 🙏\n天降好运:8月18号天降彩金2888爆赢78万 🙏\n\n❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️全球性顶级博彩盘口!支持USDT存出款、微信、支付宝、银行卡以及多种电子钱包存取款。东南亚国家地区通通不限ip U存U取(无须实名绑卡) \n\n\n巨额出款稳定 U存提款 每日提款不限额度800/1000万+随便提 欢迎各位大佬休闲娱乐❗️\n\n客服专员: @VIP360 ❤️\n代理专员: @Lafei (拉菲)\n注册网址: 000999c.com",
|
||||
"Media": "[{\"photo\": \"6210809064531805472.jpg\", \"msg_id\": 2901, \"asset_id\": 6210809064531805472}]",
|
||||
"PublishedAt": "2024-09-20T16:29:06+08:00",
|
||||
"CreatedAt": "2024-09-20T17:01:43.656+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196958,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2898,
|
||||
"Content": "石家庄 正定当街 #抓小三",
|
||||
"Media": "[{\"msg_id\": 2898, \"asset_id\": 6287331963812450638, \"document\": {\"Ext\": \".mp4\", \"Size\": 37600341, \"Video\": {\"Width\": 464, \"Height\": 848, \"Duration\": 99.354}, \"Filename\": \"IMG_5669.MP4\", \"MimeType\": \"video/mp4\"}}, {\"msg_id\": 2899, \"asset_id\": 6287331963812450639, \"document\": {\"Ext\": \".mp4\", \"Size\": 6529727, \"Video\": {\"Width\": 720, \"Height\": 1280, \"Duration\": 18.5}, \"Filename\": \"IMG_5670.MP4\", \"MimeType\": \"video/mp4\"}}, {\"msg_id\": 2900, \"asset_id\": 6287331963812450640, \"document\": {\"Ext\": \".mp4\", \"Size\": 2397718, \"Video\": {\"Width\": 720, \"Height\": 1280, \"Duration\": 6.234}, \"Filename\": \"IMG_5671.MP4\", \"MimeType\": \"video/mp4\"}}]",
|
||||
"PublishedAt": "2024-09-20T16:01:06+08:00",
|
||||
"CreatedAt": "2024-09-20T17:01:33.203+08:00",
|
||||
"GroupID": 13814554131228860,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196957,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2897,
|
||||
"Content": "🇰🇭柬埔寨旅游部长和监察部长互换职位\n\n9月20日上午,柬埔寨国会召开特别会议,批准柬埔寨旅游部长宋速甘和监察部长何哈互换职位,即何哈出任旅游部长,宋速甘出任监察部长。\n\n宋速甘(图左)出任旅游部长,何哈(图右)出任监察部长\n\n此次会议由国会主席昆素达丽主持,106名国会议员全部投票支持原旅游部长宋速甘改任监察部长、原监察部长何哈改任旅游部长。\n\n对此,洪马耐表示:部长职位的调整旨在更好地适应政府的需要,确保各项工作的顺利进行。\n\n据了解,宋速甘是已故副总理宋安之子,其岳父是原工业部部长占比塞,其哥哥宋卜提武是洪森女婿。\n\n何哈是洪森夫人洪文拉妮的侄女婿。",
|
||||
"Media": "[{\"photo\": \"6298423987893551139.jpg\", \"msg_id\": 2897, \"asset_id\": 6298423987893551139}]",
|
||||
"PublishedAt": "2024-09-20T15:33:06+08:00",
|
||||
"CreatedAt": "2024-09-20T16:59:46.494+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196956,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2895,
|
||||
"Content": "#群友投稿 \n\n#我要匿名投诉华泰11楼B区24号办公室。这逼阴险歹毒,对上边领导殷勤奉承,对下诋毁挤兑。通过各种蛇形走位鸭形走位爬到组长的位置,管理一团糟。\n\n调戏已经有男朋友的泰国女同事和越南女同事,搞得对方上班不自在。没办法,他故意把组内的女同事调到自己身边,完全无视人家有男朋友这个事实。对组里的同事说话直接就是能干就干,不能干就滚。自己上班就是经常睡觉,但是我们愣着电脑屏幕都不行。自己唯一拿得出手的活儿就是操纵别人,搞对方心理,有一点小问题就要搞你。我们管理平台运营,本来就是要有点是非判断。\n\n现在只要他看不顺眼,主播就是工资全扣,白干走人。主播辛辛苦苦一个月,一分工资拿不到,或者就是十天半个月的扣工资。以前没有权利的时候就已经不是人了,这权利到手还不张扬跋扈,公泄私愤?被打压的主播就在圈子里报平台黑料,因为他一个人,搞得我们的工作越来越难做。\n\n平时对我们各种施压,一幅小人得志的嘴脸。平台让这样的人上位,早晚要完蛋。这个人工作名字叫长生,但是我们私下里都叫他CS(畜生)。\n\n------------------------------------\n⚡️ 查看: 凤凰娱乐已在爆料瓜田上押70000U保证金 🙏 点击\n\n群主担保: 000999c.com 放心娱乐\n\n☎️ 免费投稿爆料: @TT9533",
|
||||
"Media": "[{\"photo\": \"6291948676154768953.jpg\", \"msg_id\": 2895, \"asset_id\": 6291948676154768953}, {\"photo\": \"6291948676154768954.jpg\", \"msg_id\": 2896, \"asset_id\": 6291948676154768954}]",
|
||||
"PublishedAt": "2024-09-20T15:02:07+08:00",
|
||||
"CreatedAt": "2024-09-20T16:59:45.405+08:00",
|
||||
"GroupID": 13814525816373596,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196955,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2892,
|
||||
"Content": "【 湖南省 财政厅 厅长 刘文杰 意外 去世 】2024年9月19日上午,湖南省财政厅党组书记、厅长刘文杰意外去世。此事或涉及刑事案件。9月19日下午,包括刘文杰的同事在内的多位知情人士告知了记者这一消息。",
|
||||
"Media": "[{\"photo\": \"6296265990165611162.jpg\", \"msg_id\": 2892, \"asset_id\": 6296265990165611162}, {\"photo\": \"6296265990165611163.jpg\", \"msg_id\": 2893, \"asset_id\": 6296265990165611163}, {\"photo\": \"6296265990165611164.jpg\", \"msg_id\": 2894, \"asset_id\": 6296265990165611164}]",
|
||||
"PublishedAt": "2024-09-20T14:55:06+08:00",
|
||||
"CreatedAt": "2024-09-20T16:59:44.085+08:00",
|
||||
"GroupID": 13814522448560924,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196954,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2889,
|
||||
"Content": "#每日眼力考试\n\n屌大的都看的到,看到的评论区发个6,不然都是小屌子~\n\n#抖音 #快手 #直播 #露点 #网红",
|
||||
"Media": "[{\"msg_id\": 2889, \"asset_id\": 4976560077386285696, \"document\": {\"Ext\": \".mp4\", \"Size\": 2732376, \"Video\": {\"Width\": 576, \"Height\": 1280, \"Duration\": 6}, \"Filename\": \"VID_20230702_172252_533.mp4\", \"MimeType\": \"video/mp4\"}}]",
|
||||
"PublishedAt": "2024-09-20T14:29:06+08:00",
|
||||
"CreatedAt": "2024-09-20T16:59:43.625+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196953,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2888,
|
||||
"Content": "前两天从厦门驾驶快艇 #偷渡 到 #台湾 的哥们。",
|
||||
"Media": "[{\"msg_id\": 2888, \"asset_id\": 6287331963812450416, \"document\": {\"Ext\": \".mp4\", \"Size\": 17103391, \"Video\": {\"Width\": 848, \"Height\": 496, \"Duration\": 82.802222222222}, \"Filename\": \"IMG_5556.MP4\", \"MimeType\": \"video/mp4\"}}]",
|
||||
"PublishedAt": "2024-09-20T14:01:06+08:00",
|
||||
"CreatedAt": "2024-09-20T16:59:40.273+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
},
|
||||
{
|
||||
"ID": 196952,
|
||||
"ChannelID": 2023304596,
|
||||
"UUID": 2887,
|
||||
"Content": "#群友投稿 妈的昨晚上去珍珠收了一下尾款遭遇抢劫\n\n大意了坐了出租车想着也不远,走到半路这狗司机不讲武德:给,我来了先来了句:你好,我没搭理,又给我来了句:kuya,我特么就想着不对劲了,回了句yes,妈的直接给我锁车门了,拉到个贫民窟给我洗劫了二部手机,六万p现金,让我雨中凌乱,我恳求他给我😞退我一部手机,该说不说给了我一部手机和50p现金让我自己想办法离开那里.\n\n没办法了我特么走路3公里走到shore找了个地方手机充电找了个朋友帮忙一下,所以以后各位多打出租车,让这些出租车司机吃饱喝足,只有这样了才会源源不断的抢劫,最后提醒一下出租车的车牌:NIK90136\n\n已经报警了,说安排查出租车公司,但是希望不大,车能找回来,人和钱肯定是找不到了\n\nPS:白色出租车 ?\n\n------------------------------------\n⚡️ 查看: 凤凰娱乐已在爆料瓜田上押70000U保证金 🙏 点击\n\n群主担保: 000999c.com 放心娱乐\n\n☎️ 免费投稿爆料: @TT9533",
|
||||
"Media": "[{\"photo\": \"6003642163117802028.jpg\", \"msg_id\": 2887, \"asset_id\": 6003642163117802028}]",
|
||||
"PublishedAt": "2024-09-20T13:02:06+08:00",
|
||||
"CreatedAt": "2024-09-20T16:59:02.3+08:00",
|
||||
"GroupID": 0,
|
||||
"Published": false,
|
||||
"Like": false
|
||||
}
|
||||
]
|
||||
13
frontend/src/main.js
Normal file
13
frontend/src/main.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createPinia } from 'pinia'
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
@@ -1,25 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './style.css'
|
||||
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
import ChannelMessages from './views/ChannelMessages.vue'
|
||||
import FavoritesMessages from './views/FavoritesMessages.vue'
|
||||
import Home from './views/Home.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', component: Home, name: 'home' },
|
||||
{ path: '/favorites', component: FavoritesMessages, name: 'favorites' },
|
||||
// { path: '/channels', component: Channel, name: 'channels' },
|
||||
{ path: '/channels/:channel/messages', component: ChannelMessages, name: 'channel-messages' },
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
})
|
||||
|
||||
createApp(App)
|
||||
.use(router)
|
||||
.mount('#app')
|
||||
25
frontend/src/router/index.js
Normal file
25
frontend/src/router/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('@/views/Home.vue'),
|
||||
},
|
||||
{
|
||||
path: '/favorites',
|
||||
name: 'favorites',
|
||||
component: () => import('@/views/FavoriteMessages.vue'),
|
||||
},
|
||||
{
|
||||
path: '/channels/:channel/messages',
|
||||
name: 'channel-messages',
|
||||
component: () => import('@/views/ChannelMessages.vue'),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
15
frontend/src/services/channels.js
Normal file
15
frontend/src/services/channels.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { http } from './http';
|
||||
|
||||
export async function getChannels() {
|
||||
// return mock('channels')
|
||||
|
||||
const resp = await http.get('/channels');
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function getChannel(channelId) {
|
||||
// return mock('channels', (data) => data[0])
|
||||
|
||||
const resp = await http.get(`/channels/${channelId}`);
|
||||
return resp.data;
|
||||
}
|
||||
22
frontend/src/services/http.js
Normal file
22
frontend/src/services/http.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import fixtures from '@/fixtures/index.js';
|
||||
import axios from 'axios';
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: '/api',
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
}
|
||||
});
|
||||
|
||||
const mock = async (fixture, process) => {
|
||||
let data = fixtures[fixture]
|
||||
if (typeof process === 'function') {
|
||||
data = process(data);
|
||||
}
|
||||
|
||||
console.log('mock', fixture, data);
|
||||
return data
|
||||
}
|
||||
|
||||
export { http, mock };
|
||||
|
||||
38
frontend/src/services/messages.js
Normal file
38
frontend/src/services/messages.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { http } from './http';
|
||||
|
||||
function processResponseMessage(data) {
|
||||
// let copyData = JSON.parse(JSON.stringify(data));
|
||||
return data.map((item) => {
|
||||
console.log(typeof item.Media)
|
||||
let media = item.Media.replace(/"asset_id":\s(\d+)/g, (match, p1, p2, p3, offset, string) => {
|
||||
return `"asset_id": "${p1}"`
|
||||
})
|
||||
|
||||
item.Media = JSON.parse(media).filter((item) => {
|
||||
return Object.keys(item).length > 0;
|
||||
});
|
||||
|
||||
return item
|
||||
});
|
||||
}
|
||||
|
||||
export async function toggleFavorite(messageId) {
|
||||
// return mock('messages', processResponseMessage)
|
||||
|
||||
const resp = await http.patch('/messages/' + messageId + '/favorite');
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function getChannelMessages(channelId, params) {
|
||||
// return mock('messages', processResponseMessage)
|
||||
|
||||
const resp = await http.get(`/channels/${channelId}/messages`, { params });
|
||||
return processResponseMessage(resp.data);
|
||||
}
|
||||
|
||||
export async function getFavoriteMessages(params) {
|
||||
// return mock('messages', processResponseMessage)
|
||||
|
||||
const resp = await http.get(`/favorites`, { params });
|
||||
return processResponseMessage(resp.data);
|
||||
}
|
||||
12
frontend/src/stores/counter.js
Normal file
12
frontend/src/stores/counter.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
17
frontend/src/stores/navigation.js
Normal file
17
frontend/src/stores/navigation.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
|
||||
export const useNavigationStore = defineStore('navigation', () => {
|
||||
const links = ref([
|
||||
{ name: "Home", href: "/", current: true },
|
||||
{ name: "Favorites", href: "/favorites", current: false },
|
||||
]);
|
||||
|
||||
function setActive(name) {
|
||||
links.value.forEach(link => {
|
||||
link.current = link.name === name;
|
||||
});
|
||||
}
|
||||
|
||||
return { links, setActive }
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
export interface Channel {
|
||||
UUID: number;
|
||||
Username: string;
|
||||
Title: string;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
Offset: number;
|
||||
MinID: number;
|
||||
ExportMedia: boolean;
|
||||
}
|
||||
|
||||
export interface PostItem {
|
||||
id: number;
|
||||
content: string;
|
||||
medias: Array<Media>;
|
||||
}
|
||||
|
||||
export interface Media {
|
||||
photo?: string;
|
||||
msg_id: number;
|
||||
asset_id: number;
|
||||
document?: Document;
|
||||
}
|
||||
|
||||
export interface Document {
|
||||
Ext: string;
|
||||
Size: number;
|
||||
Video: Video;
|
||||
Filename: string;
|
||||
MimeType: string;
|
||||
}
|
||||
|
||||
export interface Video {
|
||||
Width: number;
|
||||
Height: number;
|
||||
Duration: number;
|
||||
}
|
||||
@@ -1,37 +1,40 @@
|
||||
<template>
|
||||
<h1 class="mb-4 font-semibold text-xl">{{ channel.Title }}</h1>
|
||||
<ListItem v-for="item in items" :key="item.id" :item="item" />
|
||||
<h1 class="mb-4 font-semibold text-xl">{{ channel.Title }}</h1>
|
||||
<div v-if="messages.length == 0">Empty...</div>
|
||||
<template v-else>
|
||||
<ListItem v-for="message in messages" :key="message.ID" :item="message" />
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="px-4 py-2 hover:bg-slate-100 rounded border text-xl font-semibold w-full"
|
||||
@click="loadMore">LoadMore</button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRoute } from "vue-router";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import { getChannel } from "@/services/channels";
|
||||
import { getChannelMessages } from "@/services/messages";
|
||||
import { onMounted, ref } from "vue";
|
||||
import ListItem from "../components/ListItem.vue";
|
||||
import axios from "axios";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const channel = ref({});
|
||||
const items = ref([]);
|
||||
const messages = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
axios.get(`/channels/${route.params.channel}`).then((resp) => {
|
||||
channel.value = resp.data;
|
||||
});
|
||||
const loadMore = async () => {
|
||||
const items = await getChannelMessages(route.params.channel, { offset: messages.value[messages.value.length - 1].ID });
|
||||
messages.value.push(...items);
|
||||
}
|
||||
|
||||
axios.get(`/channels/${route.params.channel}/messages`).then((resp) => {
|
||||
let data = resp.data;
|
||||
data.map((item) => {
|
||||
let media = item.Media.replace(/"asset_id":\s(\d+)/g, (match, p1, p2, p3, offset, string) => {
|
||||
return `"asset_id": "${p1}"`
|
||||
})
|
||||
onMounted(async () => {
|
||||
// get channel info
|
||||
channel.value = await getChannel(route.params.channel);
|
||||
console.log("channel", channel.value);
|
||||
|
||||
item.Media = JSON.parse(media).filter((item) => {
|
||||
return Object.keys(item).length > 0;
|
||||
});
|
||||
console.log(item);
|
||||
});
|
||||
items.value = data;
|
||||
});
|
||||
// get channel messages
|
||||
messages.value = await getChannelMessages(route.params.channel);
|
||||
console.log("messages", messages.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
31
frontend/src/views/FavoriteMessages.vue
Normal file
31
frontend/src/views/FavoriteMessages.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<h1 class="mb-4 font-semibold text-xl">Favorites</h1>
|
||||
|
||||
|
||||
<div v-if="messages.length == 0">Empty...</div>
|
||||
<template v-else>
|
||||
<ListItem v-for="message in messages" :key="message.ID" :item="message" />
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="px-4 py-2 hover:bg-slate-100 rounded border text-xl font-semibold w-full"
|
||||
@click="loadMore">LoadMore</button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import { getFavoriteMessages } from "@/services/messages";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const messages = ref([]);
|
||||
|
||||
const loadMore = async () => {
|
||||
const items = await getChannelMessages(route.params.channel, { offset: messages.value[messages.value.length - 1].ID });
|
||||
messages.value.push(...items);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
messages.value = await getFavoriteMessages();
|
||||
});
|
||||
</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<template>
|
||||
<h1 class="mb-4 font-semibold text-xl">Favorites</h1>
|
||||
|
||||
<ListItem v-for="item in items" :key="item.id" :item="item" />
|
||||
<div v-if="items.length==0">Empty...</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import ListItem from '../components/ListItem.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const items = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
axios.get('/favorites').then(resp => {
|
||||
let data = resp.data
|
||||
data.map(item => {
|
||||
item.Media = JSON.parse(item.Media).filter(item => {
|
||||
return Object.keys(item).length > 0
|
||||
})
|
||||
})
|
||||
items.value = data
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -1,27 +1,21 @@
|
||||
<template>
|
||||
<h1>Home</h1>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<RouterLink
|
||||
:to="`/channels/${item.UUID}/messages`"
|
||||
v-for="item in items"
|
||||
:key="item.UUID"
|
||||
class="border p-4 hover:shadow-lg hover:bg-slate-100 bg-slate-100 rounded"
|
||||
>
|
||||
<h2 class="font-semibold text-lg">{{ item.Title }}</h2>
|
||||
<span>{{ item.Username }}</span>
|
||||
<RouterLink :to="`/channels/${item.UUID}/messages`" v-for="item in items" :key="item.UUID"
|
||||
class="border p-4 hover:shadow-lg hover:bg-slate-100 bg-slate-100 rounded">
|
||||
<h2 class="mb-2 font-semibold text-lg">{{ item.Title }}</h2>
|
||||
<small class="text-gray-500">{{ item.Username }}</small>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Channel } from "@/types";
|
||||
<script setup>
|
||||
import { getChannels } from "@/services/channels";
|
||||
import { onMounted, ref } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
const items = ref<Channel[]>([]);
|
||||
const items = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
axios.get('/channels').then((resp) => {
|
||||
items.value = resp.data;
|
||||
});
|
||||
onMounted(async () => {
|
||||
items.value = await getChannels();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
<template>
|
||||
<h1>Tag: {{ route.params.tag }}</h1>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
</script>
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
<template>
|
||||
<h1>Tags</h1>
|
||||
</template>
|
||||
13
frontend/src/vite-env.d.ts
vendored
13
frontend/src/vite-env.d.ts
vendored
@@ -1,13 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
|
||||
import { DefineComponent } from 'vue'
|
||||
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
|
||||
export default component
|
||||
|
||||
}
|
||||
|
||||
// Remove the relative module declaration
|
||||
Reference in New Issue
Block a user