├── .gitignore ├── LICENSE ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public └── preface.jpg ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── capabilities │ └── default.json ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── src │ ├── lcu.rs │ ├── lcu │ │ ├── global_key.rs │ │ ├── listener.rs │ │ └── matchlisthanle.rs │ ├── lib.rs │ ├── main.rs │ └── shaco │ │ ├── error.rs │ │ ├── ingame.rs │ │ ├── lib.rs │ │ ├── mod.rs │ │ ├── model │ │ ├── ingame.rs │ │ ├── mod.rs │ │ └── ws.rs │ │ ├── rest.rs │ │ ├── riotgames.pem │ │ ├── utils │ │ ├── mod.rs │ │ ├── process_info.rs │ │ └── request.rs │ │ └── ws.rs └── tauri.conf.json ├── src ├── assets │ ├── font │ │ └── DingTalk JinBuTi.ttf │ ├── icon │ │ ├── Frank.png │ │ ├── app-icon.png │ │ ├── favicon.ico │ │ └── siteLogo.png │ ├── matchImage │ │ ├── Alipay.JPEG │ │ ├── Wechat.JPEG │ │ ├── assists.png │ │ ├── firstBlood.png │ │ ├── fiveKills.png │ │ ├── fourKills.png │ │ ├── friend.png │ │ ├── god.png │ │ ├── goldEarned.png │ │ ├── kills.png │ │ ├── mvp.png │ │ ├── quitGame.png │ │ ├── svp.png │ │ ├── threeKills.png │ │ ├── totalDamageDealtToChampions.png │ │ ├── totalDamageTaken.png │ │ ├── totalMinionsKilled.png │ │ ├── turretKills.png │ │ └── visionScore.png │ ├── runes │ │ ├── 5001.png │ │ ├── 5005.png │ │ ├── 5007.png │ │ ├── 5008.png │ │ ├── 5010.png │ │ ├── 5011.png │ │ ├── 5013.png │ │ ├── 8000.png │ │ ├── 8005.png │ │ ├── 8008.png │ │ ├── 8009.png │ │ ├── 8010.png │ │ ├── 8014.png │ │ ├── 8017.png │ │ ├── 8021.png │ │ ├── 8100.png │ │ ├── 8105.png │ │ ├── 8106.png │ │ ├── 8112.png │ │ ├── 8126.png │ │ ├── 8128.png │ │ ├── 8135.png │ │ ├── 8137.png │ │ ├── 8139.png │ │ ├── 8140.png │ │ ├── 8141.png │ │ ├── 8143.png │ │ ├── 8200.png │ │ ├── 8210.png │ │ ├── 8214.png │ │ ├── 8224.png │ │ ├── 8226.png │ │ ├── 8229.png │ │ ├── 8230.png │ │ ├── 8232.png │ │ ├── 8233.png │ │ ├── 8234.png │ │ ├── 8236.png │ │ ├── 8237.png │ │ ├── 8242.png │ │ ├── 8275.png │ │ ├── 8299.png │ │ ├── 8300.png │ │ ├── 8304.png │ │ ├── 8306.png │ │ ├── 8313.png │ │ ├── 8316.png │ │ ├── 8321.png │ │ ├── 8345.png │ │ ├── 8347.png │ │ ├── 8351.png │ │ ├── 8352.png │ │ ├── 8360.png │ │ ├── 8369.png │ │ ├── 8400.png │ │ ├── 8401.png │ │ ├── 8410.png │ │ ├── 8429.png │ │ ├── 8437.png │ │ ├── 8439.png │ │ ├── 8444.png │ │ ├── 8446.png │ │ ├── 8451.png │ │ ├── 8453.png │ │ ├── 8463.png │ │ ├── 8465.png │ │ ├── 8473.png │ │ ├── 9101.png │ │ ├── 9103.png │ │ ├── 9104.png │ │ ├── 9105.png │ │ ├── 9111.png │ │ └── 9923.png │ ├── svg │ │ ├── assassin.svg │ │ ├── fighter.svg │ │ ├── image.png │ │ ├── imageDark.png │ │ ├── mage.svg │ │ ├── marksman.svg │ │ ├── support.svg │ │ └── tank.svg │ └── tLevel │ │ ├── bot.svg │ │ ├── jug.svg │ │ ├── mid.svg │ │ ├── sup.svg │ │ ├── t0.svg │ │ ├── t1.svg │ │ ├── t2.svg │ │ ├── t3.svg │ │ ├── t4.svg │ │ ├── t5.svg │ │ └── top.svg ├── background │ ├── background.ts │ ├── gameFlow.ts │ ├── index.html │ ├── types │ │ └── index.d.ts │ └── utils │ │ ├── TaskTracker.ts │ │ ├── config.ts │ │ ├── creatWindow.ts │ │ └── tray.ts ├── lcu │ ├── aboutMatch.ts │ ├── aboutRune.ts │ ├── aboutSummoner.ts │ ├── autoBP.ts │ ├── index.ts │ ├── types │ │ ├── SummonerTypes.d.ts │ │ ├── queryDetailedGameTypes.d.ts │ │ ├── queryMatchLcuTypes.d.ts │ │ └── runeLcuTypes.d.ts │ └── utils.ts ├── main │ ├── common │ │ ├── dashboard.vue │ │ ├── navigation.vue │ │ ├── searchChamp.vue │ │ ├── setting.vue │ │ ├── sponsor.vue │ │ └── summonerMasteryChamp.vue │ ├── index.html │ ├── index.vue │ ├── main.ts │ ├── main.vue │ ├── router │ │ └── index.ts │ ├── store │ │ ├── useRecord.ts │ │ ├── useRune.ts │ │ └── useTeammate.ts │ ├── style.css │ ├── utils │ │ ├── notice.ts │ │ ├── request.ts │ │ └── theme.ts │ └── views │ │ ├── home │ │ ├── getHomeData.ts │ │ ├── index.vue │ │ └── startGame.vue │ │ ├── rank │ │ ├── assistCommon.css │ │ ├── champDetail.vue │ │ ├── champListLoad.vue │ │ ├── champWinRate.vue │ │ ├── index.vue │ │ ├── rankTypes.d.ts │ │ └── utils.ts │ │ ├── record │ │ ├── addBlackList.vue │ │ ├── blackList.ts │ │ ├── blackListTypes.d.ts │ │ ├── blackSummonerList.vue │ │ ├── gameEnd.vue │ │ ├── haterDetails.vue │ │ ├── index.vue │ │ ├── summonerEnd.vue │ │ └── utils.ts │ │ ├── rune │ │ ├── blockContent.vue │ │ ├── get101Runes.ts │ │ ├── index.vue │ │ ├── queryRune.ts │ │ ├── runeAuto.vue │ │ ├── runeContent.vue │ │ ├── runeHeader.vue │ │ ├── runeMain.vue │ │ ├── runeTypes.d.ts │ │ └── runes.ts │ │ └── teammate │ │ ├── index.vue │ │ ├── loadMatch.vue │ │ ├── matchAnalysis.vue │ │ ├── queryMatch.ts │ │ ├── summonerDetail.vue │ │ ├── summonerKdaName.vue │ │ ├── summonerList.vue │ │ ├── summonerMatch.vue │ │ ├── summonerMatchLoad.vue │ │ ├── teammateTypes.d.ts │ │ └── utils.ts ├── matchAnalysis │ ├── components │ │ ├── analysisMain.vue │ │ ├── analysisSum.vue │ │ └── dashboard.vue │ ├── index.html │ ├── main.ts │ ├── main.vue │ ├── matchAnalysis.vue │ ├── style.css │ └── utils │ │ └── MatchAnalysisTypes.d.ts ├── queryMatch │ ├── common │ │ ├── matchConHeader.vue │ │ ├── matchContent.vue │ │ ├── matchDetails.vue │ │ ├── matchDetailsFighter.vue │ │ ├── matchDrawer.vue │ │ └── matchSumDetails.vue │ ├── components │ │ ├── loadingAnime.vue │ │ ├── matchErr.vue │ │ ├── matchList.vue │ │ ├── matchMain.vue │ │ ├── queryHeader.vue │ │ └── summonerInfoView.vue │ ├── index.html │ ├── main.ts │ ├── main.vue │ ├── queryMatch.vue │ ├── store │ │ └── index.ts │ ├── style.css │ └── utils │ │ ├── MatchDetail.d.ts │ │ ├── baseMatch.ts │ │ ├── matchDetails.ts │ │ └── tools.ts ├── recentMatch │ ├── components │ │ ├── champInfo.vue │ │ ├── dashboard.vue │ │ ├── nullPage.vue │ │ └── recentMatchList.vue │ ├── index.html │ ├── main.ts │ ├── main.vue │ ├── recentMatch.vue │ ├── style.css │ └── utils │ │ ├── queryMatch.ts │ │ ├── querySummoner.ts │ │ └── queryTypes.d.ts ├── resources │ ├── areaList.ts │ ├── champList.ts │ └── otherList.ts └── test │ └── index.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Java_S 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Frank Powered By Java_S 3 |

4 |

5 | 6 |

7 |

8 | 🚀立即下载 9 | 🔎使用手册 10 |

11 | 12 | ## 👋 介绍 13 | Frank是一款简洁的,轻量的,免费的,开源的英雄联盟助手。 本软件的使命是当好游戏玩家的幕后小助手,提供一些便捷游戏的服务,让你的游戏体验更上一层楼。 当然,Frank是正经助手,不搞幺蛾子!任何违反 Riot 和腾讯规定的行为,我们都不鼓励,更不支持。 毕竟,公平游戏才是真高手的战场! 根据官方要求,Frank不再提供查询战绩的功能,如果您有这方面的需求,请使用WeGame或者掌上英雄联盟APP。 14 | 15 | ## 🔧 技术栈 16 | 17 | - Rust 18 | - Tauri 19 | - Vue3 20 | - Typescript 21 | - TailwindCSS 22 | 23 | ## 📊 目录结构 24 | 25 | ``` 26 | src 27 | ├─assets # 一些资源文件 28 | ├─background # 前端的逻辑窗口 29 | ├─lcu # Lcu接口 30 | ├─main # 主窗口 31 | │ ├─router 32 | │ ├─store 33 | │ └─views 34 | │ ├─home # 首页 35 | │ ├─rank # 英雄数据排行 36 | │ ├─record # 排位笔记 37 | │ ├─rune # 符文配置 38 | │ └─teammate # 队友数据 39 | ├─matchAnalysis # 战绩数据分析窗口 40 | ├─queryMatch # 我的战绩窗口 41 | ├─recentMatch # 对局详情战绩窗口 42 | ├─resources # 一些资源 43 | 44 | src-tauri 45 | ├─icons 46 | ├─src 47 | │ ├─lcu 48 | │ └─shaco # 连接lcu第三方库 49 | │ └─lib.rs # 入口函数 50 | │ └─main.rs 51 | │ └─lcu.rs 52 | ``` 53 | 54 | ## 📥 运行 55 | 56 | ``` 57 | git clone https://github.com/Java-S12138/frank.git 58 | cd src 59 | pnpm install 60 | cd src-tauri # 安装rust所需要的组件 61 | cd.. 62 | pnpm run tauri dev 63 | ``` 64 | 65 | 构建 66 | 67 | ``` 68 | pnpm run tauri build 69 | ``` 70 | 71 | 72 | ## 点个 Star 支持我们 ⭐ 73 |

74 | 75 | 76 | 77 |

-------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tauri + Vue + Typescript App 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Frank", 3 | "private": true, 4 | "version": "3.15.1505", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "tauri": "tauri" 11 | }, 12 | "dependencies": { 13 | "@tauri-apps/api": "^2", 14 | "@tauri-apps/plugin-opener": "^2", 15 | "@tauri-apps/plugin-process": "^2.2.0", 16 | "@tauri-apps/plugin-window-state": "^2.2.1", 17 | "vue": "^3.5.13" 18 | }, 19 | "devDependencies": { 20 | "@tauri-apps/cli": "^2", 21 | "@tauri-apps/plugin-global-shortcut": "^2.2.0", 22 | "@tauri-apps/plugin-http": "^2.2.0", 23 | "@tauri-apps/plugin-shell": "^2.2.0", 24 | "@types/node": "^22.10.2", 25 | "@vicons/tabler": "^0.12.0", 26 | "@vitejs/plugin-vue": "^5.2.1", 27 | "autoprefixer": "^10.4.16", 28 | "axios": "^1.6.3", 29 | "lodash": "^4.17.21", 30 | "naive-ui": "^2.36.0", 31 | "pinia": "^2.1.7", 32 | "postcss": "^8.4.32", 33 | "tailwindcss": "^3.4.0", 34 | "typescript": "~5.6.2", 35 | "vite": "^6.0.3", 36 | "vue-router": "^4.2.5", 37 | "vue-tsc": "^2.1.10" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/preface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/public/preface.jpg -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Frank" 3 | version = "0.1.0" 4 | description = "League of Legends Assistant On Frank" 5 | authors = ["Java_S"] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [lib] 11 | # The `_lib` suffix may seem redundant but it is necessary 12 | # to make the lib name unique and wouldn't conflict with the bin name. 13 | # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 14 | name = "frank_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2", features = [] } 19 | 20 | [dependencies] 21 | tauri = { version = "2.3.1", features = ["tray-icon"] } 22 | tauri-plugin-opener = "2" 23 | tauri-plugin-process = "2.2.0" 24 | tauri-plugin-http = "2.2.0" 25 | tauri-plugin-shell = "2.2.0" 26 | tauri-plugin-window-state = "2.2.1" 27 | 28 | serde = { version = "1", features = ["derive"] } 29 | serde_json = "1" 30 | sysinfo = "0.29.11" 31 | base64 = "0.21.0" 32 | native-tls = "0.2.11" 33 | futures-util = "0.3.25" 34 | tokio = { version = "1.17.0", features = ["full"] } 35 | tokio-tungstenite = { version = "0.20.1", features = ["native-tls"] } 36 | reqwest = { version = "0.11.14", features = ["json"] } 37 | serde-single-key-map = "0.1.0" 38 | derive_more = { version = "0.99.17", features = ["display"] } 39 | once_cell = "1.20.2" 40 | rdev = { version="0.5.3"} 41 | 42 | [profile.release] 43 | opt-level = "s" # Optimize for size. 44 | lto = true # Enable Link Time Optimization 45 | codegen-units = 1 # Reduce number of codegen units to increase optimizations. 46 | strip = true # Automatically strip symbols from the binary. 47 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut windows = tauri_build::WindowsAttributes::new(); 3 | windows = windows.app_manifest(r#" 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | "#); 26 | tauri_build::try_build( 27 | tauri_build::Attributes::new().windows_attributes(windows) 28 | ).expect("failed to run build script"); 29 | } 30 | -------------------------------------------------------------------------------- /src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": [ 6 | "background", 7 | "mainWindow", 8 | "queryMatchWindow", 9 | "matchAnalysisWindow", 10 | "recentMatchWindow" 11 | ], 12 | "permissions": [ 13 | "core:default", 14 | "opener:default", 15 | "process:default", 16 | "core:window:allow-show", 17 | "core:window:allow-hide", 18 | "core:window:allow-close", 19 | "core:window:allow-unminimize", 20 | "core:window:allow-is-visible", 21 | "core:window:allow-minimize", 22 | "core:window:allow-set-position", 23 | "core:window:allow-start-dragging", 24 | "core:webview:allow-create-webview-window", 25 | "http:default", 26 | "shell:allow-open", 27 | "core:tray:allow-set-icon", 28 | "core:app:allow-default-window-icon", 29 | { 30 | "identifier": "http:default", 31 | "allow": [ 32 | { 33 | "url": "https://*.myqcloud.com" 34 | }, 35 | { 36 | "url": "https://*.qq.com" 37 | }, 38 | { 39 | "url": "https://lol.ps" 40 | }, 41 | { 42 | "url": "http://121.40.58.64:8412" 43 | }, 44 | { 45 | "url": "https://game.gtimg.cn" 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/src/lcu/global_key.rs: -------------------------------------------------------------------------------- 1 | use rdev::{listen, Event, EventType, Key}; 2 | use tauri::{AppHandle, Manager}; 3 | 4 | pub fn init_global_keyboard(app: AppHandle) { 5 | let mut shift_state: bool = false; 6 | // Capture global keyboard events 7 | if let Err(error) = listen(move |event: Event| callback(event, &mut shift_state, &app)) { 8 | println!("Error: {:?}", error); 9 | } 10 | } 11 | 12 | fn callback(event: Event, shift_state: &mut bool, app: &AppHandle) { 13 | match event.event_type { 14 | // Handle key press events 15 | EventType::KeyPress(key_event) => match key_event { 16 | Key::ShiftLeft => handle_shift_press(shift_state), 17 | Key::Tab => handle_tab_press(shift_state, event, app), 18 | _ => (), 19 | }, 20 | 21 | // Handle key release events 22 | EventType::KeyRelease(key_event) => { 23 | if key_event == Key::ShiftLeft && *shift_state { 24 | // Reset Shift key status 25 | *shift_state = false; 26 | } 27 | } 28 | _ => (), 29 | } 30 | } 31 | 32 | // 处理 Shift 键按下事件 33 | fn handle_shift_press(shift_state: &mut bool) { 34 | if !*shift_state { 35 | // The Shift key is pressed for the first time 36 | *shift_state = true; 37 | } 38 | } 39 | 40 | // Handle Tab key press event 41 | fn handle_tab_press(shift_state: &mut bool, event: Event, app: &AppHandle) { 42 | if *shift_state { 43 | *shift_state = false; 44 | let win = app.get_webview_window("recentMatchWindow"); 45 | if let Some(win) = win { 46 | if !win.is_visible().unwrap_or(false) { 47 | win.show().expect("hide window failed"); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src-tauri/src/lcu/listener.rs: -------------------------------------------------------------------------------- 1 | use crate::shaco::{model::ws::LcuSubscriptionType, ws}; 2 | use futures_util::stream::StreamExt; 3 | use tauri::{AppHandle, Emitter, EventTarget}; 4 | 5 | pub async fn listen_client(app: AppHandle) { 6 | let mut client = ws::LcuWebsocketClient::connect().await.unwrap(); 7 | client 8 | .subscribe(LcuSubscriptionType::JsonApiEvent( 9 | "/lol-gameflow/v1/gameflow-phase".to_string(), 10 | )) 11 | .await 12 | .unwrap(); 13 | 14 | while let Some(event) = client.next().await { 15 | // println!("Event: {:?}", event); 16 | app.emit_to( 17 | EventTarget::labeled("background"), 18 | "client_status", 19 | event.data, 20 | ) 21 | .unwrap(); 22 | } 23 | } 24 | 25 | pub async fn listen_champ_select(app: AppHandle) { 26 | let mut client = ws::LcuWebsocketClient::connect().await.unwrap(); 27 | client 28 | .subscribe(LcuSubscriptionType::JsonApiEvent( 29 | "/lol-champ-select/v1/session".to_string(), 30 | )) 31 | .await 32 | .unwrap(); 33 | while let Some(event) = client.next().await { 34 | app.emit_to( 35 | EventTarget::labeled("background"), 36 | "lol-champ-select", 37 | event.data).unwrap(); 38 | } 39 | } 40 | 41 | pub async fn listen_current_champ_select(app: AppHandle) { 42 | let mut client = ws::LcuWebsocketClient::connect().await.unwrap(); 43 | client 44 | .unsubscribe(LcuSubscriptionType::JsonApiEvent( 45 | "/lol-champ-select/v1/session".to_string(), 46 | )) 47 | .await 48 | .unwrap(); 49 | 50 | client 51 | .subscribe(LcuSubscriptionType::JsonApiEvent( 52 | "/lol-champ-select/v1/current-champion".to_string(), 53 | )) 54 | .await 55 | .unwrap(); 56 | 57 | while let Some(event) = client.next().await { 58 | app.emit_to( 59 | EventTarget::labeled("background"), 60 | "lol-current-champ-select", 61 | event.data).unwrap(); 62 | } 63 | } -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod lcu; 2 | mod shaco; 3 | use lcu::{ 4 | get_match_list, init_keyboard, invoke_lcu, is_game_start, is_lol_cilent, 5 | listen_for_client_start, start_listener, start_champ_select,start_current_champ_select 6 | }; 7 | use tauri::Emitter; 8 | use tauri::{Listener, Manager}; 9 | 10 | #[tokio::main] 11 | pub async fn run() { 12 | tauri::Builder::default() 13 | /* .setup(|app| { 14 | #[cfg(debug_assertions)] // only include this code on debug builds 15 | { 16 | let window = app.get_webview_window("background").unwrap(); 17 | window.open_devtools(); 18 | } 19 | Ok(()) 20 | })*/ 21 | .invoke_handler(tauri::generate_handler![ 22 | is_lol_cilent, 23 | start_listener, 24 | start_champ_select, 25 | invoke_lcu, 26 | get_match_list, 27 | is_game_start, 28 | init_keyboard, 29 | listen_for_client_start, 30 | start_current_champ_select 31 | ]) 32 | .plugin(tauri_plugin_http::init()) 33 | .plugin(tauri_plugin_shell::init()) 34 | .plugin(tauri_plugin_opener::init()) 35 | .plugin(tauri_plugin_process::init()) 36 | .plugin(tauri_plugin_window_state::Builder::default() 37 | .with_denylist(&["background","queryMatchWindow","matchAnalysisWindow","recentMatchWindow"]).build()) 38 | .run(tauri::generate_context!()) 39 | .expect("error while running tauri application"); 40 | } 41 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | #[allow(unused)] 4 | 5 | 6 | fn main() { 7 | frank_lib::run() 8 | } 9 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Shaco 2 | //! 3 | //! A wrapper for the League-Client and LoL-Ingame APIs 4 | //! 5 | //! - [RESTClient](rest::RESTClient): A REST client for the League-Client(LCU) API 6 | //! - [LcuWebsocketClient](ws::LcuWebsocketClient): Subscription based Websocket API for the League-Client(LCU) API 7 | //! - [IngameClient](ingame::IngameClient): A REST client for the LoL-Ingame API 8 | //! - [EventStream](ingame::EventStream): A wrapper around polling ingame events implementing the [futures_util::Stream] Trait 9 | //! 10 | //! If you are looking for a Rust library for the Riot Games API see [Riven](https://docs.rs/riven/latest/riven/) 11 | 12 | /// Error types for the whole library 13 | pub mod error; 14 | /// Contains the [IngameClient](ingame::IngameClient) and [IngameClient](ingame::EventStream) 15 | pub mod model; 16 | /// Contains the [RESTClient](rest::RESTClient) 17 | pub mod rest; 18 | mod utils; 19 | /// Contains the [LcuWebsocketClient](ws::LcuWebsocketClient) 20 | pub mod ws; 21 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod model; 2 | pub mod ws; 3 | pub mod error; 4 | pub mod utils; 5 | pub mod ingame; 6 | pub mod rest; -------------------------------------------------------------------------------- /src-tauri/src/shaco/model/mod.rs: -------------------------------------------------------------------------------- 1 | /// Type defintions for the LoL-Ingame API 2 | pub mod ingame; 3 | /// Type definitions for the League-Client(LCU) Websocket API 4 | pub mod ws; 5 | 6 | 7 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/model/ws.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, fmt::Display}; 2 | 3 | use serde::{de, Deserialize, Deserializer}; 4 | use serde_json::Value; 5 | 6 | /// The Websocket connection returns LcuEvents 7 | #[derive(Debug, Clone)] 8 | pub struct LcuEvent { 9 | pub subscription_type: LcuSubscriptionType, 10 | pub data: Value, 11 | pub event_type: String, 12 | } 13 | 14 | /// LcuEvents first get deserialized to deserialize::DeEvent and then to LcuEvent 15 | /// because the data formats are not directly deserializable by serde 16 | impl<'de> Deserialize<'de> for LcuEvent { 17 | fn deserialize(deserializer: D) -> Result 18 | where 19 | D: Deserializer<'de>, 20 | { 21 | /// Intermediate data-structure for deserializing LcuEvents 22 | #[derive(Deserialize, Debug)] 23 | pub struct DeEvent { 24 | _opcode: i64, 25 | pub(crate) subscription_type: LcuSubscriptionType, 26 | pub(crate) data: Data, 27 | } 28 | /// Intermediate data-structure for deserializing LcuEvents 29 | #[derive(Deserialize, Debug)] 30 | #[serde(rename_all = "camelCase")] 31 | pub struct Data { 32 | pub(crate) data: Value, 33 | pub(crate) event_type: String, 34 | } 35 | 36 | let de_event = DeEvent::deserialize(deserializer)?; 37 | Ok(Self { 38 | subscription_type: de_event.subscription_type, 39 | data: de_event.data.data, 40 | event_type: de_event.data.event_type, 41 | }) 42 | } 43 | } 44 | 45 | /// The Websocket events to subscribe to. 46 | /// Look at the in-official documentation for event strings to subscribe to. 47 | /// 48 | /// 49 | /// 50 | /// e.g.: [LcuSubscriptionType::JsonApiEvent]\("/lol-gameflow/v1/gameflow-phase".to_string()) 51 | #[derive(Debug, Clone)] 52 | pub enum LcuSubscriptionType { 53 | AllJsonApiEvents, 54 | AllLcdsEvents, 55 | JsonApiEvent(String), 56 | LcdsEvent(String), 57 | } 58 | 59 | impl Display for LcuSubscriptionType { 60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 | match self { 62 | LcuSubscriptionType::AllJsonApiEvents => f.write_str("OnJsonApiEvent"), 63 | LcuSubscriptionType::AllLcdsEvents => f.write_str("OnJsonApiEvent"), 64 | LcuSubscriptionType::JsonApiEvent(s) => f.write_str(&format!( 65 | "OnJsonApiEvent_{}", 66 | s.trim_start_matches('/').replace('/', "_") 67 | )), 68 | LcuSubscriptionType::LcdsEvent(s) => f.write_str(&format!( 69 | "OnLcdsEvent_{}", 70 | s.trim_start_matches('/').replace('/', "_") 71 | )), 72 | } 73 | } 74 | } 75 | 76 | /// Custom deserializer to differentiate between the different subscription types 77 | impl<'de> Deserialize<'de> for LcuSubscriptionType { 78 | fn deserialize(deserializer: D) -> Result 79 | where 80 | D: Deserializer<'de>, 81 | { 82 | let s = String::deserialize(deserializer)?; 83 | 84 | if s.starts_with("OnJsonApiEvent") { 85 | if s.len() > 14 { 86 | Ok(LcuSubscriptionType::JsonApiEvent(s[15..].to_string())) 87 | } else { 88 | Ok(LcuSubscriptionType::AllJsonApiEvents) 89 | } 90 | } else if s.starts_with("OnLcdsApiEvent") { 91 | if s.len() > 14 { 92 | Ok(LcuSubscriptionType::LcdsEvent(s[15..].to_string())) 93 | } else { 94 | Ok(LcuSubscriptionType::AllLcdsEvents) 95 | } 96 | } else { 97 | Err(de::Error::custom(format!( 98 | "Unknown SubscriptionType: {}", 99 | s 100 | ))) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/rest.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::shaco::utils::{process_info, request::build_reqwest_client}; 4 | 5 | /// A client for the League-Client(LCU) REST API 6 | pub struct RESTClient { 7 | port: String, 8 | reqwest_client: reqwest::Client, 9 | } 10 | 11 | type Error = Box; 12 | 13 | impl RESTClient { 14 | /// Create a new instance of the LCU REST wrapper 15 | pub fn new(auth_token:String,port:String) -> Result { 16 | // let (auth_token, port) = process_info::get_auth_info()?; 17 | let reqwest_client = build_reqwest_client(Some(auth_token)); 18 | Ok(Self { 19 | port, 20 | reqwest_client, 21 | }) 22 | } 23 | 24 | /// Make a get request to the specified endpoint 25 | pub async fn get(&self, endpoint: &str) -> Result { 26 | self.reqwest_client 27 | .get(format!("https://127.0.0.1:{}{}", self.port, endpoint)) 28 | .send() 29 | .await? 30 | .error_for_status()? 31 | .json() 32 | .await 33 | .or_else(|_| Ok(serde_json::Value::Null)) 34 | } 35 | 36 | /// Make a post request to the specified endpoint 37 | pub async fn post( 38 | &self, 39 | endpoint: &str, 40 | body: T, 41 | ) -> Result { 42 | self.reqwest_client 43 | .post(format!("https://127.0.0.1:{}{}", self.port, endpoint)) 44 | .json(&body) 45 | .send() 46 | .await? 47 | .error_for_status()? 48 | .json() 49 | .await 50 | .or_else(|_| Ok(serde_json::Value::Null)) 51 | } 52 | 53 | /// Make a put request to the specified endpoint 54 | pub async fn put( 55 | &self, 56 | endpoint: &str, 57 | body: T, 58 | ) -> Result { 59 | self.reqwest_client 60 | .put(format!("https://127.0.0.1:{}{}", self.port, endpoint)) 61 | .json(&body) 62 | .send() 63 | .await? 64 | .error_for_status()? 65 | .json() 66 | .await 67 | .or_else(|_| Ok(serde_json::Value::Null)) 68 | } 69 | 70 | /// Make a delete request to the specified endpoint 71 | pub async fn delete(&self, endpoint: &str) -> Result { 72 | self.reqwest_client 73 | .delete(format!("https://127.0.0.1:{}{}", self.port, endpoint)) 74 | .send() 75 | .await? 76 | .error_for_status()? 77 | .json() 78 | .await 79 | .or_else(|_| Ok(serde_json::Value::Null)) 80 | } 81 | 82 | /// Make a patch request to the specified endpoint 83 | pub async fn patch( 84 | &self, 85 | endpoint: &str, 86 | body: T, 87 | ) -> Result { 88 | self.reqwest_client 89 | .patch(format!("https://127.0.0.1:{}{}", self.port, endpoint)) 90 | .json(&body) 91 | .send() 92 | .await? 93 | .error_for_status()? 94 | .json() 95 | .await 96 | .or_else(|_| Ok(serde_json::Value::Null)) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/riotgames.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEIDCCAwgCCQDJC+QAdVx4UDANBgkqhkiG9w0BAQUFADCB0TELMAkGA1UEBhMC 3 | VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFTATBgNVBAcTDFNhbnRhIE1vbmljYTET 4 | MBEGA1UEChMKUmlvdCBHYW1lczEdMBsGA1UECxMUTG9MIEdhbWUgRW5naW5lZXJp 5 | bmcxMzAxBgNVBAMTKkxvTCBHYW1lIEVuZ2luZWVyaW5nIENlcnRpZmljYXRlIEF1 6 | dGhvcml0eTEtMCsGCSqGSIb3DQEJARYeZ2FtZXRlY2hub2xvZ2llc0ByaW90Z2Ft 7 | ZXMuY29tMB4XDTEzMTIwNDAwNDgzOVoXDTQzMTEyNzAwNDgzOVowgdExCzAJBgNV 8 | BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRUwEwYDVQQHEwxTYW50YSBNb25p 9 | Y2ExEzARBgNVBAoTClJpb3QgR2FtZXMxHTAbBgNVBAsTFExvTCBHYW1lIEVuZ2lu 10 | ZWVyaW5nMTMwMQYDVQQDEypMb0wgR2FtZSBFbmdpbmVlcmluZyBDZXJ0aWZpY2F0 11 | ZSBBdXRob3JpdHkxLTArBgkqhkiG9w0BCQEWHmdhbWV0ZWNobm9sb2dpZXNAcmlv 12 | dGdhbWVzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKoJemF/ 13 | 6PNG3GRJGbjzImTdOo1OJRDI7noRwJgDqkaJFkwv0X8aPUGbZSUzUO23cQcCgpYj 14 | 21ygzKu5dtCN2EcQVVpNtyPuM2V4eEGr1woodzALtufL3Nlyh6g5jKKuDIfeUBHv 15 | JNyQf2h3Uha16lnrXmz9o9wsX/jf+jUAljBJqsMeACOpXfuZy+YKUCxSPOZaYTLC 16 | y+0GQfiT431pJHBQlrXAUwzOmaJPQ7M6mLfsnpHibSkxUfMfHROaYCZ/sbWKl3lr 17 | ZA9DbwaKKfS1Iw0ucAeDudyuqb4JntGU/W0aboKA0c3YB02mxAM4oDnqseuKV/CX 18 | 8SQAiaXnYotuNXMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAf3KPmddqEqqC8iLs 19 | lcd0euC4F5+USp9YsrZ3WuOzHqVxTtX3hR1scdlDXNvrsebQZUqwGdZGMS16ln3k 20 | WObw7BbhU89tDNCN7Lt/IjT4MGRYRE+TmRc5EeIXxHkQ78bQqbmAI3GsW+7kJsoO 21 | q3DdeE+M+BUJrhWorsAQCgUyZO166SAtKXKLIcxa+ddC49NvMQPJyzm3V+2b1roP 22 | SvD2WV8gRYUnGmy/N0+u6ANq5EsbhZ548zZc+BI4upsWChTLyxt2RxR7+uGlS1+5 23 | EcGfKZ+g024k/J32XP4hdho7WYAS2xMiV83CfLR/MNi8oSMaVQTdKD8cpgiWJk3L 24 | XWehWA== 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod process_info; 2 | pub(crate) mod request; 3 | -------------------------------------------------------------------------------- /src-tauri/src/shaco/utils/process_info.rs: -------------------------------------------------------------------------------- 1 | use base64::{engine::general_purpose, Engine}; 2 | use sysinfo::{ProcessExt, System, SystemExt}; 3 | 4 | use crate::shaco::error::ProcessInfoError; 5 | 6 | #[cfg(target_os = "windows")] 7 | const TARGET_PROCESS: &str = "LeagueClientUx.exe"; 8 | #[cfg(target_os = "linux")] 9 | const TARGET_PROCESS: &str = "LeagueClientUx."; 10 | #[cfg(target_os = "macos")] 11 | const TARGET_PROCESS: &str = "LeagueClientUx"; 12 | 13 | pub(crate) fn get_auth_info() -> Result<(String, String), ProcessInfoError> { 14 | let mut sys = System::new_all(); 15 | sys.refresh_processes(); 16 | 17 | let args = sys 18 | .processes() 19 | .values() 20 | .find(|p| p.name() == TARGET_PROCESS) 21 | .map(|p| p.cmd()) 22 | .ok_or(ProcessInfoError::ProcessNotAvailable)?; 23 | 24 | let port = args 25 | .iter() 26 | .find(|arg| arg.starts_with("--app-port=")) 27 | .map(|arg| arg.strip_prefix("--app-port=").unwrap().to_string()) 28 | .ok_or(ProcessInfoError::PortNotFound)?; 29 | let auth_token = args 30 | .iter() 31 | .find(|arg| arg.starts_with("--remoting-auth-token=")) 32 | .map(|arg| { 33 | arg.strip_prefix("--remoting-auth-token=") 34 | .unwrap() 35 | .to_string() 36 | }) 37 | .ok_or(ProcessInfoError::AuthTokenNotFound)?; 38 | 39 | Ok(( 40 | general_purpose::STANDARD.encode(format!("riot:{}", auth_token)), 41 | port, 42 | )) 43 | } -------------------------------------------------------------------------------- /src-tauri/src/shaco/utils/request.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use reqwest::{header, Certificate}; 4 | 5 | pub(crate) fn build_reqwest_client(auth_token: Option) -> reqwest::Client { 6 | let cert = Certificate::from_pem(include_bytes!("../riotgames.pem")).unwrap(); 7 | let mut headers = header::HeaderMap::new(); 8 | 9 | if let Some(token) = auth_token { 10 | let auth_header = 11 | header::HeaderValue::from_str(format!("Basic {}", token).as_str()).unwrap(); 12 | headers.insert("Authorization", auth_header); 13 | } 14 | 15 | reqwest::ClientBuilder::new() 16 | .add_root_certificate(cert) 17 | .default_headers(headers) 18 | .timeout(Duration::from_millis(3000)) 19 | .build() 20 | .unwrap() 21 | } 22 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Frank", 4 | "version": "3.15.1505", 5 | "identifier": "Frank", 6 | "build": { 7 | "beforeDevCommand": "pnpm dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "pnpm build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "label": "background", 16 | "title": "background", 17 | "url": "src/background/index.html", 18 | "width": 4, 19 | "height": 12, 20 | "visible": false, 21 | "decorations": false 22 | } 23 | ], 24 | "security": { 25 | "csp": null 26 | } 27 | }, 28 | "bundle": { 29 | "active": true, 30 | "targets": "all", 31 | "icon": [ 32 | "icons/32x32.png", 33 | "icons/128x128.png", 34 | "icons/128x128@2x.png", 35 | "icons/icon.icns", 36 | "icons/icon.ico" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/assets/font/DingTalk JinBuTi.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/font/DingTalk JinBuTi.ttf -------------------------------------------------------------------------------- /src/assets/icon/Frank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/icon/Frank.png -------------------------------------------------------------------------------- /src/assets/icon/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/icon/app-icon.png -------------------------------------------------------------------------------- /src/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /src/assets/icon/siteLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/icon/siteLogo.png -------------------------------------------------------------------------------- /src/assets/matchImage/Alipay.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/Alipay.JPEG -------------------------------------------------------------------------------- /src/assets/matchImage/Wechat.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/Wechat.JPEG -------------------------------------------------------------------------------- /src/assets/matchImage/assists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/assists.png -------------------------------------------------------------------------------- /src/assets/matchImage/firstBlood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/firstBlood.png -------------------------------------------------------------------------------- /src/assets/matchImage/fiveKills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/fiveKills.png -------------------------------------------------------------------------------- /src/assets/matchImage/fourKills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/fourKills.png -------------------------------------------------------------------------------- /src/assets/matchImage/friend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/friend.png -------------------------------------------------------------------------------- /src/assets/matchImage/god.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/god.png -------------------------------------------------------------------------------- /src/assets/matchImage/goldEarned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/goldEarned.png -------------------------------------------------------------------------------- /src/assets/matchImage/kills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/kills.png -------------------------------------------------------------------------------- /src/assets/matchImage/mvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/mvp.png -------------------------------------------------------------------------------- /src/assets/matchImage/quitGame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/quitGame.png -------------------------------------------------------------------------------- /src/assets/matchImage/svp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/svp.png -------------------------------------------------------------------------------- /src/assets/matchImage/threeKills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/threeKills.png -------------------------------------------------------------------------------- /src/assets/matchImage/totalDamageDealtToChampions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/totalDamageDealtToChampions.png -------------------------------------------------------------------------------- /src/assets/matchImage/totalDamageTaken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/totalDamageTaken.png -------------------------------------------------------------------------------- /src/assets/matchImage/totalMinionsKilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/totalMinionsKilled.png -------------------------------------------------------------------------------- /src/assets/matchImage/turretKills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/turretKills.png -------------------------------------------------------------------------------- /src/assets/matchImage/visionScore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/matchImage/visionScore.png -------------------------------------------------------------------------------- /src/assets/runes/5001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5001.png -------------------------------------------------------------------------------- /src/assets/runes/5005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5005.png -------------------------------------------------------------------------------- /src/assets/runes/5007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5007.png -------------------------------------------------------------------------------- /src/assets/runes/5008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5008.png -------------------------------------------------------------------------------- /src/assets/runes/5010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5010.png -------------------------------------------------------------------------------- /src/assets/runes/5011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5011.png -------------------------------------------------------------------------------- /src/assets/runes/5013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/5013.png -------------------------------------------------------------------------------- /src/assets/runes/8000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8000.png -------------------------------------------------------------------------------- /src/assets/runes/8005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8005.png -------------------------------------------------------------------------------- /src/assets/runes/8008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8008.png -------------------------------------------------------------------------------- /src/assets/runes/8009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8009.png -------------------------------------------------------------------------------- /src/assets/runes/8010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8010.png -------------------------------------------------------------------------------- /src/assets/runes/8014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8014.png -------------------------------------------------------------------------------- /src/assets/runes/8017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8017.png -------------------------------------------------------------------------------- /src/assets/runes/8021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8021.png -------------------------------------------------------------------------------- /src/assets/runes/8100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8100.png -------------------------------------------------------------------------------- /src/assets/runes/8105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8105.png -------------------------------------------------------------------------------- /src/assets/runes/8106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8106.png -------------------------------------------------------------------------------- /src/assets/runes/8112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8112.png -------------------------------------------------------------------------------- /src/assets/runes/8126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8126.png -------------------------------------------------------------------------------- /src/assets/runes/8128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8128.png -------------------------------------------------------------------------------- /src/assets/runes/8135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8135.png -------------------------------------------------------------------------------- /src/assets/runes/8137.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8137.png -------------------------------------------------------------------------------- /src/assets/runes/8139.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8139.png -------------------------------------------------------------------------------- /src/assets/runes/8140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8140.png -------------------------------------------------------------------------------- /src/assets/runes/8141.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8141.png -------------------------------------------------------------------------------- /src/assets/runes/8143.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8143.png -------------------------------------------------------------------------------- /src/assets/runes/8200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8200.png -------------------------------------------------------------------------------- /src/assets/runes/8210.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8210.png -------------------------------------------------------------------------------- /src/assets/runes/8214.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8214.png -------------------------------------------------------------------------------- /src/assets/runes/8224.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8224.png -------------------------------------------------------------------------------- /src/assets/runes/8226.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8226.png -------------------------------------------------------------------------------- /src/assets/runes/8229.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8229.png -------------------------------------------------------------------------------- /src/assets/runes/8230.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8230.png -------------------------------------------------------------------------------- /src/assets/runes/8232.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8232.png -------------------------------------------------------------------------------- /src/assets/runes/8233.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8233.png -------------------------------------------------------------------------------- /src/assets/runes/8234.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8234.png -------------------------------------------------------------------------------- /src/assets/runes/8236.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8236.png -------------------------------------------------------------------------------- /src/assets/runes/8237.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8237.png -------------------------------------------------------------------------------- /src/assets/runes/8242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8242.png -------------------------------------------------------------------------------- /src/assets/runes/8275.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8275.png -------------------------------------------------------------------------------- /src/assets/runes/8299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8299.png -------------------------------------------------------------------------------- /src/assets/runes/8300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8300.png -------------------------------------------------------------------------------- /src/assets/runes/8304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8304.png -------------------------------------------------------------------------------- /src/assets/runes/8306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8306.png -------------------------------------------------------------------------------- /src/assets/runes/8313.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8313.png -------------------------------------------------------------------------------- /src/assets/runes/8316.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8316.png -------------------------------------------------------------------------------- /src/assets/runes/8321.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8321.png -------------------------------------------------------------------------------- /src/assets/runes/8345.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8345.png -------------------------------------------------------------------------------- /src/assets/runes/8347.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8347.png -------------------------------------------------------------------------------- /src/assets/runes/8351.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8351.png -------------------------------------------------------------------------------- /src/assets/runes/8352.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8352.png -------------------------------------------------------------------------------- /src/assets/runes/8360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8360.png -------------------------------------------------------------------------------- /src/assets/runes/8369.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8369.png -------------------------------------------------------------------------------- /src/assets/runes/8400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8400.png -------------------------------------------------------------------------------- /src/assets/runes/8401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8401.png -------------------------------------------------------------------------------- /src/assets/runes/8410.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8410.png -------------------------------------------------------------------------------- /src/assets/runes/8429.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8429.png -------------------------------------------------------------------------------- /src/assets/runes/8437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8437.png -------------------------------------------------------------------------------- /src/assets/runes/8439.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8439.png -------------------------------------------------------------------------------- /src/assets/runes/8444.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8444.png -------------------------------------------------------------------------------- /src/assets/runes/8446.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8446.png -------------------------------------------------------------------------------- /src/assets/runes/8451.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8451.png -------------------------------------------------------------------------------- /src/assets/runes/8453.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8453.png -------------------------------------------------------------------------------- /src/assets/runes/8463.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8463.png -------------------------------------------------------------------------------- /src/assets/runes/8465.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8465.png -------------------------------------------------------------------------------- /src/assets/runes/8473.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/8473.png -------------------------------------------------------------------------------- /src/assets/runes/9101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/9101.png -------------------------------------------------------------------------------- /src/assets/runes/9103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/9103.png -------------------------------------------------------------------------------- /src/assets/runes/9104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/9104.png -------------------------------------------------------------------------------- /src/assets/runes/9105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/9105.png -------------------------------------------------------------------------------- /src/assets/runes/9111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/9111.png -------------------------------------------------------------------------------- /src/assets/runes/9923.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/runes/9923.png -------------------------------------------------------------------------------- /src/assets/svg/assassin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/fighter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/svg/image.png -------------------------------------------------------------------------------- /src/assets/svg/imageDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Java-S12138/frank/20581109e7f79cb12ebca36912b51fb1e4cfb3a4/src/assets/svg/imageDark.png -------------------------------------------------------------------------------- /src/assets/svg/mage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/marksman.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/support.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/tank.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/tLevel/bot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/tLevel/jug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/tLevel/mid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/tLevel/sup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/tLevel/t0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/tLevel/t1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/tLevel/t2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/tLevel/t3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/tLevel/t4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/tLevel/t5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/tLevel/top.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/background/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Frank Background 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/background/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface ConfigSettingTypes { 2 | autoPickChampion: { championId: string; isAuto: boolean }; 3 | autoBanChampion: { championId: string; isAuto: boolean }; 4 | autoIsOne:boolean; 5 | autoAccept: number; 6 | theme: string; 7 | isGameInWindow:boolean; 8 | isGameInTips:boolean; 9 | autoWriteBlock:boolean; 10 | inWinOpacity:number; 11 | warmTips:{ 12 | autoRune:boolean, 13 | rankTips:boolean, 14 | teamTips:boolean, 15 | } 16 | } 17 | export interface ConfigRank { 18 | tier: number; 19 | lane: string; 20 | is101: boolean; 21 | } 22 | export interface sumInfoTypes{ 23 | name:string; 24 | puuid:string; 25 | summonerId:number; 26 | platformId:string; 27 | } 28 | 29 | export interface ChampionSession { 30 | actions: Array; 31 | allowBattleBoost: boolean; 32 | allowDuplicatePicks: boolean; 33 | allowLockedEvents: boolean; 34 | allowRerolling: boolean; 35 | allowSkinSelection: boolean; 36 | bans: Bans; 37 | benchChampions: any[]; 38 | benchEnabled: boolean; 39 | boostableSkinCount: number; 40 | chatDetails: ChatDetails; 41 | counter: number; 42 | gameId: number; 43 | hasSimultaneousBans: boolean; 44 | hasSimultaneousPicks: boolean; 45 | isCustomGame: boolean; 46 | isSpectating: boolean; 47 | localPlayerCellId: number; 48 | lockedEventIndex: number; 49 | myTeam: MyTeam[]; 50 | pickOrderSwaps: any[]; 51 | recoveryCounter: number; 52 | rerollsRemaining: number; 53 | skipChampionSelect: boolean; 54 | theirTeam: any[]; 55 | timer: Timer; 56 | trades: any[]; 57 | } 58 | 59 | export interface Action { 60 | actorCellId: number; 61 | championId: number; 62 | completed: boolean; 63 | id: number; 64 | isAllyAction: boolean; 65 | isInProgress: boolean; 66 | pickTurn: number; 67 | type: string; 68 | } 69 | 70 | export interface Bans { 71 | myTeamBans: any[]; 72 | numBans: number; 73 | theirTeamBans: any[]; 74 | } 75 | 76 | export interface ChatDetails { 77 | mucJwtDto: MucJwtDto; 78 | multiUserChatId: string; 79 | multiUserChatPassword: string; 80 | } 81 | 82 | export interface MucJwtDto { 83 | channelClaim: string; 84 | domain: string; 85 | jwt: string; 86 | targetRegion: string; 87 | } 88 | 89 | export interface MyTeam { 90 | assignedPosition: string; 91 | cellId: number; 92 | championId: number; 93 | championPickIntent: number; 94 | nameVisibilityType: string; 95 | obfuscatedPuuid: string; 96 | obfuscatedSummonerId: number; 97 | puuid: string; 98 | selectedSkinId: number; 99 | spell1Id: number; 100 | spell2Id: number; 101 | summonerId: number; 102 | team: number; 103 | wardSkinId: number; 104 | } 105 | 106 | export interface Timer { 107 | adjustedTimeLeftInPhase: number; 108 | internalNowInEpochMs: number; 109 | isInfinite: boolean; 110 | phase: string; 111 | totalTimeInPhase: number; 112 | } 113 | -------------------------------------------------------------------------------- /src/background/utils/TaskTracker.ts: -------------------------------------------------------------------------------- 1 | export class TaskTracker { 2 | private currentMonth: number; 3 | private taskCount: number; 4 | private storageKey: string; 5 | 6 | constructor(storageKey: string = "taskTracker") { 7 | this.storageKey = storageKey; 8 | const now = new Date(); 9 | const savedData = this.loadData(); 10 | 11 | if (savedData) { 12 | // 如果有保存的数据,加载并检查月份是否需要重置 13 | this.currentMonth = savedData.currentMonth; 14 | this.taskCount = savedData.taskCount; 15 | this.checkMonth(); 16 | } else { 17 | // 如果没有保存的数据,初始化 18 | this.currentMonth = now.getMonth() + 1; 19 | this.taskCount = 0; 20 | this.saveData(); 21 | } 22 | } 23 | 24 | // 保存数据到 localStorage 25 | private saveData(): void { 26 | const data = { 27 | currentMonth: this.currentMonth, 28 | taskCount: this.taskCount, 29 | }; 30 | localStorage.setItem(this.storageKey, JSON.stringify(data)); 31 | } 32 | 33 | // 从 localStorage 加载数据 34 | private loadData(): { currentMonth: number; taskCount: number } | null { 35 | const data = localStorage.getItem(this.storageKey); 36 | return data ? JSON.parse(data) : null; 37 | } 38 | 39 | // 检测当前月份是否变更 40 | private checkMonth(): void { 41 | const now = new Date(); 42 | const newMonth = now.getMonth() + 1; 43 | 44 | if (newMonth !== this.currentMonth) { 45 | this.currentMonth = newMonth; 46 | this.taskCount = 0; // 如果月份变化,清理数据 47 | this.saveData(); 48 | } 49 | } 50 | 51 | // 完成任务的方法 52 | public completeTask(): void { 53 | this.checkMonth(); 54 | 55 | if (this.taskCount >= 24) { 56 | return; 57 | } 58 | 59 | this.taskCount += 1; 60 | this.saveData(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/background/utils/config.ts: -------------------------------------------------------------------------------- 1 | import {ConfigRank, ConfigSettingTypes} from "../types"; 2 | import {invokeLcu} from "@/lcu"; 3 | 4 | const configSetting: ConfigSettingTypes = { 5 | 'autoPickChampion': { 6 | championId: "157", 7 | isAuto: false 8 | }, 9 | 'autoBanChampion': { 10 | championId: "101", 11 | isAuto: false 12 | }, 13 | 'autoIsOne':true, 14 | 'autoAccept': 50, 15 | 'theme': 'light', 16 | 'isGameInWindow':true, 17 | 'isGameInTips':false, 18 | 'autoWriteBlock':true, 19 | 'inWinOpacity':100, 20 | 'warmTips':{ 21 | autoRune:false, 22 | rankTips:false, 23 | teamTips:false 24 | } 25 | } 26 | 27 | 28 | const configRank: ConfigRank = { 29 | 'tier': 200, 30 | 'lane': 'mid', 31 | 'is101': true, 32 | } 33 | 34 | const addConfig = (configName:string,configObj:any) => { 35 | const localS = JSON.parse((localStorage.getItem(configName))) 36 | if (Object.keys(localS).length === Object.keys(configObj).length){ 37 | return 38 | } 39 | 40 | for (const configKey of Object.keys(configObj)) { 41 | if (!localS.hasOwnProperty(configKey)) { 42 | // @ts-ignore 43 | localS[configKey] = configObj[configKey] 44 | localStorage.setItem(configName, JSON.stringify(localS)) 45 | } 46 | } 47 | } 48 | 49 | export const configInit = () => { 50 | if (localStorage.getItem('configSetting') === null) { 51 | localStorage.setItem('configSetting', JSON.stringify(configSetting)) 52 | localStorage.setItem('configRank', JSON.stringify(configRank)) 53 | } else { 54 | addConfig('configSetting',configSetting) 55 | addConfig('configRank',configRank) 56 | } 57 | } 58 | 59 | export const getClientPath = async () => { 60 | const clientPath = await invokeLcu('get', '/data-store/v1/install-dir'); 61 | 62 | if (clientPath === null) return false; // 早期返回,避免后续代码执行 63 | 64 | const storedPath = localStorage.getItem('clientPath'); 65 | const updatedPath = clientPath.replace('LeagueClient', 'TCLS\\client.exe'); 66 | 67 | // 只在路径不一致时更新 68 | if (storedPath?.toLowerCase() !== updatedPath.toLowerCase()) { 69 | localStorage.setItem('clientPath', updatedPath); 70 | } 71 | return true 72 | } 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/background/utils/creatWindow.ts: -------------------------------------------------------------------------------- 1 | import {WebviewWindow} from "@tauri-apps/api/webviewWindow"; 2 | 3 | export class MainWindow { 4 | constructor() { 5 | const webview = new WebviewWindow('mainWindow', { 6 | title: 'Frank', 7 | url: 'src/main/index.html', 8 | width: 320, 9 | height: 720, 10 | visible: false, 11 | resizable: false, 12 | decorations: false, 13 | center: true, 14 | transparent: true 15 | }) 16 | webview.once('tauri://created', async function () { 17 | webview.show() 18 | }) 19 | } 20 | } 21 | 22 | export class QueryMatchWindow { 23 | constructor() { 24 | const webview = new WebviewWindow('queryMatchWindow', { 25 | title: '我的战绩', 26 | url: 'src/queryMatch/index.html', 27 | width: 1174, 28 | height: 668, 29 | resizable: false, 30 | decorations: false, 31 | center: true, 32 | visible: false, 33 | transparent: true 34 | }) 35 | webview.once('tauri://created', async function () { 36 | webview.show() 37 | }) 38 | } 39 | } 40 | 41 | export class MatchAnalysisWindow { 42 | constructor() { 43 | const webview = new WebviewWindow('matchAnalysisWindow', { 44 | title: '战绩分析', 45 | url: 'src/matchAnalysis/index.html', 46 | width: 1024, 47 | height: 576, 48 | resizable: false, 49 | decorations: false, 50 | center: true, 51 | visible: false, 52 | transparent: true 53 | }) 54 | webview.once('tauri://created', async function () { 55 | webview.show() 56 | }) 57 | } 58 | } 59 | 60 | export class RecentMatchWindow { 61 | 62 | constructor() { 63 | const webview = new WebviewWindow('recentMatchWindow', { 64 | title: '对局详情', 65 | url: 'src/recentMatch/index.html', 66 | width: 1254, 67 | height: 562, 68 | resizable: false, 69 | decorations: false, 70 | center: true, 71 | visible: false, 72 | skipTaskbar: true, 73 | alwaysOnTop: true, 74 | transparent: true 75 | }) 76 | webview.once('tauri://created', async function () { 77 | webview.show() 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/background/utils/tray.ts: -------------------------------------------------------------------------------- 1 | import {TrayIcon, TrayIconEvent, TrayIconOptions} from '@tauri-apps/api/tray'; 2 | import {defaultWindowIcon} from '@tauri-apps/api/app'; 3 | import {Menu} from '@tauri-apps/api/menu'; 4 | import {exit} from "@tauri-apps/plugin-process"; 5 | import {window} from "@tauri-apps/api"; 6 | import {QueryMatchWindow, RecentMatchWindow} from "./creatWindow.ts"; 7 | import {Image} from "@tauri-apps/api/image"; 8 | 9 | const showMain = (isHide:boolean) => { 10 | window.Window.getByLabel('mainWindow').then(async (win) => { 11 | if (!win) return; 12 | 13 | if (isHide) { 14 | await win.hide(); 15 | return; 16 | } 17 | 18 | const isVisible = await win.isVisible(); 19 | const isMinimized = await win.isMinimized(); 20 | 21 | if (!isVisible) { 22 | await win.show(); 23 | } 24 | 25 | if (isMinimized) { 26 | await win.unminimize(); 27 | } else if (isVisible) { 28 | await win.hide(); // 如果窗口已经显示,则隐藏 29 | } 30 | }) 31 | } 32 | 33 | const menu = await Menu.new({ 34 | items: [ 35 | { 36 | id: 'showMain', 37 | text: '隐藏助手', 38 | action: () => { 39 | showMain(true); 40 | }, 41 | }, 42 | { 43 | id: 'matchDetail', 44 | text: '对局详情', 45 | action: () => { 46 | window.Window.getByLabel('recentMatchWindow').then(async (win) => { 47 | if (win === null) { 48 | new RecentMatchWindow(); 49 | }else{ 50 | if (await win.isVisible()) { 51 | win.hide(); 52 | }else { 53 | win.show(); 54 | } 55 | } 56 | }) 57 | }, 58 | }, 59 | { 60 | id: 'queryMatch', 61 | text: '我的战绩', 62 | action: () => { 63 | window.Window.getByLabel('queryMatchWindow').then((win) => { 64 | if (win === null) { 65 | new QueryMatchWindow() 66 | } 67 | }) 68 | }, 69 | }, 70 | { 71 | id: 'quit', 72 | text: '退出软件', 73 | action: () => { 74 | exit(1); 75 | }, 76 | }, 77 | { 78 | id: 'author', 79 | text: '@Java_S', 80 | }, 81 | ], 82 | }); 83 | 84 | const leftClick = async (event: TrayIconEvent) => { 85 | if (event.type === "Click" && event.button==='Left' && event.buttonState==='Down') { 86 | showMain(false); 87 | } 88 | } 89 | 90 | const options:TrayIconOptions = { 91 | icon: await defaultWindowIcon() as Image, 92 | menu, 93 | menuOnLeftClick: false, 94 | action:leftClick 95 | }; 96 | 97 | TrayIcon.new(options); 98 | -------------------------------------------------------------------------------- /src/lcu/aboutMatch.ts: -------------------------------------------------------------------------------- 1 | import {invoke} from "@tauri-apps/api/core"; 2 | import {Games, LcuMatchList} from "./types/queryMatchLcuTypes"; 3 | 4 | // 辅助函数:处理单次请求 5 | const fetchMatchHistory = async (puuid: string, begIndex: number, endIndex: number): Promise => { 6 | const uri = `/lol-match-history/v1/products/lol/${puuid}/matches?begIndex=${begIndex}&endIndex=${endIndex}`; 7 | const matchList = await invoke("get_match_list", { uri }); 8 | if (matchList === null) return []; 9 | return matchList.games.games || []; 10 | } 11 | 12 | const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); 13 | 14 | // 辅助函数:拆分请求区间 15 | const splitRequests = async (puuid: string, begIndex: number, endIndex: number): Promise => { 16 | const step = 10; // 每段的范围为 10 17 | let allGames: Games[] = []; 18 | let currentBegIndex = begIndex; 19 | 20 | // 循环拆分请求 21 | while (currentBegIndex < endIndex) { 22 | const currentEndIndex = Math.min(currentBegIndex + step - 1, endIndex); 23 | const games = await fetchMatchHistory(puuid, currentBegIndex, currentEndIndex); 24 | 25 | if (games.length > 0) { 26 | allGames = allGames.concat(games); 27 | } 28 | 29 | currentBegIndex = currentEndIndex + 1; 30 | await delay(200); 31 | } 32 | 33 | return allGames; 34 | }; 35 | 36 | // 主函数:查询历史比赛数据 37 | export const queryMatchHistory = async (puuid: string, begIndex: number, endIndex: number): Promise => { 38 | try { 39 | let allGames: Games[] = []; 40 | // 如果 begIndex 与 endIndex 的差值大于 15,拆分请求 41 | if (endIndex - begIndex > 15) { 42 | allGames = await splitRequests(puuid, begIndex, endIndex); 43 | } else { 44 | // 如果差值不大于 15,直接请求整个范围的数据 45 | allGames = await fetchMatchHistory(puuid, begIndex, endIndex); 46 | } 47 | 48 | // 如果没有游戏数据,则返回空数组 49 | if (allGames.length === 0) { 50 | return []; 51 | } 52 | 53 | // 判断是否需要反转游戏数据 54 | if (allGames[0].gameCreation > allGames[allGames.length - 1].gameCreation) { 55 | return allGames; 56 | } 57 | 58 | return allGames.reverse(); 59 | } catch (e) { 60 | return null; 61 | } 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /src/lcu/aboutRune.ts: -------------------------------------------------------------------------------- 1 | import {invokeLcu} from "./index"; 2 | import {LcuRuneInfo} from "./types/runeLcuTypes"; 3 | interface T { 4 | alias: string; 5 | name: string; 6 | position: string; 7 | pickCount: number; 8 | winRate: string; 9 | primaryStyleId: number; 10 | subStyleId: number; 11 | selectedPerkIds: number[]; 12 | score: number; 13 | type: string; 14 | } 15 | 16 | // 应用符文页面 17 | export const applyRunePage = async (data: T) => { 18 | try { 19 | // 获取符文页信息 20 | const currentRuneList: Array|null = await invokeLcu('get', '/lol-perks/v1/pages') 21 | if (currentRuneList===null) return false; 22 | 23 | const current = currentRuneList.find((i: LcuRuneInfo) => i.isDeletable && !i.isTemporary) 24 | if (current === undefined) { 25 | return false 26 | } 27 | // 删除当前符文页 28 | await invokeLcu('delete', `/lol-perks/v1/pages/${current.id}`) 29 | // 写入新的符文页 30 | await invokeLcu('post', '/lol-perks/v1/pages', JSON.stringify(data)) 31 | return true 32 | } catch (e) { 33 | return false 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/lcu/autoBP.ts: -------------------------------------------------------------------------------- 1 | import {invokeLcu} from "./index"; 2 | import {ConfigSettingTypes} from "@/background/types"; 3 | 4 | //选择或者禁用英雄共用函数 5 | const champSelectPatchAction = async (actionID:any, champId:any, type:string,idSetInterval:number) => { 6 | const localBody:any = { 7 | "completed": true, 8 | "type": type, 9 | "championId": champId 10 | } 11 | invokeLcu('patch',`/lol-champ-select/v1/session/actions/${actionID}`,JSON.stringify(localBody)).then(value => { 12 | clearInterval(idSetInterval) 13 | if (!value?.success && type==='pick'){ 14 | clearInterval(idSetInterval) 15 | } 16 | }) 17 | } 18 | 19 | // 获取选人会话 20 | export const champSelectSession = async (idSetInterval:number,config:ConfigSettingTypes) => { 21 | try { 22 | const res = await invokeLcu('get','/lol-champ-select/v1/session') 23 | const localPlayerCellId = res?.localPlayerCellId 24 | const actions = res?.actions 25 | if (actions === undefined){ 26 | clearInterval(idSetInterval) 27 | } 28 | 29 | for (let action of actions) { 30 | for (let actionElement of action) { 31 | if (actionElement.actorCellId == localPlayerCellId && actionElement.isInProgress) { 32 | if (actionElement.type === 'pick' && config.autoPickChampion.isAuto) { 33 | champSelectPatchAction(actionElement.id,config.autoPickChampion.championId,'pick',idSetInterval) 34 | if (config.autoIsOne){ 35 | config.autoPickChampion.isAuto = false 36 | localStorage.setItem('configSetting',JSON.stringify(config)) 37 | } 38 | } else if (actionElement.type === 'ban'&& config.autoBanChampion.isAuto) { 39 | champSelectPatchAction(actionElement.id,config.autoBanChampion.championId,'ban',idSetInterval) 40 | if (config.autoIsOne) { 41 | config.autoBanChampion.isAuto = false 42 | localStorage.setItem('configSetting', JSON.stringify(config)) 43 | } 44 | } 45 | } 46 | if (actionElement.actorCellId == localPlayerCellId && !actionElement.isInProgress){ 47 | if (actionElement.type === 'pick' && actionElement.completed){ 48 | clearInterval(idSetInterval) 49 | } 50 | } 51 | } 52 | } 53 | }catch (e:any){ 54 | clearInterval(idSetInterval) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/lcu/index.ts: -------------------------------------------------------------------------------- 1 | import {invoke} from "@tauri-apps/api/core"; 2 | 3 | export const invokeLcu = (method: string, uri: string, body: string = ''): Promise => { 4 | return invoke("invoke_lcu", {method: method, uri: uri, body: body}) 5 | .then((result) => { 6 | if (result === null) { 7 | return null 8 | } 9 | return result as T 10 | }) 11 | .catch(() => { 12 | return null 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /src/lcu/types/SummonerTypes.d.ts: -------------------------------------------------------------------------------- 1 | export interface lcuSummonerInfo { 2 | accountId: number; 3 | displayName: string; 4 | gameName: string; 5 | internalName: string; 6 | nameChangeFlag: boolean; 7 | percentCompleteForNextLevel: number; 8 | privacy: string; 9 | profileIconId: number; 10 | puuid: string; 11 | rerollPoints: IRerollPoint; 12 | summonerId: number; 13 | summonerLevel: number; 14 | unnamed: boolean; 15 | xpSinceLastLevel: number; 16 | xpUntilNextLevel: number; 17 | success?:boolean 18 | tagLine:string 19 | } 20 | 21 | interface IRerollPoint { 22 | currentPoints: number; 23 | maxRolls: number; 24 | numberOfRolls: number; 25 | pointsCostToRoll: number; 26 | pointsToReroll: number; 27 | } 28 | 29 | export interface summonerInfo { 30 | name:string, 31 | privacy:string, 32 | imgUrl:string, 33 | lv:string|number, 34 | xp:number, 35 | puuid:string, 36 | currentId:number, 37 | tagLine:string|undefined 38 | } 39 | 40 | 41 | export interface SummonerData { 42 | summonerInfo:summonerInfo|null; 43 | rankList: string[]|null; 44 | champLevel: any[][]|null; 45 | } 46 | export interface ChampionMasteryTypes { 47 | championId: number; 48 | championLevel: number; 49 | championPoints: number; 50 | championPointsSinceLastLevel: number; 51 | championPointsUntilNextLevel: number; 52 | chestGranted: boolean; 53 | formattedChampionPoints: string; 54 | formattedMasteryGoal: string; 55 | highestGrade: string; 56 | lastPlayTime: number; 57 | playerId: number; 58 | puuid: string; 59 | tokensEarned: number; 60 | } 61 | 62 | export interface sumInfoTypes{ 63 | puuid:string; 64 | name:string 65 | summonerId:number; 66 | platformId:string; 67 | } 68 | export interface TaskTrackerTypes { 69 | currentMonth:number; 70 | taskCount:number; 71 | } 72 | -------------------------------------------------------------------------------- /src/lcu/types/runeLcuTypes.d.ts: -------------------------------------------------------------------------------- 1 | export interface LcuRuneInfo { 2 | autoModifiedSelections: any[]; 3 | current: boolean; 4 | id: number; 5 | isActive: boolean; 6 | isDeletable: boolean; 7 | isEditable: boolean; 8 | isTemporary: boolean; 9 | isValid: boolean; 10 | lastModified: number; 11 | name: string; 12 | order: number; 13 | primaryStyleId: number; 14 | selectedPerkIds: number[]; 15 | subStyleId: number; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/common/dashboard.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 87 | -------------------------------------------------------------------------------- /src/main/common/navigation.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 35 | -------------------------------------------------------------------------------- /src/main/common/searchChamp.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 68 | -------------------------------------------------------------------------------- /src/main/common/sponsor.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 47 | -------------------------------------------------------------------------------- /src/main/common/summonerMasteryChamp.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 78 | -------------------------------------------------------------------------------- /src/main/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | // @ts-ignore 3 | import App from './main.vue'; 4 | import router from './router'; 5 | import { createApp } from 'vue'; 6 | import { createPinia } from 'pinia'; 7 | 8 | createApp(App).use(router).use(createPinia()).mount('#app') 9 | -------------------------------------------------------------------------------- /src/main/main.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router'; 2 | 3 | //@ts-ignore 4 | const pageComps = import.meta.glob('../views/**/index.vue') 5 | const routes:any[] = Object.entries(pageComps).map(component=>{ 6 | const regex =/\/(\w+)\/\w+\.vue/ 7 | const match = component[0].match(regex) 8 | const pathText = match?.[1] 9 | return { 10 | path:'/'+pathText, 11 | name:pathText, 12 | component:component[1] 13 | } 14 | }) 15 | 16 | const router = createRouter({ 17 | history: createWebHashHistory(''), 18 | routes:[{ 19 | path: '/', 20 | redirect: '/home' 21 | }].concat(routes) 22 | }); 23 | 24 | export default router; 25 | 26 | -------------------------------------------------------------------------------- /src/main/store/useRune.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from 'pinia' 2 | import {champDict} from "@/resources/champList"; 3 | import {RuneStoreActions, RuneStoreState} from "@/main/views/rune/runeTypes"; 4 | import {QueryRune} from "@/main/views/rune/queryRune"; 5 | 6 | const queryRune = new QueryRune() 7 | 8 | 9 | export const useRuneStore = defineStore<'useRuneStore', RuneStoreState, {}, RuneStoreActions>('useRuneStore', { 10 | state: () => { 11 | return { 12 | currentChamp: 0, 13 | currentChampImgUrl: '', 14 | currentChampAlias: '', 15 | currentChampTitle: '', 16 | runeDataList: [] , 17 | blockDataList: [] , 18 | skillsList: [], 19 | } 20 | }, 21 | actions: { 22 | mapChampInfo(champId: number) { 23 | this.currentChamp = champId 24 | this.currentChampImgUrl = `https://game.gtimg.cn/images/lol/act/img/champion/${champDict[champId].alias}.png` 25 | this.currentChampAlias = champDict[champId].alias 26 | this.currentChampTitle = champDict[champId].title 27 | }, 28 | async initStore(champId: number){ 29 | // 如果英雄相同,说明已然存在数据 30 | if (champId === this.currentChamp){ 31 | return false 32 | } 33 | if (this.runeDataList.length !== 0 ) { 34 | this.$reset() 35 | } 36 | 37 | this.mapChampInfo(champId) 38 | 39 | 40 | const runesData = await queryRune.getRunesData(this.currentChampAlias) 41 | if (runesData !== null){ 42 | this.skillsList = runesData.skillsList 43 | this.runeDataList = runesData.runeDataList 44 | this.blockDataList = runesData.blockDataList 45 | return false 46 | }else { 47 | return true 48 | } 49 | } 50 | } 51 | }) 52 | -------------------------------------------------------------------------------- /src/main/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @font-face { 6 | font-family: "DingTalk"; 7 | src: url("../assets/font/DingTalk JinBuTi.ttf"); 8 | } 9 | 10 | :root { 11 | font-size: 16px; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 0; 16 | } 17 | 18 | body { 19 | font-family: DingTalk; 20 | -webkit-user-select: none; 21 | } 22 | 23 | .main { 24 | height: 100vh; 25 | weight: 100vw; 26 | box-sizing: border-box; 27 | padding: .5rem .5rem 3.5rem .5rem; 28 | position: relative; 29 | } 30 | .mainContent { 31 | height: 616px; 32 | width: 100%; 33 | display: flex; 34 | justify-content: space-between; 35 | flex-direction: column; 36 | } 37 | 38 | .dragDiv { 39 | width: 100vw; 40 | height: 30px; 41 | position: absolute; 42 | top: -8px; 43 | left: -8px; 44 | } 45 | 46 | .navDiv { 47 | border: 1px solid rgba(255,255,255,0.09); 48 | box-sizing: border-box; 49 | display: flex; 50 | align-items: center; 51 | position: absolute; 52 | bottom: 0px; 53 | left: 0px; 54 | width: 100vw; 55 | height: 48px; 56 | border-top-left-radius: .75rem; 57 | border-top-right-radius: .75rem; 58 | } 59 | 60 | .avatarEffect { 61 | animation: light 4s ease-in-out infinite; 62 | transition: 0.5s; 63 | } 64 | 65 | @keyframes light { 66 | 0% { 67 | box-shadow: 0 0 4px #ff6666; 68 | } 69 | 70 | 25% { 71 | box-shadow: 0 0 16px #18a058; 72 | } 73 | 74 | 50% { 75 | box-shadow: 0 0 4px #2080f0; 76 | } 77 | 78 | 75% { 79 | box-shadow: 0 0 16px #f0a020; 80 | } 81 | 82 | 100% { 83 | box-shadow: 0 0 4px #d03050; 84 | } 85 | } 86 | 87 | .imgItem { 88 | width: 25px; 89 | height: 25px; 90 | border-radius: 3px; 91 | display: block; 92 | } 93 | 94 | .runeDivDash { 95 | border-radius: 8px; 96 | border: 1px dashed #dfdfdf; 97 | } 98 | .matchAnalysisDash { 99 | margin-top: 2px; 100 | padding: 4px; 101 | border-radius: 99px; 102 | border: 1px dashed #dfdfdf; 103 | } 104 | 105 | .text-ss { 106 | font-size: 13px; 107 | line-height: 17px; 108 | } 109 | -------------------------------------------------------------------------------- /src/main/utils/request.ts: -------------------------------------------------------------------------------- 1 | import {BlacklistListTypes, Hater, UserInfos} from "@/main/views/record/blackListTypes"; 2 | import {fetch} from "@tauri-apps/plugin-http"; 3 | 4 | 5 | export const requestFetch = async (url: string, method: string, body?: string,timeout?:number): Promise => { 6 | const res = await fetch(url, { method, body,connectTimeout:timeout }); 7 | 8 | if (res.status === 200) { 9 | const data: T = await res.json(); 10 | return data; 11 | } else { 12 | return null; 13 | } 14 | }; 15 | 16 | 17 | const blacklistServe = (config: any): Promise => { 18 | return requestFetch('http://121.40.58.64:8412' + config.url, 19 | config.method, JSON.stringify(config?.data) 20 | ).then((res) => { 21 | if (res === null) {return null} 22 | return res 23 | }).catch(() => null) 24 | } 25 | 26 | 27 | export const findPlayerByPlayerId = async (config: any): Promise => { 28 | const res = await blacklistServe(config) 29 | 30 | if (res === null || res.code !== 0) { 31 | return null 32 | } 33 | return res.data 34 | } 35 | 36 | export const findHaterByHaterId = async (config: any): Promise => { 37 | const res = await blacklistServe(config) 38 | if (res === null || res.code !== 0) { 39 | return null 40 | } 41 | return res.data 42 | } 43 | export const findBlacklistByHId = async (config: any): Promise => { 44 | const res = await blacklistServe(config) 45 | if (res === null || res.code !== 0) { 46 | return null 47 | } 48 | return res.data 49 | } 50 | 51 | const handleRequest = (res:any) => { 52 | if (res === null || res.code !== 0) { 53 | return false 54 | } 55 | return true 56 | } 57 | 58 | export const reviseHaterContent = async (config: any): Promise => { 59 | const res = await blacklistServe(config) 60 | return handleRequest(res) 61 | } 62 | export const deleteBlacklist = async (config: any): Promise => { 63 | const res = await blacklistServe(config) 64 | return handleRequest(res) 65 | } 66 | export const deleteHater = async (config: any): Promise => { 67 | const res = await blacklistServe(config) 68 | return handleRequest(res) 69 | } 70 | export const createHaterContent = async (config: any): Promise => { 71 | const res = await blacklistServe(config) 72 | return handleRequest(res) 73 | } 74 | export const updatePlayerRecord = async (config: any): Promise => { 75 | const res = await blacklistServe(config) 76 | return handleRequest(res) 77 | } 78 | -------------------------------------------------------------------------------- /src/main/utils/theme.ts: -------------------------------------------------------------------------------- 1 | import {GlobalThemeOverrides} from "naive-ui"; 2 | 3 | export const themeOverrides: GlobalThemeOverrides = { 4 | Card:{ 5 | borderRadius:'0.75rem', 6 | paddingSmall:'0.75rem' 7 | }, 8 | Drawer:{ 9 | bodyPadding:'0.5rem' 10 | }, 11 | Button:{ 12 | borderRadiusSmall:'0.25rem', 13 | borderRadiusMedium:'0.25rem', 14 | borderRadiusLarge:'0.25rem', 15 | }, 16 | Message:{ 17 | margin:'0 0 8px 0', 18 | borderRadius:'0.25rem' 19 | }, 20 | Progress:{ 21 | fontSizeCircle:'14px' 22 | }, 23 | Tag:{ 24 | borderRadius:'0.25rem' 25 | }, 26 | Dialog:{ 27 | iconSize:'25px', 28 | titleFontSize:'16px', 29 | borderRadius:'0.25rem', 30 | padding:'12px', 31 | }, 32 | Carousel:{ 33 | dotColor:"#d1d5db", 34 | dotColorActive:'#10b981', 35 | dotColorFocus:'#d1d5db' 36 | }, 37 | Tabs:{ 38 | tabTextColorLine:'#9ca3af', 39 | tabFontSizeSmall:'13px' 40 | }, 41 | Steps:{ 42 | splitorColorProcess:'#ffffff00' 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/views/home/getHomeData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | queryRankPoint, 3 | querySummonerInfo, 4 | querySummonerHonorLevel, 5 | queryMasteryChampList 6 | } from "@/lcu/aboutSummoner"; 7 | 8 | 9 | // 返回首页最终需要的数据 10 | export const getCurrentSummonerAllInfo = async () => { 11 | const summonerInfo = await querySummonerInfo() 12 | 13 | if (summonerInfo===null) { 14 | return null 15 | } 16 | 17 | const [rankList, honorData,champLevel] = await Promise.all([ 18 | queryRankPoint(), 19 | querySummonerHonorLevel(), 20 | queryMasteryChampList() 21 | ]) 22 | 23 | rankList.push(honorData) 24 | return { summonerInfo, rankList,champLevel} 25 | } 26 | -------------------------------------------------------------------------------- /src/main/views/rank/assistCommon.css: -------------------------------------------------------------------------------- 1 | .top { 2 | width: 36px; 3 | height: 36px; 4 | background-image: url('@/assets/tLevel/top.svg'); 5 | } 6 | 7 | .mid { 8 | width: 36px; 9 | height: 36px; 10 | background-image: url('@/assets/tLevel/mid.svg'); 11 | } 12 | 13 | .jungle { 14 | width: 36px; 15 | height: 36px; 16 | background-image: url('@/assets/tLevel/jug.svg'); 17 | } 18 | 19 | .bottom { 20 | width: 36px; 21 | height: 36px; 22 | background-image: url('@/assets/tLevel/bot.svg'); 23 | } 24 | 25 | .support { 26 | width: 36px; 27 | height: 36px; 28 | background-image: url('@/assets/tLevel/sup.svg'); 29 | } 30 | .imgT0 { 31 | width: 24px; 32 | height: 24px; 33 | background-image: url('@/assets/tLevel/t0.svg'); 34 | } 35 | 36 | .imgT1 { 37 | width: 24px; 38 | height: 24px; 39 | background-image: url('@/assets/tLevel/t1.svg'); 40 | } 41 | 42 | .imgT2 { 43 | width: 24px; 44 | height: 24px; 45 | background-image: url('@/assets/tLevel/t2.svg'); 46 | } 47 | 48 | .imgT3 { 49 | width: 24px; 50 | height: 24px; 51 | background-image: url('@/assets/tLevel/t3.svg'); 52 | } 53 | 54 | .imgT4 { 55 | width: 24px; 56 | height: 24px; 57 | background-image: url('@/assets/tLevel/t4.svg'); 58 | } 59 | .imgT5 { 60 | width: 24px; 61 | height: 24px; 62 | background-image: url('@/assets/tLevel/t5.svg'); 63 | } 64 | -------------------------------------------------------------------------------- /src/main/views/rank/champListLoad.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | -------------------------------------------------------------------------------- /src/main/views/rank/champWinRate.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/views/rank/rankTypes.d.ts: -------------------------------------------------------------------------------- 1 | export interface ChampInfo { 2 | sortId: number; 3 | imgUrl: string; 4 | name: string; 5 | tLevel: string; 6 | win: string; 7 | ban: string; 8 | appearance: string; 9 | champId: number; 10 | } 11 | 12 | export interface ChampDetailDrawer { 13 | champId: number; 14 | lane: string; 15 | tier: number; 16 | is101: boolean; 17 | selectedList: string[]; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/views/record/blackList.ts: -------------------------------------------------------------------------------- 1 | import {findHaterByHaterId, findPlayerByPlayerId, updatePlayerRecord} from "@/main/utils/request"; 2 | import {ExsitDataTypes, HateIdListType, Hater, UserInfos} from "./blackListTypes"; 3 | import {sumInfoTypes} from "@/background/types"; 4 | 5 | class BlackList { 6 | public sumInfo:sumInfoTypes|null = null 7 | 8 | // 从本地查询黑名单列表 9 | public queryBlacklist = async (playerPuuid:string):Promise<[string[],UserInfos]|null> => { 10 | this.sumInfo = this.sumInfo || JSON.parse(localStorage.getItem('sumInfo') as string) as sumInfoTypes 11 | 12 | const res = await findPlayerByPlayerId({ 13 | url:`/player/findPlayerByPlayerId?playerId=${playerPuuid}`, 14 | method:'GET' 15 | }) 16 | if (res === null){ 17 | return null 18 | } 19 | 20 | // 查询当前大区是否存在数据 21 | const haterIdList:HateIdListType = JSON.parse(res.haterIdList) 22 | const haterList = haterIdList[this.sumInfo.platformId] 23 | 24 | if (haterList === undefined){ 25 | return [[],res] 26 | }else { 27 | // 如果存在数据,判断是否为当前召唤师的数据 28 | const existData:undefined|ExsitDataTypes = haterList[this.sumInfo.summonerId] 29 | if (existData === undefined){ 30 | return [[],res] 31 | } 32 | return [existData.sumIdList,res] 33 | } 34 | } 35 | // 通过summonerId获取黑名单数据 36 | public querySumDetails = async (sumIdList:string[],isInit:boolean):Promise => { 37 | this.sumInfo = this.sumInfo|| JSON.parse(localStorage.getItem('sumInfo') as string) as sumInfoTypes 38 | const res = await findHaterByHaterId({ 39 | url:'/hater/findHaterBySumId', 40 | data:{'sumIdList':sumIdList,'area':this.sumInfo.platformId}, 41 | method:'POST' 42 | }) 43 | if (res === null){ 44 | return null 45 | } 46 | return this.transformArray(res,this.sumInfo.summonerId,isInit) 47 | } 48 | 49 | public transformArray = (res: Hater[],localSumId:number, isInit:boolean) => { 50 | return [...res] 51 | .map(item => { 52 | // 根据 isInit 决定是否过滤 blacklist 53 | let blacklist = isInit 54 | ? item.blacklist.filter(b => Number(b.playerSumId) === localSumId) 55 | : [...item.blacklist]; // 如果不过滤,复制一份 56 | 57 | // 逆转 blacklist 58 | blacklist = blacklist.reverse(); 59 | // 返回新对象, 覆盖原对象中的blacklist 60 | return { ...item, blacklist }; 61 | }) 62 | .filter(item => item.blacklist.length > 0) 63 | .reverse(); // 逆转主数组 64 | } 65 | 66 | // 更新user过期的数据 67 | public updateUserInfo = async (userInfos:UserInfos,newSumId:string[]) => { 68 | this.sumInfo = this.sumInfo|| JSON.parse(localStorage.getItem('sumInfo') as string) as sumInfoTypes 69 | 70 | const haterIdListObj = JSON.parse(userInfos.haterIdList) 71 | haterIdListObj[this.sumInfo.platformId][this.sumInfo.summonerId].sumIdList = newSumId 72 | return await updatePlayerRecord({ 73 | url: '/player/updatePlayer', 74 | method: 'PUT', 75 | data: { 76 | ID: userInfos.ID, 77 | CreatedAt: userInfos.CreatedAt, 78 | playerId: userInfos.playerId, 79 | haterIdList: JSON.stringify(haterIdListObj) 80 | } 81 | }) 82 | } 83 | } 84 | 85 | export default BlackList 86 | -------------------------------------------------------------------------------- /src/main/views/record/blackListTypes.d.ts: -------------------------------------------------------------------------------- 1 | import {SummonerDetailInfo} from "@/queryMatch/utils/MatchDetail"; 2 | 3 | export interface UserInfos { 4 | ID: number; 5 | CreatedAt: string; 6 | UpdatedAt: string; 7 | playerId: string; 8 | haterIdList: string; 9 | } 10 | 11 | export interface ExsitDataTypes { 12 | nickname: string, 13 | sumIdList: string[] 14 | } 15 | 16 | export interface SumIdToHateListType { 17 | [summonerId: string]: ExsitDataTypes 18 | } 19 | 20 | export interface HateIdListType { 21 | [platformId: string]: SumIdToHateListType 22 | } 23 | 24 | export interface Hater { 25 | ID: number; 26 | CreatedAt: string; 27 | UpdatedAt: string; 28 | sumId: string; 29 | area: string; 30 | nickName: string; 31 | signCount: number; 32 | blacklist: HaterItem[]; 33 | } 34 | 35 | export interface HaterItem { 36 | ID: number; 37 | CreatedAt: string; 38 | UpdatedAt: string; 39 | hId: number; 40 | playerSumName: string; 41 | playerSumId: string; 42 | matchId: string; 43 | sumId: string; 44 | tag: string; 45 | content: string; 46 | handAdd: boolean; 47 | isShow: boolean; 48 | } 49 | 50 | export interface HaterStructTypes { 51 | sumId: string; 52 | area: string; 53 | nickName: string; 54 | signCount: number; 55 | isShow: boolean; 56 | } 57 | 58 | export interface BlacklistListTypes { 59 | list: HaterItem[] 60 | } 61 | 62 | export interface BlackItemsTypes { 63 | hInfo: { name: string, sumId: string }, 64 | hContent: HaterItem, 65 | } 66 | export interface ParticipantsInfoPlanB { 67 | teamOne: BlacklistPlanbTypes[], 68 | teamTwo: BlacklistPlanbTypes[], 69 | headerInfo: string[], 70 | queueId:number, 71 | gameId:number 72 | } 73 | export interface BlacklistPlanbTypes { 74 | name:string; 75 | accountId:number; 76 | champImgUrl:string; 77 | score:string; 78 | iconList:string[]; 79 | isMvp:boolean; 80 | isWin:boolean; 81 | } 82 | -------------------------------------------------------------------------------- /src/main/views/record/blackSummonerList.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 84 | -------------------------------------------------------------------------------- /src/main/views/record/gameEnd.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/main/views/record/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 70 | -------------------------------------------------------------------------------- /src/main/views/record/utils.ts: -------------------------------------------------------------------------------- 1 | type ThrottleFunction = (...args: any[]) => void 2 | 3 | export function throttle(fn: ThrottleFunction, delay: number): ThrottleFunction { 4 | let lastExecutionTime = 0 5 | 6 | return function (...args: any[]) { 7 | const currentTime = Date.now() 8 | 9 | if (currentTime - lastExecutionTime >= delay) { 10 | // 超过指定的延迟时间,执行函数 11 | // @ts-ignore 12 | fn.apply(this, args) 13 | lastExecutionTime = currentTime 14 | } 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/views/rune/blockContent.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 40 | 41 | 82 | -------------------------------------------------------------------------------- /src/main/views/rune/get101Runes.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import _orderBy from "lodash/orderBy"; 3 | import {flatRunes} from './runes' 4 | import {champDict} from "@/resources/champList"; 5 | import {fetch} from "@tauri-apps/plugin-http"; 6 | 7 | const parseCode = (string:any) => { 8 | try { 9 | const [result] = string.match(/{"(.*)"}/); 10 | const data = JSON.parse(result) 11 | return data; 12 | } catch (error:any) { 13 | throw new Error(error); 14 | } 15 | }; 16 | 17 | const getStyleId = (i:any) => { 18 | let result = null; 19 | for (const [mId, ids] of flatRunes) { 20 | if (+i === +mId) { 21 | result = +i; 22 | break; 23 | } 24 | 25 | if (ids.includes(+i)) { 26 | result = +mId; 27 | break; 28 | } 29 | } 30 | return result; 31 | }; 32 | 33 | const isDifferentStyleId = (a:any, b:any) => { 34 | if (!a || !b) { 35 | return false; 36 | } 37 | 38 | const idA = getStyleId(a); 39 | const idB = getStyleId(b); 40 | const notSame = idA !== idB; 41 | 42 | return idA && idB && notSame; 43 | } 44 | 45 | const makePerkData = (perk:any, champion:string, position:string) => { 46 | const { runes,igamecnt } = perk; 47 | const data = runes.reduce( 48 | ({ primaryStyleId, subStyleId }:any, i:any) => { 49 | if (!primaryStyleId) { 50 | primaryStyleId = getStyleId(+i); 51 | } 52 | if (primaryStyleId && !subStyleId) { 53 | const isStyleId = isDifferentStyleId(+primaryStyleId, +i); 54 | if (isStyleId) { 55 | subStyleId = getStyleId(+i); 56 | } 57 | } 58 | return { 59 | primaryStyleId, 60 | subStyleId, 61 | }; 62 | }, 63 | { 64 | primaryStyleId: ``, 65 | subStyleId: ``, 66 | }, 67 | ); 68 | if (position === 'mid'){ 69 | position = 'middle' 70 | } 71 | data.selectedPerkIds = runes 72 | data.alias = champion; 73 | data.position = position; 74 | data.pickCount = igamecnt; 75 | return data; 76 | }; 77 | 78 | export const get101Runes = async (champId:string|number) => { 79 | const url = `https://lol.qq.com/act/lbp/common/guides/champDetail/champDetail_${champId}.js` 80 | 81 | const res = await fetch(url, {method:'GET'}) 82 | const data = await res.text() 83 | 84 | if (res.status !== 200) {return null} 85 | const jsonRes = parseCode(data) 86 | const perks:any = Object.values(jsonRes.list.championLane).reduce((res:any, l:any) => { 87 | if (l.hold3 !==''){ 88 | const perkDetail = JSON.parse(l.perkdetail); 89 | const position = l.lane; 90 | const pData:any = Object.values(perkDetail).reduce((result:any, i:any) => { 91 | const vals = Object.values(i).map(({ perk,...rest }:any) => ({ 92 | runes: perk.split(`&`), 93 | ...rest, 94 | })); 95 | return result.concat(vals); 96 | }, []); 97 | 98 | const sorted = _orderBy(pData, (i:any) => i.igamecnt, [`desc`]); 99 | 100 | const filteredData = sorted.filter((item: { runes: any[]; }) => { 101 | const valueAtIndex4 = Number(item.runes[4]); 102 | return valueAtIndex4 >= 6000; 103 | }) 104 | 105 | var pages:any = filteredData.slice(0, 2).map((i:any) => makePerkData(i, champDict[champId].alias, position)); 106 | } 107 | return res.concat(pages); 108 | }, []); 109 | // @ts-ignore 110 | return _orderBy(perks.filter(x=>!!x==true), `pickCount`, [`desc`]); 111 | } 112 | -------------------------------------------------------------------------------- /src/main/views/rune/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | -------------------------------------------------------------------------------- /src/main/views/rune/queryRune.ts: -------------------------------------------------------------------------------- 1 | import {requestFetch} from "@/main/utils/request"; 2 | import {mapNameFromUrl} from "@/resources/champList"; 3 | import {Block, OnlineRunes} from "@/main/views/rune/runeTypes"; 4 | 5 | export class QueryRune { 6 | 7 | public mapId = 0 8 | 9 | // 获取英雄数据 10 | public getChampInfo = async (alias:string): Promise => { 11 | const timestamp = new Date().getTime() 12 | const baseUrl = 'https://frank-1304009809.cos.ap-chongqing.myqcloud.com' 13 | if (this.mapId === 12) { 14 | const res = await requestFetch(`${baseUrl}/op.gg-aram/${alias}.json?date${timestamp}`,'GET') 15 | return res === null ? [] : res 16 | } else { 17 | const res = await requestFetch(`${baseUrl}/op.gg/${alias}.json?date${timestamp}`,'GET') 18 | return res === null ? [] : res 19 | } 20 | } 21 | // 获取技能数据 22 | public getSkillsImgUrl = (skillsImg: any, skills: any) => { 23 | return skillsImg.map((img:string, i:number) => [ 24 | `https://game.gtimg.cn/images/lol/act/img/spell/${img}`, 25 | skills[i] 26 | ]) 27 | } 28 | // 获取符文数据 29 | public getRunesData = async (alias:string) => { 30 | try { 31 | this.mapId = (JSON.parse(localStorage.getItem('gameInfo') as string)).mapId 32 | const champInfo: OnlineRunes[] = await this.getChampInfo(alias) 33 | // 技能 34 | const skillsList = this.getSkillsImgUrl(champInfo[0].skillsImg, champInfo[0].skills) 35 | const runeDataList = [] 36 | const blockDataList = [] 37 | 38 | for (const champ of champInfo) { 39 | // 符文 40 | for (const rune of champ.runes) { 41 | if (this.mapId === 12) { 42 | rune.position = 'aram' 43 | } 44 | runeDataList.push(rune) 45 | } 46 | // 出装 47 | const block = this.getBlocksData(JSON.parse(JSON.stringify(champ))) 48 | if (block !== null) { 49 | blockDataList.push(block) 50 | } 51 | } 52 | return {skillsList,runeDataList,blockDataList} 53 | 54 | } catch (e) { 55 | return null 56 | } 57 | } 58 | // 获取出装数据 59 | public getBlocksData = (champ: OnlineRunes) => { 60 | try { 61 | if (this.mapId === 12) { 62 | champ.position = 'aram' 63 | } 64 | const position = this.getPosition(champ.position) 65 | const buildItems = champ.itemBuilds[0] 66 | const name = mapNameFromUrl[champ.alias].label + '-' + mapNameFromUrl[champ.alias].name 67 | buildItems.title = name + ' 推荐出装 ' + 'lolfrank.cn' 68 | buildItems.blocks = this.handleBlocks(buildItems.blocks) 69 | return {position: position as string, buildItems: buildItems, ps: champ.position} 70 | } catch (e) { 71 | return null 72 | } 73 | } 74 | // 处理出装字段 75 | public handleBlocks = (blocks: Block[]) => { 76 | const blocksResult: Block[] = [] 77 | for (const block of blocks) { 78 | if (block.type.indexOf('Starter') !== -1) { 79 | blocksResult.push(this.translateTitle(block, 'Starter Items,', '出门:')) 80 | } else if (block.type.indexOf('Core') !== -1) { 81 | blocksResult.push(this.translateTitle(block, 'Core Items,', '核心:')) 82 | } 83 | } 84 | return blocksResult 85 | } 86 | // 翻译中文 87 | public translateTitle = (block: Block, english: string, chinese: string) => { 88 | block.type = block.type.replace(english, chinese).replace('Pick', '选择次数').replace('Win Rate', '胜率') 89 | if (block.items.length > 3) { 90 | block.items = block.items.slice(0, 3) 91 | } 92 | return block 93 | } 94 | // 获取位置信息 95 | public getPosition = (pos: string) => { 96 | switch (pos) { 97 | case 'middle': 98 | return '中单'; 99 | case 'top': 100 | return '上单'; 101 | case 'support': 102 | return '辅助'; 103 | case 'jungle': 104 | return '打野'; 105 | case 'bottom': 106 | return '射手'; 107 | case 'aram': 108 | return '极地'; 109 | case 'mid': 110 | return '中单'; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/views/rune/runeContent.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 91 | 92 | 104 | -------------------------------------------------------------------------------- /src/main/views/rune/runeMain.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 43 | -------------------------------------------------------------------------------- /src/main/views/rune/runeTypes.d.ts: -------------------------------------------------------------------------------- 1 | export interface Item { 2 | id: string; 3 | count: number; 4 | } 5 | 6 | export interface Block { 7 | type: string; 8 | items: Item[]; 9 | } 10 | 11 | export interface ItemBuild { 12 | title: string; 13 | associatedMaps: number[]; 14 | associatedChampions: number[]; 15 | blocks: Block[]; 16 | map: string; 17 | mode: string; 18 | preferredItemSlots: any[]; 19 | sortrank: number; 20 | startedFrom: string; 21 | type: string; 22 | } 23 | 24 | export interface Rune { 25 | alias: string; 26 | name: string; 27 | position: string; 28 | pickCount: number; 29 | winRate?: string; 30 | primaryStyleId: number; 31 | subStyleId: number; 32 | selectedPerkIds: number[]; 33 | score?: number; 34 | type?: string; 35 | } 36 | 37 | export interface OnlineRunes { 38 | index: number; 39 | id: string; 40 | version: string; 41 | officialVersion: string; 42 | pickCount: number; 43 | winRate: string; 44 | timestamp: number; 45 | alias: string; 46 | name: string; 47 | position: string; 48 | skills: string[]; 49 | spells?: any; 50 | itemBuilds: ItemBuild[]; 51 | runes: Rune[]; 52 | skillsImg: string[]; 53 | } 54 | 55 | export interface RuneStoreState { 56 | currentChamp: number 57 | currentChampImgUrl: string 58 | currentChampAlias: string 59 | currentChampTitle: string 60 | runeDataList: Rune[] 61 | blockDataList: {position:string,buildItems:ItemBuild,ps:string}[] 62 | skillsList: string[][] 63 | } 64 | 65 | export interface RuneStoreActions { 66 | mapChampInfo(champId: number): void 67 | initStore(champId: number): Promise 68 | } -------------------------------------------------------------------------------- /src/main/views/rune/runes.ts: -------------------------------------------------------------------------------- 1 | import {applyRunePage} from "@/lcu/aboutRune"; 2 | import {invokeLcu} from "@/lcu"; 3 | 4 | const Precision = { 5 | 8000: [ 6 | 8005, 7 | 8008, 8 | 8021, 9 | 8010, 10 | 9101, 11 | 9111, 12 | 8009, 13 | 9104, 14 | 9105, 15 | 9103, 16 | 8014, 17 | 8017, 18 | 8299 19 | ] 20 | } 21 | 22 | const Domination = { 23 | 8100: [ 24 | 8112, 25 | 8128, 26 | 9923, 27 | 8126, 28 | 8139, 29 | 8143, 30 | 8137, 31 | 8140, 32 | 8141, 33 | 8135, 34 | 8105, 35 | 8106 36 | ] 37 | } 38 | 39 | const Sorcery = { 40 | 8200: [8214, 8229, 8230, 8224, 8226, 8275, 8210, 8234, 8233, 8237, 8232, 8236] 41 | } 42 | 43 | const Whimsy = { 44 | 8300: [8351, 8360, 8369, 8306, 8304, 8321, 8313, 8352, 8345, 8347, 8410,8316] 45 | } 46 | 47 | const Resolve = { 48 | 8400: [8437, 8439, 8465, 8446, 8463, 8401, 8429, 8444, 8473, 8451, 8453, 8242] 49 | } 50 | const RuneMap = { 51 | ...Precision, 52 | ...Domination, 53 | ...Sorcery, 54 | ...Whimsy, 55 | ...Resolve 56 | } 57 | 58 | export const flatRunes = Object.entries(RuneMap) 59 | 60 | export const handleRunesWrite = (runeData:any) => { 61 | return applyRunePage(runeData).then(async(isApplySuccess) => { 62 | if (!isApplySuccess) { 63 | return false 64 | }else return true 65 | }) 66 | } 67 | 68 | export const writeAutoRune = async (champ:string,champName:string,message:any) => { 69 | if (champ ===''){ 70 | return 71 | } 72 | const localAutoRune = localStorage.getItem('autoRune') 73 | const runeData = await getCurrentRune(champName) 74 | if (runeData===null){ 75 | message.warning('英雄符文获取异常') 76 | return 77 | } 78 | if (localAutoRune === null || localAutoRune ==='{}'){ 79 | const autoRuneDict = { 80 | [champ]:runeData 81 | } 82 | localStorage.setItem('autoRune',JSON.stringify(autoRuneDict)) 83 | message.success('自动配置符文 设置成功') 84 | }else { 85 | const autoRuneDict = JSON.parse(localAutoRune) 86 | const isExist = autoRuneDict[champ] 87 | autoRuneDict[champ] = runeData 88 | localStorage.setItem('autoRune',JSON.stringify(autoRuneDict)) 89 | if (isExist === undefined){ 90 | message.success('自动配置符文 设置成功') 91 | }else { 92 | message.warning('自动符文 数据已更新') 93 | } 94 | } 95 | } 96 | 97 | const getCurrentRune = async (champName:string) => { 98 | const currentRuneList = await invokeLcu('get','/lol-perks/v1/pages') 99 | if (currentRuneList===null){ 100 | return null 101 | } 102 | const current = currentRuneList.find((i:any) => i.current) 103 | if (current !== undefined){ 104 | return { 105 | name:champName+ " lolfrank.cn", 106 | primaryStyleId:current.primaryStyleId, 107 | subStyleId:current.subStyleId, 108 | selectedPerkIds:current.selectedPerkIds 109 | } 110 | }else { 111 | return null 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /src/main/views/teammate/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 59 | -------------------------------------------------------------------------------- /src/main/views/teammate/loadMatch.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/views/teammate/summonerDetail.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/main/views/teammate/summonerKdaName.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/views/teammate/summonerMatch.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 92 | -------------------------------------------------------------------------------- /src/main/views/teammate/summonerMatchLoad.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 29 | -------------------------------------------------------------------------------- /src/main/views/teammate/teammateTypes.d.ts: -------------------------------------------------------------------------------- 1 | export interface MyTeamObject { 2 | assignedPosition: string; 3 | cellId: number; 4 | championId: number; 5 | championPickIntent: number; 6 | nameVisibilityType: string; 7 | obfuscatedPuuid: string; 8 | obfuscatedSummonerId: number; 9 | puuid: string; 10 | selectedSkinId: number; 11 | spell1Id: number; 12 | spell2Id: number; 13 | summonerId: number; 14 | team: number; 15 | wardSkinId: number; 16 | } 17 | 18 | export interface SummonerInfoList { 19 | name: string, 20 | summonerId: string, 21 | puuid: string, 22 | imgUrl: string, 23 | rank: string, 24 | kda?: string, 25 | hater?:boolean 26 | haterIndex?:number 27 | } 28 | 29 | export interface CurrentSumInfoTypes { 30 | kda:string|undefined, 31 | hater:boolean|undefined, 32 | haterIndex:number|undefined 33 | name: string, 34 | puuid: string 35 | rank: string, 36 | imgUrl: string, 37 | index: number, 38 | } 39 | 40 | export interface RoleCountMapTypes { 41 | assassin: number; 42 | fighter: number; 43 | mage: number; 44 | marksman: number; 45 | support: number; 46 | tank: number; 47 | } 48 | 49 | export interface RencentDataAnalysisTypes { 50 | top3Champions: { champId: number; count: number; }[], 51 | totalChampions: number, 52 | roleCountMap: RoleCountMapTypes, 53 | oneGameId:number 54 | } 55 | -------------------------------------------------------------------------------- /src/matchAnalysis/components/analysisMain.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/matchAnalysis/components/dashboard.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 62 | -------------------------------------------------------------------------------- /src/matchAnalysis/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frank 对局分析 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/matchAnalysis/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | // @ts-ignore 3 | import App from './main.vue'; 4 | import { createApp } from 'vue'; 5 | 6 | createApp(App).mount('#app') 7 | -------------------------------------------------------------------------------- /src/matchAnalysis/main.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | -------------------------------------------------------------------------------- /src/matchAnalysis/matchAnalysis.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 62 | -------------------------------------------------------------------------------- /src/matchAnalysis/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @font-face { 6 | font-family: "DingTalk"; 7 | src: url("../assets/font/DingTalk JinBuTi.ttf"); 8 | } 9 | 10 | :root { 11 | font-size: 16px; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 0; 16 | } 17 | 18 | body { 19 | font-family: DingTalk; 20 | -webkit-user-select: none; 21 | } 22 | 23 | .main { 24 | height: calc(100vh - 1rem); 25 | weight: calc(100vw - 1rem); 26 | padding: 0.5rem; 27 | } 28 | 29 | .text-ss { 30 | font-size: 13px; 31 | line-height: 16px; 32 | } 33 | 34 | .text-ss5 { 35 | font-size: 15px; 36 | line-height: 20px; 37 | } 38 | 39 | .dragDiv { 40 | width: 100vw; 41 | height: 18px; 42 | position: absolute; 43 | top: -8px; 44 | left: -8px; 45 | } 46 | .champAvatarColorBlue { 47 | background-color: #66B3FF; 48 | } 49 | .champAvatarColorRed { 50 | background-color: #ff6666; 51 | } 52 | -------------------------------------------------------------------------------- /src/matchAnalysis/utils/MatchAnalysisTypes.d.ts: -------------------------------------------------------------------------------- 1 | import {SimpleMatchTypes} from "@/lcu/types/queryMatchLcuTypes"; 2 | import {RencentDataAnalysisTypes} from "@/main/views/teammate/teammateTypes"; 3 | 4 | export interface SummonerAnaInfo { 5 | name: string; 6 | summonerId: string; 7 | queueId: number; 8 | isThumbUp: boolean; 9 | showType: boolean; 10 | imgUrl: string; 11 | rankList: string[]; 12 | matchList: SimpleMatchTypes[]; 13 | matchAnalysis: RencentDataAnalysisTypes; 14 | openDetailDrawer: (gameId:number,summonerId:number) => void; 15 | } 16 | -------------------------------------------------------------------------------- /src/queryMatch/common/matchConHeader.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/queryMatch/common/matchDetails.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 60 | 61 | 96 | -------------------------------------------------------------------------------- /src/queryMatch/common/matchDetailsFighter.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 65 | -------------------------------------------------------------------------------- /src/queryMatch/common/matchSumDetails.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 47 | 86 | -------------------------------------------------------------------------------- /src/queryMatch/components/loadingAnime.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | 44 | -------------------------------------------------------------------------------- /src/queryMatch/components/matchErr.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 24 | -------------------------------------------------------------------------------- /src/queryMatch/components/matchList.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 68 | -------------------------------------------------------------------------------- /src/queryMatch/components/matchMain.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | 43 | -------------------------------------------------------------------------------- /src/queryMatch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frank 查询战绩 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/queryMatch/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | // @ts-ignore 3 | import App from './main.vue'; 4 | import { createApp } from 'vue'; 5 | import { createPinia } from 'pinia'; 6 | 7 | createApp(App).use(createPinia()).mount('#app') 8 | -------------------------------------------------------------------------------- /src/queryMatch/main.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | -------------------------------------------------------------------------------- /src/queryMatch/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @font-face { 6 | font-family: "DingTalk"; 7 | src: url("../assets/font/DingTalk JinBuTi.ttf"); 8 | } 9 | 10 | :root { 11 | font-size: 16px; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 0; 16 | } 17 | 18 | body { 19 | font-family: DingTalk; 20 | -webkit-user-select: none; 21 | } 22 | 23 | .main { 24 | height: calc(100vh - 1.5rem); 25 | width: calc(100vw - 1.5rem); 26 | padding: 12px; 27 | position: relative; 28 | } 29 | 30 | 31 | .avatarEffect { 32 | animation: light 4s ease-in-out infinite; 33 | transition: 0.5s; 34 | } 35 | 36 | @keyframes light { 37 | 0% { 38 | box-shadow: 0 0 4px #ff6666; 39 | } 40 | 41 | 25% { 42 | box-shadow: 0 0 16px #18a058; 43 | } 44 | 45 | 50% { 46 | box-shadow: 0 0 4px #2080f0; 47 | } 48 | 49 | 75% { 50 | box-shadow: 0 0 16px #f0a020; 51 | } 52 | 53 | 100% { 54 | box-shadow: 0 0 4px #d03050; 55 | } 56 | } 57 | 58 | .imgItem { 59 | width: 25px; 60 | height: 25px; 61 | border-radius: 3px; 62 | display: block; 63 | } 64 | 65 | .text-ss { 66 | font-size: 13px; 67 | line-height: 17px; 68 | } 69 | 70 | .text-ss5 { 71 | font-size: 15px; 72 | line-height: 20px; 73 | } 74 | 75 | .dragDiv { 76 | width: calc(100vw - 1rem); 77 | height: 18px; 78 | position: absolute; 79 | top: 0px; 80 | } 81 | 82 | .scale-in-hor-left { 83 | animation: scale-in-hor-left .8s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; 84 | } 85 | 86 | @keyframes scale-in-hor-left { 87 | 0% { 88 | transform: scaleX(0); 89 | transform-origin: 0% 0%; 90 | opacity: 1; 91 | } 92 | 100% { 93 | transform: scaleX(1); 94 | transform-origin: 0% 0%; 95 | opacity: 1; 96 | } 97 | } 98 | 99 | .slideSum { 100 | animation: slideSum-in-right .8s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; 101 | } 102 | 103 | @keyframes slideSum-in-right { 104 | 0% { 105 | transform: translateX(150px); 106 | opacity: 0; 107 | } 108 | 100% { 109 | transform: translateX(0); 110 | opacity: 1; 111 | } 112 | } 113 | 114 | .champAvatarColorBlue { 115 | background-color: #66B3FF; 116 | } 117 | .champAvatarColorRed { 118 | background-color: #ff6666; 119 | } 120 | -------------------------------------------------------------------------------- /src/queryMatch/utils/baseMatch.ts: -------------------------------------------------------------------------------- 1 | import {queryRankPoint, querySummonerInfo} from "@/lcu/aboutSummoner" 2 | import {Games, SimpleMatchDetailsTypes} from "@/lcu/types/queryMatchLcuTypes"; 3 | import {queryMatchHistory} from "@/lcu/aboutMatch"; 4 | import {queryGameType} from "@/lcu/utils" 5 | import {champDict} from "@/resources/champList"; 6 | 7 | export default class BaseMatch { 8 | 9 | public gerSummonerInfo = async (summonerId?: number) => { 10 | const summonerInfo = await querySummonerInfo(summonerId) 11 | if (summonerInfo !== null) { 12 | const rankList = await queryRankPoint(summonerInfo.puuid) 13 | return {summonerInfo,rankList} 14 | } 15 | return null 16 | } 17 | 18 | public dealMatchHistory = async (puuid: string, begIndex: number, endIndex: number): Promise => { 19 | const matchList = await queryMatchHistory(puuid, begIndex, endIndex) 20 | if (matchList === null) { 21 | return null 22 | } 23 | 24 | return matchList.map((matchListElement) => { 25 | return this.getSimpleMatch(matchListElement) 26 | }) 27 | } 28 | 29 | public getSimpleMatch = (match: Games): SimpleMatchDetailsTypes => { 30 | const times = this.timestampToDate(match.gameCreation) 31 | const kills = match.participants[0].stats.kills 32 | const deaths = match.participants[0].stats.deaths 33 | const assists = match.participants[0].stats.assists 34 | const kda = deaths===0? kills+assists : Math.round((kills+assists)/deaths*3) 35 | 36 | return { 37 | gameId:match.gameId, 38 | champImgUrl: `${champDict[String(match.participants[0].championId)].alias}.png`, 39 | // 是否取得胜利 40 | isWin: match.participants[0].stats.win === true ? true : false, 41 | // 击杀数目 42 | kills: kills, 43 | // 死亡数目 44 | deaths: deaths, 45 | // 助攻数目 46 | assists: assists, 47 | //kda 48 | kda: kda, 49 | // 游戏时间 50 | matchTime: times[1], 51 | // 开始时间 22:00 52 | startTime:times[0], 53 | // 游戏模式 54 | gameModel:queryGameType(match.queueId), 55 | //游戏对局ID 56 | queueId:match.queueId, 57 | champId: match.participants[0].championId 58 | } 59 | } 60 | 61 | public querySpecialMatch = async (puuid: string,queueId:number) => { 62 | const matchList = await queryMatchHistory(puuid, 0, 60) 63 | if (matchList === null){ 64 | return [] 65 | } 66 | const specialList = matchList 67 | .filter(matchList => matchList.queueId === queueId) 68 | 69 | return specialList.map((matchListElement) => { 70 | return this.getSimpleMatch(matchListElement) 71 | }) 72 | } 73 | public timestampToDate = (timestamp: number):[string,string] => { 74 | const date = new Date(timestamp) 75 | // 获取时间 76 | const hours = date.getHours().toString().padStart(2, '0') 77 | const minutes = date.getMinutes().toString().padStart(2, '0') 78 | return [ 79 | `${hours} : ${minutes}`, 80 | date.getMonth() + 1 81 | + '-' 82 | + date.getDate() 83 | ] 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/queryMatch/utils/tools.ts: -------------------------------------------------------------------------------- 1 | const iconDict:{ [key: string]: string } = { 2 | 'assists':'助攻最多, 从不K头', 3 | 'firstBlood':'第一滴血, 这局我Carry', 4 | 'fiveKills':'五杀! Superexcellent', 5 | 'fourKills':'四杀! Excellent', 6 | 'god':'超神! So Easy', 7 | 'goldEarned':'获得最多金币 So Rich', 8 | 'kills':'击杀最多 是在下无敌啦', 9 | 'mvp':'Most Valuable Player', 10 | 'svp':'Super Valuable Player', 11 | 'threeKills':'三杀! Good Job', 12 | 'totalDamageDealtToChampions':'输出最高伤害 人称小代', 13 | 'totalDamageTaken':'承伤最多的坦克爸爸', 14 | 'totalMinionsKilled':'补刀最多 随便玩玩啦', 15 | 'turretKills':'拆塔最多, 人在塔拆', 16 | 'visionScore':'视野得分最高' 17 | } 18 | 19 | // 获取iconImgUrl 20 | const getIconEle = (key:string) => { 21 | const imgUrl = new URL(`/src/assets/matchImage/${key}.png`, import.meta.url).href 22 | return [iconDict[key],imgUrl] 23 | } 24 | export const getIconImg = (iconList:string[],isMvp:boolean,isWin:boolean) => { 25 | const iconImgEle = iconList.map((icon) => { 26 | return getIconEle(icon) 27 | }) 28 | if (isMvp && isWin){ 29 | iconImgEle.unshift(getIconEle('mvp')) 30 | }else if (isMvp && !isWin){ 31 | iconImgEle.unshift(getIconEle('svp')) 32 | } 33 | return iconImgEle 34 | } 35 | -------------------------------------------------------------------------------- /src/recentMatch/components/nullPage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 23 | 24 | -------------------------------------------------------------------------------- /src/recentMatch/components/recentMatchList.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 91 | -------------------------------------------------------------------------------- /src/recentMatch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frank 最近战绩 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 15 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/recentMatch/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | // @ts-ignore 3 | import App from './main.vue'; 4 | import { createApp } from 'vue'; 5 | 6 | createApp(App).mount('#app') 7 | -------------------------------------------------------------------------------- /src/recentMatch/main.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | -------------------------------------------------------------------------------- /src/recentMatch/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @font-face { 6 | font-family: "DingTalk"; 7 | src: url("../assets/font/DingTalk JinBuTi.ttf"); 8 | } 9 | 10 | :root { 11 | font-size: 16px; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 0; 16 | } 17 | 18 | body { 19 | font-family: DingTalk; 20 | -webkit-user-select: none; 21 | } 22 | 23 | .main { 24 | height: calc(100vh - 1rem); 25 | weight: calc(100vw - 1rem); 26 | padding: 0.5rem; 27 | /*border-radius: 0.5rem;*/ 28 | } 29 | 30 | .text-ss { 31 | font-size: 13px; 32 | line-height: 16px; 33 | } 34 | 35 | .text-ss5 { 36 | font-size: 15px; 37 | line-height: 20px; 38 | } 39 | 40 | .champAvatarColorBlue { 41 | background-color: #66B3FF; 42 | } 43 | .champAvatarColorRed { 44 | background-color: #ff6666; 45 | } 46 | .dragDiv { 47 | width: calc(100vw - 1rem); 48 | height: 18px; 49 | position: absolute; 50 | top: -8px; 51 | } 52 | -------------------------------------------------------------------------------- /src/recentMatch/utils/queryMatch.ts: -------------------------------------------------------------------------------- 1 | import {MatchItemTypes} from "@/recentMatch/utils/queryTypes"; 2 | import {champDict} from "@/resources/champList"; 3 | import {queryMatchHistory} from "@/lcu/aboutMatch"; 4 | import {Games} from "@/lcu/types/queryMatchLcuTypes"; 5 | 6 | class QueryMatch { 7 | public winCount = 0 8 | 9 | public queryMatchHistory = async (puuid: string, queueId: number, summonerState: string): Promise<[MatchItemTypes[], number, boolean]> => { 10 | let matchList 11 | 12 | if (queueId === 420 || queueId === 440) { 13 | matchList = await this.findSpecialMatch(puuid, queueId) 14 | } else { 15 | matchList = await this.findMatch(puuid) 16 | } 17 | 18 | const winCount = matchList.length > 0 ? this.winCount : 0 19 | const isExcel = this.isExcelPlayer(summonerState, matchList) 20 | this.winCount = 0 21 | return [matchList, winCount, isExcel] 22 | } 23 | 24 | public parseMatch = (games: Games) => { 25 | this.winCount = games.participants[0].stats.win === true ? this.winCount + 1 : this.winCount 26 | return { 27 | champImg: `https://game.gtimg.cn/images/lol/act/img/champion/${champDict[String(games.participants[0].championId)].alias}.png`, 28 | kills: games.participants[0].stats.kills, 29 | deaths: games.participants[0].stats.deaths, 30 | assists: games.participants[0].stats.assists, 31 | isWin: games.participants[0].stats.win, 32 | gameId: games.gameId, 33 | queueId: games.queueId 34 | } 35 | } 36 | 37 | public isExcelPlayer = (summonerState: string, matchList: MatchItemTypes[]) => { 38 | // 判断是否为小代 39 | if (summonerState !== "Y") { 40 | return false 41 | } 42 | let excellentCount = 0 43 | for (let match of matchList.slice(0, 5)) { 44 | const kda = match.deaths === 0 ? (match.kills + match.assists) * 2 : (match.kills + match.assists) / match.deaths * 3 45 | if (kda >= 12) { 46 | excellentCount += 1 47 | } 48 | } 49 | return excellentCount >= 3 50 | } 51 | 52 | public findMatch = async (puuid: string): Promise => { 53 | const matchList = await queryMatchHistory(puuid, 0, 9) 54 | if (matchList !== null) { 55 | return matchList.map((games) => this.parseMatch(games)) 56 | } else { 57 | return [] 58 | } 59 | } 60 | 61 | public findSpecialMatch = async (puuid: string, queueId: number): Promise => { 62 | const latestMatch = await queryMatchHistory(puuid, 0, 9) 63 | const specialList: MatchItemTypes[] = [] 64 | 65 | let offset = 0 66 | while (offset < 30 ) { 67 | const matchHistory = 68 | offset === 0 ? latestMatch : await queryMatchHistory(puuid, offset, offset+9) 69 | if (!matchHistory || matchHistory.length === 0) { 70 | break 71 | } 72 | const filterMatch = matchHistory.filter((games) => queueId === games.queueId) 73 | 74 | for (const game of filterMatch) { 75 | specialList.push(this.parseMatch(game)) 76 | if (specialList.length === 10) { 77 | return specialList 78 | } 79 | } 80 | 81 | offset += 10 82 | } 83 | if (specialList.length === 0 && latestMatch !== null){ 84 | return latestMatch.map((games) => this.parseMatch(games)) 85 | }else return specialList 86 | } 87 | } 88 | 89 | export default QueryMatch 90 | -------------------------------------------------------------------------------- /src/resources/areaList.ts: -------------------------------------------------------------------------------- 1 | export const TencentRsoPlatformId: any = { 2 | 'NJ100':'NJ100', 3 | //电信 4 | 'HN1': 't_1', 5 | 'HN1_NEW': 't_1', 6 | 'HN2': 't_2', 7 | 'HN2_NEW': 't_2', 8 | 'HN3': 't_3', 9 | 'HN3_NEW': 't_3', 10 | 'HN4': 't_4', 11 | 'HN4_NEW': 't_4', 12 | 'HN5': 't_5', 13 | 'HN5_NEW': 't_5', 14 | 'HN6': 't_6', 15 | 'HN6_NEW': 't_6', 16 | 'HN7': 't_7', 17 | 'HN7_NEW': 't_7', 18 | 'HN8': 't_8', 19 | 'HN8_NEW': 't_8', 20 | 'HN9': 't_9', 21 | 'HN9_NEW': 't_9', 22 | 'HN10': 't_10', 23 | 'HN10_NEW': 't_10', 24 | 'HN11': 't_11', 25 | 'HN11_NEW': 't_11', 26 | 'HN12': 't_12', 27 | 'HN12_NEW': 't_12', 28 | 'HN13': 't_13', 29 | 'HN13_NEW': 't_13', 30 | 'HN14': 't_14', 31 | 'HN14_NEW': 't_14', 32 | 'HN15': 't_15', 33 | 'HN15_NEW': 't_15', 34 | 'HN16': 't_16', 35 | 'HN16_NEW': 't_16', 36 | 'HN17': 't_17', 37 | 'HN17_NEW': 't_17', 38 | 'HN18': 't_18', 39 | 'HN18_NEW': 't_18', 40 | 'HN19': 't_19', 41 | 'HN19_NEW': 't_19', 42 | //网通 43 | 'WT1_NEW': 'n_1', 44 | 'WT1': 'n_1', 45 | 'WT2_NEW': 'n_2', 46 | 'WT2': 'n_2', 47 | 'WT3_NEW': 'n_3', 48 | 'WT3': 'n_3', 49 | 'WT4_NEW': 'n_4', 50 | 'WT4': 'n_4', 51 | 'WT5_NEW': 'n_5', 52 | 'WT5': 'n_5', 53 | 'WT6_NEW': 'n_6', 54 | 'WT6': 'n_6', 55 | 'WT7_NEW': 'n_7', 56 | 'WT7': 'n_7', 57 | //其他网络 58 | 'EDU1': 'o_3', 59 | 'EDU1_NEW': 'o_3', 60 | //全网络大区 61 | 'BGP1': 'o_2', 62 | 'BGP1_NEW': 'o_2', 63 | 'BGP2': 'o_1', 64 | 'BGP2_NEW': 'o_1', 65 | 'TW2': 'zh_tw', 66 | 'TW2_NEW': 'zh_tw', 67 | } 68 | /* 69 | export const areaList = { 70 | "t_1":"艾欧尼亚", 71 | "t_2":"祖安", 72 | "t_3":"诺克萨斯", 73 | // "t_4":"班德尔城", 74 | "t_5":"皮尔特沃夫", 75 | "t_6":"战争学院", 76 | "t_7":"巨神峰", 77 | "t_8":"雷瑟守备", 78 | // "t_9":"裁决之地", 79 | "t_10":"黑色玫瑰", 80 | "t_11":"暗影岛", 81 | // "t_12":"钢铁烈阳", 82 | // "t_13":"水晶之痕", 83 | "t_14":"均衡教派", 84 | "t_15":"影流", 85 | "t_16":"守望之海", 86 | "t_17":"征服之海", 87 | "t_18":"卡拉曼达", 88 | // "t_19":"皮城警备", 89 | "n_1":"比尔吉沃特", 90 | "n_2":"德玛西亚", 91 | "n_3":"弗雷尔卓德", 92 | "n_4":"无畏先锋", 93 | "n_5":"恕瑞玛", 94 | "n_6":"扭曲丛林", 95 | "n_7":"巨龙之巢", 96 | "o_1":"峡谷之巅", 97 | "o_2":"男爵领域", 98 | "o_3":"教育网专区", 99 | "zh_tw":'中国台湾' 100 | }*/ 101 | -------------------------------------------------------------------------------- /src/resources/otherList.ts: -------------------------------------------------------------------------------- 1 | export const posRate = [ 2 | { 3 | imgUrl:new URL("@/assets/svg/fighter.svg", import.meta.url).href, 4 | name:'战士', 5 | key:'fighter' 6 | }, 7 | { 8 | imgUrl:new URL("@/assets/svg/tank.svg", import.meta.url).href, 9 | name:'坦克', 10 | key:'tank' 11 | }, 12 | { 13 | imgUrl:new URL("@/assets/svg/mage.svg", import.meta.url).href, 14 | name:'法师', 15 | key:'mage' 16 | }, 17 | { 18 | imgUrl:new URL("@/assets/svg/assassin.svg", import.meta.url).href, 19 | name:'刺客', 20 | key:'assassin' 21 | }, 22 | { 23 | imgUrl:new URL("@/assets/svg/marksman.svg", import.meta.url).href, 24 | name:'射手', 25 | key:'marksman' 26 | }, 27 | { 28 | imgUrl:new URL("@/assets/svg/support.svg", import.meta.url).href, 29 | name:'辅助', 30 | key:'support' 31 | }, 32 | ] 33 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | darkMode: 'class', 4 | corePlugins: { 5 | preflight: false, 6 | }, 7 | content: [ 8 | "./index.html", 9 | "./src/**/*.{vue,js,ts,jsx,tsx}", 10 | ], 11 | theme: { 12 | extend: { 13 | }, 14 | }, 15 | plugins: [], 16 | } 17 | 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | "baseUrl": "./", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | }, 12 | 13 | /* Bundler mode */ 14 | "moduleResolution": "bundler", 15 | "allowImportingTsExtensions": true, 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "preserve", 20 | 21 | /* Linting */ 22 | "strict": true, 23 | "noUnusedLocals": true, 24 | "noUnusedParameters": true, 25 | "noFallthroughCasesInSwitch": true 26 | }, 27 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 28 | "references": [{ "path": "./tsconfig.node.json" }] 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import packageInfo from './package.json'; 4 | // @ts-ignore 5 | import * as path from "path"; 6 | 7 | const host = process.env.TAURI_DEV_HOST; 8 | 9 | // https://vitejs.dev/config/ 10 | // @ts-ignore 11 | export default defineConfig(async () => ({ 12 | plugins: [vue()], 13 | publicDir: false, 14 | define: { 15 | __APP_VERSION__: JSON.stringify(packageInfo.version), 16 | }, 17 | clearScreen: false, 18 | server: { 19 | port: 1420, 20 | strictPort: true, 21 | host: host || false, 22 | hmr: host 23 | ? { 24 | protocol: "ws", 25 | host, 26 | port: 1421, 27 | } 28 | : undefined, 29 | watch: { 30 | // 3. tell vite to ignore watching `src-tauri` 31 | ignored: ["**/src-tauri/**"], 32 | }, 33 | }, 34 | resolve: { 35 | alias: { 36 | '@': path.resolve('./src') // @代替src 37 | } 38 | }, 39 | envPrefix: ['VITE_', 'TAURI_ENV_*'], 40 | build: { 41 | rollupOptions: { 42 | input: { 43 | main: path.resolve(__dirname, 'src/main/index.html'), 44 | background: path.resolve(__dirname, 'src/background/index.html'), 45 | queryMatch: path.resolve(__dirname, 'src/queryMatch/index.html'), 46 | recentMatch: path.resolve(__dirname, 'src/recentMatch/index.html'), 47 | matchAnalysis: path.resolve(__dirname, 'src/matchAnalysis/index.html'), 48 | }, 49 | }, 50 | // Tauri uses Chromium on Windows and WebKit on macOS and Linux 51 | target: 52 | process.env.TAURI_ENV_PLATFORM == 'windows' 53 | ? 'chrome105' 54 | : 'safari13', 55 | // don't minify for debug builds 56 | minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false, 57 | // produce sourcemaps for debug builds 58 | sourcemap: !!process.env.TAURI_ENV_DEBUG, 59 | }, 60 | })); 61 | --------------------------------------------------------------------------------