Compare commits

...

92 Commits

Author SHA1 Message Date
目棃
89a79dc968 🚀 v0.7.2 2025-03-27 20:47:38 +08:00
目棃
e1653bdfb0 🍱 更新卡池数据 2025-03-27 20:45:27 +08:00
目棃
8af5136a9c 🚸 调整保存hint 2025-03-27 18:00:47 +08:00
目棃
a8694104a9 🚸 查询时重置选中成就系列 2025-03-27 15:53:01 +08:00
目棃
570cbb5fed 🍱 补充遗漏数据 2025-03-27 15:44:45 +08:00
目棃
dd339285cb 🎨 代码格式化 2025-03-27 14:53:58 +08:00
目棃
31311510ef 🍱 更新5.5成就资源
close #147
2025-03-27 14:43:30 +08:00
目棃
f367e797a9 🍱 更新5.5角色&武器&素材日历
#147
2025-03-27 14:30:08 +08:00
目棃
082fe9dfab ♻️ 更新5.5名片资源,重构名片数据结构
#147
2025-03-27 11:06:16 +08:00
目棃
3769859611 🍱 更新5.5材料资源
#147
2025-03-27 09:57:24 +08:00
目棃
928c71ead8 🍱 更新5.5部分资源,适配新版本返回数据
#147
2025-03-27 09:53:39 +08:00
目棃
39f2a7dc31 💄 调整mention组件样式 2025-03-26 17:50:05 +08:00
目棃
1d637705b7 获取用户关注动态 2025-03-26 17:31:10 +08:00
目棃
9e2eb80a21 🌱 获取用户关注动态 2025-03-26 16:58:33 +08:00
目棃
927d4545a1 🚸 更新图片下载提示信息 2025-03-26 16:40:45 +08:00
目棃
45217d423a ⬆️ 更新依赖 2025-03-26 09:21:53 +08:00
目棃
d430cd4672 🚸 优化自定义表情处理 2025-03-25 14:08:33 +08:00
目棃
d15d78b73f 🚸 调整触发dom 2025-03-25 11:25:48 +08:00
目棃
00fdcc79d6 ️ use sass-embedded 2025-03-25 10:26:04 +08:00
目棃
20438106a4 💄 调整UI 2025-03-24 09:14:19 +08:00
目棃
f92684c7b3 ⬆️ 更新依赖 2025-03-21 10:45:35 +08:00
目棃
3228b6a305 💄 调整UI 2025-03-21 10:28:20 +08:00
目棃
a47afbf2e0 💄 调整UI 2025-03-20 16:52:41 +08:00
目棃
f4a0165e81 💄 调整UI 2025-03-20 10:32:31 +08:00
目棃
31874e07c4 💄 调整UI 2025-03-19 17:52:06 +08:00
目棃
c696d3b51d 💄 调整UI 2025-03-19 17:06:43 +08:00
目棃
8a2a3f5279 💄 调整角色页面UI 2025-03-19 16:50:09 +08:00
目棃
a2f68b92c7 💄 调整战绩页面UI 2025-03-19 16:20:40 +08:00
目棃
96dfec969c 💄 调整成就页面UI 2025-03-19 14:59:24 +08:00
目棃
1b5ddf9b3f 💄 调整select 2025-03-19 10:45:49 +08:00
目棃
4dc273662d 🚸 调整hint 2025-03-19 09:16:16 +08:00
目棃
83d52c6f40 ♻️ 迁移数据而非清空 2025-03-19 09:12:39 +08:00
目棃
0aabd2fa80 🚨 fix qodana warn 2025-03-18 15:39:19 +08:00
目棃
01dc6a6ea7 ♻️ use opener instead of window.open/shell 2025-03-18 14:52:00 +08:00
目棃
765c860473 💄 add time info 2025-03-18 14:04:52 +08:00
目棃
b276b04f23 🏷️ typo 2025-03-18 13:42:21 +08:00
目棃
e308e4789c 🐛 fix gt call err 2025-03-18 09:28:33 +08:00
目棃
522add2441 ♻️ erasableSyntaxOnly 2025-03-17 17:58:15 +08:00
目棃
08f74ce6a0 🚸 hide copy on gif overlay 2025-03-17 17:32:59 +08:00
目棃
3be703d329 🚨 fix command warn 2025-03-17 16:14:52 +08:00
目棃
a3e42b63e6 💄 调整样式 2025-03-17 15:07:42 +08:00
目棃
4ef3313eb1 ⬆️ 更新依赖 2025-03-17 09:33:34 +08:00
目棃
a22aac457a 🚸 采用原图 2025-03-14 20:39:53 +08:00
目棃
8a2524d6b1 🙈 ignore gen json schema 2025-03-14 11:32:00 +08:00
目棃
b504f043b4 comboToken 2025-03-12 17:47:04 +08:00
目棃
f842975f21 💄 调整UI 2025-03-12 16:00:10 +08:00
目棃
577f86248e 🚸 首页卡池点击唤起子窗口 2025-03-12 14:45:29 +08:00
目棃
c3246e95ce 加载指定用户帖子 2025-03-12 11:06:12 +08:00
目棃
e3fb88fd44 🎨 仅刷新部分账户 2025-03-11 17:15:20 +08:00
目棃
0f6fe11c75 🔥 移除无用sql 2025-03-11 11:00:04 +08:00
目棃
9d212a5b87 🚸 支持删除无用账户,重构游戏账号表格 2025-03-11 10:54:34 +08:00
目棃
c0d9830670 ♻️ 调整结构 2025-03-11 10:28:17 +08:00
目棃
c577a6f1ea 🚸 调整Hint 2025-03-10 18:00:20 +08:00
目棃
0e6210ee8e 💄 调整偏移 2025-03-10 17:43:26 +08:00
目棃
adfe55054e 🚨 修复devtool警告 2025-03-10 17:31:33 +08:00
目棃
fecad43dec 🚸 投票显示相对进度 2025-03-10 17:14:13 +08:00
目棃
4c93bb0d42 🐛 修正判断逻辑 2025-03-10 16:52:08 +08:00
目棃
667aa6fb94 🐛 修复扫码登录异常 2025-03-10 15:12:05 +08:00
目棃
a1a5271603 💄 替换icon 2025-03-10 14:57:38 +08:00
目棃
4a646c2c4e 🎨 优化链接识别 2025-03-10 14:54:40 +08:00
目棃
48b976ac84 🚸 识别ys.mihoyo.com 2025-03-10 14:26:31 +08:00
目棃
d7250ce13c 🌱 游戏签到 2025-03-10 14:18:23 +08:00
目棃
1cad9891dc 🚸 一些优化 2025-03-10 13:39:50 +08:00
目棃
6302f171e9 🎨 记录所有账号但只显示原神 2025-03-09 22:12:28 +08:00
目棃
bb67da034b 🚸 优化逻辑 2025-03-09 21:54:48 +08:00
目棃
804b735e95 ✏️ TakumiApi → takumiReq 2025-03-09 21:48:39 +08:00
目棃
e6904e14b5 🚸 切换账户时清空数据 2025-03-09 21:32:11 +08:00
目棃
b4954bd74c 💄 显示连续执行天数 2025-03-08 23:12:11 +08:00
目棃
8f9006b9f4 👔 调整判断逻辑 2025-03-08 22:51:25 +08:00
目棃
fba7a6c088 🚸 移除每次路由变化引起的居中 2025-03-08 19:19:31 +08:00
目棃
0fdbea55eb 💄 调整标题占据空间 2025-03-08 19:04:46 +08:00
dependabot[bot]
b93ae791f2 Bump ring from 0.17.11 to 0.17.13 in /src-tauri (#146)
Bumps [ring](https://github.com/briansmith/ring) from 0.17.11 to 0.17.13.
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: ring
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-08 17:50:15 +08:00
目棃
b6b0b90ae8 👔 调整判断逻辑 2025-03-08 17:48:47 +08:00
目棃
7e7fb318ea 💄 调整抽奖UI 2025-03-08 17:37:20 +08:00
目棃
3a102f31be 🚸 一些调整 2025-03-08 17:01:25 +08:00
目棃
9dba531bb2 💄 调整深色模式下帖子卡片样式 2025-03-08 16:48:43 +08:00
目棃
6b5959ff76 🎨 调整帖子卡片点击处理 2025-03-08 16:41:54 +08:00
目棃
d8267cbf7b 💄 调整整体滚动条样式 2025-03-08 16:30:33 +08:00
目棃
ac4d2a319f 🚸 即时响应页面适配 2025-03-08 16:06:06 +08:00
目棃
214dec29d8 ♻️ 卡池&活动组件重构 2025-03-08 15:39:27 +08:00
目棃
fcb16c9299 💄 调整UI 2025-03-08 11:36:24 +08:00
目棃
37ea45f0c1 💄 组件复用 2025-03-08 11:31:27 +08:00
目棃
dafa153b62 🌱 获取合集信息 2025-03-08 11:18:09 +08:00
目棃
9b152eb59f 💄 渲染推荐理由 2025-03-08 10:28:52 +08:00
目棃
d5aaecbf72 ♻️ 类型迁移&重构 2025-03-08 09:53:25 +08:00
目棃
ce5a88954a ♻️ 请求重构 2025-03-07 17:53:35 +08:00
目棃
fee1872b46 🚸 处理下线villaCard渲染 2025-03-07 15:54:57 +08:00
目棃
dd3ce101a6 🎨 重构滚动高度计算 2025-03-07 15:13:20 +08:00
目棃
d32d7b69f1 👽️ 接口适配
https://github.com/Womsxd/MihoyoBBSTools/issues/237
2025-03-07 11:13:12 +08:00
目棃
11a6157af0 ⬆️ 更新依赖 2025-03-06 16:23:27 +08:00
目棃
1a40f88027 🐛 修复参数调用异常 2025-03-06 15:59:09 +08:00
目棃
81fc5f9dca 🔧 参考最新项目修改配置 2025-03-06 15:56:08 +08:00
252 changed files with 10427 additions and 27912 deletions

View File

@@ -2,12 +2,36 @@
Author: 目棃 Author: 目棃
Description: CHANGELOG Description: CHANGELOG
Date: 2024-10-09 Date: 2024-10-09
Update: 2025-03-06 Update: 2025-03-27
--- ---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-10-09 15:51:43` > 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-10-09 15:51:43`
> >
> 更新于 `2025-03-06 10:01:22` > 更新于 `2025-03-27 14:45:54`
## [0.7.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.7.2) (2025-03-27)
- 🍱 更新5.5资源 [`#147`](https://github.com/BTMuli/TeyvatGuide/issues/147)
- ✨ 新增游戏签到脚本
- ✨ 扫码登录新增游戏登录方式
- ✨ 获取登录用户关注帖子
- 🐛 修复获取深渊数据概览异常
- 🐛 修复扫码登录异常
- ♻️ 首页卡池&活动组件重构
- ♻️ 重构游戏账号数据库
- ♻️ 调用浏览器而非webview2打开外部链接
- 💄 帖子卡片UI调整增加时间&推荐理由数据
- 💄 调整整体滚动条样式
- 💄 调整抽奖UI
- 💄 米游币脚本显示连续执行天数
- 💄 帖子投票组件进度条显示相对进度(以最高数为基准)
- 💄 调整多页面UI
- 🎨 重构合集浮窗滚动高度计算
- 🎨 调整帖子卡片点击处理
- 🚸 处理下线villaCard渲染
- 🚸 即时响应页面适配
- 🚸 完善部分请求防抖处理
- 🚸 链接识别`ys.mihoyo.com`
## [0.7.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.7.1) (2025-03-06) ## [0.7.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.7.1) (2025-03-06)

View File

@@ -2,12 +2,12 @@
Author: 目棃 Author: 目棃
Description: 说明文档 Description: 说明文档
Date: 2023-03-05 Date: 2023-03-05
Update: 2025-02-28 Update: 2025-03-27
--- ---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55` > 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
> >
> 更新于 `2025-02-28 10:54:28` > 更新于 `2025-03-27 14:45:50`
![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide?style=for-the-badge) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases&style=for-the-badge) ![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide?style=for-the-badge) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases&style=for-the-badge)
@@ -66,7 +66,9 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [x] 真境剧诗数据获取 - [x] 真境剧诗数据获取
- [x] 祈愿数据获取(近一年) - [x] 祈愿数据获取(近一年)
- [x] 用户收藏帖子获取 - [x] 用户收藏帖子获取
- [x] 一键完成米游币每日任务 **需要验证码登录** - [x] 用户关注帖子获取
- [x] 一键完成米游币每日任务
- [x] 一键完成游戏签到
- Wiki 功能: - Wiki 功能:

View File

@@ -5,7 +5,6 @@
<link rel="icon" type="image/svg+xml" href="/icon.svg" /> <link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TeyvatGuide</title> <title>TeyvatGuide</title>
<script src="https://static.geetest.com/v4/gt4.js"></script>
</head> </head>
<body> <body>

View File

@@ -1,18 +1,19 @@
{ {
"name": "teyvatguide", "name": "teyvatguide",
"version": "0.7.1", "version": "0.7.2",
"description": "Game Tool for GenshinImpact player", "description": "Game Tool for GenshinImpact player",
"private": true, "private": true,
"packageManager": "pnpm@10.5.2", "packageManager": "pnpm@10.6.5",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "tauri build", "build": "tauri build",
"debug": "tauri build --debug", "debug": "tauri build --debug",
"dev": "tauri dev --exit-on-panic", "dev": "tauri dev --exit-on-panic",
"eslint:pre": "pnpx @eslint/config-inspector@latest", "eslint:pre": "pnpx @eslint/config-inspector@latest",
"oxlint": "oxlint",
"lint": "concurrently \"pnpm:lint:*(!fix)\"", "lint": "concurrently \"pnpm:lint:*(!fix)\"",
"lint:fix": "concurrently \"pnpm:lint:*:fix\"", "lint:fix": "concurrently \"pnpm:lint:*:fix\"",
"lint:vue": "vue-tsc --noEmit", "lint-vue": "vue-tsc --noEmit",
"lint:code": "eslint .", "lint:code": "eslint .",
"lint:code:fix": "eslint . --fix", "lint:code:fix": "eslint . --fix",
"lint:style": "stylelint \"src/**/*.{vue,css,scss}\" -f verbose", "lint:style": "stylelint \"src/**/*.{vue,css,scss}\" -f verbose",
@@ -69,13 +70,14 @@
}, },
"dependencies": { "dependencies": {
"@mdi/font": "7.4.47", "@mdi/font": "7.4.47",
"@tauri-apps/api": "^2.3.0", "@tauri-apps/api": "^2.4.0",
"@tauri-apps/plugin-deep-link": "^2.2.0", "@tauri-apps/plugin-deep-link": "^2.2.0",
"@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0", "@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-http": "^2.3.0", "@tauri-apps/plugin-http": "^2.4.2",
"@tauri-apps/plugin-log": "^2.2.3", "@tauri-apps/plugin-log": "^2.3.1",
"@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-opener": "^2.2.6",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.0", "@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-sql": "^2.2.0", "@tauri-apps/plugin-sql": "^2.2.0",
@@ -90,43 +92,42 @@
"pinia": "^3.0.1", "pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0", "pinia-plugin-persistedstate": "^4.2.0",
"qrcode.vue": "^3.6.0", "qrcode.vue": "^3.6.0",
"sass": "^1.85.1", "sass-embedded": "^1.86.0",
"sass-loader": "^16.0.5",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-json-pretty": "^2.4.0", "vue-json-pretty": "^2.4.0",
"vue-router": "^4.5.0", "vue-router": "^4.5.0",
"vuetify": "^3.7.14", "vuetify": "^3.7.19",
"wcag-color": "^1.1.1", "wcag-color": "^1.1.1",
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.3.0", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.21.0", "@eslint/js": "^9.23.0",
"@tauri-apps/cli": "2.3.1", "@tauri-apps/cli": "2.4.0",
"@types/color-convert": "^2.0.4", "@types/color-convert": "^2.0.4",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/js-md5": "^0.7.2", "@types/js-md5": "^0.7.2",
"@types/node": "^22.13.7", "@types/node": "^22.13.13",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"@typescript-eslint/parser": "^8.25.0", "@typescript-eslint/parser": "^8.28.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.3",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"eslint": "^9.21.0", "eslint": "^9.23.0",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsonc": "^2.19.1", "eslint-plugin-jsonc": "^2.20.0",
"eslint-plugin-prettier": "^5.2.3", "eslint-plugin-prettier": "^5.2.5",
"eslint-plugin-vue": "^9.32.0", "eslint-plugin-vue": "^10.0.0",
"eslint-plugin-yml": "^1.17.0", "eslint-plugin-yml": "^1.17.0",
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"jsonc-eslint-parser": "^2.4.0", "jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^15.4.3", "lint-staged": "^15.5.0",
"oxlint": "^0.15.12", "oxlint": "^0.16.3",
"prettier": "3.5.2", "prettier": "3.5.3",
"stylelint": "^16.15.0", "stylelint": "^16.16.0",
"stylelint-config-idiomatic-order": "^10.0.0", "stylelint-config-idiomatic-order": "^10.0.0",
"stylelint-config-standard-vue": "^1.0.0", "stylelint-config-standard-vue": "^1.0.0",
"stylelint-declaration-block-no-ignored-properties": "^2.8.0", "stylelint-declaration-block-no-ignored-properties": "^2.8.0",
@@ -136,12 +137,12 @@
"stylelint-scss": "^6.11.1", "stylelint-scss": "^6.11.1",
"tsx": "^4.19.3", "tsx": "^4.19.3",
"typescript": "^5.8.2", "typescript": "^5.8.2",
"typescript-eslint": "^8.25.0", "typescript-eslint": "^8.28.0",
"vite": "^6.2.0", "vite": "^6.2.3",
"vite-plugin-vue-devtools": "^7.7.2", "vite-plugin-vue-devtools": "^7.7.2",
"vite-plugin-vuetify": "^2.1.0", "vite-plugin-vuetify": "^2.1.0",
"vue-eslint-parser": "^9.4.3", "vue-eslint-parser": "^10.1.1",
"vue-tsc": "^2.2.4", "vue-tsc": "^2.2.8",
"yaml-eslint-parser": "^1.3.0" "yaml-eslint-parser": "^1.3.0"
} }
} }

2769
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -2,3 +2,6 @@
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas

1043
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "TeyvatGuide" name = "TeyvatGuide"
version = "0.7.1" version = "0.7.2"
description = "Game Tool for Genshin Impact player" description = "Game Tool for Genshin Impact player"
authors = ["BTMuli <bt-muli@outlook.com>"] authors = ["BTMuli <bt-muli@outlook.com>"]
license = "MIT" license = "MIT"
@@ -9,16 +9,23 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
# The `_lib` suffix may seem redundant, but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "teyvat_guide_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.0.6", features = [] } tauri-build = { version = "2.1.0", features = [] }
[dependencies] [dependencies]
chrono = "0.4.40" chrono = "0.4.40"
log = "0.4.26" log = "0.4.26"
serde = { version = "1.0.218", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.139" serde_json = "1.0.140"
tauri = { version = "2.3.1", features = [] } tauri = { version = "2.4.0", features = [] }
tauri-utils = "2.2.0" tauri-utils = "2.3.0"
url = "2.5.4" url = "2.5.4"
walkdir = "2.5.0" walkdir = "2.5.0"
@@ -48,6 +55,11 @@ features = ["unsafe-headers"]
git = "ssh://git@github.com/tauri-apps/plugins-workspace.git" git = "ssh://git@github.com/tauri-apps/plugins-workspace.git"
branch = "v2" branch = "v2"
# opener 插件
[dependencies.tauri-plugin-opener]
git = "ssh://git@github.com/tauri-apps/plugins-workspace.git"
branch = "v2"
# os 插件 # os 插件
[dependencies.tauri-plugin-os] [dependencies.tauri-plugin-os]
git = "ssh://git@github.com/tauri-apps/plugins-workspace.git" git = "ssh://git@github.com/tauri-apps/plugins-workspace.git"
@@ -73,8 +85,3 @@ branch = "v2"
git = "ssh://git@github.com/tauri-apps/plugins-workspace.git" git = "ssh://git@github.com/tauri-apps/plugins-workspace.git"
branch = "v2" branch = "v2"
features = ["sqlite"] features = ["sqlite"]
[features]
# this feature is used for production builds or when `devPath` points to the filesystem
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]

View File

@@ -21,6 +21,7 @@
"core:window:allow-center", "core:window:allow-center",
"core:window:allow-close", "core:window:allow-close",
"core:window:allow-destroy", "core:window:allow-destroy",
"core:window:allow-set-always-on-top",
"core:window:allow-set-size", "core:window:allow-set-size",
"core:window:allow-set-title", "core:window:allow-set-title",
"core:window:allow-set-fullscreen", "core:window:allow-set-fullscreen",

View File

@@ -14,6 +14,7 @@
"core:window:default", "core:window:default",
"core:path:allow-resolve-directory", "core:path:allow-resolve-directory",
"core:path:default", "core:path:default",
"opener:default",
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },

View File

@@ -37,6 +37,7 @@
"core:window:allow-set-focus", "core:window:allow-set-focus",
"core:window:allow-show", "core:window:allow-show",
"core:window:allow-unminimize", "core:window:allow-unminimize",
"opener:default",
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
@@ -44,6 +45,7 @@
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] }, { "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "opener:allow-open-path", "allow": [{ "path": "**" }] },
{ {
"identifier": "http:default", "identifier": "http:default",
"allow": [ "allow": [
@@ -55,11 +57,7 @@
}, },
{ {
"identifier": "shell:allow-execute", "identifier": "shell:allow-execute",
"allow": [ "allow": [{ "name": "exec-sh", "cmd": "powershell", "args": true }]
{ "name": "win_open", "cmd": "explorer", "args": true },
{ "name": "mac_open", "cmd": "open", "args": true },
{ "name": "exec-sh", "cmd": "powershell", "args": true }
]
} }
], ],
"platforms": ["windows", "macOS"] "platforms": ["windows", "macOS"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,201 +0,0 @@
{
"DevJson": {
"identifier": "DevJson",
"description": "Capability for the dev json window",
"local": true,
"windows": ["Dev_JSON"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"core:event:allow-listen",
"core:event:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"core:webview:allow-set-webview-zoom",
"core:webview:default",
"core:window:allow-center",
"core:window:allow-close",
"core:window:allow-destroy",
"core:window:allow-set-size",
"core:window:allow-set-title",
"core:window:allow-set-fullscreen",
"core:window:allow-show",
"core:window:default",
{
"identifier": "http:default",
"allow": [
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://*.hoyoverse.com/*" }
]
}
],
"platforms": ["windows", "macOS"]
},
"Mys": {
"identifier": "Mys",
"description": "Capability for the mys client window",
"remote": {
"urls": ["https://*.mihoyo.com/*", "https://*.miyoushe.com/*", "https://*.genshinnet.com/*"]
},
"local": true,
"windows": ["mhy_client"],
"permissions": [
"dialog:allow-message",
"dialog:default",
"core:event:allow-emit",
"http:allow-fetch",
"core:webview:allow-set-webview-zoom",
"core:webview:default",
"core:window:allow-center",
"core:window:default",
"core:path:allow-resolve-directory",
"core:path:default",
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{
"identifier": "http:default",
"allow": [
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://*.genshinnet.com/*" }
]
}
],
"platforms": ["windows", "macOS"]
},
"SubWindow": {
"identifier": "SubWindow",
"description": "Capability for the sub window",
"local": true,
"windows": ["Sub_window"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"dialog:allow-save",
"dialog:default",
"core:event:allow-listen",
"core:event:default",
"fs:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"shell:allow-open",
"shell:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"core:webview:allow-set-webview-zoom",
"core:webview:default",
"core:window:allow-center",
"core:window:allow-close",
"core:window:allow-destroy",
"core:window:allow-set-size",
"core:window:allow-set-title",
"core:window:allow-set-focus",
"core:window:allow-set-fullscreen",
"core:window:allow-show",
"core:window:allow-set-always-on-top",
"core:window:default",
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{
"identifier": "http:default",
"allow": [
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://*.mihoyogift.com/*" },
{ "url": "https://*.bilibili.com/*" },
{ "url": "http://*.hdslb.com/*" },
{ "url": "https://*.hoyoverse.com/*" },
{ "url": "https://*.genshinnet.com/*" }
]
}
],
"platforms": ["windows", "macOS"]
},
"TeyvatGuide": {
"identifier": "TeyvatGuide",
"description": "Capability for the main window",
"local": true,
"windows": ["TeyvatGuide"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"dialog:allow-save",
"dialog:default",
"core:event:allow-listen",
"core:event:default",
"fs:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"process:allow-exit",
"process:default",
"shell:allow-execute",
"shell:allow-open",
"shell:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"core:webview:allow-create-webview-window",
"core:webview:allow-set-webview-zoom",
"core:webview:default",
"core:window:allow-center",
"core:window:allow-close",
"core:window:allow-destroy",
"core:window:allow-set-size",
"core:window:default",
"core:window:allow-is-minimized",
"core:window:allow-set-title",
"core:window:allow-set-focus",
"core:window:allow-show",
"core:window:allow-unminimize",
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{
"identifier": "http:default",
"allow": [
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://homa.snapgenshin.com/*" },
{ "url": "https://*.hoyoverse.com/*" }
]
},
{
"identifier": "shell:allow-execute",
"allow": [
{ "args": true, "cmd": "explorer", "name": "win_open" },
{ "args": true, "cmd": "open", "name": "mac_open" },
{ "args": true, "cmd": "powershell", "name": "exec-sh" }
]
}
],
"platforms": ["windows", "macOS"]
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,7 @@ pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
.title("米游社") .title("米游社")
.center() .center()
.user_agent(&win_ua) .user_agent(&win_ua)
.additional_browser_args("--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --autoplay-policy=no-user-gesture-required")
// todo mac环境下没看到menu // todo mac环境下没看到menu
.menu(menu::create_mhy_menu(handle.clone())) .menu(menu::create_mhy_menu(handle.clone()))
.on_menu_event(move |app, event| menu::handle_menu_event(app, event)) .on_menu_event(move |app, event| menu::handle_menu_event(app, event))

View File

@@ -1,6 +1,6 @@
//! @file src/commands.rs //! @file src/commands.rs
//! @desc 命令模块,负责处理命令 //! @desc 命令模块,负责处理命令
//! @since Beta v0.5.1 //! @since Beta v0.7.2
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder}; use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
use tauri_utils::config::{WebviewUrl, WindowConfig}; use tauri_utils::config::{WebviewUrl, WindowConfig};
@@ -42,6 +42,7 @@ pub async fn create_window(
.visible(option.visible) .visible(option.visible)
.title(option.title) .title(option.title)
.center() .center()
.additional_browser_args("--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --autoplay-policy=no-user-gesture-required")
.build() .build()
.unwrap(); .unwrap();
} }

68
src-tauri/src/lib.rs Normal file
View File

@@ -0,0 +1,68 @@
//! @file src/lib.rs
//! @desc 主模块,用于启动应用
//! @since Beta v0.7.2
mod client;
mod commands;
mod plugins;
mod utils;
use crate::client::create_mhy_client;
use crate::commands::{create_window, execute_js, get_dir_size, init_app};
use crate::plugins::{build_log_plugin, build_si_plugin};
use tauri::{generate_context, generate_handler, Builder, Manager, Window, WindowEvent};
// 窗口事件处理
fn window_event_handler(app: &Window, event: &WindowEvent) {
match event {
WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
if app.label() == "TeyvatGuide" {
// 子窗口 label 的数组
const SUB_WINDOW_LABELS: [&str; 3] = ["Sub_window", "Dev_JSON", "mhy_client"];
for label in SUB_WINDOW_LABELS.iter() {
let sub = app.get_webview_window(label);
if sub.is_some() {
sub.unwrap().destroy().unwrap();
}
}
}
app.destroy().unwrap();
}
_ => {}
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
Builder::default()
.on_window_event(move |app, event| window_event_handler(app, event))
.plugin(build_si_plugin())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_sql::Builder::default().build())
.plugin(build_log_plugin())
.setup(|_app| {
let _window = _app.get_webview_window("TeyvatGuide");
#[cfg(debug_assertions)]
if _window.is_some() {
_window.unwrap().open_devtools();
}
Ok(())
})
.invoke_handler(generate_handler![
init_app,
create_window,
execute_js,
get_dir_size,
create_mhy_client
])
.run(generate_context!())
.expect("error while running tauri application");
}

View File

@@ -1,69 +1,10 @@
//! @file src/main.rs //! @file src/main.rs
//! @desc 主模块,用于启动应用 //! @desc 主模块,用于启动应用
//! @since Beta v0.6.2 //! @since Beta v0.7.2
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod client;
mod commands;
mod plugins;
mod utils;
use crate::client::create_mhy_client;
use crate::commands::{create_window, execute_js, get_dir_size, init_app};
use crate::plugins::{build_log_plugin, build_si_plugin};
use tauri::{generate_context, generate_handler, Builder, Manager, Window, WindowEvent};
// 窗口事件处理
fn window_event_handler(app: &Window, event: &WindowEvent) {
match event {
WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
if app.label() == "TeyvatGuide" {
// 子窗口 label 的数组
const SUB_WINDOW_LABELS: [&str; 3] = ["Sub_window", "Dev_JSON", "mhy_client"];
for label in SUB_WINDOW_LABELS.iter() {
let sub = app.get_webview_window(label);
if sub.is_some() {
sub.unwrap().destroy().unwrap();
}
}
}
app.destroy().unwrap();
}
_ => {}
}
}
fn main() { fn main() {
Builder::default() teyvat_guide_lib::run()
.on_window_event(move |app, event| window_event_handler(app, event))
.plugin(build_si_plugin())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_sql::Builder::default().build())
.plugin(build_log_plugin())
.setup(|_app| {
let _window = _app.get_webview_window("TeyvatGuide");
#[cfg(debug_assertions)]
if _window.is_some() {
_window.unwrap().open_devtools();
}
Ok(())
})
.invoke_handler(generate_handler![
init_app,
create_window,
execute_js,
get_dir_size,
create_mhy_client
])
.run(generate_context!())
.expect("error while running tauri application");
} }

View File

@@ -1,7 +1,8 @@
{ {
"$schema": "https://schema.tauri.app/config/2",
"productName": "TeyvatGuide", "productName": "TeyvatGuide",
"identifier": "TeyvatGuide", "identifier": "TeyvatGuide",
"version": "0.7.1", "version": "0.7.2",
"build": { "build": {
"beforeDevCommand": "pnpm vite:dev", "beforeDevCommand": "pnpm vite:dev",
"beforeBuildCommand": "pnpm vite:build", "beforeBuildCommand": "pnpm vite:build",
@@ -39,6 +40,7 @@
"resizable": true, "resizable": true,
"title": "Teyvat Guide", "title": "Teyvat Guide",
"label": "TeyvatGuide", "label": "TeyvatGuide",
"additionalBrowserArgs": "--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --autoplay-policy=no-user-gesture-required",
"width": 1600, "width": 1600,
"height": 900, "height": 900,
"center": true, "center": true,

View File

@@ -18,10 +18,10 @@ import showSnackbar from "@comp/func/snackbar.js";
import TGSqlite from "@Sqlite/index.js"; import TGSqlite from "@Sqlite/index.js";
import TSUserAccount from "@Sqlite/modules/userAccount.js"; import TSUserAccount from "@Sqlite/modules/userAccount.js";
import { app, core, event, webviewWindow } from "@tauri-apps/api"; import { app, core, event, webviewWindow } from "@tauri-apps/api";
import { PhysicalSize } from "@tauri-apps/api/dpi";
import type { Event, UnlistenFn } from "@tauri-apps/api/event"; import type { Event, UnlistenFn } from "@tauri-apps/api/event";
import { currentMonitor, getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow, LogicalSize } from "@tauri-apps/api/window";
import { mkdir } from "@tauri-apps/plugin-fs"; import { mkdir } from "@tauri-apps/plugin-fs";
import { openUrl } from "@tauri-apps/plugin-opener";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { computed, onMounted, onUnmounted, ref } from "vue"; import { computed, onMounted, onUnmounted, ref } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
@@ -30,6 +30,7 @@ import { useAppStore } from "@/store/modules/app.js";
import { useUserStore } from "@/store/modules/user.js"; import { useUserStore } from "@/store/modules/user.js";
import { getBuildTime } from "@/utils/TGBuild.js"; import { getBuildTime } from "@/utils/TGBuild.js";
import TGLogger from "@/utils/TGLogger.js"; import TGLogger from "@/utils/TGLogger.js";
import { getWindowSize, resizeWindow } from "@/utils/TGWindow.js";
import OtherApi from "@/web/request/otherReq.js"; import OtherApi from "@/web/request/otherReq.js";
const router = useRouter(); const router = useRouter();
@@ -40,6 +41,7 @@ const vuetifyTheme = computed<string>(() => (theme.value === "dark" ? "dark" : "
let themeListener: UnlistenFn | null = null; let themeListener: UnlistenFn | null = null;
let urlListener: UnlistenFn | null = null; let urlListener: UnlistenFn | null = null;
let resizeListener: UnlistenFn | null = null;
onMounted(async () => { onMounted(async () => {
const win = getCurrentWindow(); const win = getCurrentWindow();
@@ -51,43 +53,27 @@ onMounted(async () => {
await core.invoke("init_app"); await core.invoke("init_app");
urlListener = await getDeepLink(); urlListener = await getDeepLink();
} }
if (needResize.value !== "false") await checkResize(); if (needResize.value !== "false") await resizeWindow();
document.documentElement.className = theme.value; document.documentElement.className = theme.value;
themeListener = await event.listen<string>("readTheme", (e: Event<string>) => { themeListener = await event.listen<string>("readTheme", (e: Event<string>) => {
theme.value = e.payload; theme.value = e.payload;
document.documentElement.className = theme.value; document.documentElement.className = theme.value;
}); });
resizeListener = await event.listen<string>("needResize", async (e: Event<string>) => {
console.log(needResize);
const windowCur = webviewWindow.getCurrentWebviewWindow();
if (e.payload !== "false") {
await resizeWindow();
} else {
const size = getWindowSize(windowCur.label);
await windowCur.setSize(new LogicalSize(size.width, size.height));
await windowCur.setZoom(1);
}
await windowCur.center();
});
await getCurrentWindow().show(); await getCurrentWindow().show();
}); });
async function checkResize(): Promise<void> {
const screen = await currentMonitor();
if (screen === null) {
showSnackbar.error("获取屏幕信息失败!", 3000);
return;
}
const windowCur = webviewWindow.getCurrentWebviewWindow();
if (await windowCur.isMaximized()) return;
const designSize = getSize(windowCur.label);
const widthScale = screen.size.width / 1920;
const heightScale = screen.size.height / 1080;
await windowCur.setSize(
new PhysicalSize(
Math.round(designSize.width * widthScale),
Math.round(designSize.height * heightScale),
),
);
await windowCur.setZoom((1 / screen.scaleFactor) * Math.min(widthScale, heightScale));
await windowCur.setFocus();
return;
}
function getSize(label: string): PhysicalSize {
if (label === "TeyvatGuide") return new PhysicalSize(1600, 900);
if (label === "Sub_window" || label === "Dev_JSON") return new PhysicalSize(960, 720);
return new PhysicalSize(1280, 720);
}
// 启动后只执行一次的监听 // 启动后只执行一次的监听
async function listenOnInit(): Promise<void> { async function listenOnInit(): Promise<void> {
console.info("[App][listenOnInit] 监听初始化事件!"); console.info("[App][listenOnInit] 监听初始化事件!");
@@ -248,7 +234,7 @@ async function checkUpdate(): Promise<void> {
buildTime.value = getBuildTime(); buildTime.value = getBuildTime();
await TGSqlite.update(); await TGSqlite.update();
showSnackbar.success("数据库已更新!", 3000); showSnackbar.success("数据库已更新!", 3000);
window.open("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html"); await openUrl("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html");
} }
} }
@@ -261,6 +247,10 @@ onUnmounted(() => {
urlListener(); urlListener();
urlListener = null; urlListener = null;
} }
if (resizeListener !== null) {
resizeListener();
resizeListener = null;
}
}); });
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>

View File

@@ -1,12 +1,12 @@
/* /*
* @file assets/index.css * @file assets/index.css
* @description 全局样式文件 * @description 全局样式文件
* @since Beta v0.5.5 * @since Beta v0.7.2
*/ */
@import "fonts/index.css"; @import "fonts/index.css";
@import url("themes/default.css"); @import "themes/default.css";
@import url("themes/dark.css"); @import "themes/dark.css";
:root { :root {
/* font */ /* font */
@@ -62,26 +62,27 @@ html {
/* /*
* @description 侧边滚动条样式 * @description 侧边滚动条样式
* @since Beta v0.5.5 * @since Beta v0.7.2
*/ */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 4px;
height: 8px; height: 4px;
border-radius: 5px; border-radius: 4px;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
border-radius: 5px; border-radius: 4px;
background: var(--common-shadow-2); background: var(--common-shadow-2);
} }
::-webkit-scrollbar-corner { ::-webkit-scrollbar-corner {
border-radius: 5px; border-radius: 4px;
background: var(--common-shadow-2); background: var(--common-shadow-2);
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
border-radius: 5px; min-height: 48px;
border-radius: 4px;
background: var(--tgc-od-white); background: var(--tgc-od-white);
} }

View File

@@ -0,0 +1,49 @@
/**
* @file styles/github.styles.scss
* @description GitHub styles
* @since Beta v0.7.2
*/
/* Card传入theme参数 */
@mixin github-card($theme: "default") {
@include github-card-shadow($theme);
@if $theme == "default" {
border: 1px solid #d1d9e0;
background: #ffffff;
} @else {
border: 1px solid #3d444d;
background: #0d1117;
}
}
/* CardShadow传入theme参数 */
@mixin github-card-shadow($theme: "default") {
@if $theme == "default" {
box-shadow:
rgba(31, 35, 40, 0.06) 0 1px 1px 0,
rgba(31, 35, 40, 0.06) 0 1px 3px 0;
} @else {
box-shadow:
rgba(1, 4, 9, 0.6) 0 1px 1px 0,
rgba(1, 4, 9, 0.6) 0 1px 3px 0;
}
}
/* Btn传入theme参数 */
@mixin github-btn($theme: "default") {
@if $theme == "default" {
border: 1px solid #d1d9e0;
background: #f6f8fa;
} @else {
border: 1px solid #3d444d;
background: #212830;
}
}
/* tags深色主题 */
@mixin github-tag-dark-gen($color: #548af7) {
border: 1px solid rgba($color, 0.3);
background: rgba($color, 0.18);
color: $color;
}

View File

@@ -1,7 +1,7 @@
/** /**
* @file assets/themes/dark.css * @file assets/themes/dark.css
* @description 主题样式文件-深色主题 * @description 主题样式文件-深色主题
* @since Beta v0.4.5 * @since Beta v0.7.2
*/ */
/* dark mode */ /* dark mode */
@@ -33,8 +33,6 @@ html.dark {
/* button */ /* button */
--btn-text: var(--tgc-yellow-1); --btn-text: var(--tgc-yellow-1);
--btn-bg-1: var(--tgc-dark-5);
--btn-text-1: var(--tgc-yellow-1);
/* box-shadow */ /* box-shadow */
--common-shadow-1: rgb(255 255 255 / 10%); --common-shadow-1: rgb(255 255 255 / 10%);

View File

@@ -1,7 +1,7 @@
/** /**
* @file assets/themes/default.css * @file assets/themes/default.css
* @description 主题样式文件-默认(浅色)主题 * @description 主题样式文件-默认(浅色)主题
* @since Beta v0.4.5 * @since Beta v0.7.2
*/ */
/* default(light) theme */ /* default(light) theme */
@@ -33,8 +33,6 @@ html.default {
/* button */ /* button */
--btn-text: var(--tgc-yellow-2); /* with tgc-btn-1 */ --btn-text: var(--tgc-yellow-2); /* with tgc-btn-1 */
--btn-bg-1: var(--tgc-yellow-1);
--btn-text-1: var(--tgc-blue-1);
/* box-shadow */ /* box-shadow */
--common-shadow-1: rgb(0 0 0 / 10%); --common-shadow-1: rgb(0 0 0 / 10%);

View File

@@ -4,7 +4,7 @@
<TMiImg alt="navIcon" :src="navItem.icon" :ori="true" /> <TMiImg alt="navIcon" :src="navItem.icon" :ori="true" />
<span>{{ navItem.name }}</span> <span>{{ navItem.name }}</span>
</div> </div>
<div v-if="props.modelValue === 2 && hasNav" class="tgn-nav"> <div v-if="hasNav" class="tgn-nav">
<v-btn size="25" @click="tryGetCode" title="查看兑换码" icon="mdi-code-tags-check"></v-btn> <v-btn size="25" @click="tryGetCode" title="查看兑换码" icon="mdi-code-tags-check"></v-btn>
</div> </div>
<ToLivecode v-model="showOverlay" :data="codeData" v-model:actId="actId" /> <ToLivecode v-model="showOverlay" :data="codeData" v-model:actId="actId" />
@@ -15,6 +15,7 @@ import TMiImg from "@comp/app/t-mi-img.vue";
import showDialog from "@comp/func/dialog.js"; import showDialog from "@comp/func/dialog.js";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import { emit } from "@tauri-apps/api/event"; import { emit } from "@tauri-apps/api/event";
import { openUrl } from "@tauri-apps/plugin-opener";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { computed, onMounted, ref, shallowRef, watch } from "vue"; import { computed, onMounted, ref, shallowRef, watch } from "vue";
@@ -37,7 +38,6 @@ const showOverlay = ref<boolean>(false);
const actId = ref<string>(); const actId = ref<string>();
const hasNav = computed<TGApp.BBS.Navigator.Navigator | undefined>(() => { const hasNav = computed<TGApp.BBS.Navigator.Navigator | undefined>(() => {
if (props.modelValue !== 2) return undefined;
return nav.value.find((item) => item.name === "前瞻直播" || item.name === "直播兑换码"); return nav.value.find((item) => item.name === "前瞻直播" || item.name === "直播兑换码");
}); });
@@ -81,6 +81,7 @@ async function toNav(item: TGApp.BBS.Navigator.Navigator): Promise<void> {
await TGLogger.Info(`[TGameNav][toNav] ${item.app_path}`); await TGLogger.Info(`[TGameNav][toNav] ${item.app_path}`);
const link = new URL(item.app_path); const link = new URL(item.app_path);
const mysList = [ const mysList = [
"https://ys.mihoyo.com",
"https://act.mihoyo.com", "https://act.mihoyo.com",
"https://webstatic.mihoyo.com", "https://webstatic.mihoyo.com",
"https://bbs.mihoyo.com", "https://bbs.mihoyo.com",
@@ -93,10 +94,10 @@ async function toNav(item: TGApp.BBS.Navigator.Navigator): Promise<void> {
} }
// 如果不在上面的域名里面,就直接打开 // 如果不在上面的域名里面,就直接打开
if (!mysList.includes(link.origin)) { if (!mysList.includes(link.origin)) {
window.open(item.app_path); await openUrl(item.app_path);
return; return;
} }
if (item.name === "签到福利") { if (item.name === "签到福利" || item.name === "每日签到") {
await TGClient.open("web_act_thin", item.app_path); await TGClient.open("web_act_thin", item.app_path);
return; return;
} }
@@ -112,78 +113,49 @@ async function toNav(item: TGApp.BBS.Navigator.Navigator): Promise<void> {
// 处理 protocol // 处理 protocol
async function toBBS(link: URL): Promise<void> { async function toBBS(link: URL): Promise<void> {
if (link.protocol == "mihoyobbs:") { if (link.protocol == "mihoyobbs:") {
if (link.pathname.startsWith("//article")) { if (link.hostname === "article") {
const postId = link.pathname.split("/").pop(); const postId = link.pathname.split("/").pop();
await createPost(<string>postId); await createPost(<string>postId);
return; return;
} }
if (link.pathname.startsWith("//forum")) { if (link.hostname === "forum") {
const forumId = link.pathname.split("/").pop(); const forumId = link.pathname.split("/").pop();
const localPath = getLocalPath(forumId); const localPath = `/posts/forum/${props.modelValue}/${forumId}`;
if (localPath === "") {
showSnackbar.warn(`不支持的链接:${link.href}`);
return;
}
await emit("active_deep_link", `router?path=${localPath}`); await emit("active_deep_link", `router?path=${localPath}`);
return; return;
} }
} }
showSnackbar.warn(`不支持的链接:${link.href}`); showSnackbar.warn(`不支持的链接:${link.href}`);
} }
// todo 动态获取版块列表
function getLocalPath(forum?: string): string {
if (!forum) return "";
const forumLocalMap: Record<string, string> = {
"31": "/news/3", // 崩坏2 官方
"6": "/news/1", // 崩坏3 官方
"28": "/news/2", // 原神官方
"33": "/news/4", // 未定官方
"58": "/news/8", // 绝区零官方
"36": "/news/5", // 大别野公告
};
if (forumLocalMap[forum]) return forumLocalMap[forum];
const ysForums = ["26", "43", "29", "49", "50"];
const srForums = ["52", "61", "56", "62"];
const bh3Forums = ["1", "14", "4", "41"];
const bh2Forums = ["30", "51", "40"];
const wdForums = ["37", "60", "42", "38"];
const zzzForums = ["57", "59", "64", "65"];
const dbyForums = ["54", "35", "34", "39", "47", "48", "55", "36"];
if (ysForums.includes(forum)) return `/posts/forum/2/${forum}`;
if (srForums.includes(forum)) return `/posts/forum/6/${forum}`;
if (bh3Forums.includes(forum)) return `/posts/forum/1/${forum}`;
if (bh2Forums.includes(forum)) return `/posts/forum/3/${forum}`;
if (wdForums.includes(forum)) return `/posts/forum/4/${forum}`;
if (zzzForums.includes(forum)) return `/posts/forum/8/${forum}`;
if (dbyForums.includes(forum)) return `/posts/forum/5/${forum}`;
return "";
}
</script> </script>
<style lang="css" scoped> <style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
.tgn-container { .tgn-container {
position: relative;
display: flex; display: flex;
padding: 5px; padding: 8px;
gap: 10px; gap: 8px;
} }
.tgn-nav { .tgn-nav {
@include github-styles.github-card();
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 5px; padding: 4px;
border-radius: 5px; border-radius: 4px;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
background: var(--common-shadow-t-4);
box-shadow: 0 0 5px var(--common-shadow-4);
color: var(--tgc-white-1); color: var(--tgc-white-1);
cursor: pointer; cursor: pointer;
} }
.dark .tgn-nav {
@include github-styles.github-card("dark");
}
.tgn-nav img { .tgn-nav img {
width: 25px; width: 28px;
height: 25px; height: 28px;
} }
.tgn-nav span { .tgn-nav span {

View File

@@ -68,8 +68,8 @@ const props = defineProps<TItemBoxProps>();
left: 0; left: 0;
overflow: hidden; overflow: hidden;
width: v-bind("props.modelValue.size"); width: v-bind("props.modelValue.size");
border-radius: 5px; height: v-bind("props.modelValue.height");
aspect-ratio: 1; border-radius: 4px;
} }
.tib-bg img { .tib-bg img {
@@ -82,8 +82,8 @@ const props = defineProps<TItemBoxProps>();
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: v-bind("props.modelValue.size"); width: v-bind("props.modelValue.size");
border-radius: 5px; height: v-bind("props.modelValue.height");
aspect-ratio: 1; border-radius: 4px;
} }
.tib-icon img { .tib-icon img {
@@ -98,11 +98,11 @@ const props = defineProps<TItemBoxProps>();
left: 0; left: 0;
display: flex; display: flex;
width: v-bind("props.modelValue.size"); width: v-bind("props.modelValue.size");
height: v-bind("props.modelValue.height");
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 5px; border-radius: 4px;
aspect-ratio: 1;
} }
.tib-lt { .tib-lt {
@@ -111,9 +111,9 @@ const props = defineProps<TItemBoxProps>();
left: 3%; left: 3%;
display: flex; display: flex;
width: v-bind("props.modelValue.ltSize"); width: v-bind("props.modelValue.ltSize");
height: v-bind("props.modelValue.ltSize");
align-items: center; align-items: center;
justify-content: center; justify-content: center;
aspect-ratio: 1;
} }
.tib-lt img { .tib-lt img {
@@ -128,12 +128,12 @@ const props = defineProps<TItemBoxProps>();
right: 0; right: 0;
display: flex; display: flex;
width: v-bind("props.modelValue.rtSize"); width: v-bind("props.modelValue.rtSize");
height: v-bind("props.modelValue.rtSize");
align-items: center; align-items: center;
justify-content: center; justify-content: center;
aspect-ratio: 1;
background: rgb(0 0 0 / 40%); background: rgb(0 0 0 / 40%);
border-bottom-left-radius: 5px; border-bottom-left-radius: 4px;
border-top-right-radius: 5px; border-top-right-radius: 4px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
font-family: var(--font-title); font-family: var(--font-title);
} }
@@ -145,13 +145,15 @@ const props = defineProps<TItemBoxProps>();
display: flex; display: flex;
width: 100%; width: 100%;
height: v-bind("props.modelValue.innerHeight ?? 0") px; height: v-bind("props.modelValue.innerHeight ?? 0") px;
flex-grow: 1;
flex-shrink: 0;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
-webkit-backdrop-filter: blur(v-bind("props.modelValue.innerBlur ?? 0")); -webkit-backdrop-filter: blur(v-bind("props.modelValue.innerBlur ?? 0"));
backdrop-filter: blur(v-bind("props.modelValue.innerBlur ?? 0")); backdrop-filter: blur(v-bind("props.modelValue.innerBlur ?? 0"));
background: rgb(20 20 20 / 40%); background: rgb(20 20 20 / 40%);
border-bottom-left-radius: 5px; border-bottom-left-radius: 4px;
border-bottom-right-radius: 5px; border-bottom-right-radius: 4px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
font-family: var(--font-title); font-family: var(--font-title);
font-size: v-bind("((props.modelValue.innerHeight ?? 0) / 2).toString() + 'px'"); font-size: v-bind("((props.modelValue.innerHeight ?? 0) / 2).toString() + 'px'");
@@ -159,8 +161,8 @@ const props = defineProps<TItemBoxProps>();
.tib-inner img { .tib-inner img {
width: v-bind("(props.modelValue.innerHeight ?? 0).toString() + 'px'"); width: v-bind("(props.modelValue.innerHeight ?? 0).toString() + 'px'");
height: v-bind("(props.modelValue.innerHeight ?? 0).toString() + 'px'");
padding: 1px; padding: 1px;
aspect-ratio: 1;
} }
.tib-inner span { .tib-inner span {

View File

@@ -14,11 +14,7 @@ import { onMounted, ref } from "vue";
const isPined = ref<boolean>(false); const isPined = ref<boolean>(false);
onMounted(async () => { onMounted(async () => (isPined.value = await getCurrentWindow().isAlwaysOnTop()));
// 因为无法获取窗口是否置顶,这边手动取消置顶
// 详见https://github.com/tauri-apps/tauri/issues/11078
await getCurrentWindow().setAlwaysOnTop(false);
});
async function switchPin(): Promise<void> { async function switchPin(): Promise<void> {
isPined.value = !isPined.value; isPined.value = !isPined.value;

View File

@@ -1,7 +1,7 @@
<template> <template>
<div v-if="card" :id="`post-card-${card.postId}`" class="tpc-card"> <div v-if="card" :id="`post-card-${card.postId}`" class="tpc-card">
<div class="tpc-top"> <div class="tpc-top">
<div class="tpc-cover" @click="createPost(card)"> <div class="tpc-cover" @click="toPost()">
<TMiImg :src="card.cover" alt="cover" :ori="true" v-if="card.cover !== ''" /> <TMiImg :src="card.cover" alt="cover" :ori="true" v-if="card.cover !== ''" />
<img src="/source/UI/defaultCover.webp" alt="cover" v-else /> <img src="/source/UI/defaultCover.webp" alt="cover" v-else />
<div v-if="card.status" class="tpc-act"> <div v-if="card.status" class="tpc-act">
@@ -15,37 +15,60 @@
<div class="tpc-title" :title="card.title" @click="shareCard">{{ card.title }}</div> <div class="tpc-title" :title="card.title" @click="shareCard">{{ card.title }}</div>
</div> </div>
<div class="tpc-mid" v-if="card.user !== null"> <div class="tpc-mid" v-if="card.user !== null">
<TpAvatar :data="card.user" position="left" /> <TpAvatar
:data="card.user"
position="left"
:style="{ cursor: props.userClick ? 'pointer' : 'default' }"
@click="onUserClick()"
/>
</div> </div>
<div class="tpc-bottom" v-if="card.data !== null"> <div class="tpc-bottom">
<div class="tpc-tags"> <div class="tpc-tags">
<div v-for="(reason, idx) in card.reasons" :key="idx" class="tpc-reason" title="推荐理由">
<v-icon size="12">mdi-lightbulb-on</v-icon>
<span>{{ reason.text }}</span>
</div>
<div v-for="topic in card.topics" :key="topic.id" class="tpc-tag" @click="toTopic(topic)"> <div v-for="topic in card.topics" :key="topic.id" class="tpc-tag" @click="toTopic(topic)">
<v-icon size="10">mdi-tag</v-icon> <v-icon size="12">mdi-tag</v-icon>
<span>{{ topic.name }}</span> <span>{{ topic.name }}</span>
</div> </div>
</div> </div>
<div class="tpc-data"> <div class="tpc-data" v-if="card.data !== null">
<div class="tpc-info-item" :title="`浏览数:${card.data.view}`"> <div class="tpc-info-item" :title="`浏览数:${card.data.view}`">
<v-icon>mdi-eye</v-icon> <v-icon size="12">mdi-eye</v-icon>
<span>{{ card.data.view }}</span> <span>{{ card.data.view }}</span>
</div> </div>
<div class="tpc-info-item" :title="`收藏数:${card.data.mark}`"> <div class="tpc-info-item" :title="`收藏数:${card.data.mark}`">
<v-icon>mdi-star</v-icon> <v-icon size="12">mdi-star</v-icon>
<span>{{ card.data.mark }}</span> <span>{{ card.data.mark }}</span>
</div> </div>
<div class="tpc-info-item" :title="`回复数:${card.data.reply}`"> <div class="tpc-info-item" :title="`回复数:${card.data.reply}`">
<v-icon>mdi-comment</v-icon> <v-icon size="12">mdi-comment</v-icon>
<span>{{ card.data.reply }}</span> <span>{{ card.data.reply }}</span>
</div> </div>
<div class="tpc-info-item" :title="`点赞数:${card.data.like}`"> <div class="tpc-info-item" :title="`点赞数:${card.data.like}`">
<v-icon>mdi-thumb-up</v-icon> <v-icon size="12">mdi-thumb-up</v-icon>
<span>{{ card.data.like }}</span> <span>{{ card.data.like }}</span>
</div> </div>
<div class="tpc-info-item" :title="`转发数:${card.data.forward}`"> <div class="tpc-info-item" :title="`转发数:${card.data.forward}`">
<v-icon>mdi-share-variant</v-icon> <v-icon size="12">mdi-share-variant</v-icon>
<span>{{ card.data.forward }}</span> <span>{{ card.data.forward }}</span>
</div> </div>
</div> </div>
<div class="tpc-data">
<div class="tpc-info-item" :title="`创建时间: ${card.meta.create_time}`">
<v-icon size="12">mdi-calendar-clock</v-icon>
<span>{{ card.meta.create_time }}</span>
</div>
<div
v-if="card.meta.update_time"
class="tpc-info-item"
:title="`更新时间: ${card.meta.update_time}`"
>
<v-icon size="12">mdi-calendar-edit</v-icon>
<span>{{ card.meta.update_time }}</span>
</div>
</div>
</div> </div>
<div <div
class="tpc-forum" class="tpc-forum"
@@ -53,7 +76,7 @@
:title="`频道: ${card.forum.name}`" :title="`频道: ${card.forum.name}`"
@click="toForum(card.forum)" @click="toForum(card.forum)"
> >
<img :src="card.forum.icon" :alt="card.forum.name" /> <img v-if="card.forum.icon !== ''" :src="card.forum.icon" :alt="card.forum.name" />
<span>{{ card.forum.name }}</span> <span>{{ card.forum.name }}</span>
</div> </div>
<v-checkbox-btn <v-checkbox-btn
@@ -71,41 +94,47 @@ import showSnackbar from "@comp/func/snackbar.js";
import TpAvatar from "@comp/viewPost/tp-avatar.vue"; import TpAvatar from "@comp/viewPost/tp-avatar.vue";
import { emit } from "@tauri-apps/api/event"; import { emit } from "@tauri-apps/api/event";
import { computed, onMounted, shallowRef, watch } from "vue"; import { computed, onMounted, shallowRef, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { generateShareImg } from "@/utils/TGShare.js"; import { generateShareImg } from "@/utils/TGShare.js";
import { createPost } from "@/utils/TGWindow.js"; import { createPost } from "@/utils/TGWindow.js";
import { timestampToDate } from "@/utils/toolFunc.js";
type TPostCardProps = { modelValue: TGApp.Plugins.Mys.Post.FullData; selectMode?: boolean }; type TPostCardProps = {
type TPostCardEmits = (e: "onSelected", v: string) => void; modelValue: TGApp.BBS.Post.FullData;
type TPostStatus = RenderStatus & { stat: ActStat }; selectMode?: boolean;
userClick?: boolean;
};
type TPostCardEmits = {
(e: "onSelected", v: string): void;
(e: "onUserClick", v: TGApp.BBS.Post.User): void;
};
type RenderForum = { name: string; icon: string; id: number }; type RenderForum = { name: string; icon: string; id: number };
type RenderStatus = { stat: number; label: string; color: string }; type RenderStatus = { stat: number; label: string; color: string };
type RenderData = { mark: number; forward: number; like: number; reply: number; view: number }; type RenderData = { mark: number; forward: number; like: number; reply: number; view: number };
type RenderMeta = { create_time: string; update_time?: string };
export type RenderCard = { export type RenderCard = {
title: string; title: string;
cover: string; cover: string;
postId: number; postId: number;
subtitle: string; subtitle: string;
user: TGApp.Plugins.Mys.User.Post | null; user: TGApp.BBS.Post.User | null;
forum: RenderForum | null; forum: RenderForum | null;
data: RenderData | null; data: RenderData | null;
meta: RenderMeta;
status?: RenderStatus; status?: RenderStatus;
topics: Array<TGApp.BBS.Topic.Info>; topics: Array<TGApp.BBS.Post.Topic>;
reasons: Array<TGApp.BBS.Post.RecommendTags>;
}; };
enum ActStat { const stats: Readonly<Array<RenderStatus>> = [
UNKNOWN, { stat: 0, label: "未知", color: "var(--tgc-od-red)" },
STARTED, { stat: 1, label: "进行中", color: "var(--tgc-od-green)" },
FINISHED, { stat: 2, label: "已结束", color: "var(--tgc-od-white)" },
SELECTION, { stat: 3, label: "评选中", color: "var(--tgc-od-orange)" },
}
const stats: Readonly<Array<TPostStatus>> = [
{ stat: ActStat.UNKNOWN, label: "未知", color: "var(--tgc-od-red)" },
{ stat: ActStat.STARTED, label: "进行中", color: "var(--tgc-od-green)" },
{ stat: ActStat.FINISHED, label: "已结束", color: "var(--tgc-od-white)" },
{ stat: ActStat.SELECTION, label: "评选中", color: "var(--tgc-od-orange)" },
]; ];
const route = useRoute();
const router = useRouter();
const props = withDefaults(defineProps<TPostCardProps>(), { selectMode: false }); const props = withDefaults(defineProps<TPostCardProps>(), { selectMode: false });
const emits = defineEmits<TPostCardEmits>(); const emits = defineEmits<TPostCardEmits>();
const card = shallowRef<RenderCard>(); const card = shallowRef<RenderCard>();
@@ -122,25 +151,36 @@ watch(
async () => (card.value = getPostCard(props.modelValue)), async () => (card.value = getPostCard(props.modelValue)),
); );
function getActivityStatus(status: number): RenderStatus { async function toPost(): Promise<void> {
if (status satisfies ActStat) { if (!card.value) return;
const stat: ActStat = status; if (route.name !== "帖子详情") {
return stats[stat]; await createPost(card.value);
return;
} }
return stats[ActStat.UNKNOWN]; if (route.params.post_id.toString() === card.value.postId.toString()) {
showSnackbar.warn("当前已在该帖子详情页", 3000);
return;
}
await router.push({ name: "帖子详情", params: { post_id: card.value.postId.toString() } });
} }
function getPostCover(item: TGApp.Plugins.Mys.Post.FullData): string { function getActivityStatus(status: number): RenderStatus {
let idx = stats.findIndex((v) => v.stat === status);
if (idx === -1) idx = 0;
return stats[idx];
}
function getPostCover(item: TGApp.BBS.Post.FullData): string {
let cover; let cover;
if (item.cover) cover = item.cover.url; if (item.cover) cover = item.cover.url;
else if (item.post.cover) cover = item.post.cover; else if (item.post.cover) cover = item.post.cover;
else if (item.post.images.length > 0) cover = item.post.images[0]; else if (item.post.images.length > 0) cover = item.post.images[0];
if (cover === undefined) return ""; if (cover === undefined) return "";
if (cover.endsWith(".gif")) return cover; if (cover.endsWith(".gif")) return cover;
return `${cover}?x-oss-process=image/resize,m_fill,w_360,h_130,limit_0/format,png`; return `${cover}?x-oss-process=image/resize,m_fill,w_690,h_320,limit_0/format,png`;
} }
function getCommonCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard { function getCommonCard(item: TGApp.BBS.Post.FullData): RenderCard {
let forumData: RenderForum | null = null; let forumData: RenderForum | null = null;
let statData: RenderData | null = null; let statData: RenderData | null = null;
if (item.forum !== null) { if (item.forum !== null) {
@@ -155,6 +195,11 @@ function getCommonCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard {
view: item.stat.view_num, view: item.stat.view_num,
}; };
} }
const metaData: RenderMeta = {
create_time: timestampToDate(Number(item.post.created_at) * 1000),
update_time:
item.post.updated_at === 0 ? undefined : timestampToDate(Number(item.post.updated_at) * 1000),
};
return { return {
title: item.post.subject, title: item.post.subject,
cover: getPostCover(item), cover: getPostCover(item),
@@ -163,11 +208,13 @@ function getCommonCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard {
user: item.user, user: item.user,
forum: forumData, forum: forumData,
data: statData, data: statData,
meta: metaData,
topics: item.topics, topics: item.topics,
reasons: item.recommend_reason?.tags ?? [],
}; };
} }
function getPostCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard { function getPostCard(item: TGApp.BBS.Post.FullData): RenderCard {
const commonCard = getCommonCard(item); const commonCard = getCommonCard(item);
if ( if (
item.news_meta !== undefined && item.news_meta !== undefined &&
@@ -184,6 +231,7 @@ function getPostCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard {
} }
async function shareCard(): Promise<void> { async function shareCard(): Promise<void> {
console.log(props.modelValue);
if (!card.value) return; if (!card.value) return;
const shareDom = document.querySelector<HTMLDivElement>(`#post-card-${card.value.postId}`); const shareDom = document.querySelector<HTMLDivElement>(`#post-card-${card.value.postId}`);
if (shareDom === null) { if (shareDom === null) {
@@ -194,7 +242,7 @@ async function shareCard(): Promise<void> {
await generateShareImg(fileName, shareDom, 2.5); await generateShareImg(fileName, shareDom, 2.5);
} }
async function toTopic(topic: TGApp.BBS.Topic.Info): Promise<void> { async function toTopic(topic: TGApp.BBS.Post.Topic): Promise<void> {
const gid = props.modelValue.post.game_id; const gid = props.modelValue.post.game_id;
await emit("active_deep_link", `router?path=/posts/topic/${gid}/${topic.id}`); await emit("active_deep_link", `router?path=/posts/topic/${gid}/${topic.id}`);
} }
@@ -203,9 +251,17 @@ async function toForum(forum: RenderForum): Promise<void> {
const gid = props.modelValue.post.game_id; const gid = props.modelValue.post.game_id;
await emit("active_deep_link", `router?path=/posts/forum/${gid}/${forum.id}`); await emit("active_deep_link", `router?path=/posts/forum/${gid}/${forum.id}`);
} }
function onUserClick(): void {
if (!card.value || card.value.user === null) return;
emits("onUserClick", card.value.user);
}
</script> </script>
<style lang="css" scoped> <style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
.tpc-card { .tpc-card {
@include github-styles.github-card();
position: relative; position: relative;
display: flex; display: flex;
overflow: hidden; overflow: hidden;
@@ -214,11 +270,12 @@ async function toForum(forum: RenderForum): Promise<void> {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
border: 1px solid var(--common-shadow-1); border-radius: 4px;
border-radius: 5px; row-gap: 8px;
background: var(--box-bg-1); }
box-shadow: 2px 2px 5px var(--common-shadow-2);
row-gap: 10px; .dark .tpc-card {
@include github-styles.github-card("dark");
} }
.tpc-top { .tpc-top {
@@ -227,7 +284,7 @@ async function toForum(forum: RenderForum): Promise<void> {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
row-gap: 5px; row-gap: 4px;
} }
.tpc-cover { .tpc-cover {
@@ -237,13 +294,14 @@ async function toForum(forum: RenderForum): Promise<void> {
width: 100%; width: 100%;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
aspect-ratio: 36 / 13; aspect-ratio: 69 / 32;
background: var(--common-shadow-2); background: var(--common-shadow-2);
cursor: pointer; cursor: pointer;
} }
.tpc-cover img { .tpc-cover img {
width: 100%; width: 100%;
height: 100%;
object-fit: cover; object-fit: cover;
object-position: center; object-position: center;
transition: all 0.3s linear; transition: all 0.3s linear;
@@ -251,8 +309,11 @@ async function toForum(forum: RenderForum): Promise<void> {
.tpc-mid { .tpc-mid {
position: relative; position: relative;
width: 100%; width: fit-content;
max-width: 100%;
box-sizing: border-box;
padding: 0 10px; padding: 0 10px;
margin-right: auto;
} }
.tpc-bottom { .tpc-bottom {
@@ -260,45 +321,55 @@ async function toForum(forum: RenderForum): Promise<void> {
display: flex; display: flex;
width: 100%; width: 100%;
flex-direction: column; flex-direction: column;
padding: 5px 10px; padding: 4px 8px;
row-gap: 5px;
} }
.tpc-title { .tpc-title {
overflow: hidden; position: relative;
width: 100%; width: fit-content;
padding: 5px 10px; max-width: 100%;
padding: 4px 8px;
margin-right: auto;
cursor: pointer; cursor: pointer;
font-family: var(--font-title); font-family: var(--font-title);
font-size: 18px; font-size: 18px;
text-overflow: ellipsis;
white-space: nowrap;
} }
.tpc-tags { .tpc-tags {
display: flex; display: flex;
width: fit-content;
max-width: 100%;
flex-wrap: wrap; flex-wrap: wrap;
align-items: flex-start; align-items: flex-start;
justify-content: flex-start; justify-content: flex-start;
color: var(--box-text-5);
font-size: 12px; font-size: 12px;
gap: 5px; gap: 4px;
:hover {
color: var(--box-text-3);
}
} }
.tpc-tag { .tpc-tag {
@include github-styles.github-tag-dark-gen(#e06c63);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 3px; padding: 0 4px;
border: 1px solid var(--common-shadow-1); border-radius: 4px;
border-radius: 5px;
background: var(--box-bg-2);
cursor: pointer; cursor: pointer;
gap: 3px; gap: 4px;
&:hover {
@include github-styles.github-tag-dark-gen(#00aeec);
}
}
.tpc-reason {
@include github-styles.github-tag-dark-gen(#d19a66);
display: flex;
align-items: center;
justify-content: center;
padding: 0 4px;
border-radius: 4px;
gap: 4px;
} }
.tpc-forum { .tpc-forum {
@@ -308,15 +379,15 @@ async function toForum(forum: RenderForum): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
padding: 5px; padding: 4px;
background: var(--tgc-od-white); background: var(--tgc-od-white);
border-bottom-left-radius: 5px; border-bottom-left-radius: 4px;
border-top-right-radius: 5px; border-top-right-radius: 4px;
box-shadow: 0 0 10px var(--tgc-dark-1); box-shadow: 0 0 10px var(--tgc-dark-1);
color: var(--tgc-white-1); color: var(--tgc-white-1);
cursor: pointer; cursor: pointer;
opacity: 0.8; opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1); text-shadow: 0 0 4px var(--tgc-dark-1);
} }
.tpc-select { .tpc-select {
@@ -349,12 +420,13 @@ async function toForum(forum: RenderForum): Promise<void> {
.tpc-data { .tpc-data {
display: flex; display: flex;
width: 100%; width: fit-content;
max-width: 100%;
height: 20px; height: 20px;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: center;
padding: 5px; margin-left: auto;
column-gap: 10px; column-gap: 8px;
} }
.tpc-info-item { .tpc-info-item {
@@ -363,7 +435,8 @@ async function toForum(forum: RenderForum): Promise<void> {
justify-content: flex-start; justify-content: flex-start;
color: var(--box-text-7); color: var(--box-text-7);
font-size: 12px; font-size: 12px;
gap: 5px; gap: 2px;
white-space: nowrap;
opacity: 0.6; opacity: 0.6;
} }
@@ -386,7 +459,7 @@ async function toForum(forum: RenderForum): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
padding: 5px 30px 5px 5px; padding: 4px 30px 4px 4px;
background-color: v-bind(cardBg); background-color: v-bind(cardBg);
clip-path: polygon(0 0, calc(100% - 15px) 0, 100% 50%, calc(100% - 15px) 100%, 0 100%); clip-path: polygon(0 0, calc(100% - 15px) 0, 100% 50%, calc(100% - 15px) 100%, 0 100%);
color: var(--tgc-white-1); color: var(--tgc-white-1);
@@ -413,9 +486,9 @@ async function toForum(forum: RenderForum): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
margin: 5px; margin: 4px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
gap: 5px; gap: 4px;
opacity: 0.8; opacity: 0.8;
} }
@@ -426,16 +499,16 @@ async function toForum(forum: RenderForum): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 5px; padding: 0 4px;
-webkit-backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
background: var(--tgc-od-orange); background: var(--tgc-od-orange);
border-bottom-right-radius: 5px; border-bottom-right-radius: 4px;
border-top-left-radius: 5px; border-top-left-radius: 4px;
box-shadow: 2px 2px 5px var(--tgc-dark-1); box-shadow: 2px 2px 4px var(--tgc-dark-1);
color: var(--tgc-white-1); color: var(--tgc-white-1);
font-size: 12px; font-size: 12px;
opacity: 0.8; opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1); text-shadow: 0 0 4px var(--tgc-dark-1);
} }
</style> </style>

View File

@@ -79,7 +79,7 @@
<template #title>测试页面</template> <template #title>测试页面</template>
</v-list-item> </v-list-item>
<v-divider v-show="isDevEnv" /> <v-divider v-show="isDevEnv" />
<v-menu :open-on-click="true" location="end"> <v-menu :open-on-click="true" location="end" :offset="[8, 0]">
<template #activator="{ props }"> <template #activator="{ props }">
<v-list-item :title.attr="'图鉴'" v-bind="props"> <v-list-item :title.attr="'图鉴'" v-bind="props">
<template #title>图鉴</template> <template #title>图鉴</template>
@@ -88,7 +88,7 @@
</template> </template>
</v-list-item> </v-list-item>
</template> </template>
<v-list class="side-list-menu wiki" density="compact" :nav="true"> <v-list class="side-list-menu sub" density="compact" :nav="true">
<v-list-item class="side-item-menu" title="深渊数据库" :link="true" href="/wiki/abyss"> <v-list-item class="side-item-menu" title="深渊数据库" :link="true" href="/wiki/abyss">
<template #prepend> <template #prepend>
<img src="/source/UI/wikiAbyss.webp" alt="abyssIcon" class="side-icon-menu" /> <img src="/source/UI/wikiAbyss.webp" alt="abyssIcon" class="side-icon-menu" />
@@ -133,7 +133,7 @@
</template> </template>
</v-list-item> </v-list-item>
</template> </template>
<v-list class="side-list-menu" density="compact" :nav="true"> <v-list class="side-list-menu sub" density="compact" :nav="true">
<v-list-item class="side-item-menu" title="签到" @click="openClient('sign_in')"> <v-list-item class="side-item-menu" title="签到" @click="openClient('sign_in')">
<template #prepend> <template #prepend>
<img src="/source/UI/userGacha.webp" class="side-icon-menu" alt="sing_in" /> <img src="/source/UI/userGacha.webp" class="side-icon-menu" alt="sing_in" />
@@ -154,6 +154,11 @@
<img src="/source/UI/posts.webp" alt="collect" class="side-icon-menu" /> <img src="/source/UI/posts.webp" alt="collect" class="side-icon-menu" />
</template> </template>
</v-list-item> </v-list-item>
<v-list-item class="side-item-menu" title="关注" @click="showFollow = true">
<template #prepend>
<img src="/platforms/mhy/mys.webp" alt="follow" class="side-icon-menu" />
</template>
</v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
<v-list-item <v-list-item
@@ -172,13 +177,15 @@
</div> </div>
</v-list> </v-list>
</v-navigation-drawer> </v-navigation-drawer>
<vp-overlay-follow v-model="showFollow" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import VpOverlayFollow from "@comp/viewPost/vp-overlay-follow.vue";
import { event, webviewWindow } from "@tauri-apps/api"; import { event, webviewWindow } from "@tauri-apps/api";
import type { Event, UnlistenFn } from "@tauri-apps/api/event"; import type { Event, UnlistenFn } from "@tauri-apps/api/event";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { computed, onMounted, onUnmounted } from "vue"; import { computed, onMounted, onUnmounted, ref } from "vue";
import { useAppStore } from "@/store/modules/app.js"; import { useAppStore } from "@/store/modules/app.js";
import { useUserStore } from "@/store/modules/user.js"; import { useUserStore } from "@/store/modules/user.js";
@@ -189,6 +196,7 @@ const { briefInfo } = storeToRefs(useUserStore());
let themeListener: UnlistenFn | null = null; let themeListener: UnlistenFn | null = null;
// @ts-expect-error The import.meta meta-property is not allowed in files which will build into CommonJS output. // @ts-expect-error The import.meta meta-property is not allowed in files which will build into CommonJS output.
const isDevEnv = import.meta.env.MODE === "development"; const isDevEnv = import.meta.env.MODE === "development";
const showFollow = ref<boolean>();
const rail = computed<boolean>({ const rail = computed<boolean>({
get: () => sidebar.value.collapse, get: () => sidebar.value.collapse,
set: (v) => (sidebar.value.collapse = v), set: (v) => (sidebar.value.collapse = v),
@@ -260,8 +268,8 @@ onUnmounted(() => {
font-family: var(--font-title); font-family: var(--font-title);
} }
.side-list-menu.wiki { .side-list-menu.sub {
margin-left: 10px; box-shadow: -2px 0 4px var(--common-shadow-2) !important;
} }
.side-item-menu { .side-item-menu {

View File

@@ -8,26 +8,20 @@
.tsl-box { .tsl-box {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 5px; padding: 4px;
border-radius: 5px; border-radius: 4px;
margin: 5px 0; background: var(--box-bg-1);
background: var(--box-bg-3); color: var(--box-text-1);
color: var(--box-text-4);
font-family: var(--font-title); font-family: var(--font-title);
gap: 5px; gap: 4px;
} }
.tsl-box :first-child { .tsl-box :first-child {
width: 20px; width: 24px;
height: 20px; height: 24px;
box-sizing: border-box;
padding: 2px; padding: 2px;
border-radius: 5px; filter: invert(0.75);
filter: invert(22%) sepia(7%) saturate(1241%) hue-rotate(182deg) brightness(95%) contrast(99%);
}
.dark .tsl-box {
background: #2c313a;
color: #faf7e8;
} }
.dark .tsl-box :first-child { .dark .tsl-box :first-child {

View File

@@ -22,11 +22,10 @@
<span title="开放时间">{{ timestampToDate(Number(item.to_get_time) * 1000) }}</span> <span title="开放时间">{{ timestampToDate(Number(item.to_get_time) * 1000) }}</span>
</template> </template>
<template #prepend> <template #prepend>
<img <div class="tolc-icon">
:src="item.img === '' ? '/source/UI/empty.webp' : item.img" <img v-if="item.img === ''" src="/source/UI/empty.webp" alt="empty" />
alt="icon" <TMiImg :src="item.img" :ori="true" v-else alt="award" />
class="tolc-icon" </div>
/>
</template> </template>
<template #append> <template #append>
<v-btn <v-btn
@@ -46,6 +45,7 @@
<script setup lang="ts"> <script setup lang="ts">
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import TMiImg from "./t-mi-img.vue";
import TOverlay from "./t-overlay.vue"; import TOverlay from "./t-overlay.vue";
import { generateShareImg } from "@/utils/TGShare.js"; import { generateShareImg } from "@/utils/TGShare.js";
@@ -105,9 +105,19 @@ async function shareImg(): Promise<void> {
} }
.tolc-icon { .tolc-icon {
position: relative;
display: flex;
width: 40px; width: 40px;
height: 40px;
align-items: center;
justify-content: center;
margin-right: 10px; margin-right: 10px;
aspect-ratio: 1;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
} }
.tolc-btn { .tolc-btn {

View File

@@ -14,7 +14,7 @@
<span>{{ parseNameCard(props.data.desc) }}</span> <span>{{ parseNameCard(props.data.desc) }}</span>
<span>获取途径{{ props.data.source }}</span> <span>获取途径{{ props.data.source }}</span>
</div> </div>
<div class="ton-type">{{ getType }}</div> <div class="ton-type">{{ props.data.type }}</div>
<v-btn <v-btn
class="ton-share" class="ton-share"
@click="shareNameCard" @click="shareNameCard"
@@ -32,41 +32,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import { computed, ref } from "vue"; import { ref } from "vue";
import TOverlay from "./t-overlay.vue"; import TOverlay from "./t-overlay.vue";
import { generateShareImg } from "@/utils/TGShare.js"; import { generateShareImg } from "@/utils/TGShare.js";
enum ToNameCardTypeEnum {
other,
achievement,
role,
record,
activity,
unknown,
}
type ToNameCardTypeMap = { [key in ToNameCardTypeEnum]: string };
type ToNameCardProps = { data?: TGApp.App.NameCard.Item }; type ToNameCardProps = { data?: TGApp.App.NameCard.Item };
const props = defineProps<ToNameCardProps>(); const props = defineProps<ToNameCardProps>();
const visible = defineModel<boolean>(); const visible = defineModel<boolean>();
const typeMap: ToNameCardTypeMap = {
0: "其他",
1: "成就",
2: "角色",
3: "纪行",
4: "活动",
5: "未知",
};
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const getType = computed<string>(() => {
if (!props.data) return typeMap[ToNameCardTypeEnum.unknown];
if (!(props.data.type satisfies ToNameCardTypeEnum)) return typeMap[5];
const type: ToNameCardTypeEnum = props.data.type;
return typeMap[type];
});
function parseNameCard(desc: string): string { function parseNameCard(desc: string): string {
let array = []; let array = [];
@@ -100,7 +76,7 @@ function parseNameCard(desc: string): string {
function parseDesc(desc: string, inQuote: boolean = false): string[] { function parseDesc(desc: string, inQuote: boolean = false): string[] {
let res = desc.replace(/。/g, "。\n"); let res = desc.replace(/。/g, "。\n");
res = res.replace(//g, "\n"); res = res.replace(//g, "\n");
if (props?.data?.index !== 187) { if (props?.data?.id !== 210187) {
res = res.replace(//g, "\n"); res = res.replace(//g, "\n");
res = res.replace(//g, "\n"); res = res.replace(//g, "\n");
} else { } else {
@@ -129,7 +105,7 @@ async function shareNameCard(): Promise<void> {
showSnackbar.error("未找到名片内容"); showSnackbar.error("未找到名片内容");
return; return;
} }
const fileName = `${getType.value}名片】-${props.data?.name}`; const fileName = `${props.data?.type}名片】-${props.data?.name}`;
loading.value = true; loading.value = true;
await generateShareImg(fileName, nameCardBox); await generateShareImg(fileName, nameCardBox);
loading.value = false; loading.value = false;
@@ -149,23 +125,23 @@ async function shareNameCard(): Promise<void> {
overflow: hidden; overflow: hidden;
width: 800px; width: 800px;
height: 400px; height: 400px;
border-radius: 10px; border-radius: 4px;
} }
.ton-bg { .ton-bg {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 10px; border-radius: 4px;
} }
.ton-type { .ton-type {
position: absolute; position: absolute;
top: 10px; top: 10px;
left: 10px; left: 10px;
padding: 0 5px; padding: 0 4px;
border: 1px solid var(--tgc-white-1); border: 1px solid var(--tgc-white-1);
border-radius: 5px; border-radius: 4px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
} }
@@ -180,8 +156,8 @@ async function shareNameCard(): Promise<void> {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
justify-content: flex-end; justify-content: flex-end;
padding: 10px; padding: 8px;
border-radius: 10px; border-radius: 4px;
backdrop-filter: blur(5px); backdrop-filter: blur(5px);
background: rgb(0 0 0 / 25%); background: rgb(0 0 0 / 25%);
color: var(--tgc-white-1); color: var(--tgc-white-1);
@@ -213,7 +189,7 @@ async function shareNameCard(): Promise<void> {
right: 10px; right: 10px;
bottom: 10px; bottom: 10px;
border: 1px solid var(--tgc-white-1); border: 1px solid var(--tgc-white-1);
border-radius: 5px; border-radius: 4px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
} }
</style> </style>

View File

@@ -7,7 +7,7 @@
<template #prepend> <template #prepend>
<v-img <v-img
width="80px" width="80px"
style="margin-right: 10px" style="margin-right: 8px"
:src="`/WIKI/nameCard/icon/${props.data.name}.webp`" :src="`/WIKI/nameCard/icon/${props.data.name}.webp`"
/> />
</template> </template>
@@ -28,13 +28,17 @@ const bgImage = computed<string>(() => {
return `url("/WIKI/nameCard/bg/${props.data.name}.webp")`; return `url("/WIKI/nameCard/bg/${props.data.name}.webp")`;
}); });
</script> </script>
<style lang="css" scoped> <style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
.top-nc-box { .top-nc-box {
@include github-styles.github-card-shadow();
width: 100%; width: 100%;
height: 80px; height: 80px;
border: 1px solid var(--common-shadow-2); border: 1px solid var(--common-shadow-1);
border-radius: 10px 50px 50px 10px; border-radius: 4px 50px 50px 4px;
margin-bottom: 10px; margin-bottom: 8px;
background-color: var(--box-bg-1); background-color: var(--box-bg-1);
background-image: v-bind(bgImage); background-image: v-bind(bgImage);
background-position: right; background-position: right;
@@ -43,7 +47,11 @@ const bgImage = computed<string>(() => {
font-family: var(--font-title); font-family: var(--font-title);
} }
.dark .top-nc-box {
@include github-styles.github-card-shadow("dark");
}
.desc { .desc {
text-shadow: 1px 1px 1px #222; text-shadow: 0 0 2px var(--common-shadow-t-8);
} }
</style> </style>

View File

@@ -252,7 +252,7 @@ defineExpose({ displayInputBox, displayCheckBox });
font-size: 20px; font-size: 20px;
text-align: center; text-align: center;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: normal; white-space: pre-wrap;
word-break: break-all; word-break: break-all;
} }

View File

@@ -15,6 +15,7 @@
</transition> </transition>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import "https://static.geetest.com/static/js/gt.0.4.9.js";
import { ref, useTemplateRef, watch } from "vue"; import { ref, useTemplateRef, watch } from "vue";
const show = ref<boolean>(false); const show = ref<boolean>(false);

View File

@@ -1,13 +1,13 @@
<template> <template>
<div :id="`anno_card_${props.modelValue.id}`" class="anno-card"> <div :id="`anno_card_${props.modelValue.id}`" class="anno-card">
<div class="anno-cover" :title="props.modelValue.title" @click="createAnno"> <div :title="props.modelValue.title" class="anno-cover" @click="createAnno">
<TMiImg <TMiImg
v-if="props.modelValue.banner !== ''"
:ori="true"
:src="props.modelValue.banner" :src="props.modelValue.banner"
alt="cover" alt="cover"
:ori="true"
v-if="props.modelValue.banner !== ''"
/> />
<img alt="cover" src="/source/UI/defaultCover.webp" v-else /> <img v-else alt="cover" src="/source/UI/defaultCover.webp" />
<div class="anno-info"> <div class="anno-info">
<div class="anno-time"> <div class="anno-time">
<v-icon>mdi-clock-time-four-outline</v-icon> <v-icon>mdi-clock-time-four-outline</v-icon>
@@ -15,10 +15,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="anno-title" :title="props.modelValue.title" @click="shareAnno"> <div :title="props.modelValue.title" class="anno-title" @click="shareAnno">
{{ parseTitle(props.modelValue.subtitle) }} {{ parseTitle(props.modelValue.subtitle) }}
</div> </div>
<div class="anno-label" :title="`标签:${props.modelValue.tagLabel}`"> <div :title="`标签:${props.modelValue.tagLabel}`" class="anno-label">
<img :src="props.modelValue.tagIcon" alt="tag" /> <img :src="props.modelValue.tagIcon" alt="tag" />
<span>{{ props.modelValue.tagLabel }}</span> <span>{{ props.modelValue.tagLabel }}</span>
</div> </div>
@@ -29,11 +29,12 @@
import TMiImg from "@comp/app/t-mi-img.vue"; import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import type { AnnoCard } from "@/pages/common/PageAnno.vue";
import TGLogger from "@/utils/TGLogger.js"; import TGLogger from "@/utils/TGLogger.js";
import { generateShareImg } from "@/utils/TGShare.js"; import { generateShareImg } from "@/utils/TGShare.js";
import { createTGWindow } from "@/utils/TGWindow.js"; import { createTGWindow } from "@/utils/TGWindow.js";
type TAnnoCardProps = { region: string; modelValue: TGApp.App.Announcement.ListCard; lang: string }; type TAnnoCardProps = { region: string; modelValue: AnnoCard; lang: string };
const props = defineProps<TAnnoCardProps>(); const props = defineProps<TAnnoCardProps>();
function parseTitle(title: string): string { function parseTitle(title: string): string {
@@ -58,14 +59,20 @@ async function shareAnno(): Promise<void> {
await generateShareImg(fileName, element, 2.5); await generateShareImg(fileName, element, 2.5);
} }
</script> </script>
<style lang="css" scoped> <style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
.anno-card { .anno-card {
@include github-styles.github-card();
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
border: 1px solid var(--common-shadow-1); border-radius: 6px;
border-radius: 5px; box-sizing: border-box;
box-shadow: 2px 2px 5px var(--common-shadow-2); }
.dark .anno-card {
@include github-styles.github-card("dark");
} }
.anno-cover { .anno-cover {
@@ -89,13 +96,15 @@ async function shareAnno(): Promise<void> {
.anno-title { .anno-title {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%; max-width: 100%;
padding: 5px; width: fit-content;
padding: 4px;
cursor: pointer; cursor: pointer;
font-size: 18px; font-size: 18px;
text-align: right; margin-left: auto;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
box-sizing: border-box;
} }
.anno-info { .anno-info {
@@ -116,9 +125,9 @@ async function shareAnno(): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
margin: 5px; margin: 4px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
gap: 5px; gap: 4px;
} }
.anno-label { .anno-label {
@@ -128,19 +137,19 @@ async function shareAnno(): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
padding: 5px; padding: 4px;
background-color: var(--tgc-od-white); background-color: var(--tgc-od-white);
border-bottom-left-radius: 5px; border-bottom-left-radius: 6px;
box-shadow: 0 0 10px var(--tgc-dark-1); box-shadow: 0 0 10px var(--tgc-dark-1);
color: var(--tgc-white-1); color: var(--tgc-white-1);
opacity: 0.8; opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1); text-shadow: 0 0 4px var(--tgc-dark-1);
} }
.anno-label img { .anno-label img {
width: 20px; width: 20px;
height: 20px; height: 20px;
margin-right: 5px; margin-right: 4px;
} }
.anno-cover img:hover { .anno-cover img:hover {
@@ -155,14 +164,14 @@ async function shareAnno(): Promise<void> {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 5px; padding: 0 4px;
background: var(--tgc-od-orange); background: var(--tgc-od-orange);
border-bottom-right-radius: 5px; border-bottom-right-radius: 6px;
border-top-left-radius: 5px; border-top-left-radius: 6px;
box-shadow: 0 0 10px var(--tgc-dark-1); box-shadow: 0 0 8px var(--tgc-dark-1);
color: var(--tgc-white-1); color: var(--tgc-white-1);
font-size: 12px; font-size: 12px;
opacity: 0.8; opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1); text-shadow: 0 0 4px var(--tgc-dark-1);
} }
</style> </style>

View File

@@ -21,7 +21,7 @@ import parseAnnoContent from "@/web/utils/annoParser.js";
type TaParserProps = { data: TGApp.BBS.Announcement.ContentItem }; type TaParserProps = { data: TGApp.BBS.Announcement.ContentItem };
const props = defineProps<TaParserProps>(); const props = defineProps<TaParserProps>();
function getTaName(ta: TGApp.Plugins.Mys.SctPost.Base): Component { function getTaName(ta: TGApp.BBS.SctPost.Base): Component {
if (ta.children) return TpTexts; if (ta.children) return TpTexts;
if (typeof ta.insert === "string") return TpText; if (typeof ta.insert === "string") return TpText;
if ("image" in ta.insert) return TpImage; if ("image" in ta.insert) return TpImage;

View File

@@ -133,8 +133,8 @@ async function freshData(): Promise<void> {
} }
.tocp-btn { .tocp-btn {
background: var(--btn-bg-1); background: var(--tgc-btn-1);
color: var(--btn-text-1); color: var(--btn-text);
font-family: var(--font-title); font-family: var(--font-title);
} }

View File

@@ -23,6 +23,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { app } from "@tauri-apps/api"; import { app } from "@tauri-apps/api";
import { openUrl } from "@tauri-apps/plugin-opener";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
@@ -33,24 +34,24 @@ const versionApp = ref<string>();
onMounted(async () => (versionApp.value = await app.getVersion())); onMounted(async () => (versionApp.value = await app.getVersion()));
function toRelease(): void { async function toRelease(): Promise<void> {
window.open("https://github.com/BTMuli/TeyvatGuide/releases/latest"); await openUrl("https://github.com/BTMuli/TeyvatGuide/releases/latest");
} }
function toGroup(): void { async function toGroup(): Promise<void> {
window.open("https://h5.qun.qq.com/s/3cgX0hJ4GA"); await openUrl("https://h5.qun.qq.com/s/3cgX0hJ4GA");
} }
function toGithub(): void { async function toGithub(): Promise<void> {
window.open("https://github.com/BTMuli/TeyvatGuide"); await openUrl("https://github.com/BTMuli/TeyvatGuide");
} }
function toStore(): void { async function toStore(): Promise<void> {
window.open("https://www.microsoft.com/store/productId/9NLBNNNBNSJN"); await openUrl("https://www.microsoft.com/store/productId/9NLBNNNBNSJN");
} }
function toSite(): void { async function toSite(): Promise<void> {
window.open("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html"); await openUrl("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html");
} }
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>

View File

@@ -11,7 +11,7 @@
<template #append> <template #append>
<div class="config-opers"> <div class="config-opers">
<v-icon @click="confirmCUD()" title="修改用户数据目录"> mdi-pencil</v-icon> <v-icon @click="confirmCUD()" title="修改用户数据目录"> mdi-pencil</v-icon>
<v-icon @click="openPath('user')" title="打开用户数据目录"> mdi-folder-open</v-icon> <v-icon @click="openDataPath('user')" title="打开用户数据目录"> mdi-folder-open</v-icon>
<v-icon @click="copyPath('user')" title="复制用户数据目录路径"> mdi-content-copy</v-icon> <v-icon @click="copyPath('user')" title="复制用户数据目录路径"> mdi-content-copy</v-icon>
</div> </div>
</template> </template>
@@ -24,7 +24,7 @@
</template> </template>
<template #append> <template #append>
<div class="config-opers"> <div class="config-opers">
<v-icon @click="openPath('db')" title="打开数据库目录"> mdi-folder-open</v-icon> <v-icon @click="openDataPath('db')" title="打开数据库目录"> mdi-folder-open</v-icon>
<v-icon @click="copyPath('db')" title="复制数据库目录路径"> mdi-content-copy</v-icon> <v-icon @click="copyPath('db')" title="复制数据库目录路径"> mdi-content-copy</v-icon>
</div> </div>
</template> </template>
@@ -38,7 +38,7 @@
<template #append> <template #append>
<div class="config-opers"> <div class="config-opers">
<v-icon @click="confirmCGD()" title="修改游戏安装目录"> mdi-pencil</v-icon> <v-icon @click="confirmCGD()" title="修改游戏安装目录"> mdi-pencil</v-icon>
<v-icon @click="openPath('game')" title="打开游戏安装目录"> mdi-folder-open</v-icon> <v-icon @click="openDataPath('game')" title="打开游戏安装目录"> mdi-folder-open</v-icon>
<v-icon @click="copyPath('game')" title="复制游戏安装目录"> mdi-content-copy</v-icon> <v-icon @click="copyPath('game')" title="复制游戏安装目录"> mdi-content-copy</v-icon>
</div> </div>
</template> </template>
@@ -52,7 +52,7 @@
<template #append> <template #append>
<div class="config-opers"> <div class="config-opers">
<v-icon @click="confirmCLD()" title="清理日志文件"> mdi-delete</v-icon> <v-icon @click="confirmCLD()" title="清理日志文件"> mdi-delete</v-icon>
<v-icon @click="openPath('log')" title="打开日志目录"> mdi-folder-open</v-icon> <v-icon @click="openDataPath('log')" title="打开日志目录"> mdi-folder-open</v-icon>
<v-icon @click="copyPath('log')" title="复制日志目录路径"> mdi-content-copy</v-icon> <v-icon @click="copyPath('log')" title="复制日志目录路径"> mdi-content-copy</v-icon>
</div> </div>
</template> </template>
@@ -68,13 +68,13 @@ import { path } from "@tauri-apps/api";
import { sep } from "@tauri-apps/api/path"; import { sep } from "@tauri-apps/api/path";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
import { exists, readDir, remove } from "@tauri-apps/plugin-fs"; import { exists, readDir, remove } from "@tauri-apps/plugin-fs";
import { openPath } from "@tauri-apps/plugin-opener";
import { platform } from "@tauri-apps/plugin-os"; import { platform } from "@tauri-apps/plugin-os";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { onMounted } from "vue"; import { onMounted } from "vue";
import { useAppStore } from "@/store/modules/app.js"; import { useAppStore } from "@/store/modules/app.js";
import { backUpUserData } from "@/utils/dataBS.js"; import { backUpUserData } from "@/utils/dataBS.js";
import TGShell from "@/utils/TGShell.js";
const { dbPath, logDir, userDir, gameDir } = storeToRefs(useAppStore()); const { dbPath, logDir, userDir, gameDir } = storeToRefs(useAppStore());
@@ -225,7 +225,7 @@ function copyPath(type: "db" | "user" | "log" | "game"): void {
showSnackbar.success(`${targetName}路径已复制!`); showSnackbar.success(`${targetName}路径已复制!`);
} }
async function openPath(type: "db" | "user" | "log" | "game"): Promise<void> { async function openDataPath(type: "db" | "user" | "log" | "game"): Promise<void> {
let targetPath: string; let targetPath: string;
switch (type) { switch (type) {
case "db": case "db":
@@ -245,7 +245,7 @@ async function openPath(type: "db" | "user" | "log" | "game"): Promise<void> {
targetPath = gameDir.value; targetPath = gameDir.value;
break; break;
} }
await TGShell.openPath(targetPath); await openPath(targetPath);
} }
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>

View File

@@ -2,7 +2,7 @@
<v-list class="config-list"> <v-list class="config-list">
<v-list-subheader :inset="true" class="config-header" title="相关信息" /> <v-list-subheader :inset="true" class="config-header" title="相关信息" />
<v-divider :inset="true" class="border-opacity-75" /> <v-divider :inset="true" class="border-opacity-75" />
<v-list-item title="Tauri 版本" @click="toOuter('https://v2.tauri.app/')"> <v-list-item title="Tauri 版本" @click="openUrl('https://v2.tauri.app/')">
<template #prepend> <template #prepend>
<v-img class="config-icon" src="/platforms/tauri.webp" alt="Tauri" /> <v-img class="config-icon" src="/platforms/tauri.webp" alt="Tauri" />
</template> </template>
@@ -77,6 +77,7 @@ import showSnackbar from "@comp/func/snackbar.js";
import TGSqlite from "@Sqlite/index.js"; import TGSqlite from "@Sqlite/index.js";
import TSUserAchi from "@Sqlite/modules/userAchi.js"; import TSUserAchi from "@Sqlite/modules/userAchi.js";
import { app } from "@tauri-apps/api"; import { app } from "@tauri-apps/api";
import { openUrl } from "@tauri-apps/plugin-opener";
import { platform, version } from "@tauri-apps/plugin-os"; import { platform, version } from "@tauri-apps/plugin-os";
import { onMounted, ref, shallowRef } from "vue"; import { onMounted, ref, shallowRef } from "vue";
@@ -123,10 +124,6 @@ onMounted(async () => {
await TGLogger.Error(`加载数据库错误: ${e}`); await TGLogger.Error(`加载数据库错误: ${e}`);
} }
}); });
function toOuter(url: string): void {
window.open(url);
}
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.config-header { .config-header {

View File

@@ -107,9 +107,12 @@
<img src="/platforms/mhy/launcher.webp" alt="launcher" class="menu-icon" /> <img src="/platforms/mhy/launcher.webp" alt="launcher" class="menu-icon" />
</template> </template>
</v-list-item> </v-list-item>
<v-list-item @click="tryCodeLogin(false)" append-icon="mdi-qrcode-scan" v-if="false"> <v-list-item @click="tryCodeLogin(false)">
<v-list-item-title>扫码登录(游戏)</v-list-item-title> <v-list-item-title>扫码登录(游戏)</v-list-item-title>
<v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle> <v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle>
<template #append>
<img src="/platforms/mhy/mys.webp" alt="launcher" class="menu-icon" />
</template>
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
@@ -122,7 +125,6 @@ import showGeetest from "@comp/func/geetest.js";
import showLoading from "@comp/func/loading.js"; import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import ToGameLogin from "@comp/pageConfig/tco-gameLogin.vue"; import ToGameLogin from "@comp/pageConfig/tco-gameLogin.vue";
import Mys from "@Mys/index.js";
import TSUserAccount from "@Sqlite/modules/userAccount.js"; import TSUserAccount from "@Sqlite/modules/userAccount.js";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { computed, ref, shallowRef } from "vue"; import { computed, ref, shallowRef } from "vue";
@@ -132,7 +134,8 @@ import { useUserStore } from "@/store/modules/user.js";
import TGLogger from "@/utils/TGLogger.js"; import TGLogger from "@/utils/TGLogger.js";
import BBSApi from "@/web/request/bbsReq.js"; import BBSApi from "@/web/request/bbsReq.js";
import PassportApi from "@/web/request/passportReq.js"; import PassportApi from "@/web/request/passportReq.js";
import TakumiApi from "@/web/request/takumiReq.js"; import passportReq from "@/web/request/passportReq.js";
import takumiReq from "@/web/request/takumiReq.js";
const { isLogin } = storeToRefs(useAppStore()); const { isLogin } = storeToRefs(useAppStore());
const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore()); const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore());
@@ -201,7 +204,7 @@ async function tryGetTokens(ck: TGApp.App.Account.Cookie): Promise<void> {
cookie.value = ck; cookie.value = ck;
isLogin.value = true; isLogin.value = true;
await showLoading.update("正在获取游戏账号"); await showLoading.update("正在获取游戏账号");
const gameRes = await TakumiApi.bind.gameRoles(cookie.value); const gameRes = await takumiReq.bind.gameRoles(cookie.value);
if (!Array.isArray(gameRes)) { if (!Array.isArray(gameRes)) {
await showLoading.end(); await showLoading.end();
showSnackbar.error(`[${gameRes.retcode}]${gameRes.message}`); showSnackbar.error(`[${gameRes.retcode}]${gameRes.message}`);
@@ -328,7 +331,7 @@ async function refreshUser(uid: string) {
} }
await TSUserAccount.account.saveAccount(account); await TSUserAccount.account.saveAccount(account);
await showLoading.update("正在获取账号信息"); await showLoading.update("正在获取账号信息");
const accountRes = await TakumiApi.bind.gameRoles(ck); const accountRes = await takumiReq.bind.gameRoles(ck);
if (Array.isArray(accountRes)) { if (Array.isArray(accountRes)) {
await showLoading.update("获取账号信息成功"); await showLoading.update("获取账号信息成功");
await TGLogger.Info("[tc-userBadge][refreshUserInfo] 获取账号信息成功"); await TGLogger.Info("[tc-userBadge][refreshUserInfo] 获取账号信息成功");
@@ -398,13 +401,13 @@ async function confirmCopyCookie(): Promise<void> {
} }
async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | false> { async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | false> {
const captchaResp = await Mys.User.getCaptcha(phone, aigis); const captchaResp = await passportReq.captcha.create(phone, aigis);
if ("retcode" in captchaResp) { if ("retcode" in captchaResp) {
if (!captchaResp.data || captchaResp.data === "") { if (!captchaResp.data || captchaResp.data === "") {
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`); showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
return false; return false;
} }
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data); const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
const resp = await showGeetest(JSON.parse(aigisResp.data)); const resp = await showGeetest(JSON.parse(aigisResp.data));
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`; const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
return await tryGetCaptcha(phone, aigisStr); return await tryGetCaptcha(phone, aigisStr);
@@ -417,14 +420,14 @@ async function tryLoginByCaptcha(
captcha: string, captcha: string,
actionType: string, actionType: string,
aigis?: string, aigis?: string,
): Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | false> { ): Promise<TGApp.BBS.CaptchaLogin.LoginRes | false> {
const loginResp = await Mys.User.login(phone, captcha, actionType, aigis); const loginResp = await passportReq.captcha.login(phone, captcha, actionType, aigis);
if ("retcode" in loginResp) { if ("retcode" in loginResp) {
if (!loginResp.data || loginResp.data === "") { if (!loginResp.data || loginResp.data === "") {
showSnackbar.error(`[${loginResp.retcode}] ${loginResp.message}`); showSnackbar.error(`[${loginResp.retcode}] ${loginResp.message}`);
return false; return false;
} }
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data); const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);
const resp = await showGeetest(JSON.parse(aigisResp.data)); const resp = await showGeetest(JSON.parse(aigisResp.data));
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`; const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
return await tryLoginByCaptcha(phone, captcha, actionType, aigisStr); return await tryLoginByCaptcha(phone, captcha, actionType, aigisStr);
@@ -441,7 +444,8 @@ async function showAccounts(): Promise<void> {
showSnackbar.warn("未登录!"); showSnackbar.warn("未登录!");
return; return;
} }
gameAccounts.value = await TSUserAccount.game.getAccount(uid.value); const accountsGet = await TSUserAccount.game.getAccount(uid.value);
gameAccounts.value = accountsGet.filter((a) => a.gameBiz === "hk4e_cn");
if (gameAccounts.value.length === 0) { if (gameAccounts.value.length === 0) {
showSnackbar.warn("未找到账户的游戏数据,请尝试刷新!"); showSnackbar.warn("未找到账户的游戏数据,请尝试刷新!");
return; return;
@@ -522,7 +526,7 @@ async function addByCookie(): Promise<void> {
updated: "", updated: "",
}); });
await showLoading.update("正在获取游戏账号"); await showLoading.update("正在获取游戏账号");
const gameRes = await TakumiApi.bind.gameRoles(ck); const gameRes = await takumiReq.bind.gameRoles(ck);
if (!Array.isArray(gameRes)) { if (!Array.isArray(gameRes)) {
await showLoading.end(); await showLoading.end();
showSnackbar.error(`[${gameRes.retcode}]${gameRes.message}`); showSnackbar.error(`[${gameRes.retcode}]${gameRes.message}`);
@@ -570,5 +574,6 @@ async function clearUser(user: TGApp.App.Account.User): Promise<void> {
.menu-icon { .menu-icon {
width: 24px; width: 24px;
height: 24px; height: 24px;
border-radius: 4px;
} }
</style> </style>

View File

@@ -3,7 +3,20 @@
<div class="tog-box"> <div class="tog-box">
<div class="tog-top"> <div class="tog-top">
<div class="tog-title">请使用米游社进行扫码操作</div> <div class="tog-title">请使用米游社进行扫码操作</div>
<div class="tog-select" v-if="!isLauncherCode">
<div
class="tog-select-item"
v-for="item in selects"
:key="item.value"
:class="{ active: codeGid === item.value }"
@click="codeGid = item.value"
:title="item.title"
>
<img :src="item.icon" alt="icon" />
</div>
</div>
</div> </div>
<div class="tog-divider" />
<div class="tog-mid"> <div class="tog-mid">
<qrcode-vue <qrcode-vue
v-if="codeUrl" v-if="codeUrl"
@@ -26,34 +39,72 @@ import TOverlay from "@comp/app/t-overlay.vue";
import showLoading from "@comp/func/loading.js"; import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import QrcodeVue from "qrcode.vue"; import QrcodeVue from "qrcode.vue";
import { computed, onUnmounted, ref, watch } from "vue"; import { onUnmounted, ref, watch } from "vue";
import { generateShareImg } from "@/utils/TGShare.js"; import { generateShareImg } from "@/utils/TGShare.js";
import hk4eReq from "@/web/request/hk4eReq.js"; import hk4eReq from "@/web/request/hk4eReq.js";
import PassportReq from "@/web/request/passportReq.js"; import PassportReq from "@/web/request/passportReq.js";
import passportReq from "@/web/request/passportReq.js";
import takumiReq from "@/web/request/takumiReq.js"; import takumiReq from "@/web/request/takumiReq.js";
type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void; type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void;
type ToGameLoginSelect = { title: string; value: number; icon: string };
const selects: Array<ToGameLoginSelect> = [
{
title: "未定事件簿",
value: 2,
icon: "/platforms/mhy/wd.webp",
},
{
title: "崩坏学园2",
value: 7,
icon: "/platforms/mhy/bh2.webp",
},
// {
// title: "星布谷地",
// value: 13,
// icon: "/platforms/mhy/xbgd.webp",
// },
];
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
let cycleTimer: NodeJS.Timeout | null = null; let cycleTimer: NodeJS.Timeout | null = null;
const model = defineModel<boolean>({ default: false }); const model = defineModel<boolean>({ default: false });
const isLauncherCode = defineModel<boolean>("launcher", { default: false }); const isLauncherCode = defineModel<boolean>("launcher", { default: true });
const emits = defineEmits<ToGameLoginEmits>(); const emits = defineEmits<ToGameLoginEmits>();
const codeGid = ref<number>(7);
const codeUrl = ref<string>(); const codeUrl = ref<string>();
const codeTicket = computed<string>(() => { const codeTicket = ref<string>("");
if (!codeUrl.value) return "";
const url = new URL(codeUrl.value);
return url.searchParams.get("ticket") || "";
});
watch(model, async (value) => { watch(
if (value) { () => model.value,
async () => {
if (model.value) {
await freshQr();
if (cycleTimer) {
clearInterval(cycleTimer);
cycleTimer = null;
}
if (isLauncherCode.value) cycleTimer = setInterval(cycleGetDataLauncher, 1000);
else cycleTimer = setInterval(cycleGetDataGame, 1000);
} else {
if (cycleTimer) clearInterval(cycleTimer);
cycleTimer = null;
}
},
);
watch(
() => codeGid.value,
async () => {
if (isLauncherCode.value) return;
await freshQr(); await freshQr();
cycleTimer = setInterval(cycleGetData, 1000); if (cycleTimer) clearInterval(cycleTimer);
} cycleTimer = setInterval(cycleGetDataGame, 1000);
}); },
);
async function share(): Promise<void> { async function share(): Promise<void> {
const shareDom = document.querySelector<HTMLDivElement>(".tog-box"); const shareDom = document.querySelector<HTMLDivElement>(".tog-box");
@@ -65,25 +116,26 @@ async function share(): Promise<void> {
} }
async function freshQr(): Promise<void> { async function freshQr(): Promise<void> {
let res; if (isLauncherCode.value) {
if (isLauncherCode.value) res = await PassportReq.qrLogin.create(); const resp = await passportReq.qrLogin.create();
else res = await hk4eReq.loginQr.create(); if ("retcode" in resp) {
console.log(res); showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
if ("retcode" in res) { return;
showSnackbar.error(`[${res.retcode}] ${res.message}`); }
codeUrl.value = resp.url;
codeTicket.value = resp.ticket;
return; return;
} }
codeUrl.value = res.url; const resp = await hk4eReq.loginQr.create(codeGid.value);
if ("retcode" in resp) {
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
return;
}
codeUrl.value = resp.url;
codeTicket.value = new URL(codeUrl.value).searchParams.get("ticket") || "";
} }
async function cycleGetData() { async function cycleGetDataLauncher(): Promise<void> {
if (cycleTimer === null || codeTicket.value === "") return;
if (isLauncherCode.value) await cycleGetDataLauncher(cycleTimer);
else await cycleGetDataGame(cycleTimer);
}
// eslint-disable-next-line no-undef
async function cycleGetDataLauncher(timer: NodeJS.Timeout): Promise<void> {
const res = await PassportReq.qrLogin.query(codeTicket.value); const res = await PassportReq.qrLogin.query(codeTicket.value);
console.log(res); console.log(res);
if ("retcode" in res) { if ("retcode" in res) {
@@ -91,7 +143,7 @@ async function cycleGetDataLauncher(timer: NodeJS.Timeout): Promise<void> {
if (res.retcode === -106) { if (res.retcode === -106) {
await freshQr(); await freshQr();
} else { } else {
clearInterval(timer); if (cycleTimer) clearInterval(cycleTimer);
cycleTimer = null; cycleTimer = null;
model.value = false; model.value = false;
} }
@@ -99,7 +151,7 @@ async function cycleGetDataLauncher(timer: NodeJS.Timeout): Promise<void> {
} }
if (res.status === "Created" || res.status === "Scanned") return; if (res.status === "Created" || res.status === "Scanned") return;
if (res.status === "Confirmed") { if (res.status === "Confirmed") {
clearInterval(timer); if (cycleTimer) clearInterval(cycleTimer);
cycleTimer = null; cycleTimer = null;
const ck: TGApp.App.Account.Cookie = { const ck: TGApp.App.Account.Cookie = {
account_id: res.user_info.aid, account_id: res.user_info.aid,
@@ -115,16 +167,15 @@ async function cycleGetDataLauncher(timer: NodeJS.Timeout): Promise<void> {
} }
} }
// eslint-disable-next-line no-undef async function cycleGetDataGame(): Promise<void> {
async function cycleGetDataGame(timer: NodeJS.Timeout): Promise<void> { const res = await hk4eReq.loginQr.state(codeTicket.value, codeGid.value);
const res = await hk4eReq.loginQr.state(codeTicket.value);
console.log(res); console.log(res);
if ("retcode" in res) { if ("retcode" in res) {
showSnackbar.error(`[${res.retcode}] ${res.message}`); showSnackbar.error(`[${res.retcode}] ${res.message}`);
if (res.retcode === -106) { if (res.retcode === -106) {
await freshQr(); await freshQr();
} else { } else {
clearInterval(timer); if (cycleTimer) clearInterval(cycleTimer);
cycleTimer = null; cycleTimer = null;
model.value = false; model.value = false;
} }
@@ -132,7 +183,7 @@ async function cycleGetDataGame(timer: NodeJS.Timeout): Promise<void> {
} }
if (res.stat === "Init" || res.stat === "Scanned") return; if (res.stat === "Init" || res.stat === "Scanned") return;
if (res.stat === "Confirmed") { if (res.stat === "Confirmed") {
clearInterval(timer); if (cycleTimer) clearInterval(cycleTimer);
cycleTimer = null; cycleTimer = null;
if (res.payload.proto === "Raw") { if (res.payload.proto === "Raw") {
showSnackbar.error(`返回数据异常:${res.payload}`); showSnackbar.error(`返回数据异常:${res.payload}`);
@@ -142,23 +193,22 @@ async function cycleGetDataGame(timer: NodeJS.Timeout): Promise<void> {
const statusRaw: TGApp.Game.Login.StatusPayloadRaw = JSON.parse(res.payload.raw); const statusRaw: TGApp.Game.Login.StatusPayloadRaw = JSON.parse(res.payload.raw);
await showLoading.start("正在获取SToken"); await showLoading.start("正在获取SToken");
const stResp = await takumiReq.game.stoken(statusRaw); const stResp = await takumiReq.game.stoken(statusRaw);
console.log(stResp);
await showLoading.end(); await showLoading.end();
if ("retcode" in stResp) { if ("retcode" in stResp) {
showSnackbar.error(`[${stResp.retcode}] ${stResp.message}`); showSnackbar.error(`[${stResp.retcode}] ${stResp.message}`);
model.value = false; model.value = false;
return; return;
} }
// const ck: TGApp.App.Account.Cookie = { const ck: TGApp.App.Account.Cookie = {
// account_id: statusRaw.uid, account_id: statusRaw.uid,
// ltuid: statusRaw.uid, ltuid: statusRaw.uid,
// stuid: statusRaw.uid, stuid: statusRaw.uid,
// mid: res.user_info.mid, mid: stResp.user_info.mid,
// cookie_token: "", cookie_token: "",
// stoken: res.tokens[0].token, stoken: stResp.token.token,
// ltoken: "", ltoken: "",
// }; };
// emits("success", ck); emits("success", ck);
model.value = false; model.value = false;
} }
} }
@@ -180,8 +230,13 @@ onUnmounted(() => {
} }
.tog-top { .tog-top {
border-bottom: 1px solid var(--common-shadow-4); position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: var(--font-title); font-family: var(--font-title);
row-gap: 4px;
text-align: center; text-align: center;
} }
@@ -190,6 +245,42 @@ onUnmounted(() => {
font-size: 20px; font-size: 20px;
} }
.tog-select {
position: relative;
display: flex;
align-items: center;
justify-content: center;
column-gap: 8px;
}
.tog-select-item {
position: relative;
width: 36px;
height: 36px;
border-radius: 4px;
cursor: pointer;
opacity: 0.6;
&.active {
border: 2px solid var(--tgc-od-orange);
cursor: default;
opacity: 1;
}
img {
width: 100%;
height: 100%;
border-radius: 4px;
object-fit: contain;
}
}
.tog-divider {
width: 100%;
height: 1px;
background-color: var(--common-shadow-2);
}
.tog-mid { .tog-mid {
display: flex; display: flex;
width: 100%; width: 100%;

View File

@@ -95,14 +95,15 @@ function parseDesc(intro: string): string {
display: flex; display: flex;
width: 100%; width: 100%;
height: 100%; height: 100%;
box-sizing: border-box;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
justify-content: flex-start; justify-content: flex-start;
padding: 5px; padding: 8px;
border-radius: 5px; border-radius: 4px;
box-shadow: 0 0 5px inset var(--common-shadow-1); background: var(--box-bg-1);
overflow-y: auto; overflow-y: auto;
row-gap: 5px; row-gap: 4px;
} }
.tcb-top-none, .tcb-top-none,
@@ -136,15 +137,17 @@ function parseDesc(intro: string): string {
width: 100%; width: 100%;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
padding: 5px; padding: 8px;
border-radius: 10px; border: 1px solid var(--common-shadow-1);
background: var(--box-bg-1); border-radius: 4px;
background: var(--box-bg-2);
} }
.tcb-item img { .tcb-item img {
height: 100px; height: 80px;
border-radius: 50%; border-radius: 50%;
aspect-ratio: 1; aspect-ratio: 1;
background: var(--box-bg-3);
cursor: pointer; cursor: pointer;
} }

View File

@@ -120,7 +120,7 @@ function getBoxData(item: TGApp.App.Calendar.Item): TItemBoxData {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-bottom: 10px; margin-bottom: 8px;
font-family: var(--font-title); font-family: var(--font-title);
font-size: 20px; font-size: 20px;
} }
@@ -130,14 +130,14 @@ function getBoxData(item: TGApp.App.Calendar.Item): TItemBoxData {
height: 36px; height: 36px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: 5px; margin-right: 4px;
} }
.tc-btn-list { .tc-btn-list {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
column-gap: 5px; column-gap: 8px;
} }
.tc-btn { .tc-btn {
@@ -162,16 +162,16 @@ function getBoxData(item: TGApp.App.Calendar.Item): TItemBoxData {
.tc-content { .tc-content {
position: relative; position: relative;
display: flex; display: flex;
height: 210px; height: 208px;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
column-gap: 10px; column-gap: 8px;
} }
.calendar-grid { .calendar-grid {
display: grid; display: grid;
height: 100%; height: 100%;
grid-gap: 10px; grid-gap: 8px;
grid-template-columns: repeat(8, 100px); grid-template-columns: repeat(8, 100px);
place-items: flex-start flex-start; place-items: flex-start flex-start;
} }

View File

@@ -1,51 +1,61 @@
<template> <template>
<div class="thc-container"> <div class="thc-container">
<div class="thc-title"> <div class="thc-title">
<slot name="title"></slot> <slot name="title" />
</div> </div>
<div v-if="append" class="thc-append"> <div v-if="append" class="thc-append">
<slot name="title-append"></slot> <slot name="title-append" />
</div> </div>
<div class="thc-box"> <div class="thc-box">
<slot name="default"></slot> <slot name="default" />
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineProps<{ append?: boolean }>(); defineProps<{ append?: boolean }>();
</script> </script>
<style lang="css" scoped> <style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
.thc-container { .thc-container {
@include github-styles.github-card();
position: relative; position: relative;
min-height: 100px; min-height: 100px;
padding: 20px 10px 10px; padding: 24px 8px 8px;
border: 1px solid var(--common-shadow-2); box-sizing: border-box;
border-radius: 5px; border-radius: 4px;
margin-top: 30px; margin-top: 24px;
box-shadow: 2px 2px 5px var(--common-shadow-1); }
.dark .thc-container {
@include github-styles.github-card("dark");
} }
.thc-title, .thc-title,
.thc-append { .thc-append {
position: absolute; position: absolute;
top: -20px; top: -16px;
height: 32px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 10px; padding: 0 10px;
border-radius: 5px; border-radius: 4px;
background: var(--tgc-od-blue); background: var(--tgc-od-blue);
font-family: var(--font-title); font-family: var(--font-title);
border: 1px solid var(--tgc-od-white);
box-sizing: border-box;
} }
.thc-title { .thc-title {
left: 10px; left: 8px;
color: var(--tgc-white-4); color: var(--tgc-white-1);
font-size: 20px; font-size: 20px;
} }
.thc-append { .thc-append {
right: 10px; right: 8px;
color: var(--tgc-white-1); color: var(--tgc-white-1);
font-size: 16px; font-size: 16px;
} }

View File

@@ -1,305 +1,44 @@
<template> <template>
<THomeCard :append="hasNew"> <THomeCard :append="false">
<template #title>限时祈愿</template> <template #title>限时祈愿</template>
<template #title-append>
<v-switch class="pool-switch" @change="showNew = !showNew" />
<span>{{ showNew ? "查看当前祈愿" : "查看后续祈愿" }}</span>
</template>
<template #default> <template #default>
<div class="pool-grid"> <div class="pool-grid">
<div v-for="pool in poolSelect" :key="pool.postId" class="pool-card"> <PhPoolCard v-for="(pool, idx) in pools" :key="idx" :pool="pool" />
<div class="pool-cover" @click="createPost(pool.postId, pool.title)">
<TMiImg :src="pool.cover" alt="cover" :ori="true" />
</div>
<div class="pool-bottom">
<div class="pool-character">
<div class="pool-icons">
<div
v-for="character in pool.characters"
:key="character.url"
class="pool-icon"
@click="toOuter(character, pool.title)"
>
<TItembox
:title="character.info.name"
v-if="character.info"
:model-value="getCBox(character.info)"
/>
<img v-else :src="character.icon" alt="character" />
</div>
</div>
</div>
<div class="pool-time">
<div class="left">
<v-icon>mdi-calendar-clock</v-icon>
<span>{{ pool.time.str }}</span>
</div>
<v-progress-linear
:model-value="
pool.stat !== 'now' ? 100 : (pool.timeRest * 100) / pool.time.totalStamp
"
:rounded="true"
/>
<div v-if="pool.stat !== 'now'" class="time">
{{ pool.stat === "future" ? "未开始" : "已结束" }}
</div>
<div v-else class="time">
<span>剩余时间</span>
<span>{{ stamp2LastTime(pool.timeRest) }}</span>
</div>
</div>
</div>
</div>
</div> </div>
</template> </template>
</THomeCard> </THomeCard>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TItembox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import Mys from "@Mys/index.js"; import PhPoolCard from "@comp/pageHome/ph-pool-card.vue";
import { storeToRefs } from "pinia"; import { onMounted, shallowRef } from "vue";
import { computed, onMounted, onUnmounted, ref } from "vue";
import { useRouter } from "vue-router";
import THomeCard from "./ph-comp-card.vue"; import THomeCard from "./ph-comp-card.vue";
import { useHomeStore } from "@/store/modules/home.js"; import TGLogger from "@/utils/TGLogger.js";
import { createPost, createTGWindow } from "@/utils/TGWindow.js"; import takumiReq from "@/web/request/takumiReq.js";
import { stamp2LastTime } from "@/utils/toolFunc.js";
type TPoolEmits = (e: "success") => void; type TPoolEmits = (e: "success") => void;
type PoolStat = "future" | "now" | "past"; // 未开始 | 进行中 | 已结束
type PoolItem = TGApp.Plugins.Mys.Gacha.RenderCard & { timeRest: number; stat: PoolStat };
const emits = defineEmits<TPoolEmits>(); const emits = defineEmits<TPoolEmits>();
const { poolCover } = storeToRefs(useHomeStore()); const pools = shallowRef<Array<TGApp.BBS.Obc.GachaItem>>([]);
const router = useRouter();
// eslint-disable-next-line no-undef
let timer: NodeJS.Timeout | null = null;
const showNew = ref<boolean>(false);
const poolCards = ref<Array<PoolItem>>([]);
const hasNew = computed<boolean>(
() => poolCards.value.find((pool) => pool.stat === "future") !== undefined,
);
const poolSelect = computed<Array<PoolItem>>(() => {
if (!hasNew.value) return poolCards.value;
if (showNew.value) return poolCards.value.filter((pool) => pool.stat === "future");
return poolCards.value.filter((pool) => pool.stat !== "future");
});
onMounted(async () => { onMounted(async () => {
const gachaData = await Mys.Gacha.get(); const resp = await takumiReq.obc.gacha();
let cards: Array<TGApp.Plugins.Mys.Gacha.RenderCard>; if (Array.isArray(resp)) pools.value = resp;
if (!checkCover(gachaData)) { else {
cards = await Mys.Gacha.card(gachaData); showSnackbar.error(`获取限时祈愿失败:[${resp.retcode}-${resp.message}`);
const coverData: Record<string, string> = {}; await TGLogger.Error(`获取限时祈愿失败:[${resp.retcode}-${resp.message}`);
poolCards.value.map((pool) => {
coverData[pool.id] = pool.cover;
return pool;
});
poolCover.value = coverData;
} else {
cards = await Mys.Gacha.card(gachaData, poolCover.value);
} }
for (const pool of cards) {
const timeRest = pool.time.endStamp - Date.now();
poolCards.value.push({
...pool,
stat: timeRest > pool.time.totalStamp ? "future" : timeRest > 0 ? "now" : "past",
timeRest: timeRest,
});
}
if (timer !== null) clearInterval(timer);
timer = setInterval(poolTimeout, 1000);
emits("success"); emits("success");
}); });
function poolTimeout(): void {
for (const pool of poolCards.value) {
if (pool.stat === "past") {
if (pool.timeRest !== -1) pool.timeRest = -1;
continue;
}
const timeRest = pool.time.endStamp - Date.now();
if (timeRest >= pool.time.totalStamp) {
pool.stat = "future";
pool.timeRest = timeRest;
continue;
}
if (timeRest <= 0) {
pool.stat = "past";
pool.timeRest = -1;
continue;
}
pool.stat = "now";
pool.timeRest = timeRest;
}
}
function checkCover(data: Array<TGApp.Plugins.Mys.Gacha.Data>): boolean {
if (poolCover.value === undefined || Object.keys(poolCover.value).length === 0) return false;
let checkList = data.length;
Object.entries(poolCover.value).forEach(([key, value]: [string, unknown]) => {
const pool = data.find((item) => item.id.toString() === key);
if (pool && value !== "/source/UI/empty.webp") checkList--;
});
return checkList === 0;
}
async function toOuter(
character: TGApp.Plugins.Mys.Gacha.RenderItem,
title: string,
): Promise<void> {
if (character.info !== undefined) {
await router.push({ name: "角色图鉴", params: { id: character.info.id } });
return;
}
const url = character.url;
if (url === "") {
showSnackbar.warn("链接为空!");
return;
}
await createTGWindow(url, "Sub_window", `Pool_${title}`, 1200, 800, true, true);
}
function getCBox(info: TGApp.App.Character.WikiBriefInfo): TItemBoxData {
return {
bg: `/icon/bg/${info.star}-Star.webp`,
icon: `/WIKI/character/${info.id}.webp`,
size: "60px",
height: "60px",
display: "inner",
clickable: true,
lt: `/icon/element/${info.element}元素.webp`,
ltSize: "15px",
innerHeight: 20,
innerIcon: `/icon/weapon/${info.weapon}.webp`,
innerText: info.name,
};
}
onUnmounted(() => {
if (timer !== null) clearInterval(timer);
timer = null;
});
</script> </script>
<style lang="scss" scoped>
<style lang="css" scoped>
.pool-switch {
display: flex;
height: 36px;
align-items: center;
justify-content: center;
margin-right: 5px;
}
.pool-grid { .pool-grid {
display: grid; display: grid;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 10px; gap: 8px;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 0.5fr);
}
.pool-card {
position: relative;
overflow: hidden;
width: 100%;
border-radius: 5px;
aspect-ratio: 69 / 32;
box-shadow: 2px 2px 5px var(--common-shadow-2);
}
.pool-cover {
display: flex;
overflow: hidden;
width: 100%;
height: auto;
align-items: center;
justify-content: center;
border-radius: 5px;
}
.pool-cover img {
width: 100%;
border-radius: 5px;
transition: all 0.5s;
}
.pool-cover :hover {
cursor: pointer;
transform: scale(1.1);
transition: all 0.5s;
}
.pool-bottom {
position: absolute;
bottom: 0;
left: 0;
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.pool-character {
display: flex;
overflow: hidden auto;
width: auto;
max-width: 280px;
height: 60px;
margin: 10px;
}
.pool-character::-webkit-scrollbar-thumb {
border-radius: 10px;
background: var(--common-shadow-t-4);
}
.pool-icons {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
margin-bottom: 10px;
gap: 10px;
}
.pool-icon {
width: 60px;
height: 60px;
transition: all ease-in-out 0.3s;
}
.pool-icon:hover {
transform: scale(0.95);
transition: all ease-in-out 0.3s;
}
.pool-icon img {
position: absolute;
width: 60px;
height: 60px;
border-radius: 5px;
cursor: pointer;
}
.pool-time {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
margin-right: 10px;
color: var(--tgc-white-1);
font-size: 12px;
gap: 10px;
text-align: left;
} }
</style> </style>

View File

@@ -3,84 +3,39 @@
<template #title>近期活动</template> <template #title>近期活动</template>
<template #default> <template #default>
<div class="position-grid"> <div class="position-grid">
<PhPositionCard v-for="(card, index) in positionCards" :key="index" :position="card" /> <PhPositionCard v-for="(card, index) in positions" :key="index" :pos="card" />
</div> </div>
</template> </template>
</THomeCard> </THomeCard>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import showSnackbar from "@comp/func/snackbar.js";
import PhPositionCard from "@comp/pageHome/ph-position-card.vue"; import PhPositionCard from "@comp/pageHome/ph-position-card.vue";
import Mys from "@Mys/index.js"; import { onMounted, shallowRef } from "vue";
import { onMounted, onUnmounted, ref } from "vue";
import THomeCard from "./ph-comp-card.vue"; import THomeCard from "./ph-comp-card.vue";
import TGLogger from "@/utils/TGLogger.js";
import takumiReq from "@/web/request/takumiReq.js";
type TPositionEmits = (e: "success") => void; type TPositionEmits = (e: "success") => void;
type PositionStat = "past" | "now" | "future" | "unknown"; // 已结束 | 进行中 | 未开始 | 未知
export type PositionItem = TGApp.Plugins.Mys.Position.RenderCard & {
timeRest: number;
stat: PositionStat;
};
const emits = defineEmits<TPositionEmits>(); const emits = defineEmits<TPositionEmits>();
// eslint-disable-next-line no-undef const positions = shallowRef<Array<TGApp.BBS.Obc.PositionItem>>([]);
let timer: NodeJS.Timeout | null = null;
const positionCards = ref<Array<PositionItem>>([]);
onMounted(async () => { onMounted(async () => {
const positionData = await Mys.Position.get(); const resp = await takumiReq.obc.position();
console.log(positionData); if (Array.isArray(resp)) positions.value = resp;
const cards = Mys.Position.card(positionData); else {
for (const position of cards) { showSnackbar.error(`获取近期活动失败:[${resp.retcode}-${resp.message}`);
if (position.time.endStamp === 0 || position.time.totalStamp < 0) { await TGLogger.Error(`获取近期活动失败:[${resp.retcode}-${resp.message}`);
positionCards.value.push({
...position,
timeRest: 0,
stat: "unknown",
});
continue;
}
const timeRest = position.time.endStamp - Date.now();
positionCards.value.push({
...position,
timeRest,
stat: timeRest > position.time.totalStamp ? "future" : timeRest > 0 ? "now" : "past",
});
} }
if (timer !== null) clearInterval(timer);
timer = setInterval(getPositionTimer, 1000);
emits("success"); emits("success");
}); });
function getPositionTimer(): void {
for (const position of positionCards.value) {
if (position.stat === "unknown") continue;
if (position.stat === "past") {
position.timeRest = 0;
continue;
}
position.timeRest = position.time.endStamp - Date.now();
if (position.timeRest <= 0) {
position.stat = "past";
position.timeRest = 0;
} else if (position.timeRest > position.time.totalStamp) {
position.stat = "future";
} else {
position.stat = "now";
}
}
}
onUnmounted(() => {
if (timer !== null) clearInterval(timer);
timer = null;
});
</script> </script>
<style lang="scss" scoped>
<style lang="css" scoped>
.position-grid { .position-grid {
display: grid; display: grid;
margin-top: 10px; grid-gap: 12px;
grid-gap: 20px; grid-template-columns: repeat(auto-fill, minmax(calc(400px), 0.5fr));
grid-template-columns: repeat(auto-fill, minmax(calc(400px + 2rem), 0.5fr));
} }
</style> </style>

Some files were not shown because too many files have changed in this diff Show More