mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-03 06:55:06 +08:00
232
src/components/userChallenge/tuc-challenge-item.vue
Normal file
232
src/components/userChallenge/tuc-challenge-item.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<!-- 幽境危战,单个怪物挑战 -->
|
||||
<template>
|
||||
<div class="tuc-challenge-item-comp" @click="console.log(props.data)">
|
||||
<div class="top-title">
|
||||
<div class="name">{{ props.data.name }} Lv.{{ props.data.monster.level }}</div>
|
||||
<div class="append">
|
||||
<span>战斗用时:</span>
|
||||
<span>{{ props.data.second }}</span>
|
||||
<span>秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-box">
|
||||
<div class="left-info">
|
||||
<div class="team-box">
|
||||
<TItemBox
|
||||
:model-value="getTeamBox(avatar)"
|
||||
v-for="(avatar, idx) in props.data.teams"
|
||||
:key="idx"
|
||||
/>
|
||||
</div>
|
||||
<div class="best-dps">
|
||||
<div class="best-dps-item" v-for="(avatar, idx) in props.data.best_avatar" :key="idx">
|
||||
<TMiImg :src="avatar.side_icon" :ori="true" :alt="`${avatar.avatar_id}`" />
|
||||
<span>{{ avatar.type === 1 ? "最强一击" : "最高总伤害" }}</span>
|
||||
<span>{{ avatar.dps }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-monster">
|
||||
<div class="icon">
|
||||
<TMiImg :src="props.data.monster.icon" :alt="props.data.name" :ori="true" />
|
||||
</div>
|
||||
<div class="tags">
|
||||
<div class="tag" v-for="(tag, idx) in props.data.monster.tags" :key="idx">
|
||||
<TucMonsterTag :data="tag" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TItemBox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
|
||||
import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
import TucMonsterTag from "@comp/userChallenge/tuc-monster-tag.vue";
|
||||
import { getZhElement } from "@utils/toolFunc.js";
|
||||
|
||||
import { AppCharacterData } from "@/data/index.js";
|
||||
|
||||
type TucChallengeItemProps = { data: TGApp.Game.Challenge.ChallengeList };
|
||||
|
||||
const props = defineProps<TucChallengeItemProps>();
|
||||
|
||||
function getTeamBox(avatar: TGApp.Game.Challenge.ChallengeTeam): TItemBoxData {
|
||||
const find = AppCharacterData.find((i) => i.id === avatar.avatar_id);
|
||||
if (!find) {
|
||||
return {
|
||||
bg: `/icon/bg/${avatar.rarity}-BGC.webp`,
|
||||
clickable: false,
|
||||
icon: avatar.image,
|
||||
lt: `/icon/element/${getZhElement(avatar.element)}元素.webp`,
|
||||
ltSize: "20px",
|
||||
rt: avatar.rank.toString(),
|
||||
rtSize: "20px",
|
||||
size: "80px",
|
||||
height: "80px",
|
||||
display: "inner",
|
||||
innerText: avatar.name,
|
||||
innerHeight: 20,
|
||||
};
|
||||
}
|
||||
return {
|
||||
bg: `/icon/bg/${find.star}-BGC.webp`,
|
||||
clickable: false,
|
||||
icon: `/WIKI/character/${find.id}.webp`,
|
||||
lt: `/icon/element/${find.element}元素.webp`,
|
||||
ltSize: "20px",
|
||||
rt: avatar.rank.toString(),
|
||||
rtSize: "20px",
|
||||
size: "80px",
|
||||
height: "80px",
|
||||
display: "inner",
|
||||
innerText: find.name,
|
||||
innerHeight: 20,
|
||||
innerIcon: `/icon/weapon/${find.weapon}.webp`,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tuc-challenge-item-comp {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--common-shadow-1);
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-1);
|
||||
color: var(--box-text-1);
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.top-title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
font-family: var(--font-title);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.append {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--box-text-2);
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
gap: 4px;
|
||||
|
||||
span {
|
||||
color: var(--box-text-2);
|
||||
font-size: 14px;
|
||||
|
||||
&:nth-child(2) {
|
||||
color: var(--tgc-yellow-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.left-info {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.team-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.best-dps {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 16px;
|
||||
}
|
||||
|
||||
.best-dps-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(to right, var(--box-bg-3), var(--box-bg-1));
|
||||
color: var(--box-text-2);
|
||||
column-gap: 4px;
|
||||
font-family: var(--font-title);
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
object-fit: contain;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
|
||||
&:last-child {
|
||||
color: var(--tgc-yellow-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark .best-dps-item {
|
||||
background: linear-gradient(to right, var(--box-bg-2) 100px, var(--box-bg-1));
|
||||
}
|
||||
|
||||
.right-monster {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
95
src/components/userChallenge/tuc-monster-tag.vue
Normal file
95
src/components/userChallenge/tuc-monster-tag.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<!-- 怪物Tag -->
|
||||
<template>
|
||||
<div class="tuc-monster-tag-comp" :class="`buff-${props.data.type}`">
|
||||
<template v-for="(descItem, idx) in desc" :key="idx">
|
||||
<TMiImg
|
||||
v-if="descItem.type === 'element'"
|
||||
:src="`/icon/element/${getElement(descItem.value)}元素.webp`"
|
||||
:alt="getElement(descItem.value)"
|
||||
/>
|
||||
<span v-else>{{ descItem.value }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
|
||||
type TucMonsterTagProps = { data: TGApp.Game.Challenge.MonsterTag };
|
||||
type MonsterDesc = { type: "element" | "text"; value: string };
|
||||
const props = defineProps<TucMonsterTagProps>();
|
||||
|
||||
const desc: Array<MonsterDesc> = parseDesc(props.data.desc);
|
||||
|
||||
function getElement(value: string): string {
|
||||
switch (value) {
|
||||
case "11001":
|
||||
return "冰";
|
||||
case "11002":
|
||||
return "水";
|
||||
case "11003":
|
||||
return "火";
|
||||
// TODO: 待确认
|
||||
case "11004":
|
||||
return "雷";
|
||||
// TODO: 待确认
|
||||
case "11005":
|
||||
return "风";
|
||||
// TODO: 待确认
|
||||
case "11006":
|
||||
return "岩";
|
||||
case "11007":
|
||||
return "草";
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function parseDesc(desc: string): Array<MonsterDesc> {
|
||||
// {SPRITE_PRESET#11003}元素优势 => [{type: "element", value: "11003"}, {type: "text", value: "元素优势"}]
|
||||
const regex = /{SPRITE_PRESET#(\d+)}([^{}]*)/g;
|
||||
const result: Array<MonsterDesc> = [];
|
||||
let match;
|
||||
while ((match = regex.exec(desc)) !== null) {
|
||||
if (match[1]) {
|
||||
result.push({ type: "element", value: match[1] });
|
||||
}
|
||||
if (match[2]) {
|
||||
result.push({ type: "text", value: match[2].trim() });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@styles/github.styles.scss" as github-styles;
|
||||
|
||||
.tuc-monster-tag-comp {
|
||||
position: relative;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 8px;
|
||||
border-radius: 16px;
|
||||
column-gap: 2px;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--box-text-1);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&.buff-0 {
|
||||
@include github-styles.github-tag-dark-gen(#e06c75);
|
||||
}
|
||||
|
||||
&.buff-1 {
|
||||
@include github-styles.github-tag-dark-gen(#98c379);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
84
src/components/userChallenge/tuc-overview.vue
Normal file
84
src/components/userChallenge/tuc-overview.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<!-- 幽境危战,单人/联机数据总览 -->
|
||||
<template>
|
||||
<div class="tuc-overview-comp" @click="onClick()">
|
||||
<TSubline>{{ props.title }}{{ props.data.has_data ? "" : " (无数据) " }}</TSubline>
|
||||
<div class="toc-best" v-if="props.data.best">
|
||||
<div class="label">
|
||||
<span>最佳记录</span>
|
||||
</div>
|
||||
<div class="append" :title="getDiffTitle(props.data.best)">
|
||||
<span>{{ getDiffTitle(props.data.best) }}</span>
|
||||
<span>{{ props.data.best.second }}s</span>
|
||||
</div>
|
||||
</div>
|
||||
<TucChallengeItem
|
||||
v-for="(challenge, idx) in props.data.challenge"
|
||||
:key="idx"
|
||||
:data="challenge"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSubline from "@comp/app/t-subline.vue";
|
||||
import TucChallengeItem from "@comp/userChallenge/tuc-challenge-item.vue";
|
||||
|
||||
type TucOverviewProps = {
|
||||
title: string;
|
||||
data: TGApp.Game.Challenge.Challenge;
|
||||
};
|
||||
|
||||
const props = defineProps<TucOverviewProps>();
|
||||
|
||||
function onClick(): void {
|
||||
console.log(props.data);
|
||||
}
|
||||
|
||||
function getDiffTitle(best: TGApp.Game.Challenge.ChallengeBest): string {
|
||||
switch (best.difficulty) {
|
||||
case 1:
|
||||
return "普通";
|
||||
case 2:
|
||||
return "进阶";
|
||||
case 3:
|
||||
return "困难";
|
||||
case 4:
|
||||
return "险恶";
|
||||
case 5:
|
||||
return "无畏";
|
||||
case 6:
|
||||
return "绝境";
|
||||
default:
|
||||
return `难度${best.difficulty}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tuc-overview-comp {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.toc-best {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--box-text-1);
|
||||
|
||||
.label {
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.append {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--box-text-2);
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user