mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-04 07:05:07 +08:00
✨ 添加简略模式切换功能,优化战斗和卡片展示
This commit is contained in:
@@ -1,30 +1,58 @@
|
||||
<!-- 剧诗,辉彩祝福 -->
|
||||
<template>
|
||||
<div class="tuc-buff-box">
|
||||
<div class="tuc-buff-item">
|
||||
<div class="tuc-buff-summary">
|
||||
<div class="tuc-buff-icon">
|
||||
<img alt="total" class="summary" src="/UI/combat/combatCrown.webp" />
|
||||
</div>
|
||||
<div class="tuc-buff-desc">
|
||||
<span>辉彩祝福</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tuc-buff-total" v-html="getBuffDesc(props.modelValue.summary.desc)" />
|
||||
<div ref="containerRef" :class="['tuc-buff-box', { 'simple-mode': props.simpleMode }]">
|
||||
<div class="tuc-buff-title">
|
||||
<span>辉彩祝福</span>
|
||||
<span v-if="props.simpleMode">Lv.{{ props.modelValue.summary.total_level }}</span>
|
||||
</div>
|
||||
<template v-for="(buff, idx) in props.modelValue.buffs" :key="idx">
|
||||
<div v-if="buff.level > 1" class="tuc-buff-item">
|
||||
<div :class="{ 'simple-mode': props.simpleMode }" class="tuc-buff-list">
|
||||
<div
|
||||
:data-key="'summary'"
|
||||
class="tuc-buff-item"
|
||||
:class="{ 'is-summary': true }"
|
||||
>
|
||||
<div class="tuc-buff-summary">
|
||||
<div class="tuc-buff-icon">
|
||||
<img :alt="buff.name" :src="buff.icon" />
|
||||
<div
|
||||
class="tuc-buff-icon"
|
||||
:title="props.simpleMode ? `辉彩祝福 Lv.${props.modelValue.summary.total_level}` : undefined"
|
||||
>
|
||||
<img alt="total" class="summary" src="/UI/combat/combatCrown.webp" />
|
||||
<span v-if="props.simpleMode" class="tuc-buff-level-badge">
|
||||
{{ props.modelValue.summary.total_level }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="tuc-buff-desc">
|
||||
<div v-show="!props.simpleMode" class="tuc-buff-desc">
|
||||
<span>辉彩祝福</span>
|
||||
<span>Lv.{{ props.modelValue.summary.total_level }}</span>
|
||||
</div>
|
||||
<span v-if="props.simpleMode" class="tuc-buff-name">辉彩祝福</span>
|
||||
</div>
|
||||
<div v-show="!props.simpleMode" class="tuc-buff-total" v-html="getBuffDesc(props.modelValue.summary.desc)" />
|
||||
</div>
|
||||
<div
|
||||
v-for="buff in props.modelValue.buffs"
|
||||
:key="buff.icon"
|
||||
:data-key="buff.icon"
|
||||
class="tuc-buff-item"
|
||||
>
|
||||
<div class="tuc-buff-summary">
|
||||
<div
|
||||
class="tuc-buff-icon"
|
||||
:title="props.simpleMode ? `${buff.name} Lv.${buff.level}` : undefined"
|
||||
>
|
||||
<img :alt="buff.name" :src="buff.icon" />
|
||||
<span v-if="props.simpleMode" class="tuc-buff-level-badge">
|
||||
{{ buff.level }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-show="!props.simpleMode" class="tuc-buff-desc">
|
||||
<span>{{ buff.name }}</span>
|
||||
<span>Lv.{{ buff.level }}</span>
|
||||
</div>
|
||||
<span v-if="props.simpleMode" class="tuc-buff-name">{{ buff.name }}</span>
|
||||
</div>
|
||||
<div class="tuc-buff-detail" @click="console.log(buff)">
|
||||
<div v-for="(effect, idx) in buff.level_effect" :key="idx" class="tuc-effect-item">
|
||||
<div v-show="!props.simpleMode" class="tuc-buff-detail">
|
||||
<div v-for="(effect, eIdx) in buff.level_effect" :key="eIdx" class="tuc-effect-item">
|
||||
<div class="tuc-effect-title">
|
||||
<img :src="effect.icon" alt="icon" />
|
||||
<span v-html="parseHtmlText(effect.name)" />
|
||||
@@ -33,19 +61,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { parseHtmlText } from "@utils/toolFunc.js";
|
||||
|
||||
type TucBuffBoxProps = {
|
||||
/* 辉彩祝福数据 */
|
||||
modelValue: TGApp.Game.Combat.SplendourBuff;
|
||||
simpleMode?: boolean;
|
||||
};
|
||||
|
||||
const props = defineProps<TucBuffBoxProps>();
|
||||
|
||||
const containerRef = ref<HTMLDivElement>();
|
||||
let animationFrameId: number | null = null;
|
||||
|
||||
function getBuffDesc(desc: string): string {
|
||||
return parseHtmlText(desc.replaceAll("点,", "点,\n"));
|
||||
}
|
||||
@@ -53,12 +86,74 @@ function getBuffDesc(desc: string): string {
|
||||
function getEffectDesc(desc: string): string {
|
||||
return parseHtmlText(desc.replaceAll(";", ";\n")).replaceAll("\n<br />", "<br />");
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.simpleMode,
|
||||
async (newVal, oldVal) => {
|
||||
if (oldVal === undefined || !containerRef.value) return;
|
||||
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
animationFrameId = null;
|
||||
}
|
||||
|
||||
const items = containerRef.value.querySelectorAll<HTMLDivElement>(".tuc-buff-item");
|
||||
|
||||
const firstRects = new Map<string, DOMRect>();
|
||||
items.forEach((el) => {
|
||||
const key = el.dataset.key;
|
||||
if (key) {
|
||||
firstRects.set(key, el.getBoundingClientRect());
|
||||
}
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
|
||||
const animations: Array<{ el: HTMLDivElement }> = [];
|
||||
|
||||
items.forEach((el) => {
|
||||
const key = el.dataset.key;
|
||||
if (!key) return;
|
||||
|
||||
const firstRect = firstRects.get(key);
|
||||
if (!firstRect) return;
|
||||
|
||||
const lastRect = el.getBoundingClientRect();
|
||||
const deltaX = firstRect.left - lastRect.left;
|
||||
const deltaY = firstRect.top - lastRect.top;
|
||||
|
||||
if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
|
||||
el.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
||||
el.style.transition = "none";
|
||||
animations.push({ el });
|
||||
}
|
||||
});
|
||||
|
||||
if (animations.length > 0) {
|
||||
containerRef.value.offsetHeight;
|
||||
|
||||
animationFrameId = requestAnimationFrame(() => {
|
||||
animations.forEach(({ el }) => {
|
||||
el.style.transition = "transform 0.3s ease";
|
||||
el.style.transform = "";
|
||||
});
|
||||
animationFrameId = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tuc-buff-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
flex: 2;
|
||||
flex-direction: column;
|
||||
@@ -69,6 +164,38 @@ function getEffectDesc(desc: string): string {
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
row-gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tuc-buff-box.simple-mode {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tuc-buff-title {
|
||||
color: var(--box-text-2);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.tuc-buff-title span:last-child {
|
||||
margin-left: 4px;
|
||||
color: var(--tgc-od-orange);
|
||||
}
|
||||
|
||||
.tuc-buff-list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.tuc-buff-list.simple-mode {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tuc-buff-item {
|
||||
@@ -81,6 +208,12 @@ function getEffectDesc(desc: string): string {
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.tuc-buff-list.simple-mode .tuc-buff-item {
|
||||
width: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tuc-buff-summary {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -98,12 +231,32 @@ function getEffectDesc(desc: string): string {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--box-bg-3);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.tuc-buff-icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.tuc-buff-level-badge {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--tgc-od-orange);
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tuc-buff-name {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tuc-buff-desc {
|
||||
@@ -132,6 +285,11 @@ function getEffectDesc(desc: string): string {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.tuc-effect-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tuc-effect-title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -140,12 +298,12 @@ function getEffectDesc(desc: string): string {
|
||||
column-gap: 4px;
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
filter: invert(0.6);
|
||||
}
|
||||
.tuc-effect-title img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
filter: invert(0.6);
|
||||
}
|
||||
|
||||
.dark .tuc-effect-title img {
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
<!-- 剧诗,神秘收获 -->
|
||||
<template>
|
||||
<div v-if="props.modelValue.length > 0" class="tuc-card-box">
|
||||
<div
|
||||
v-if="props.modelValue.length > 0"
|
||||
ref="containerRef"
|
||||
:class="['tuc-card-box', { 'simple-mode': props.simpleMode }]"
|
||||
>
|
||||
<div class="tuc-card-title">神秘收获 {{ props.modelValue.length }}</div>
|
||||
<div class="tuc-card-list">
|
||||
<div v-for="(card, idx) in props.modelValue" :key="idx" class="tuc-card-item">
|
||||
<div class="tuc-ci-icon">
|
||||
<img :src="card.icon" alt="icon" />
|
||||
<div :class="{ 'simple-mode': props.simpleMode }" class="tuc-card-list">
|
||||
<div
|
||||
v-for="card in props.modelValue"
|
||||
:key="card.id"
|
||||
:data-key="card.id"
|
||||
class="tuc-card-item"
|
||||
>
|
||||
<div class="tuc-card-summary">
|
||||
<div
|
||||
class="tuc-ci-icon"
|
||||
:title="props.simpleMode ? card.name : undefined"
|
||||
>
|
||||
<img :src="card.icon" alt="icon" />
|
||||
</div>
|
||||
<span v-if="props.simpleMode" class="tuc-card-name">{{ card.name }}</span>
|
||||
</div>
|
||||
<div class="tuc-ci-info">
|
||||
<div v-show="!props.simpleMode" class="tuc-ci-info">
|
||||
<div class="tuc-ci-title">{{ card.name }}</div>
|
||||
<div class="tuc-ci-desc" v-html="parseHtmlText(card.desc)" />
|
||||
</div>
|
||||
@@ -16,14 +31,81 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { parseHtmlText } from "@utils/toolFunc.js";
|
||||
|
||||
type TucCardBoxProps = {
|
||||
/* 神秘收获数据 */
|
||||
modelValue: Array<TGApp.Game.Combat.Card>;
|
||||
simpleMode?: boolean;
|
||||
};
|
||||
|
||||
const props = defineProps<TucCardBoxProps>();
|
||||
|
||||
const containerRef = ref<HTMLDivElement>();
|
||||
let animationFrameId: number | null = null;
|
||||
|
||||
watch(
|
||||
() => props.simpleMode,
|
||||
async (newVal, oldVal) => {
|
||||
if (oldVal === undefined || !containerRef.value) return;
|
||||
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
animationFrameId = null;
|
||||
}
|
||||
|
||||
const items = containerRef.value.querySelectorAll<HTMLDivElement>(".tuc-card-item");
|
||||
|
||||
const firstRects = new Map<number, DOMRect>();
|
||||
items.forEach((el) => {
|
||||
const key = el.dataset.key;
|
||||
if (key) {
|
||||
firstRects.set(Number(key), el.getBoundingClientRect());
|
||||
}
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
|
||||
const animations: Array<{ el: HTMLDivElement }> = [];
|
||||
|
||||
items.forEach((el) => {
|
||||
const key = el.dataset.key;
|
||||
if (!key) return;
|
||||
|
||||
const firstRect = firstRects.get(Number(key));
|
||||
if (!firstRect) return;
|
||||
|
||||
const lastRect = el.getBoundingClientRect();
|
||||
const deltaX = firstRect.left - lastRect.left;
|
||||
const deltaY = firstRect.top - lastRect.top;
|
||||
|
||||
if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
|
||||
el.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
||||
el.style.transition = "none";
|
||||
animations.push({ el });
|
||||
}
|
||||
});
|
||||
|
||||
if (animations.length > 0) {
|
||||
containerRef.value.offsetHeight;
|
||||
|
||||
animationFrameId = requestAnimationFrame(() => {
|
||||
animations.forEach(({ el }) => {
|
||||
el.style.transition = "transform 0.3s ease";
|
||||
el.style.transform = "";
|
||||
});
|
||||
animationFrameId = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tuc-card-box {
|
||||
@@ -40,6 +122,7 @@ const props = defineProps<TucCardBoxProps>();
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
row-gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tuc-card-title {
|
||||
@@ -57,6 +140,14 @@ const props = defineProps<TucCardBoxProps>();
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.tuc-card-list.simple-mode {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tuc-card-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -66,6 +157,20 @@ const props = defineProps<TucCardBoxProps>();
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.tuc-card-list.simple-mode .tuc-card-item {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tuc-card-summary {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.tuc-ci-icon {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
@@ -74,19 +179,27 @@ const props = defineProps<TucCardBoxProps>();
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--box-bg-3);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: invert(1);
|
||||
object-fit: cover;
|
||||
}
|
||||
.tuc-ci-icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: invert(1);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dark .tuc-ci-icon img {
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
.tuc-card-name {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tuc-ci-title {
|
||||
position: relative;
|
||||
font-family: var(--font-title);
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
<TucBuffBox
|
||||
:class="props.round.choice_cards.length === 0 ? 'fill' : ''"
|
||||
:model-value="props.round.splendour_buff"
|
||||
:simple-mode="props.simpleMode"
|
||||
/>
|
||||
<TucCardBox :model-value="props.round.choice_cards" />
|
||||
<TucCardBox :model-value="props.round.choice_cards" :simple-mode="props.simpleMode" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -34,7 +35,12 @@ import TucAeBox from "./tuc-ae-box.vue";
|
||||
import TucBuffBox from "./tuc-buff-box.vue";
|
||||
import TucCardBox from "./tuc-card-box.vue";
|
||||
|
||||
type TucRoundProps = { round: TGApp.Game.Combat.RoundData; uid: string; id: number };
|
||||
type TucRoundProps = {
|
||||
round: TGApp.Game.Combat.RoundData;
|
||||
uid: string;
|
||||
id: number;
|
||||
simpleMode?: boolean;
|
||||
};
|
||||
const props = defineProps<TucRoundProps>();
|
||||
const showInfo = ref<boolean>(false);
|
||||
const tucrRef = useTemplateRef<HTMLDivElement>("tucrEl");
|
||||
|
||||
@@ -72,6 +72,13 @@
|
||||
<img alt="char" src="/UI/combat/tarotDefault.webp" />
|
||||
<span>月谕圣牌</span>
|
||||
</v-btn>
|
||||
<v-switch
|
||||
v-model="simpleMode"
|
||||
class="uc-switch"
|
||||
color="var(--tgc-od-orange)"
|
||||
hide-details
|
||||
label="简略模式"
|
||||
/>
|
||||
</div>
|
||||
<div class="uct-extension-right">
|
||||
<span @click="tryLoginHutao()">{{ userName ?? "登录胡桃云" }}</span>
|
||||
@@ -129,6 +136,7 @@
|
||||
:id="item.id"
|
||||
:key="idx"
|
||||
:round="round"
|
||||
:simpleMode
|
||||
:uid="item.uid"
|
||||
/>
|
||||
</div>
|
||||
@@ -190,6 +198,7 @@ const showChar = ref<boolean>(false);
|
||||
const charMasters = shallowRef<Array<TGApp.Game.Combat.CharMaster>>([]);
|
||||
const showTarot = ref<boolean>(false);
|
||||
const tarotStat = shallowRef<TGApp.Game.Combat.TarotState>();
|
||||
const simpleMode = ref<boolean>(false);
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在加载剧诗数据");
|
||||
@@ -622,6 +631,7 @@ function isFinTarot(data: TGApp.Sqlite.Combat.TableTrans): boolean {
|
||||
height: 100%;
|
||||
padding-right: 8px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ucw-i-ref {
|
||||
@@ -696,4 +706,8 @@ function isFinTarot(data: TGApp.Sqlite.Combat.TableTrans): boolean {
|
||||
font-size: 1.5rem;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.uc-switch {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user