Files
TeyvatGuide/src/views/t-post.vue
2024-12-06 14:36:30 +08:00

402 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<TSwitchTheme />
<TPinWin />
<VpBtnCollect :model-value="postId" :data="postData" />
<TShareBtn selector=".tp-post-body" :title="`Post_${postId}`" />
<VpBtnReply :gid="postData.post.game_id" :post-id="postData.post.post_id" v-if="postData" />
<div class="tp-post-body" v-if="postData">
<div class="tp-post-info">
<div class="tp-post-version">
PostID{{ postId }} | Render by TeyvatGuide v{{ appVersion }}
</div>
<div class="tp-post-meta">
<div class="mpm-forum" v-if="postData.forum" @click="toForum(postData.forum)">
<img :src="getGameIcon(postData.forum.game_id)" alt="gameIcon" />
<img :src="postData.forum.icon" alt="forumIcon" />
<span>{{ postData.forum.name }}</span>
</div>
<div class="mpm-item" :title="`浏览数:${postData?.stat?.view_num}`">
<v-icon>mdi-eye</v-icon>
<span>{{ postData?.stat?.view_num }}</span>
</div>
<div class="mpm-item" :title="`收藏数:${postData?.stat?.bookmark_num}`">
<v-icon>mdi-star</v-icon>
<span>{{ postData?.stat?.bookmark_num }}</span>
</div>
<div class="mpm-item" :title="`回复数:${postData?.stat?.reply_num}`">
<v-icon>mdi-comment</v-icon>
<span>{{ postData?.stat?.reply_num }}</span>
</div>
<div class="mpm-item" :title="`点赞数:${postData?.stat?.like_num}`">
<v-icon>mdi-thumb-up</v-icon>
<span>{{ postData?.stat?.like_num }}</span>
</div>
<div class="mpm-item" :title="`转发数:${postData?.stat?.forward_num}`">
<v-icon>mdi-share-variant</v-icon>
<span>{{ postData?.stat?.forward_num }}</span>
</div>
</div>
<TpAvatar :data="postData.user" position="right" v-if="postData.user" />
</div>
<div class="tp-post-title" @click="toPost()" title="点击查看评论">
<span class="mpt-official" v-if="postData.post.post_status.is_official"></span>
<span>{{ postData.post.subject }}</span>
</div>
<!-- 一些附加信息比如 topiccollection -->
<div class="tp-post-extra">
<div
class="tp-post-collection"
:title="`合集ID${postData.collection.collection_id}`"
v-if="postData.collection"
@click="showOverlayC()"
>
<v-icon size="12">mdi-widgets</v-icon>
<span>{{ postData.collection.collection_title }}</span>
<span>{{ postData.collection.cur }}/{{ postData.collection.total }}</span>
</div>
<div
v-for="topic in postData.topics"
:key="topic.id"
class="tp-post-topic"
@click="toTopic(topic)"
>
<v-icon size="12">mdi-tag</v-icon>
<span>{{ topic.name }}</span>
</div>
</div>
<div class="tp-post-subtitle">
<span :title="`更新于:${getDate(postData.post.updated_at)}`">
创建于:{{ getDate(postData.post.created_at) }}
</span>
<span>分享于:{{ getDate(shareTime) }}</span>
<span v-if="postData.post.republish_authorization !== 0" class="tp-post-copyright">
<v-icon size="16">mdi-copyright</v-icon>
<span>{{ getRepublishAuthorization(postData.post.republish_authorization) }}</span>
</span>
</div>
<TpParser v-model:data="renderPost" />
</div>
<VpOverlayCollection
v-model="showCollection"
:collection="postData.collection"
v-if="postData?.collection"
/>
</template>
<script lang="ts" setup>
import { app, webviewWindow } from "@tauri-apps/api";
import { emit } from "@tauri-apps/api/event";
import { nextTick, onMounted, onUnmounted, ref, shallowRef } from "vue";
import { useRoute } from "vue-router";
import TPinWin from "../components/app/t-pinWin.vue";
import TShareBtn from "../components/app/t-shareBtn.vue";
import TSwitchTheme from "../components/app/t-switchTheme.vue";
import showLoading from "../components/func/loading.js";
import showSnackbar from "../components/func/snackbar.js";
import TpAvatar from "../components/viewPost/tp-avatar.vue";
import TpParser from "../components/viewPost/tp-parser.vue";
import VpBtnCollect from "../components/viewPost/vp-btn-collect.vue";
import VpBtnReply from "../components/viewPost/vp-btn-reply.vue";
import VpOverlayCollection from "../components/viewPost/vp-overlay-collection.vue";
import Mys from "../plugins/Mys/index.js";
import { useAppStore } from "../store/modules/app.js";
import TGClient from "../utils/TGClient.js";
import TGLogger from "../utils/TGLogger.js";
import { createTGWindow } from "../utils/TGWindow.js";
import TGConstant from "../web/constant/TGConstant.js";
const appVersion = await app.getVersion();
const postId = Number(useRoute().params.post_id);
const renderPost = shallowRef<TGApp.Plugins.Mys.SctPost.Base[]>([]);
const postData = shallowRef<TGApp.Plugins.Mys.Post.FullData>();
const showCollection = ref<boolean>(false);
const shareTime = ref<number>(Math.floor(Date.now() / 1000));
// eslint-disable-next-line no-undef
const shareTimeTimer = ref<NodeJS.Timeout | undefined>(undefined);
function getGameIcon(gameId: number): string {
const find = TGConstant.BBS.CHANNELS.find((item) => item.gid === gameId.toString());
if (find) return find.icon;
return "/platforms/mhy/mys.webp";
}
onMounted(async () => {
showLoading.start(`正在加载帖子数据...`);
// 检查数据
if (!postId) {
showLoading.empty("未找到数据", "PostID 不存在");
await webviewWindow.getCurrentWebviewWindow().setTitle("未找到数据");
await TGLogger.Error("[t-post][onMounted] PostID 不存在");
return;
}
showLoading.update("正在获取数据...", `帖子ID: ${postId}`);
const resp = await Mys.Post.getPostFull(postId);
if ("retcode" in resp) {
showLoading.empty(`[code]${resp.retcode}`, resp.message);
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
await webviewWindow.getCurrentWebviewWindow().setTitle(`Post_${postId} ${resp.message}`);
await TGLogger.Error(`[t-post][${postId}][onMounted] ${resp.retcode}: ${resp.message}`);
return;
}
postData.value = resp;
showLoading.update("正在渲染数据...", `帖子ID: ${postId}`);
renderPost.value = getRenderPost(postData.value);
await webviewWindow
.getCurrentWebviewWindow()
.setTitle(`Post_${postId} ${postData.value.post.subject}`);
await TGLogger.Info(`[t-post][${postId}][onMounted] ${postData.value.post.subject}`);
// 打开 json
const isDev = useAppStore().devMode ?? false;
if (isDev) {
await TGLogger.Info(`[t-post][${postId}][onMounted] 打开 JSON 窗口`);
await createPostJson(postId);
}
await nextTick();
if (shareTimeTimer.value !== undefined) {
clearInterval(shareTimeTimer.value);
shareTimeTimer.value = undefined;
}
shareTimeTimer.value = setInterval(() => (shareTime.value = Math.floor(Date.now() / 1000)), 1000);
showLoading.end();
});
function showOverlayC() {
showCollection.value = true;
}
function getDate(date: number): string {
return new Date(date * 1000).toLocaleString().replace(/\//g, "-");
}
function getRepublishAuthorization(type: number): string {
switch (type) {
case 1:
return "已开启创作声明,禁止转载或摘编";
case 2:
return "已开启创作声明,允许规范转载";
default:
return "未知";
}
}
function getRenderPost(data: TGApp.Plugins.Mys.Post.FullData): TGApp.Plugins.Mys.SctPost.Base[] {
const postContent = data.post.content;
let jsonParse: string;
if (postContent.startsWith("<")) {
jsonParse = data.post.structured_content;
} else {
try {
jsonParse = parseContent(data.post.content);
} catch (e) {
if (e instanceof SyntaxError) {
TGLogger.Warn(`[t-post][${postId}] ${e.name}: ${e.message}`);
}
jsonParse = data.post.structured_content;
}
}
return JSON.parse(jsonParse);
}
function parseContent(content: string): string {
const data: TGApp.Plugins.Mys.SctPost.Other = JSON.parse(content);
const result: TGApp.Plugins.Mys.SctPost.Base[] = [];
const keys = Object.keys(data);
keys.forEach((key) => {
switch (key) {
case "describe":
result.push({ insert: data.describe });
break;
case "imgs":
data.imgs.forEach((item) => result.push({ insert: { image: item } }));
break;
default:
TGLogger.Warn(`[t-post][${postId}][parseContent] Unknown key: ${key}`);
result.push({ insert: data[key] });
break;
}
});
return JSON.stringify(result);
}
async function createPostJson(postId: number): Promise<void> {
const jsonPath = `/post_detail_json/${postId}`;
const jsonTitle = `Post_${postId}_JSON`;
await createTGWindow(jsonPath, "Dev_JSON", jsonTitle, 960, 720, false, false);
}
async function toPost(): Promise<void> {
const url = `https://m.miyoushe.com/ys/#/article/${postId}`;
await TGClient.open("web_thin", url);
}
async function toTopic(topic: TGApp.Plugins.Mys.Topic.Info): Promise<void> {
const gid = postData.value?.post.game_id ?? topic.game_id;
await emit("active_deep_link", `router?path=/posts/topic/${gid}/${topic.id}`);
}
async function toForum(forum: TGApp.Plugins.Mys.Post.Forum): Promise<void> {
await emit("active_deep_link", `router?path=/posts/forum/${forum.game_id}/${forum.id}`);
}
onUnmounted(() => {
if (shareTimeTimer.value !== undefined) {
clearInterval(shareTimeTimer.value);
shareTimeTimer.value = undefined;
}
});
</script>
<style lang="css" scoped>
.tp-post-body {
width: 800px;
margin: 0 auto;
font-family: var(--font-text);
}
/* title */
.tp-post-title {
display: flex;
width: fit-content;
align-items: center;
justify-content: start;
color: var(--common-text-title);
cursor: pointer;
font-family: var(--font-title);
font-size: 20px;
}
.mpt-official {
display: inline-block;
width: 24px;
align-items: center;
justify-content: center;
border-radius: 5px;
margin-right: 2px;
background: var(--common-shadow-1);
color: var(--box-text-5);
font-size: 16px;
text-align: center;
}
/* subtitle */
.tp-post-subtitle {
display: flex;
align-items: center;
justify-content: flex-start;
column-gap: 10px;
font-size: 16px;
opacity: 0.6;
}
.tp-post-copyright {
display: flex;
align-items: center;
justify-content: center;
color: var(--common-text-title);
column-gap: 5px;
font-family: var(--font-title);
}
/* info */
.tp-post-info {
position: relative;
display: flex;
align-items: end;
justify-content: space-between;
padding-bottom: 10px;
border-bottom: 1px dashed var(--common-shadow-2);
margin-bottom: 10px;
}
.tp-post-version {
position: absolute;
top: 0;
left: 0;
color: var(--box-text-4);
font-family: var(--font-title);
font-size: 14px;
opacity: 0.6;
}
/* meta */
.tp-post-meta {
display: flex;
align-items: center;
justify-content: start;
color: var(--box-text-4);
column-gap: 10px;
font-size: 14px;
}
.mpm-forum {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.mpm-forum img {
width: 30px;
height: 30px;
object-fit: cover;
}
.mpm-forum img:first-child {
border-radius: 5px;
margin-right: 5px;
}
.mpm-forum span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mpm-item {
display: flex;
align-items: center;
justify-content: center;
margin-left: 10px;
column-gap: 2px;
opacity: 0.8;
}
/* extra */
.tp-post-extra {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: start;
gap: 5px 10px;
}
/* collection */
.tp-post-collection {
display: flex;
align-items: center;
justify-content: center;
padding: 0 5px;
border: 1px solid var(--common-shadow-2);
border-radius: 5px;
color: var(--box-text-3);
column-gap: 5px;
cursor: pointer;
font-family: var(--font-title);
font-size: 12px;
}
/* topic */
.tp-post-topic {
display: flex;
align-items: center;
justify-content: center;
color: var(--box-text-5);
cursor: pointer;
font-family: var(--font-title);
font-size: 12px;
&:hover {
color: var(--box-text-3);
}
}
</style>