Compare commits

...

40 Commits

Author SHA1 Message Date
BTMuli
436ed453e7 🚀 v0.1.6 版本更新 2023-05-28 16:11:58 +08:00
BTMuli
d6115cffcb 🐛 延后一段时间以防两个 overlay 覆盖 2023-05-28 16:01:31 +08:00
BTMuli
01962fffb8 🎨 代码格式化 2023-05-28 15:27:51 +08:00
BTMuli
ad2d5ca657 🎨 点击外部也给退出好了 2023-05-28 15:27:01 +08:00
BTMuli
c6af9870ab 🐛 抽奖详情页的 JSON 只在 devMode 展示 2023-05-27 22:59:46 +08:00
BTMuli
9c0fc4b315 初步写了一下,还差 overlay 跟数据转换 2023-05-27 00:40:30 +08:00
BTMuli
ddec6bdc6f 🌱 开始写数据解析 2023-05-26 22:53:29 +08:00
BTMuli
6f0813b27a 🎨 常用的给封装了一下 2023-05-26 22:39:32 +08:00
BTMuli
eb96409a5d 🚚 info → record 2023-05-26 22:39:06 +08:00
BTMuli
c91e2f80e9 🏷️ 添加常用 ck 组合 2023-05-26 22:29:16 +08:00
BTMuli
3599ef0b74 💩 不知道哪里有问题 2023-05-26 22:23:22 +08:00
BTMuli
4bf48636f9 🎨 方法优化 2023-05-26 22:08:13 +08:00
BTMuli
20060287ab 💩 添加战绩数据获取 2023-05-26 21:22:46 +08:00
BTMuli
7fd7dfc642 ♻️ 精简代码 2023-05-26 16:02:49 +08:00
BTMuli
b8a1f996f0 💄 优化抽奖页详情页面样式 2023-05-26 15:38:20 +08:00
BTMuli
4769731d52 🚚 t-loading → to-loading 2023-05-26 14:49:50 +08:00
BTMuli
d962d52bd2 🐛 修复设置页 loading 退出动画无效问题 2023-05-26 14:39:30 +08:00
BTMuli
a1b85b3526 🐛 修复卡牌查找结果渲染错误 2023-05-26 13:57:46 +08:00
BTMuli
5ee9be1079 ✏️ hoem → home 2023-05-26 13:38:23 +08:00
BTMuli
f48b7ef6ed ♻️ TLoading → TOLoading 2023-05-26 13:33:08 +08:00
BTMuli
5a14b90b67 🐛 修复首页卡素材日历的问题 2023-05-26 12:13:01 +08:00
BTMuli
9978becb3b 🐛 显示 overlay 页面进入不加载的问题 2023-05-26 10:58:09 +08:00
BTMuli
2677bc44db 🐛 修复内部点击异常 cancel 的问题 2023-05-26 01:38:33 +08:00
BTMuli
cf74f7f6b4 🎨 to-calendar 单独提出来 2023-05-26 00:35:43 +08:00
BTMuli
c68716960b 🎨 t-confirm 替换完成 2023-05-26 00:35:00 +08:00
BTMuli
6c25e56010 添加外部点击事件用于 t-confirm 2023-05-25 23:17:32 +08:00
BTMuli
f1db614dee 🎨 t-loading 外部用 t-overlay 实现 2023-05-25 22:37:21 +08:00
BTMuli
a398a5fac3 新建通用组件,看看能不能替代 v-overlay 2023-05-25 22:36:45 +08:00
BTMuli
e648ed6b59 🎨 这个搞错了草 2023-05-25 22:23:28 +08:00
BTMuli
749f68f982 💄 先把所有用到 loading 的给转了一下 2023-05-25 22:06:40 +08:00
BTMuli
9e95fb29b0 💫 loading 组件加动画 2023-05-25 21:46:37 +08:00
BTMuli
0408b222dc 🚚 test → Test 2023-05-25 20:24:27 +08:00
BTMuli
3edfff7388 🚚 loading 组件我没写 overlay 啊 2023-05-25 20:22:57 +08:00
BTMuli
2fc183bc9b 🔧 透明化,后续看看要不要用 2023-05-25 20:20:40 +08:00
BTMuli
00a2d7f009 ️ 代码格式化 2023-05-25 19:00:39 +08:00
BTMuli
7648fa4fc0 💄 成就页样式优化,间距大点就大点吧 2023-05-25 19:00:13 +08:00
BTMuli
71f5c30ff9 数据库更新后成就版本自动更新 2023-05-25 18:33:58 +08:00
BTMuli
a428c86a7e 🐛 修复间距异常 2023-05-25 18:22:02 +08:00
BTMuli
52926916b4 🔧 开写 v0.1.6 2023-05-25 18:16:09 +08:00
BTMuli
6c11a67a58 🍱 更新 latest.json 2023-05-25 17:52:24 +08:00
43 changed files with 1644 additions and 1139 deletions

View File

@@ -2,15 +2,33 @@
Author: 目棃
Date: 2023-03-30
Description: CHANGELOG
Update: 2023-05-25
Update: 2023-05-28
---
> 本文档 [`Front-matter`](https://github.com/BTMuli/Mucli#FrontMatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于`2023-03-30 15:39:49`
>
> 更新于 `2023-05-25 17:24:07`
> 更新于 `2023-05-28 16:11:30`
# CHANGELOG
## [0.1.6](https://github.com/BTMuli/Tauri.Genshin/releases/v0.1.6) (2023-05-28)
### Feat
- 用户:添加部分数据获取请求
### Fix
- 页面:修复间距显示异常 [`a428c86a`](https://github.com/BTMuli/Tauri.Genshin/commit/a428c86a)
- 成就:数据库更新后成就版本自动更新 [`71f5c30f`](https://github.com/BTMuli/Tauri.Genshin/commit/71f5c30f)
- 首页:修复首页卡素材日历的问题 [`5a14b90b`](https://github.com/BTMuli/Tauri.Genshin/commit/5a14b90b)
### Change
- 组件:部分组件实现方式更改
FullCommits: [`v0.1.5 ~ v0.1.6`](https://github.com/BTMuli/Tauri.Genshin/compare/v0.1.5...v0.1.6)
## [0.1.5](https://github.com/BTMuli/Tauri.Genshin/releases/v0.1.5) (2023-05-25)
### Feat

View File

@@ -1,11 +1,11 @@
{
"version": "0.1.4",
"version": "0.1.5",
"notes": "See the assets to download this version and install.",
"pub_date": "2023-05-02T10:45:45.017Z",
"pub_date": "2023-05-25T09:50:54.739Z",
"platforms": {
"windows-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSWkdzekNNZjh1aG54TWY5cWpnNW02d3dxSFNRSFg2aDlTOGdmSlRaM0VoQWUrR2JERE1MdGdDYUJXYUYybDlLa0RycW5SZjdjRndKVVNLdDhoQzltNVp3ZWhIVzhwRkFNPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjgzMDI0MzM4CWZpbGU6dGF1cmktZ2Vuc2hpbl8wLjEuNF94NjRfemgtQ04ubXNpLnppcApuOFNqamJqN0FwVXI2OHlTODhHVEl3Q1FTQzU2R1JVNi96b1Z4ZU5nRlk3aEY2WnMzaHZNTGJVdmpXc2wraTNWZUlkK2VZdlBxd1JYaWt3MUdGVWhBQT09Cg==",
"url": "https://github.com/BTMuli/Tauri.Genshin/releases/download/v0.1.4/tauri-genshin_0.1.4_x64_zh-CN.msi.zip"
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSWkdzekNNZjh1aHB0Y0g4b2Jyd2pMVkhKSDdGa0Q2UjM1OWRObFJFMlVEMXZkay9QNXRCOTQ5VS8wUkxPeHFCNVplRVdnTEt0QnM1U3lIZXVxK21VN3VBMlQ4TDRpMHdvPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjg1MDA4MjM5CWZpbGU6dGF1cmktZ2Vuc2hpbl8wLjEuNV94NjRfemgtQ04ubXNpLnppcAozSWFYSHJIT2JzYjdDLzhjV3FKMW9odzNTN05wZVVYalphM3ZFYS90R1E1WHl5aFMyNCt3MFFxR3hZM0tBZUE5Qjk2TG91WXorRXFzQ1VXR2pwZllDZz09Cg==",
"url": "https://github.com/BTMuli/Tauri.Genshin/releases/download/v0.1.5/tauri-genshin_0.1.5_x64_zh-CN.msi.zip"
}
}
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "tauri-genshin",
"version": "0.1.5",
"version": "0.1.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tauri-genshin",
"version": "0.1.5",
"version": "0.1.6",
"license": "MIT",
"dependencies": {
"@mdi/font": "7.2.96",

View File

@@ -2,7 +2,7 @@
"name": "tauri-genshin",
"description": "A Tauri App Demo",
"private": true,
"version": "0.1.5",
"version": "0.1.6",
"author": "BTMuli <bt-muli@outlook.com>",
"packageManager": "npm@9.6.6",
"scripts": {

2
src-tauri/Cargo.lock generated
View File

@@ -3117,7 +3117,7 @@ dependencies = [
[[package]]
name = "tauri-genshin"
version = "0.1.5"
version = "0.1.6"
dependencies = [
"serde",
"serde_json",

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-genshin"
version = "0.1.5"
version = "0.1.6"
description = "A Tauri App"
authors = ["BTMuli<bt-muli@outlook.com>"]
license = "MIT"

View File

@@ -8,7 +8,7 @@
},
"package": {
"productName": "tauri-genshin",
"version": "0.1.5"
"version": "0.1.6"
},
"tauri": {
"allowlist": {
@@ -67,7 +67,7 @@
"width": 1600,
"height": 900,
"center": true,
"transparent": false
"transparent": true
}
]
}

View File

@@ -1,5 +1,6 @@
<template>
<v-app>
<!-- glass-app{background: rgba(255, 255, 255, 0.1) !important;} -->
<v-app class="glass-app">
<TSidebar v-if="isMain" />
<v-main>
<v-container fluid class="app-container">
@@ -77,9 +78,8 @@ async function createDataDir () {
</script>
<style lang="css">
.app-container {
padding: 0;
height: 100%;
overflow: auto;
background: var(--page-bg);
height: 100%;
overflow: auto;
background: var(--page-bg);
}
</style>

View File

@@ -56,62 +56,17 @@
</div>
</div>
</div>
<v-snackbar v-model="snackbar" :timeout="1500" :color="snackbarColor">
{{ snackbarText }}
</v-snackbar>
<v-overlay v-model="showItem">
<div class="calendar-item-box">
<div class="calendar-item-top">
<div class="calendar-item-icon">
<TMiniAvatar v-if="selectedType=== 'character'" :model-value="selectedItem" size="100px" />
<TMiniWeapon v-if="selectedType=== 'weapon'" :model-value="selectedItem" size="100px" />
</div>
<div class="calendar-item-content">
<div v-for="item in selectedItem.materials" class="calendar-item-sub">
<TCalendarMaterial :item="item" />
</div>
</div>
</div>
<div class="calendar-item-line">
<img src="/source/UI/item-line.webp" alt="line">
</div>
<div class="calendar-item-bottom">
<div class="calendar-item-source">
<div class="calendar-source-text">
来源
</div>
<img :src="`/icon/nation/${selectedItem.source.area}.webp`" alt="icon">
<div class="calendar-source-text">
{{ selectedItem.source.area }} - {{ selectedItem.source.name }}
</div>
</div>
<div class="detail-btn">
<v-btn @click="showDetail(selectedItem)">
<template #append>
<img src="../../assets/icons/arrow-right.svg" alt="right">
</template>
详情
</v-btn>
</div>
</div>
</div>
<div class="calendar-item-close" @click="showItem = false">
<v-icon>mdi-close</v-icon>
</div>
</v-overlay>
<ToCalendar v-model="showItem" :data-type="selectedType" :data-val="selectedItem" />
</div>
</template>
<script lang="ts" setup>
// vue
import { computed, onMounted, ref } from "vue";
import ToCalendar from "../overlay/to-calendar.vue";
import TMiniAvatar from "../mini/t-mini-avatar.vue";
import TMiniWeapon from "../mini/t-mini-weapon.vue";
import TCalendarMaterial from "../mini/t-calendar-material.vue";
// data
import { AppCalendarData } from "../../data";
// interface
import { OBC_CONTENT_API } from "../../plugins/Mys/interface/utils";
import { createTGWindow } from "../../utils/TGWindow";
// loading
const loading = ref(true as boolean);
@@ -132,11 +87,6 @@ const showItem = ref(false as boolean);
const selectedItem = ref({} as TGApp.App.Calendar.Item);
const selectedType = ref("character");
// snackbar
const snackbar = ref(false as boolean);
const snackbarText = ref("" as string);
const snackbarColor = ref("success" as string);
const btnText = [
{
week: 7,
@@ -194,17 +144,6 @@ function selectContent (item: TGApp.App.Calendar.Item, type: string) {
showItem.value = true;
}
function showDetail (item: TGApp.App.Calendar.Item) {
if (item.contentId === 0) {
snackbarText.value = "暂无详情";
snackbarColor.value = "error";
snackbar.value = true;
return;
}
const url = OBC_CONTENT_API.replace("{content_id}", item.contentId.toString());
createTGWindow(url, "素材详情", item.name, 1200, 800, true);
}
function getContents (day: number) {
btnNow.value = day;
calendarNow.value = getCalendar(day);
@@ -216,8 +155,8 @@ function getContents (day: number) {
.calendar-box {
margin-bottom: 10px;
padding: 10px;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
background: rgb(255 255 255 / 10%);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
border-radius: 5px;
}
@@ -225,14 +164,14 @@ function getContents (day: number) {
height: 45px;
font-size: 20px;
display: flex;
color:rgba(255, 255, 255, 0.8);
color:rgb(255 255 255 / 80%);
}
.calendar-title-left {
width: 20%;
height: 45px;
font-family: Genshin, serif;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
text-shadow: 0 0 10px rgb(0 0 0 / 80%);
display: flex;
align-items: center;
}
@@ -256,7 +195,7 @@ function getContents (day: number) {
width: 100%;
height: 2px;
border-radius: 2px;
background: rgba(0, 0, 0, 0.4);
background: rgb(0 0 0 / 40%);
}
.calendar-sub {
@@ -264,7 +203,7 @@ function getContents (day: number) {
}
.calendar-sub-title {
background: rgba(0, 0, 0, 0.2);
background: rgb(0 0 0 / 20%);
display: flex;
align-items: center;
height: 30px;
@@ -272,8 +211,8 @@ function getContents (day: number) {
margin: 5px 0;
border-radius: 5px;
font-family: Genshin-Light, serif;
color: rgba(255, 255, 255, 0.8);
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
color: rgb(255 255 255 / 80%);
text-shadow: 0 0 10px rgb(0 0 0 / 80%);
}
.calendar-sub-title img {
@@ -287,129 +226,4 @@ function getContents (day: number) {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-gap: 8px;
}
/* overlay 盒子 */
.calendar-item-box {
position: absolute;
width: 440px;
height: 200px;
color: #faf7e8;
top: calc(50vh - 100px);
left: calc(50vw - 220px);
background: var(--content-bg-2);
border-radius: 10px;
padding: 10px;
align-items: center;
}
.calendar-item-top {
height: 100px;
width: 100%;
display: flex;
}
.calendar-item-icon {
height: 100px;
width: 100px;
}
.calendar-item-content {
margin-left: 10px;
font-family: Genshin, serif;
color: var(--content-bg-1);
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 10px;
row-gap: 10px;
}
.calendar-item-sub {
width: 150px;
height: 45px;
border-radius: 10px;
}
.calendar-item-sub img {
width: 40px;
height: 40px;
object-fit: cover;
border-radius: 10px;
}
.calendar-item-line {
width: 420px;
}
.calendar-item-line img {
width: 100%;
height: auto;
}
.calendar-item-bottom {
background: rgb(0 0 0 / 30%);
padding: 3px 10px;
width: 420px;
height: 56px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.calendar-item-bottom img {
width: 50px;
height: 50px;
}
.calendar-item-source {
width: 300px;
height: 50px;
display: flex;
justify-content: left;
align-items: center;
}
.calendar-source-text {
height: 50px;
font-size: 20px;
font-family: Genshin-Light, serif;
display: flex;
justify-content: center;
align-items: center;
}
.detail-btn {
font-family: Genshin, serif;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
border-radius: 5px;
transition: all 0.3s linear;
}
.detail-btn button {
background: var(--btn-bg-1);
color: #faf7e8;
}
.detail-btn button img {
width: 18px;
height: 18px;
}
.calendar-item-close {
position: absolute;
top: calc(50vh + 120px);
left: calc(50vw - 15px);
border-radius: 50%;
width: 30px;
height: 30px;
background: var(--content-bg-2);
color: #546D8B;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<transition enter-from-class="tolo-enter-from" name="tolo">
<div v-if="showTolo" class="tolo-box" @click.self.prevent="toClick">
<transition enter-from-class="toli-enter-from" name="toli">
<slot v-if="showToli" />
</transition>
</div>
</transition>
</template>
<script lang="ts" setup>
// vue
import { ref, watch } from "vue";
interface TolProps {
modelValue: boolean;
toClick?: () => void;
blurVal: string;
hide?: true;
}
const showTolo = ref(!props.hide);
const showToli = ref(!props.hide);
const props = withDefaults(defineProps<TolProps>(), {
modelValue: false,
blurVal: "20px",
});
watch(() => props.modelValue, () => {
if (props.modelValue) {
showTolo.value = true;
showToli.value = true;
} else {
setTimeout(() => {
showToli.value = false;
}, 100);
setTimeout(() => {
showTolo.value = false;
}, 300);
}
},
);
</script>
<style lang="css" scoped>
.tolo-enter-active,
.tolo-leave-active,
.toli-enter-active {
transition: all 0.3s;
}
.toli-leave-active {
transition: all 0.5s ease-in-out;
}
.tolo-enter-from,
.toli-enter-from {
opacity: 0;
transform: scale(1.5);
}
.tolo-enter-to,
.toli-enter-to {
opacity: 1;
transform: scale(1);
}
.tolo-leave-from {
opacity: 1;
}
.tolo-leave-to {
opacity: 0;
}
.toli-leave-from {
opacity: 1;
transform: scale(1);
}
.toli-leave-to {
opacity: 0;
transform: scale(0);
}
.tolo-box {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background: rgb(0 0 0 / 50%);
backdrop-filter: blur(v-bind(blurVal));
z-index: 100;
}
</style>

View File

@@ -213,14 +213,14 @@ onUnmounted(() => {
margin-bottom: 10px;
padding: 10px;
font-family: Genshin, serif;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
background: rgb(255 255 255 / 10%);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
border-radius: 5px;
}
.pool-title {
color:rgba(255, 255, 255, 0.8);
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
color:rgb(255 255 255 / 80%);
text-shadow: 0 0 10px rgb(0 0 0 / 80%);
font-size: 20px;
display: flex;
}
@@ -231,7 +231,7 @@ onUnmounted(() => {
transform: translate(0, 2px);
margin-right: 10px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.3);
background: rgb(0 0 0 / 30%);
}
.pool-grid {

View File

@@ -142,14 +142,14 @@ onUnmounted(() => {
margin-bottom: 10px;
padding: 10px;
font-family: Genshin, serif;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
background: rgb(255 255 255 / 10%);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
border-radius: 5px;
}
.position-title {
color:rgba(255, 255, 255, 0.8);
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
color:rgb(255 255 255 / 80%);
text-shadow: 0 0 10px rgb(0 0 0 / 80%);
font-size: 20px;
display: flex;
}

View File

@@ -0,0 +1,172 @@
<template>
<div class="tua-box" @click="showData">
<div class="tua-top">
<div class="top-item">
<div class="top-icon">
<div class="ti-bg">
<img :src="`/icon/bg/${props.modelValue.rarity===105?5:props.modelValue.rarity}-Star.webp`" alt="rarity">
</div>
<div class="ti-content">
<img :src="props.modelValue.icon" alt="avatar">
</div>
<div class="ti-level">
{{ props.modelValue.actived_constellation_num }}
</div>
</div>
<div class="top-text">
Lv.{{ props.modelValue.level }}
</div>
</div>
<div class="top-item">
<div class="top-icon">
<div class="ti-bg">
<img :src="`/icon/bg/${props.modelValue.weapon.rarity}-Star.webp`" alt="rarity">
</div>
<div class="ti-content">
<img :src="props.modelValue.weapon.icon" alt="avatar">
</div>
<div class="ti-level">
{{ props.modelValue.weapon.affix_level }}
</div>
</div>
<div class="top-text">
Lv.{{ props.modelValue.level }}
</div>
</div>
</div>
<!-- <div class="tua-bottom">-->
<!-- <div class="bottom-item">A</div>-->
<!-- <div class="bottom-item">E</div>-->
<!-- <div class="bottom-item">Q</div>-->
<!-- </div>-->
</div>
</template>
<script lang="ts" setup>
interface TUAProps {
modelValue: TGApp.Game.Character.ListItem
}
const props = defineProps<TUAProps>();
function showData () {
console.log(JSON.stringify(props.modelValue, null, 2));
}
function getNameCard () {
}
</script>
<style lang="css">
.tua-box {
padding: 5px;
width: 190px;
height: 100%;
background: rgb(255 255 255 / 10%);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
border-radius: 5px;
cursor: pointer;
}
.tua-top {
width: 180px;
height: 110px;
border-radius: 5px;
padding: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
.top-item {
width: 80px;
height: 100%;
background: rgb(0 0 0 / 20%);
border-radius: 5px;
}
.top-icon {
position: relative;
width: 80px;
height: 80px;
border-radius: 5px;
}
.ti-bg {
position: absolute;
top: 0;
left: 0;
width: 80px;
height: 80px;
border-radius: 5px;
}
.ti-bg img {
width: 100%;
height: 100%;
border-radius: 5px;
}
.ti-content {
position: absolute;
top: 0;
left: 0;
width: 80px;
height: 80px;
border-radius: 5px;
}
.ti-content img {
width: 100%;
height: 100%;
border-radius: 5px;
}
.ti-level {
position: absolute;
right: 0;
top: 0;
padding: 5px;
width: 20px;
height: 20px;
background: rgb(0 0 0 / 50%);
color: #faf7e8;
display: flex;
align-items: center;
justify-content: center;
border-top-right-radius: 5px;
border-bottom-left-radius: 5px;
font-family: Genshin,sans-serif;
}
.top-text {
width: 80px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
color: #faf7e8;
font-family: Genshin-Light,sans-serif;
}
.top-love img {
width: 100%;
height: 100%;
}
.tua-bottom {
width: 100%;
height: 40%;
padding: 5px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
.bottom-item {
width: 32%;
height: 100%;
background: rgb(0 0 0 / 20%);
border-radius: 5px;
}
</style>

View File

@@ -1,84 +0,0 @@
<template>
<div class="loading-div">
<div class="loading-content">
<div class="loading-title">
{{ title }}
<v-progress-circular v-show="!empty" indeterminate color="#f4d8a8" />
</div>
<div v-if="subtitle !== ''" class="loading-subtitle">
{{ subtitle }}
</div>
<div v-if="!empty" class="loading-img">
<img src="/source/UI/loading.webp" alt="loading">
</div>
<div v-else class="loading-img">
<img src="/source/UI/empty.webp" alt="empty">
</div>
<div v-if="content !== ''" class="loading-text">
{{ content }}
</div>
</div>
</div>
</template>
<script lang="ts" setup>
interface LoadingProps {
title?: string;
subtitle?: string;
content?: string;
empty?: boolean;
position?: string;
}
withDefaults(defineProps<LoadingProps>(), {
title: "加载中",
subtitle: "",
content: "",
empty: false,
position: "absolute",
});
</script>
<style lang="css" scoped>
.loading-div {
position: v-bind(position);
display: flex;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
background: rgb(57 59 64 / 50%);
backdrop-filter: blur(10px);
border-radius: 20px;
}
.loading-content {
width: 100%;
margin: 20px;
display: flex;
border: #f4d8a8 1px solid;
border-radius: 20px;
flex-direction: column;
justify-content: center;
align-items: center;
}
.loading-title {
font-size: 2rem;
font-family: Genshin, serif;
font-weight: 600;
color: #f4d8a8;
}
.loading-subtitle {
font-size: 1rem;
font-family: Genshin-Light, serif;
color: #f4d8a8;
}
.loading-img {
width: 256px;
height: 256px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,191 @@
<template>
<TOverlay v-model="visible" hide :to-click="onCancel">
<div class="toc-box">
<div class="box-div">
<div class="toc-top">
<div class="toc-icon">
<TMiniAvatar v-if="itemType=== 'character'" v-model="itemVal" size="100px" />
<TMiniWeapon v-if="itemType=== 'weapon'" v-model="itemVal" size="100px" />
</div>
<div class="toc-material-grid">
<TCalendarMaterial v-for="item in itemVal.materials" :item="item" />
</div>
</div>
<img src="/source/UI/item-line.webp" alt="line" class="toc-line">
<div class="toc-bottom">
<div class="toc-src-box">
<div class="toc-src-text">
来源
</div>
<img :src="`/icon/nation/${itemVal.source.area}.webp`" alt="icon">
<div class="toc-src-text">
{{ itemVal.source.area }} - {{ itemVal.source.name }}
</div>
</div>
<v-btn variant="outlined" @click="toDetail(itemVal)">
<template #append>
<img src="../../assets/icons/arrow-right.svg" alt="right" class="toc-btn-img">
</template>
详情
</v-btn>
</div>
</div>
<div class="close-div">
<div class="close-btn" @click="onCancel">
<v-icon>mdi-close</v-icon>
</div>
</div>
</div>
</TOverlay>
<v-snackbar v-model="snackbar" :timeout="1500" color="error">
{{ itemType === "weapon" ? "武器" : "角色" }} 暂无详情
</v-snackbar>
</template>
<script setup lang="ts">
// vue
import { computed, ref } from "vue";
import TOverlay from "../main/t-overlay.vue";
import TCalendarMaterial from "../mini/t-calendar-material.vue";
import TMiniAvatar from "../mini/t-mini-avatar.vue";
import TMiniWeapon from "../mini/t-mini-weapon.vue";
// utils
import { OBC_CONTENT_API } from "../../plugins/Mys/interface/utils";
import { createTGWindow } from "../../utils/TGWindow";
interface TOCalendarProps {
modelValue: boolean;
dataType: string;
dataVal: TGApp.App.Calendar.Item;
}
interface TOCalendarEmits {
(e: "update:modelValue", value: TGApp.App.Calendar.Item): void;
(e: "cancel"): void;
}
const emits = defineEmits<TOCalendarEmits>();
const props = withDefaults(defineProps<TOCalendarProps>(), {
modelValue: false,
dataType: "",
dataVal: {} as TGApp.App.Calendar.Item,
});
const visible = computed({
get: () => props.modelValue,
set: (value) => emits("update:modelValue", value),
});
const itemType = computed(() => props.dataType);
const itemVal = computed(() => props.dataVal);
const snackbar = ref(false);
const onCancel = () => {
visible.value = false;
emits("cancel");
};
function toDetail (item: TGApp.App.Calendar.Item) {
if (item.contentId === 0) {
snackbar.value = true;
return;
}
const url = OBC_CONTENT_API.replace("{content_id}", item.contentId.toString());
createTGWindow(url, "素材详情", item.name, 1200, 800, true);
}
</script>
<style scoped>
/* overlay 盒子 */
.toc-box {
width: 440px;
height: 260px;
}
.box-div {
height: 200px;
background: rgb(255 255 255 / 30%);
padding: 10px;
border-radius: 5px;
color: #faf7e8;
align-items: center;
}
.toc-top {
height: 100px;
width: 100%;
display: flex;
}
.toc-icon {
height: 100px;
width: 100px;
}
.toc-material-grid {
margin-left: 10px;
font-family: Genshin, serif;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 10px;
}
.toc-line {
width: 100%;
height: auto;
}
.toc-bottom {
background: rgb(0 0 0 / 30%);
padding: 3px 10px;
width: 420px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.toc-bottom img {
width: 50px;
height: 50px;
}
.toc-src-box {
width: 300px;
height: 50px;
display: flex;
justify-content: left;
align-items: center;
}
.toc-src-text {
height: 50px;
font-size: 20px;
font-family: Genshin-Light, serif;
display: flex;
justify-content: center;
align-items: center;
}
.toc-btn-img {
width: 18px !important;
height: 18px !important;
}
.close-div {
height: 60px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.close-btn {
border-radius: 50%;
width: 30px;
height: 30px;
background: rgb(255 255 255 / 30%);
color: #faf7e8;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<v-overlay v-model="visible">
<TOverlay v-model="visible" :to-click="onCancel" :blur-val="'3px'" hide>
<div class="confirm-div">
<div class="confirm-box">
<div class="confirm-title">
@@ -27,14 +27,17 @@
</div>
</div>
</div>
</v-overlay>
</TOverlay>
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import { computed, inject } from "vue";
import TOverlay from "../main/t-overlay.vue";
interface TConfirmProps {
const test = inject("hide");
interface TOConfirmProps {
title: string;
subtitle?: string;
isInput?: boolean;
@@ -45,16 +48,16 @@ interface TConfirmProps {
modelInput: string;
}
interface TConfirmEmits {
(e: "update:show", v: boolean): void;
interface TOConfirmEmits {
(e: "update:modelValue", v: boolean): void;
(e: "update:modelInput", v: string): void;
(e: "confirm"): void;
(e: "cancel"): void;
}
const emits = defineEmits<TConfirmEmits>();
const props = withDefaults(defineProps<TConfirmProps>(), {
const emits = defineEmits<TOConfirmEmits>();
const props = withDefaults(defineProps<TOConfirmProps>(), {
modelValue: false,
title: "确认",
subtitle: "",
isInput: false,

View File

@@ -0,0 +1,106 @@
<template>
<TOverlay v-model="show" :blur-val="'5px'">
<div class="tol-div">
<div class="tol-box">
<div class="tol-title">
<slot name="title">
{{ title }}
</slot>
<v-progress-circular v-show="!empty" indeterminate color="#f4d8a8" />
</div>
<div v-if="subtitle" class="tol-subtitle">
<slot name="subtitle">
{{ subtitle }}
</slot>
</div>
<div class="tol-img">
<slot name="img">
<img v-if="!empty" src="/source/UI/loading.webp" alt="loading">
<img v-else src="/source/UI/empty.webp" alt="empty">
</slot>
</div>
</div>
</div>
</TOverlay>
</template>
<script lang="ts" setup>
// vue
import { ref, watch } from "vue";
import TOverlay from "../main/t-overlay.vue";
interface LoadingProps {
modelValue: boolean;
title?: string;
subtitle?: string;
empty?: boolean;
}
const show = ref(true);
const props = withDefaults(defineProps<LoadingProps>(), {
modelValue: true,
title: "加载中",
empty: false,
});
watch(() => props.modelValue, (v) => {
show.value = v;
});
</script>
<style lang="css" scoped>
.tol-div {
display: flex;
min-width: 800px;
min-height: 300px;
padding: 15px;
background: rgb(255 255 255 / 5%);
box-shadow: 0 0 10px rgb(0 0 0 / 50%);
border-radius: 15px;
}
.tol-box {
width: 100%;
padding: 10px;
display: flex;
box-sizing: content-box;
border: #f4d8a8 1px solid;
color: #f4d8a8;
border-radius: 5px;
flex-direction: column;
justify-content: center;
align-items: center;
}
.tol-title {
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-family: Genshin, serif;
font-weight: 600;
}
.tol-subtitle {
width: 100%;
text-align: center;
font-size: 1rem;
height: 25px;
font-family: Genshin-Light, serif;
}
.tol-img {
width: 100%;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
}
.tol-img:deep(img) {
max-height: 200px;
max-width: 100%;
border-radius: 5px;
}
</style>

View File

@@ -22,10 +22,8 @@
</v-btn>
</template>
</v-app-bar>
<div v-show="loading">
<TLoading :title="loadingTitle" />
</div>
<div v-show="!loading" class="wrap">
<TOLoading v-model="loading" :title="loadingTitle" />
<div class="wrap">
<!-- 左侧菜单 -->
<div class="left-wrap">
<v-list v-for="series in seriesList" :key="series.id" class="card-left" @click="selectSeries(series.id)">
@@ -110,7 +108,7 @@
<script lang="ts" setup>
// vue
import { onMounted, ref, onBeforeMount, computed } from "vue";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
// tauri
import { dialog, fs } from "@tauri-apps/api";
// Store
@@ -321,27 +319,23 @@ async function exportJson () {
/* 内容区域 */
.wrap {
display: flex;
flex-direction: row;
overflow: auto;
max-height: 90vh;
font-family: Genshin-Light, serif;
justify-content: space-between;
}
/* 左侧系列 */
.left-wrap {
float: left;
width: 25%;
max-height: calc(100vh - 100px);
overflow: auto;
width: 400px;
height: calc(100vh - 100px);
overflow-y: scroll;
overflow-x: hidden;
}
/* 右侧成就 */
.right-wrap {
position: relative;
float: right;
width: 75%;
max-height: calc(100vh - 100px);
overflow: auto;
width: calc(100% - 410px);
height: calc(100vh - 100px);
overflow-y: scroll;
overflow-x: hidden;
}
.list-empty {

View File

@@ -1,101 +1,97 @@
<template>
<div v-if="loading">
<TLoading :title="loadingTitle" />
</div>
<div v-else>
<v-tabs v-model="tab" align-tabs="start" class="anno-tab">
<v-tab value="activity">
活动公告
</v-tab>
<v-tab value="game">
游戏公告
</v-tab>
<v-spacer />
<v-btn class="switch-btn" @click="switchNews">
<template #prepend>
<v-icon>mdi-bullhorn</v-icon>
</template>
切换米游社咨讯
</v-btn>
</v-tabs>
<v-window v-model="tab">
<v-window-item value="activity">
<div class="anno-grid">
<v-card v-for="item in annoCards.activity" :key="item.id" class="anno-card" width="340">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover">
</div>
<v-card-title>
{{ item.title }}
</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="anno-btn" @click="toPost(item)">
<template #prepend>
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right">
</template>
查看
</v-btn>
<v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon>
{{ item.startTime.split(" ")[0] }} -
{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode">
id: {{ item.id }}
</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
查看数据
</v-btn>
</v-card-actions>
</v-card>
</div>
</v-window-item>
<v-window-item value="game">
<div class="anno-grid">
<v-card v-for="item in annoCards.game" :key="item.id" class="anno-card" width="340">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover">
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="anno-btn" @click="toPost(item)">
<template #prepend>
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right">
</template>
查看
</v-btn>
<v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon>
{{ item.startTime.split(" ")[0] }} -
{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode">
id: {{ item.id }}
</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
查看数据
</v-btn>
</v-card-actions>
</v-card>
</div>
</v-window-item>
</v-window>
</div>
<TLoading v-model="loading" :title="loadingTitle" />
<v-tabs v-model="tab" align-tabs="start" class="anno-tab">
<v-tab value="activity">
活动公告
</v-tab>
<v-tab value="game">
游戏公告
</v-tab>
<v-spacer />
<v-btn class="switch-btn" @click="switchNews">
<template #prepend>
<v-icon>mdi-bullhorn</v-icon>
</template>
切换米游社咨讯
</v-btn>
</v-tabs>
<v-window v-model="tab">
<v-window-item value="activity">
<div class="anno-grid">
<v-card v-for="item in annoCards.activity" :key="item.id" class="anno-card" width="340">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover">
</div>
<v-card-title>
{{ item.title }}
</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="anno-btn" @click="toPost(item)">
<template #prepend>
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right">
</template>
查看
</v-btn>
<v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon>
{{ item.startTime.split(" ")[0] }} -
{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode">
id: {{ item.id }}
</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
查看数据
</v-btn>
</v-card-actions>
</v-card>
</div>
</v-window-item>
<v-window-item value="game">
<div class="anno-grid">
<v-card v-for="item in annoCards.game" :key="item.id" class="anno-card" width="340">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover">
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="anno-btn" @click="toPost(item)">
<template #prepend>
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right">
</template>
查看
</v-btn>
<v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon>
{{ item.startTime.split(" ")[0] }} -
{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode">
id: {{ item.id }}
</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
查看数据
</v-btn>
</v-card-actions>
</v-card>
</div>
</v-window-item>
</v-window>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import TLoading from "../components/overlay/t-loading.vue";
import TLoading from "../components/overlay/to-loading.vue";
// store
import { useAppStore } from "../store/modules/app";
// utils

View File

@@ -1,183 +1,179 @@
<template>
<div v-if="loading">
<TLoading :title="loadingTitle" />
</div>
<div v-else>
<v-list class="config-list">
<v-list-subheader inset class="config-header">
应用信息
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<template #prepend>
<img class="config-icon" src="/platforms/tauri.webp" alt="Tauri">
</template>
<template #append>
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item>
<template #prepend>
<img class="config-icon" src="/icon.webp" alt="App">
</template>
<v-list-item-title>
应用版本
<v-btn
class="card-btn" size="small"
@click="toOuter('https://github.com/BTMuli/Tauri.Genshin/releases/latest')"
>
Alpha
</v-btn>
</v-list-item-title>
<template #append>
<v-list-item-subtitle>{{ versionApp }}.{{ buildTime }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="成就版本">
<template #prepend>
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements">
</template>
<template #append>
<v-list-item-subtitle>{{ achievementsStore.lastVersion }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="登录信息">
<v-list-item-subtitle v-show="userInfo.nickname!=='未登录'">
{{ userInfo.nickname }} uid:{{ userInfo.uid }}
</v-list-item-subtitle>
<v-list-item-subtitle v-show="userInfo.nickname==='未登录'">
未登录请输入 Cookie 登录
</v-list-item-subtitle>
<template #prepend>
<img class="config-icon" :src="userInfo.avatar" alt="Login">
</template>
<template #append>
<v-btn class="card-btn" @click="tryConfirm('refreshUser')">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">
刷新数据
</template>
</v-btn>
</template>
</v-list-item>
<v-list-subheader inset class="config-header">
系统信息
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="系统平台">
<template #prepend>
<v-icon>mdi-desktop-classic</v-icon>
</template>
<template #append>
<v-list-item-subtitle>{{ osPlatform }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="系统版本">
<template #prepend>
<v-icon>mdi-desktop-classic</v-icon>
</template>
<template #append>
<v-list-item-subtitle>{{ osVersion }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="数据库更新时间" prepend-icon="mdi-database">
<template #append>
<v-list-item-subtitle>{{ dbInfo.find(item => item.key === "dataUpdated")?.value }}</v-list-item-subtitle>
</template>
<v-list-item-subtitle>更新于 {{ dbInfo.find(item => item.key === "dataUpdated")?.updated }}</v-list-item-subtitle>
</v-list-item>
<v-list-item title="数据库版本" prepend-icon="mdi-database">
<template #append>
<v-list-item-subtitle>{{ dbInfo.find(item => item.key === "appVersion")?.value }}</v-list-item-subtitle>
</template>
<v-list-item-subtitle>更新于 {{ dbInfo.find(item => item.key === "appVersion")?.updated }}</v-list-item-subtitle>
</v-list-item>
<v-list-subheader inset class="config-header">
设置
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item>
<template #prepend>
<v-icon>mdi-view-dashboard</v-icon>
</template>
<v-select v-model="showHome" :items="homeStore.getShowItems()" label="首页显示组件" multiple chips />
<template #append>
<v-btn class="card-btn" @click="submitHome">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">
确定
</template>
</v-btn>
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-content-save" title="数据备份" @click="tryConfirm('backup')" />
<v-list-item prepend-icon="mdi-content-save" title="数据恢复" @click="tryConfirm('restore')" />
<v-list-item prepend-icon="mdi-delete" title="清除用户缓存" @click="tryConfirm('delUser')" />
<v-list-item prepend-icon="mdi-delete" title="清除临时数据" @click="tryConfirm('delTemp')" />
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="tryConfirm('delApp')" />
<v-list-subheader inset class="config-header">
调试
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item v-if="appStore.devEnv" title="调试模式" subtitle="开启后将显示调试信息">
<template #prepend>
<v-icon>mdi-bug</v-icon>
</template>
<template #append>
<v-switch
v-model="appStore.devMode" :label="appStore.devMode ? '开启' : '关闭'" inset color="#FAC51E"
@click="submitDevMode"
/>
</template>
</v-list-item>
<v-list-item>
<template #prepend>
<v-icon>mdi-cookie</v-icon>
</template>
<template #title>
<span style="cursor: pointer" @click="tryConfirm('inputCookie')">手动输入 Cookie</span>
</template>
<template #append>
<v-icon style="cursor: pointer" @click="toOuter('https://github.com/BTMuli/Tauri.Genshin/issues/18')">
mdi-help-circle-outline
</v-icon>
</template>
</v-list-item>
<v-list-item title="重置数据库" prepend-icon="mdi-delete" @click="tryConfirm('resetDB')" />
<v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" />
<v-list-subheader inset class="config-header">
路径
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item prepend-icon="mdi-database">
<v-list-item-title>本地数据库路径</v-list-item-title>
<v-list-item-subtitle>{{ appStore.dataPath.dbDataPath }}</v-list-item-subtitle>
</v-list-item>
<v-list-item prepend-icon="mdi-folder">
<v-list-item-title>本地临时数据路径</v-list-item-title>
<v-list-item-subtitle>{{ appStore.dataPath.tempDataDir }}</v-list-item-subtitle>
</v-list-item>
<v-list-item prepend-icon="mdi-folder">
<v-list-item-title>本地用户数据路径</v-list-item-title>
<v-list-item-subtitle>{{ appStore.dataPath.userDataDir }}</v-list-item-subtitle>
</v-list-item>
</v-list>
<!-- 弹窗提示条 -->
<v-snackbar v-model="snackbar" timeout="1500" :color="snackbarColor">
{{ snackbarText }}
</v-snackbar>
<!-- 确认弹窗 -->
<TConfirm v-model:model-value="confirmShow" v-model:model-input="confirmInput" :title="confirmText" :subtitle="confirmSub" :is-input="isConfirmInput" @confirm="doConfirm(confirmOper)" />
</div>
<TOLoading v-model="loading" :title="loadingTitle" />
<v-list class="config-list">
<v-list-subheader inset class="config-header">
应用信息
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<template #prepend>
<img class="config-icon" src="/platforms/tauri.webp" alt="Tauri">
</template>
<template #append>
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item>
<template #prepend>
<img class="config-icon" src="/icon.webp" alt="App">
</template>
<v-list-item-title>
应用版本
<v-btn
class="card-btn" size="small"
@click="toOuter('https://github.com/BTMuli/Tauri.Genshin/releases/latest')"
>
Alpha
</v-btn>
</v-list-item-title>
<template #append>
<v-list-item-subtitle>{{ versionApp }}.{{ buildTime }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="成就版本">
<template #prepend>
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements">
</template>
<template #append>
<v-list-item-subtitle>{{ achievementsStore.lastVersion }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="登录信息">
<v-list-item-subtitle v-show="userInfo.nickname!=='未登录'">
{{ userInfo.nickname }} uid:{{ userInfo.uid }}
</v-list-item-subtitle>
<v-list-item-subtitle v-show="userInfo.nickname==='未登录'">
未登录请输入 Cookie 登录
</v-list-item-subtitle>
<template #prepend>
<img class="config-icon" :src="userInfo.avatar" alt="Login">
</template>
<template #append>
<v-btn class="card-btn" @click="tryConfirm('refreshUser')">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">
刷新数据
</template>
</v-btn>
</template>
</v-list-item>
<v-list-subheader inset class="config-header">
系统信息
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="系统平台">
<template #prepend>
<v-icon>mdi-desktop-classic</v-icon>
</template>
<template #append>
<v-list-item-subtitle>{{ osPlatform }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="系统版本">
<template #prepend>
<v-icon>mdi-desktop-classic</v-icon>
</template>
<template #append>
<v-list-item-subtitle>{{ osVersion }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="数据库更新时间" prepend-icon="mdi-database">
<template #append>
<v-list-item-subtitle>{{ dbInfo.find(item => item.key === "dataUpdated")?.value }}</v-list-item-subtitle>
</template>
<v-list-item-subtitle>更新于 {{ dbInfo.find(item => item.key === "dataUpdated")?.updated }}</v-list-item-subtitle>
</v-list-item>
<v-list-item title="数据库版本" prepend-icon="mdi-database">
<template #append>
<v-list-item-subtitle>{{ dbInfo.find(item => item.key === "appVersion")?.value }}</v-list-item-subtitle>
</template>
<v-list-item-subtitle>更新于 {{ dbInfo.find(item => item.key === "appVersion")?.updated }}</v-list-item-subtitle>
</v-list-item>
<v-list-subheader inset class="config-header">
设置
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item>
<template #prepend>
<v-icon>mdi-view-dashboard</v-icon>
</template>
<v-select v-model="showHome" :items="homeStore.getShowItems()" label="首页显示组件" multiple chips />
<template #append>
<v-btn class="card-btn" @click="submitHome">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">
确定
</template>
</v-btn>
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-content-save" title="数据备份" @click="tryConfirm('backup')" />
<v-list-item prepend-icon="mdi-content-save" title="数据恢复" @click="tryConfirm('restore')" />
<v-list-item prepend-icon="mdi-delete" title="清除用户缓存" @click="tryConfirm('delUser')" />
<v-list-item prepend-icon="mdi-delete" title="清除临时数据" @click="tryConfirm('delTemp')" />
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="tryConfirm('delApp')" />
<v-list-subheader inset class="config-header">
调试
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item v-if="appStore.devEnv" title="调试模式" subtitle="开启后将显示调试信息">
<template #prepend>
<v-icon>mdi-bug</v-icon>
</template>
<template #append>
<v-switch
v-model="appStore.devMode" :label="appStore.devMode ? '开启' : '关闭'" inset color="#FAC51E"
@click="submitDevMode"
/>
</template>
</v-list-item>
<v-list-item>
<template #prepend>
<v-icon>mdi-cookie</v-icon>
</template>
<template #title>
<span style="cursor: pointer" @click="tryConfirm('inputCookie')">手动输入 Cookie</span>
</template>
<template #append>
<v-icon style="cursor: pointer" @click="toOuter('https://github.com/BTMuli/Tauri.Genshin/issues/18')">
mdi-help-circle-outline
</v-icon>
</template>
</v-list-item>
<v-list-item title="重置数据库" prepend-icon="mdi-delete" @click="tryConfirm('resetDB')" />
<v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" />
<v-list-subheader inset class="config-header">
路径
</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item prepend-icon="mdi-database">
<v-list-item-title>本地数据库路径</v-list-item-title>
<v-list-item-subtitle>{{ appStore.dataPath.dbDataPath }}</v-list-item-subtitle>
</v-list-item>
<v-list-item prepend-icon="mdi-folder">
<v-list-item-title>本地临时数据路径</v-list-item-title>
<v-list-item-subtitle>{{ appStore.dataPath.tempDataDir }}</v-list-item-subtitle>
</v-list-item>
<v-list-item prepend-icon="mdi-folder">
<v-list-item-title>本地用户数据路径</v-list-item-title>
<v-list-item-subtitle>{{ appStore.dataPath.userDataDir }}</v-list-item-subtitle>
</v-list-item>
</v-list>
<!-- 弹窗提示条 -->
<v-snackbar v-model="snackbar" timeout="1500" :color="snackbarColor">
{{ snackbarText }}
</v-snackbar>
<!-- 确认弹窗 -->
<TOConfirm v-model:model-value="confirmShow" v-model:model-input="confirmInput" :title="confirmText" :subtitle="confirmSub" :is-input="isConfirmInput" @confirm="doConfirm(confirmOper)" />
</template>
<script lang="ts" setup>
// vue
import { computed, onMounted, ref } from "vue";
import TLoading from "../components/overlay/t-loading.vue";
import TConfirm from "../components/overlay/t-confirm.vue";
import TOLoading from "../components/overlay/to-loading.vue";
import TOConfirm from "../components/overlay/to-confirm.vue";
// tauri
import { fs, app, os } from "@tauri-apps/api";
import { app, fs, os } from "@tauri-apps/api";
// store
import { useAppStore } from "../store/modules/app";
import { useHomeStore } from "../store/modules/home";
@@ -319,6 +315,7 @@ function tryConfirm (oper: string) {
// transfer confirm oper
async function doConfirm (oper: string) {
await new Promise((resolve) => setTimeout(resolve, 500));
switch (oper) {
case "backup":
await backupData();
@@ -557,6 +554,7 @@ async function checkDB () {
loadingTitle.value = "正在检查数据库表单完整性...";
loading.value = true;
const res = await TGSqlite.check();
await new Promise((resolve) => setTimeout(resolve, 1000));
if (!res) {
confirmOper.value = "resetDB";
confirmText.value = "数据库表单不完整,是否重置数据库?";
@@ -603,9 +601,11 @@ async function resetDB () {
// 更新 SQLite 数据库
async function updateDB () {
await new Promise((resolve) => setTimeout(resolve, 500));
loadingTitle.value = "正在更新数据库...";
loading.value = true;
await TGSqlite.update();
achievementsStore.lastVersion = await TGSqlite.getLatestAchievementVersion();
loading.value = false;
snackbarText.value = "数据库已是最新!";
snackbarColor.value = "success";

View File

@@ -1,12 +1,12 @@
<template>
<TLoading v-if="loading" :title="loadingTitle" :subtitle="loadingSubtitle" />
<component :is="item" v-for="item in components" v-show="!loading" :key="item" :ref="setItemRef" />
<TOLoading v-model="loading" :title="loadingTitle" :subtitle="loadingSubtitle" />
<component :is="item" v-for="item in components" :key="item" :ref="setItemRef" />
</template>
<script lang="ts" setup>
// vue
import { ref, markRaw, onMounted, onUnmounted, onUpdated } from "vue";
import TLoading from "../components/overlay/t-loading.vue";
import { markRaw, onMounted, onUnmounted, onUpdated, ref } from "vue";
import TOLoading from "../components/overlay/to-loading.vue";
import TPool from "../components/main/t-pool.vue";
import TPosition from "../components/main/t-position.vue";
import TCalendar from "../components/main/t-calendar.vue";
@@ -45,6 +45,14 @@ function readLoading (): void {
onMounted(async () => {
loadingTitle.value = "正在加载首页";
loading.value = true;
// 获取当前环境
const timeGet = getBuildTime();
appStore.devEnv = timeGet.startsWith("dev");
if (!appStore.devEnv && appStore.devMode) {
appStore.devMode = false;
}
appStore.buildTime = getBuildTime();
const showItems = homeStore.getShowValue();
await Promise.allSettled(
showItems.map((item) => {
@@ -61,13 +69,6 @@ onMounted(async () => {
}),
);
timer.value = setInterval(readLoading, 100);
// 获取当前环境
const timeGet = getBuildTime();
appStore.devEnv = timeGet.startsWith("dev");
if (!appStore.devEnv && appStore.devMode) {
appStore.devMode = false;
}
appStore.buildTime = getBuildTime();
});
function setItemRef (item: any) {

View File

@@ -12,7 +12,7 @@
import { onMounted, ref } from "vue";
// store
import { useUserStore } from "../../store/modules/user";
// request
// utils
import TGRequest from "../../web/request/TGRequest";
import TGSqlite from "../../utils/TGSqlite";

View File

@@ -1,49 +1,100 @@
<template>
<h1>角色数据获取展示详情</h1>
<v-btn @click="getRoleList">
获取角色列表
</v-btn>
{{ roleList }}
<ToLoading v-model="loading" :title="loadingTitle" />
<div class="uc-top">
<div class="uc-top-title">
我的角色
</div>
<v-btn variant="outlined" @click="refresh">
更新数据
</v-btn>
</div>
<div class="uc-grid">
<TUserAvatar v-for="avatar in roleList" :model-value="avatar" />
</div>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import ToLoading from "../../components/overlay/to-loading.vue";
import TUserAvatar from "../../components/mini/t-user-avatar.vue";
// tauri
import { fs } from "@tauri-apps/api";
// store
import { useAppStore } from "../../store/modules/app";
import { useUserStore } from "../../store/modules/user";
// request
import TGRequest from "../../web/request/TGRequest";
// utils
import TGSqlite from "../../utils/TGSqlite";
// store
const appStore = useAppStore();
const userStore = useUserStore();
const roleList = ref([]);
// loading
const loading = ref(false);
const loadingTitle = ref("");
// data
const characterCookie = ref({
cookie_token: "",
account_id: "",
ltoken: "",
ltuid: "",
});
const roleList = ref([] as TGApp.Game.Character.ListItem[]);
const characterCookie = ref({} as TGApp.BBS.Constant.CookieGroup4);
const user = ref({} as TGApp.Sqlite.Account.Game);
const filePath = computed(() => `${appStore.dataPath.userDataDir}/roleList.json`);
onMounted(async () => {
loadingTitle.value = "正在获取角色数据";
loading.value = true;
const curUser = await TGSqlite.getCurAccount();
if (curUser) {
user.value = curUser;
}
characterCookie.value = {
cookie_token: userStore.getCookieItem("cookie_token"),
account_id: userStore.getCookieItem("account_id"),
ltoken: userStore.getCookieItem("ltoken"),
ltuid: userStore.getCookieItem("ltuid"),
};
characterCookie.value = userStore.getCookieGroup4();
const fileGet = await fs.readTextFile(filePath.value);
if (fileGet) {
roleList.value = JSON.parse(fileGet);
}
loading.value = false;
});
async function getRoleList () {
async function refresh () {
loadingTitle.value = "正在获取角色数据";
loading.value = true;
const res = await TGRequest.User.byLToken.getRoleList(characterCookie.value, user.value);
console.log(res);
if (Array.isArray(res)) {
loadingTitle.value = "正在保存角色数据";
await fs.writeTextFile({
path: filePath.value,
contents: JSON.stringify(res),
});
loadingTitle.value = "正在更新角色数据";
roleList.value = res;
}
loading.value = false;
}
</script>
<style lang="css" scoped>
.uc-top {
background: rgb(0 0 0 / 20%);
width: 100%;
height: 50px;
border-radius: 5px;
padding: 10px;
display: flex;
align-items: center;
font-family: Genshin, sans-serif;
font-size: 20px;
color: #faf7e8;
}
.uc-top-title {
margin-right: 10px;
}
.uc-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
grid-gap: 10px;
margin-top: 10px;
}
</style>

View File

@@ -1,7 +0,0 @@
<template>
<h1>实时便笺数据总览世界探索尘歌壶札记跳转</h1>
</template>
<script lang="ts" setup>
</script>
<style lang="css" scoped>
</style>

45
src/pages/User/Record.vue Normal file
View File

@@ -0,0 +1,45 @@
<template>
<h1>实时便笺数据总览世界探索尘歌壶札记跳转</h1>
<!-- todo Invalid uid -->
<v-btn @click="getInfo1">
获取用户游戏数据1
</v-btn>
<v-btn @click="getInfo2">
获取用户游戏数据2
</v-btn>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
// store
import { useUserStore } from "../../store/modules/user";
// utils
import TGRequest from "../../web/request/TGRequest";
import TGSqlite from "../../utils/TGSqlite";
// store
const userStore = useUserStore();
// data
const user = ref({} as TGApp.Sqlite.Account.Game);
onMounted(async () => {
const curUser = await TGSqlite.getCurAccount();
if (curUser) {
user.value = curUser;
}
console.log(user.value);
});
async function getInfo1 () {
const ck = userStore.getCookieGroup2() as Record<string, string>;
const res = await TGRequest.User.getRecord(ck, user.value);
}
async function getInfo2 () {
const ck = userStore.getCookieGroup3() as Record<string, string>;
const res = await TGRequest.User.getRecord(ck, user.value);
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -1,104 +1,112 @@
<template>
<div v-if="loading">
<TLoading title="正在加载卡牌列表" />
</div>
<div v-else>
<v-tabs v-model="tab" align-tabs="start" class="cards-tab">
<div v-show="!doSearch">
<v-tab value="character">
角色
</v-tab>
<v-tab value="action">
行动
</v-tab>
<v-tab value="monster">
魔物牌
</v-tab>
</div>
<v-spacer />
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="搜索"
single-line
hide-details
@click:append="searchCard"
@keyup.enter="searchCard"
/>
</v-tabs>
<div v-if="!doSearch">
<v-window v-model="tab">
<v-window-item value="character">
<div class="cards-grid">
<v-card v-for="item in CardsInfoC" :key="item.contentId" class="card-cls" @click="toOuter(item.name, item.contentId)">
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
<v-window-item value="action">
<div class="cards-grid">
<v-card v-for="item in CardsInfoA" :key="item.contentId" class="card-cls" @click="toOuter(item.name, item.contentId)">
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
<v-window-item value="monster">
<div class="cards-grid">
<v-card v-for="item in CardsInfoM" :key="item.contentId" class="card-cls" @click="toOuter(item.name, item.contentId)">
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
</v-window>
<TOLoading v-model="loading" title="正在加载卡牌列表" />
<v-tabs v-model="tab" align-tabs="start" class="cards-tab">
<div v-show="!doSearch">
<v-tab value="character">
角色牌
</v-tab>
<v-tab value="action">
行动
</v-tab>
<v-tab value="monster">
魔物
</v-tab>
</div>
<div v-else>
<div class="cards-grid">
<div v-for="item in CardsInfoS" :key="item.contentId" class="card-cls" @click="toOuter(item.name, item.contentId)">
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
<v-spacer />
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="搜索"
single-line
hide-details
@click:append="searchCard"
@keyup.enter="searchCard"
/>
</v-tabs>
<div v-show="!doSearch">
<v-window v-model="tab">
<v-window-item value="character">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoC" :key="item.contentId" class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</div>
</div>
<v-snackbar v-model="snackbar" timeout="1500" color="error">
未找到相关卡牌
</v-snackbar>
</v-window-item>
<v-window-item value="action">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoA" :key="item.contentId" class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
<v-window-item value="monster">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoM" :key="item.contentId" class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
</v-window>
</div>
<div v-show="doSearch">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoS" :key="item.contentId" class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/special.webp" alt="border">
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</div>
<v-snackbar v-model="snackbar" timeout="1500" color="error">
未找到相关卡牌
</v-snackbar>
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted, computed } from "vue";
import TLoading from "../../components/overlay/t-loading.vue";
import { computed, onMounted, ref } from "vue";
import TOLoading from "../../components/overlay/to-loading.vue";
// utils
import { createTGWindow } from "../../utils/TGWindow";
import { AppGCGData } from "../../data";
@@ -125,21 +133,35 @@ onMounted(async () => {
});
async function loadData () {
CardsInfoC.value = allCards.value.filter((item) => item.type === "角色牌");
CardsInfoA.value = allCards.value.filter((item) => item.type === "行动牌");
CardsInfoM.value = allCards.value.filter((item) => item.type === "魔物牌");
await Promise.allSettled(
allCards.value.map(async (item) => {
if (item.type === "角色牌") CardsInfoC.value.push(item);
if (item.type === "行动牌") CardsInfoA.value.push(item);
if (item.type === "魔物牌") CardsInfoM.value.push(item);
}),
);
loading.value = false;
}
function toOuter (cardName: string, cardId: number) {
const url = OBC_CONTENT_API.replace("{content_id}", cardId.toString());
createTGWindow(url, "GCG", cardName, 1200, 800, true);
}
async function searchCard () {
loading.value = true;
if (search.value === "") {
setTimeout(() => {
doSearch.value = false;
loading.value = false;
}, 1000);
return;
}
doSearch.value = true;
const res: TGApp.App.GCG.WikiBriefInfo[] = [];
allCards.value.map((item) => (item.name.includes(search.value) ? res.push(item) : null));
await Promise.allSettled(allCards.value.map((item) => (item.name.includes(search.value) ? res.push(item) : null)));
res.sort((a, b) => a.name.localeCompare(b.name));
console.log(res);
loading.value = false;
if (res.length === 0) {
snackbar.value = true;
@@ -151,7 +173,7 @@ async function searchCard () {
</script>
<style lang="css" scoped>
.cards-tab {
font-family: Genshin,serif;
font-family: Genshin, serif;
margin-bottom: 20px;
color: var(--content-text-3);
}

View File

@@ -2,7 +2,7 @@
* @file router modules main.ts
* @description 主路由模块
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.5
* @since Alpha v0.1.6
*/
// 信息展示
@@ -13,7 +13,7 @@ import Achievements from "../../pages/Achievements.vue";
// 应用配置相关
import Config from "../../pages/Config.vue";
// 测试页
import Test from "../../pages/test.vue";
import Test from "../../pages/Test.vue";
const mainRoutes = [
{

View File

@@ -9,7 +9,7 @@
import Abyss from "../../pages/User/Abyss.vue";
import Characters from "../../pages/User/Characters.vue";
import Gacha from "../../pages/User/Gacha.vue";
import Info from "../../pages/User/Info.vue";
import Record from "../../pages/User/Record.vue";
// user sub
const userRoutes = [
@@ -29,9 +29,9 @@ const userRoutes = [
component: Gacha,
},
{
path: "/user/info",
name: "基本信息",
component: Info,
path: "/user/record",
name: "原神战绩",
component: Record,
},
];

View File

@@ -2,7 +2,7 @@
* @file store modules home.ts
* @description Home store module
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.2
* @since Alpha v0.1.6
*/
// vue
@@ -24,7 +24,7 @@ export const useHomeStore = defineStore(
show: true,
order: 2,
});
const hoemShow = ref({
const homeShow = ref({
calendarShow,
poolShow,
positionShow,
@@ -113,7 +113,7 @@ export const useHomeStore = defineStore(
}
return {
hoemShow,
homeShow,
poolCover,
init,
getShowItems,

View File

@@ -32,6 +32,36 @@ export const useUserStore = defineStore(
return cookie.value[key] || "";
}
function getCookieGroup1 (): TGApp.BBS.Constant.CookieGroup1 {
return {
login_ticket: getCookieItem("login_ticket"),
login_uid: getCookieItem("login_uid"),
};
}
function getCookieGroup2 (): TGApp.BBS.Constant.CookieGroup2 {
return {
account_id: getCookieItem("account_id"),
cookie_token: getCookieItem("cookie_token"),
};
}
function getCookieGroup3 (): TGApp.BBS.Constant.CookieGroup3 {
return {
ltoken: getCookieItem("ltoken"),
ltuid: getCookieItem("ltuid"),
};
}
function getCookieGroup4 (): TGApp.BBS.Constant.CookieGroup4 {
return {
account_id: getCookieItem("account_id"),
cookie_token: getCookieItem("cookie_token"),
ltoken: getCookieItem("ltoken"),
ltuid: getCookieItem("ltuid"),
};
}
function initCookie (ck: Record<string, string>): void {
if (cookie.value !== ck) {
cookie.value = ck;
@@ -43,6 +73,10 @@ export const useUserStore = defineStore(
getBriefInfo,
setBriefInfo,
getCookieItem,
getCookieGroup1,
getCookieGroup2,
getCookieGroup3,
getCookieGroup4,
initCookie,
};
},

View File

@@ -2,7 +2,7 @@
* @file types BBS Constant.d.ts
* @description BBS 常量相关类型定义文件
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.5
* @since Alpha v0.1.6
*/
declare namespace TGApp.BBS.Constant {
@@ -45,4 +45,54 @@ declare namespace TGApp.BBS.Constant {
stuid: string
stoken_v2?: string
}
/**
* @description cookie 组合-1
* @interface CookieGroup1
* @since Alpha v0.1.6
* @property {string} login_ticket 登录凭证
* @property {string} login_uid 登录 uid
* @return CookieGroup1
*/
export interface CookieGroup1 {
login_ticket: string
login_uid: string
}
/**
* @description cookie 组合-2
* @interface CookieGroup2
* @since Alpha v0.1.6
* @property {string} account_id 账号 id
* @property {string} cookie_token cookie token
* @return CookieGroup2
*/
export interface CookieGroup2 {
account_id: string
cookie_token: string
}
/**
* @description cookie 组合-3
* @interface CookieGroup3
* @since Alpha v0.1.6
* @property {string} ltoken ltoken
* @property {string} ltuid ltoken 对应的 uid
* @return CookieGroup3
*/
export interface CookieGroup3 {
ltoken: string
ltuid: string
}
/**
* @description cookie 组合-4
* @interface CookieGroup4
* @since Alpha v0.1.6
* @extends CookieGroup2
* @extends CookieGroup3
* @return CookieGroup4
*/
export interface CookieGroup4 extends CookieGroup2, CookieGroup3 {}
}

View File

@@ -2,7 +2,7 @@
* @file utils TGSqlite.ts
* @description 数据库操作类
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.5
* @since Alpha v0.1.6
*/
// tauri
@@ -263,6 +263,20 @@ class TGSqlite {
return res[0];
}
/**
* @description 获取最新成就版本
* @since Alpha v0.1.6
* @memberOf TGSqlite
* @returns {Promise<string>}
*/
public async getLatestAchievementVersion (): Promise<string> {
const db = await Database.load(this.dbPath);
const sql = "SELECT version FROM AchievementSeries ORDER BY version DESC LIMIT 1;";
const res: Array<{ version: string }> = await db.select(sql);
await db.close();
return res[0].version;
}
/**
* @description 查询成就
* @memberOf TGSqlite

View File

@@ -1,9 +1,7 @@
<template>
<TSwitchTheme />
<div v-if="loading">
<TLoading :empty="loadingEmpty" :title="loadingTitle" />
</div>
<div v-else class="anno-json">
<TOLoading v-model="loading" :empty="loadingEmpty" :title="loadingTitle" />
<div class="anno-json">
<div class="anno-title">
活动列表 JSON
</div>
@@ -19,7 +17,7 @@
import { ref, onMounted, reactive } from "vue";
import { useRoute } from "vue-router";
import JsonViewer from "vue-json-viewer";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
import TSwitchTheme from "../components/main/t-switchTheme.vue";
// tauri
import { appWindow } from "@tauri-apps/api/window";

View File

@@ -1,10 +1,8 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<TSwitchTheme />
<div v-if="loading" class="loading">
<TLoading :title="loadingTitle" :empty="loadingEmpty" />
</div>
<div v-else class="anno-body">
<TOLoading v-model="loading" :title="loadingTitle" :empty="loadingEmpty" />
<div class="anno-body">
<div class="anno-title">
{{ annoData.title }}
</div>
@@ -19,7 +17,7 @@
// vue
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
import TSwitchTheme from "../components/main/t-switchTheme.vue";
// tauri
import { appWindow } from "@tauri-apps/api/window";
@@ -47,6 +45,7 @@ onMounted(async () => {
}
// 获取数据
loadingTitle.value = "正在获取数据...";
loading.value = true;
try {
annoData.value = await TGRequest.Anno.getContent(annoId);
loadingTitle.value = "正在渲染数据...";
@@ -56,9 +55,7 @@ onMounted(async () => {
loadingTitle.value = "公告不存在或解析失败";
return;
}
setTimeout(() => {
loading.value = false;
}, 200);
loading.value = false;
});
</script>
<style lang="css" src="../assets/css/anno-parser.css" scoped />

View File

@@ -1,79 +1,70 @@
<template>
<TSwitchTheme />
<div v-if="loading">
<TLoading :empty="loadingEmpty" :title="loadingTitle" />
</div>
<div v-else>
<div class="lottery-div">
<div class="lottery-title">
抽奖详情
<span style="color:#E06C63">{{ timeStatus === "已开奖" ? timeStatus : `待开奖 ${timeStatus}` }}</span>
</div>
<div class="lottery-list">
<v-list-item>
<template #prepend>
<v-avatar>
<v-img :src="lotteryCard.creator.avatar_url" />
</v-avatar>
</template>
{{ lotteryCard.creator.nickname }}
<v-list-item-subtitle>{{ lotteryCard.creator.introduce }}</v-list-item-subtitle>
</v-list-item>
<div class="reward-title">
参与方式{{ participationMethod }}
</div>
<div class="reward-title">
抽奖 ID{{ lotteryCard.id }}
</div>
<div class="reward-title">
奖品详情
<div v-for="reward in lotteryCard.rewards" :key="reward.rewardName" class="reward-subtitle">
{{ reward.rewardName }} {{ reward.scheduledWinnerNumber }}
</div>
</div>
</div>
<v-btn class="lottery-back" @click="backPost()">
<img src="../assets/icons/circle-cancel.svg" alt="back">
<span>返回</span>
</v-btn>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="showJson = true">
<TOLoading v-model="loading" :empty="loadingEmpty" :title="loadingTitle" />
<div v-if="!loading" class="lottery-box">
<div class="lottery-title">
抽奖详情
<span style="color:#E06C63">{{ timeStatus === "已开奖" ? timeStatus : `待开奖 ${timeStatus}` }}</span>
</div>
<div class="lottery-list">
<v-list-item>
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
<v-avatar>
<v-img :src="lotteryCard.creator.avatar_url" />
</v-avatar>
</template>
JSON
</v-btn>
</div>
<div v-show="showJson" class="lottery-json">
<JsonViewer :value="jsonData" copyable boxed />
</div>
<div v-if="timeStatus === '已开奖'" class="lottery-div">
<div class="lottery-title">
中奖详情
<template #append>
<v-btn variant="outlined" @click="backPost()">
返回
</v-btn>
</template>
{{ lotteryCard.creator.nickname }}
<v-list-item-subtitle>{{ lotteryCard.creator.introduce }}</v-list-item-subtitle>
</v-list-item>
<div class="reward-title">
参与方式{{ participationMethod }}
</div>
<div v-for="reward in lotteryCard.rewards" :key="reward.rewardName" class="lottery-list">
<div class="reward-title">
{{ reward.rewardName }} {{ reward.scheduledWinnerNumber }}/{{ reward.winnerNumber }}
<div class="reward-title">
抽奖 ID{{ lotteryCard.id }}
</div>
<div class="reward-title">
奖品详情
<div v-for="reward in lotteryCard.rewards" :key="reward.rewardName" class="reward-subtitle">
{{ reward.rewardName }} {{ reward.scheduledWinnerNumber }}
</div>
<div class="lottery-grid">
<div v-for="user in reward.users" :key="user.uid" class="lottery-sub-list">
<div class="lottery-user-avatar">
<img :src="user.avatar_url" alt="avatar">
</div>
<div class="lottery-user-nickname">
{{ user.nickname }}
</div>
</div>
</div>
</div>
<div v-if="timeStatus === '已开奖'" class="lottery-box">
<div class="lottery-title">
中奖详情
</div>
<div v-for="reward in lotteryCard.rewards" :key="reward.rewardName" class="lottery-list">
<div class="reward-title">
{{ reward.rewardName }} {{ reward.scheduledWinnerNumber }}/{{ reward.winnerNumber }}
</div>
<div class="lottery-grid">
<div v-for="user in reward.users" :key="user.uid" class="lottery-sub-list">
<div class="lottery-user-avatar">
<img :src="user.avatar_url" alt="avatar">
</div>
<div class="lottery-user-nickname">
{{ user.nickname }}
</div>
</div>
</div>
</div>
</div>
<div v-if="showJson" class="lottery-json">
<JsonViewer :value="jsonData" copyable boxed />
</div>
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted, reactive, onUpdated } from "vue";
import { computed, onMounted, onUpdated, reactive, ref } from "vue";
import { useRoute } from "vue-router";
import JsonViewer from "vue-json-viewer";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
import TSwitchTheme from "../components/main/t-switchTheme.vue";
// tauri
import { appWindow } from "@tauri-apps/api/window";
@@ -91,7 +82,7 @@ const loadingEmpty = ref(false as boolean);
// store
const appStore = useAppStore();
const showJson = computed(() => appStore.devMode);
// 定时器
const lotteryTimer = ref(null as any);
// 参与方式
@@ -115,7 +106,6 @@ function flushTimeStatus () {
// 数据
const lotteryId = useRoute().params.lottery_id as string;
const lotteryCard = ref({} as LotteryCard);
const showJson = ref(false as boolean);
let jsonData = reactive({} as LotteryData);
const timeStatus = ref("未知" as string);
@@ -174,64 +164,42 @@ onUpdated(() => {
</script>
<style lang="css">
.lottery-div {
background: var(--content-bg-2);
border-radius: 10px;
.lottery-box {
margin-bottom: 10px;
padding: 10px;
background: rgb(255 255 255 / 10%);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
border-radius: 25px 5px 5px;
}
.lottery-title {
font-family: Genshin, serif;
font-size: 20px;
height: 40px;
color: var(--content-text-3);
margin-left: 40px;
}
.reward-title {
font-family: Genshin-Light, serif;
font-size: 16px;
.lottery-list {
background: rgb(0 0 0 / 40%);
box-shadow: 0 0 10px rgb(255 255 255 / 10%);
border-radius: 5px;
padding: 5px;
margin-bottom: 10px;
color: #faf7e8;
font-family: Genshin-Light, serif;
}
.reward-title {
font-size: 16px;
margin: 10px;
}
.reward-subtitle {
font-family: Genshin-Light, serif;
font-size: 16px;
color: #faf7e8;
opacity: 0.5;
}
.lottery-list {
background: var(--content-bg-1);
border-radius: 10px;
margin: 10px;
padding: 10px;
color: #faf7e8;
font-family: Genshin-Light, serif;
}
.lottery-back {
margin: 5px;
height: 30px;
border-radius: 40px;
font-family: Genshin, serif;
background: #4A5366;
}
.lottery-back img {
position: absolute;
left: 5px;
width: 25px;
height: 25px;
}
.lottery-back span {
color: #faf7e8;
font-size: 16px;
margin-left: 10px;
}
.lottery-grid {
border-radius: 10px;
display: grid;
@@ -272,12 +240,22 @@ onUpdated(() => {
}
.lottery-json {
padding: 20px;
border-radius: 20px;
margin-bottom: 10px;
background: rgb(255 255 255 / 10%);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
border-radius: 25px 5px;
font-family: Consolas, serif;
color: #faf7e8;
}
.jv-container {
background: var(--content-bg-2) !important;
background: rgb(0 0 0 / 60%) !important;
border-radius: 25px 5px !important;
box-shadow: 0 0 10px rgb(0 0 0 / 40%) !important;
}
.jv-key,
.jv-array {
color: #f0c674 !important;
}
</style>

View File

@@ -1,224 +1,220 @@
<template>
<div v-show="loading">
<TLoading :title="loadingTitle" />
</div>
<div v-show="!loading">
<v-tabs v-model="tab" align-tabs="start" class="news-tab">
<v-tab value="notice">
公告
</v-tab>
<v-tab value="activity" @click="firstLoad('activity')">
活动
</v-tab>
<v-tab v-if="showNews" value="news" @click="firstLoad('news')">
新闻
</v-tab>
<v-spacer />
<v-btn class="switch-btn" @click="switchAnno">
<template #prepend>
<v-icon>mdi-bullhorn</v-icon>
</template>
切换游戏内公告
</v-btn>
<v-btn class="switch-chan" @click="showList=true">
<v-icon>mdi-view-list</v-icon>
</v-btn>
<v-text-field
v-show="appStore.devMode"
v-model="search"
append-icon="mdi-magnify"
label="搜索"
single-line
hide-details
@click:append="searchPost"
@keyup.enter="searchPost"
/>
</v-tabs>
<v-window v-model="tab">
<v-window-item value="notice">
<div class="news-grid">
<v-card v-for="item in postData.notice" :key="item.post_id" class="news-card" width="340">
<div class="news-cover" @click="toPost(item)">
<img :src="item.cover" alt="cover">
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-actions>
<v-btn class="card-btn" @click="toPost(item)">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">查看
</template>
</v-btn>
<v-card-subtitle>id:{{ item.post_id }}</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
JSON
</v-btn>
</v-card-actions>
</v-card>
</div>
<div class="load-news">
<v-btn :loading="loadingSub" @click="loadMore('notice')">
<template #append>
<img src="../assets/icons/arrow-left.svg" alt="right">
</template>
已加载{{ rawData.notice.lastId }}加载更多
</v-btn>
</div>
</v-window-item>
<v-window-item value="activity">
<div class="news-grid">
<v-card v-for="item in postData.activity" :key="item.post_id" class="news-card" width="340">
<div class="news-cover" @click="toPost(item)">
<img :src="item.cover" alt="cover">
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="card-btn" @click="toPost(item)">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">查看
</template>
</v-btn>
<v-card-subtitle>id:{{ item.post_id }}</v-card-subtitle>
<div v-show="!appStore.devMode">
<v-btn
:style="{
background: item.status?.colorCss,
color: '#faf7e8 !important',
}"
>
{{ item.status?.status }}
</v-btn>
</div>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
JSON
</v-btn>
</v-card-actions>
</v-card>
</div>
<div class="load-news">
<v-btn :loading="loadingSub" @click="loadMore('activity')">
<template #append>
<img src="../assets/icons/arrow-left.svg" alt="right">
</template>
已加载:{{ rawData.activity.lastId }}加载更多
</v-btn>
</div>
</v-window-item>
<v-window-item v-if="showNews" value="news">
<div class="news-grid">
<v-card v-for="item in postData.news" :key="item.post_id" class="news-card" width="340">
<div class="news-cover" @click="toPost(item)">
<img :src="item.cover" alt="cover">
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-actions>
<v-btn class="card-btn" @click="toPost(item)">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">查看
</template>
</v-btn>
<v-card-subtitle>id:{{ item.post_id }}</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
JSON
</v-btn>
</v-card-actions>
</v-card>
</div>
<div class="load-news">
<v-btn :loading="loadingSub" @click="loadMore('news')">
<template #append>
<img src="../assets/icons/arrow-left.svg" alt="right">
</template>
已加载{{ rawData.news.lastId }}加载更多
</v-btn>
</div>
</v-window-item>
</v-window>
<v-snackbar v-model="snackbar" timeout="1500" :color="snackbarColor">
{{ snackbarText }}
</v-snackbar>
<v-overlay v-model="showList">
<div class="choice-box">
<div class="choice-title">
请选择要跳转的频道
</div>
<div class="choice-list">
<div class="choice-item" @click="toChannel('/news/2')">
<div class="choice-icon">
<img src="/platforms/mhy/ys.webp" alt="ys">
</div>
<div class="choice-name">
原神
</div>
<TOLoading v-model="loading" :title="loadingTitle" />
<v-tabs v-model="tab" align-tabs="start" class="news-tab">
<v-tab value="notice">
公告
</v-tab>
<v-tab value="activity" @click="firstLoad('activity')">
活动
</v-tab>
<v-tab v-if="showNews" value="news" @click="firstLoad('news')">
新闻
</v-tab>
<v-spacer />
<v-btn class="switch-btn" @click="switchAnno">
<template #prepend>
<v-icon>mdi-bullhorn</v-icon>
</template>
切换游戏内公告
</v-btn>
<v-btn class="switch-chan" @click="showList=true">
<v-icon>mdi-view-list</v-icon>
</v-btn>
<v-text-field
v-show="appStore.devMode"
v-model="search"
append-icon="mdi-magnify"
label="搜索"
single-line
hide-details
@click:append="searchPost"
@keyup.enter="searchPost"
/>
</v-tabs>
<v-window v-model="tab">
<v-window-item value="notice">
<div class="news-grid">
<v-card v-for="item in postData.notice" :key="item.post_id" class="news-card" width="340">
<div class="news-cover" @click="toPost(item)">
<img :src="item.cover" alt="cover">
</div>
<div class="choice-item" @click="toChannel('/news/6')">
<div class="choice-icon">
<img src="/platforms/mhy/sr.webp" alt="sr">
</div>
<div class="choice-name">
崩坏星穹铁道
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-actions>
<v-btn class="card-btn" @click="toPost(item)">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">查看
</template>
</v-btn>
<v-card-subtitle>id:{{ item.post_id }}</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
JSON
</v-btn>
</v-card-actions>
</v-card>
</div>
<div class="load-news">
<v-btn :loading="loadingSub" @click="loadMore('notice')">
<template #append>
<img src="../assets/icons/arrow-left.svg" alt="right">
</template>
已加载{{ rawData.notice.lastId }}加载更多
</v-btn>
</div>
</v-window-item>
<v-window-item value="activity">
<div class="news-grid">
<v-card v-for="item in postData.activity" :key="item.post_id" class="news-card" width="340">
<div class="news-cover" @click="toPost(item)">
<img :src="item.cover" alt="cover">
</div>
<div class="choice-item" @click="toChannel('/news/1')">
<div class="choice-icon">
<img src="/platforms/mhy/bh3.webp" alt="bh3">
</div>
<div class="choice-name">
崩坏3
<v-card-title>{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="card-btn" @click="toPost(item)">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">查看
</template>
</v-btn>
<v-card-subtitle>id:{{ item.post_id }}</v-card-subtitle>
<div v-show="!appStore.devMode">
<v-btn
:style="{
background: item.status?.colorCss,
color: '#faf7e8 !important',
}"
>
{{ item.status?.status }}
</v-btn>
</div>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
JSON
</v-btn>
</v-card-actions>
</v-card>
</div>
<div class="load-news">
<v-btn :loading="loadingSub" @click="loadMore('activity')">
<template #append>
<img src="../assets/icons/arrow-left.svg" alt="right">
</template>
已加载:{{ rawData.activity.lastId }}加载更多
</v-btn>
</div>
</v-window-item>
<v-window-item v-if="showNews" value="news">
<div class="news-grid">
<v-card v-for="item in postData.news" :key="item.post_id" class="news-card" width="340">
<div class="news-cover" @click="toPost(item)">
<img :src="item.cover" alt="cover">
</div>
<div class="choice-item" @click="toChannel('/news/3')">
<div class="choice-icon">
<img src="/platforms/mhy/bh2.webp" alt="bh2">
</div>
<div class="choice-name">
崩坏2
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-actions>
<v-btn class="card-btn" @click="toPost(item)">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">查看
</template>
</v-btn>
<v-card-subtitle>id:{{ item.post_id }}</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
</template>
JSON
</v-btn>
</v-card-actions>
</v-card>
</div>
<div class="load-news">
<v-btn :loading="loadingSub" @click="loadMore('news')">
<template #append>
<img src="../assets/icons/arrow-left.svg" alt="right">
</template>
已加载{{ rawData.news.lastId }}加载更多
</v-btn>
</div>
</v-window-item>
</v-window>
<v-snackbar v-model="snackbar" timeout="1500" :color="snackbarColor">
{{ snackbarText }}
</v-snackbar>
<v-overlay v-model="showList">
<div class="choice-box">
<div class="choice-title">
请选择要跳转的频道
</div>
<div class="choice-list">
<div class="choice-item" @click="toChannel('/news/2')">
<div class="choice-icon">
<img src="/platforms/mhy/ys.webp" alt="ys">
</div>
<div class="choice-item" @click="toChannel('/news/4')">
<div class="choice-icon">
<img src="/platforms/mhy/wd.webp" alt="wd">
</div>
<div class="choice-name">
未定事件簿
</div>
<div class="choice-name">
原神
</div>
<div class="choice-item" @click="toChannel('/news/8')">
<div class="choice-icon">
<img src="/platforms/mhy/zzz.webp" alt="zzz">
</div>
<div class="choice-name">
绝区零
</div>
</div>
<div class="choice-item" @click="toChannel('/news/6')">
<div class="choice-icon">
<img src="/platforms/mhy/sr.webp" alt="sr">
</div>
<div class="choice-item" @click="toChannel('/news/5')">
<div class="choice-icon">
<img src="/platforms/mhy/dby.webp" alt="sg">
</div>
<div class="choice-name">
大别野
</div>
<div class="choice-name">
崩坏星穹铁道
</div>
</div>
<div class="choice-item" @click="toChannel('/news/1')">
<div class="choice-icon">
<img src="/platforms/mhy/bh3.webp" alt="bh3">
</div>
<div class="choice-name">
崩坏3
</div>
</div>
<div class="choice-item" @click="toChannel('/news/3')">
<div class="choice-icon">
<img src="/platforms/mhy/bh2.webp" alt="bh2">
</div>
<div class="choice-name">
崩坏2
</div>
</div>
<div class="choice-item" @click="toChannel('/news/4')">
<div class="choice-icon">
<img src="/platforms/mhy/wd.webp" alt="wd">
</div>
<div class="choice-name">
未定事件簿
</div>
</div>
<div class="choice-item" @click="toChannel('/news/8')">
<div class="choice-icon">
<img src="/platforms/mhy/zzz.webp" alt="zzz">
</div>
<div class="choice-name">
绝区零
</div>
</div>
<div class="choice-item" @click="toChannel('/news/5')">
<div class="choice-icon">
<img src="/platforms/mhy/dby.webp" alt="sg">
</div>
<div class="choice-name">
大别野
</div>
</div>
</div>
</v-overlay>
</div>
</div>
</v-overlay>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
// store
import { useAppStore } from "../store/modules/app";
// plugin
@@ -259,14 +255,17 @@ const postData = ref({
const rawData = ref({
notice: {
isLast: false,
name: "公告",
lastId: 0,
},
activity: {
isLast: false,
name: "活动",
lastId: 0,
},
news: {
isLast: false,
name: "咨讯",
lastId: 0,
},
});
@@ -278,10 +277,12 @@ onMounted(async () => {
rawData.value.notice.lastId = noticeData.list.length;
postData.value.notice = MysOper.News.card.notice(noticeData);
tab.value = "notice";
loading.value = false;
setTimeout(() => {
loading.value = false;
}, 1500);
});
async function firstLoad (data:string) {
async function firstLoad (data: string) {
if (rawData.value.activity.lastId !== 0 && rawData.value.news.lastId !== 0) {
return;
}
@@ -292,7 +293,6 @@ async function firstLoad (data:string) {
rawData.value.activity.isLast = activityData.is_last;
rawData.value.activity.lastId = activityData.list.length;
postData.value.activity = MysOper.News.card.activity(activityData);
loading.value = false;
}
if (data === "news" && rawData.value.news.lastId === 0) {
loadingTitle.value = "正在获取咨讯数据...";
@@ -301,15 +301,17 @@ async function firstLoad (data:string) {
rawData.value.news.isLast = newsData.is_last;
rawData.value.news.lastId = newsData.list.length;
postData.value.news = MysOper.News.card.news(newsData);
loading.value = false;
}
setTimeout(() => {
loading.value = false;
}, 1500);
}
async function switchAnno () {
await router.push("/announcements");
}
async function toChannel (chan:string) {
async function toChannel (chan: string) {
showList.value = false;
await router.push(chan);
await window.location.reload();
@@ -318,57 +320,32 @@ async function toChannel (chan:string) {
// 加载更多
async function loadMore (data: string) {
loadingSub.value = true;
let getData: NewsData;
let getCard: NewsCard[];
switch (data) {
case "notice":
if (rawData.value.notice.isLast) {
snackbarText.value = "已经是最后一页了";
snackbarColor.value = "#35acce";
snackbar.value = true;
loadingSub.value = false;
return;
}
getData = await MysOper.News.get.notice(gid, 20, rawData.value.notice.lastId);
rawData.value.notice.lastId = rawData.value.notice.lastId + getData.list.length;
rawData.value.notice.isLast = getData.is_last;
getCard = MysOper.News.card.notice(getData);
postData.value.notice = postData.value.notice.concat(getCard);
loadingSub.value = false;
break;
case "activity":
if (rawData.value.activity.isLast) {
snackbarText.value = "已经是最后一页了";
snackbarColor.value = "#35acce";
snackbar.value = true;
loadingSub.value = false;
return;
}
getData = await MysOper.News.get.activity(gid, 20, rawData.value.activity.lastId);
rawData.value.activity.lastId = rawData.value.activity.lastId + getData.list.length;
rawData.value.activity.isLast = getData.is_last;
getCard = MysOper.News.card.activity(getData);
postData.value.activity = postData.value.activity.concat(getCard);
loadingSub.value = false;
break;
case "news":
if (rawData.value.news.isLast) {
snackbarText.value = "已经是最后一页了";
snackbarColor.value = "#35acce";
snackbar.value = true;
loadingSub.value = false;
return;
}
getData = await MysOper.News.get.news(gid, 20, rawData.value.news.lastId);
rawData.value.news.lastId = rawData.value.news.lastId + getData.list.length;
rawData.value.news.isLast = getData.is_last;
getCard = MysOper.News.card.news(getData);
postData.value.news = postData.value.news.concat(getCard);
loadingSub.value = false;
break;
default:
break;
if (rawData.value[data].isLast) {
snackbarText.value = "已经是最后一页了";
snackbarColor.value = "#35acce";
snackbar.value = true;
loadingSub.value = false;
return;
}
loadingTitle.value = `正在获取${rawData.value[data].name}数据...`;
loading.value = true;
const getData = await MysOper.News.get[data](gid, 20, rawData.value[data].lastId);
rawData.value[data].lastId = rawData.value[data].lastId + getData.list.length;
rawData.value[data].isLast = getData.is_last;
const getCard = MysOper.News.card[data](getData);
postData.value[data] = postData.value[data].concat(getCard);
if (rawData.value[data].isLast) {
snackbarText.value = "已经是最后一页了";
snackbarColor.value = "#35acce";
snackbar.value = true;
loadingSub.value = false;
loading.value = false;
return;
}
setTimeout(() => {
loading.value = false;
loadingSub.value = false;
}, 1500);
}
async function toPost (item: NewsCard | string) {
@@ -392,6 +369,7 @@ async function toPost (item: NewsCard | string) {
createTGWindow(path, "帖子", item.title, 960, 720, false, false);
}
}
async function toJson (item: NewsCard | string) {
if (typeof item === "string") {
const path = router.resolve({

View File

@@ -1,9 +1,7 @@
<template>
<TSwitchTheme />
<div v-if="loading">
<TLoading :empty="loadingEmpty" :title="loadingTitle" />
</div>
<div v-else class="post-json">
<TOLoading v-model="loading" :empty="loadingEmpty" :title="loadingTitle" />
<div class="post-json">
<div class="post-title">
帖子返回内容 JSON
</div>
@@ -15,7 +13,7 @@
import { ref, onMounted, reactive } from "vue";
import { useRoute } from "vue-router";
import JsonViewer from "vue-json-viewer";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
import TSwitchTheme from "../components/main/t-switchTheme.vue";
// tauri
import { appWindow } from "@tauri-apps/api/window";
@@ -41,10 +39,13 @@ onMounted(async () => {
}
// 获取数据
loadingTitle.value = "正在获取数据...";
jsonData = await MysOper.Post.get(postId);
setTimeout(() => {
try {
jsonData = await MysOper.Post.get(postId);
loading.value = false;
}, 200);
} catch (e) {
loadingTitle.value = "帖子不存在或解析失败";
loadingEmpty.value = true;
}
});
</script>
<style>

View File

@@ -1,16 +1,14 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<TSwitchTheme />
<div v-if="loading">
<TLoading :empty="loadingEmpty" :title="loadingTitle" />
</div>
<div v-else class="mys-post-body" v-html="postHtml" />
<TOLoading v-model="loading" :empty="loadingEmpty" :title="loadingTitle" />
<div class="mys-post-body" v-html="postHtml" />
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import TLoading from "../components/overlay/t-loading.vue";
import TOLoading from "../components/overlay/to-loading.vue";
// tauri
import { appWindow } from "@tauri-apps/api/window";
// plugins

View File

@@ -2,7 +2,7 @@
* @file web request TGRequest.ts
* @description 应用用到的请求函数
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.5
* @since Alpha v0.1.6
*/
import { getAbyss } from "./getAbyss";
@@ -10,6 +10,7 @@ import { getAnnoList, getAnnoContent } from "./getAnno";
import { getCookieTokenBySToken } from "./getCookieToken";
// import * from "./getEnkaData.ts";
import { getGameAccountsBySToken, getGameAccountsByCookie } from "./getGameAccounts";
import { getGameRecord } from "./getGameRecord";
import { getLTokenBySToken } from "./getLToken";
import { getGameRoleListByLToken } from "./getRoleList";
// import * from "./getTickets.ts";
@@ -26,6 +27,7 @@ const TGRequest = {
},
User: {
init: initCookie,
getRecord: getGameRecord,
byLoginTicket: {
getTokens: getTokensByLoginTicket,
},

View File

@@ -10,7 +10,6 @@ import { http } from "@tauri-apps/api";
// api
import TGApi from "../api/TGApi";
// utils
import { getServerByUid } from "../utils/tools";
import { getRequestHeader } from "../utils/getRequestHeader";
/**
@@ -24,7 +23,7 @@ import { getRequestHeader } from "../utils/getRequestHeader";
export async function getAbyss (cookie: Record<string, string>, schedule_type: string, account: TGApp.Sqlite.Account.Game): Promise<TGApp.Game.Abyss.FullData | TGApp.BBS.Response.Base> {
const url = TGApi.GameData.getAbyss;
const role_id = account.gameUid;
const params = { role_id, schedule_type, server: getServerByUid(role_id) };
const params = { role_id, schedule_type, server: account.region };
const header = getRequestHeader(cookie, "GET", params, "common");
return await http.fetch<TGApp.Game.Abyss.Response>(url, {
method: "GET",

View File

@@ -0,0 +1,36 @@
/**
* @file core utils getGameRecord.ts
* @description 获取游戏数据的函数
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.2.0
*/
// tauri
import { http } from "@tauri-apps/api";
// utils
import TGApi from "../api/TGApi";
import TGUtils from "../utils/TGUtils";
/**
* @description 获取用户游戏数据
* @since Alpha v0.2.0
* @todo invalid uid
* @description 这边的 ck 可以是 cookie_token 和 account_id
* @description 也可以是 ltoken 和 ltuid
* @param {Record<string, string>} cookie cookie
* @param {TGApp.Sqlite.Account.Game} user 用户的基本信息
* @returns {Promise<unknown>} 用户基本信息
*/
export async function getGameRecord (cookie: Record<string, string>, user: TGApp.Sqlite.Account.Game): Promise<unknown> {
const url = TGApi.GameData.getUserCard;
const params = { role_id: user.gameUid, server: user.region };
const header = TGUtils.User.getHeader(cookie, "GET", params, "common");
return await http.fetch(url, {
method: "GET",
headers: header,
query: params,
}).then((res) => {
console.log(res.data);
return res.data;
});
}

View File

@@ -18,12 +18,12 @@ import TGUtils from "../utils/TGUtils";
* @param {TGApp.Sqlite.Account.Game} account 游戏账号
* @returns {Promise<TGApp.Game.Character.ListItem[]|TGApp.BBS.Response.Base>} 用户角色列表
*/
export async function getGameRoleListByLToken (cookie: Record<string, string>, account: TGApp.Sqlite.Account.Game): Promise<TGApp.Game.Character.ListItem[] | TGApp.BBS.Response.Base> {
export async function getGameRoleListByLToken (cookie: TGApp.BBS.Constant.CookieGroup4, account: TGApp.Sqlite.Account.Game): Promise<TGApp.Game.Character.ListItem[] | TGApp.BBS.Response.Base> {
const url = TGApi.GameData.byCookie.getCharacter;
const uid = account.gameUid;
// eslint-disable-next-line camelcase
const data = { role_id: uid, server: TGUtils.Tools.getServerByUid(uid) };
const header = TGUtils.User.getHeader(cookie, "POST", JSON.stringify(data), "common");
const header = TGUtils.User.getHeader(cookie as unknown as Record<string, string>, "POST", JSON.stringify(data), "common");
return await http.fetch<TGApp.Game.Character.ListResponse>(url, {
method: "POST",
headers: header,