💄 基本完成危战UI

#157
This commit is contained in:
BTMuli
2025-07-01 19:05:05 +08:00
parent bb092c0d8f
commit a4ebf47b56
5 changed files with 458 additions and 9 deletions

View 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>

View 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>

View 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>