重构真境剧诗页面UI,优化组件结构与样式

This commit is contained in:
BTMuli
2025-12-06 20:36:53 +08:00
parent 58c2044f47
commit 2ca77b9944
16 changed files with 575 additions and 231 deletions

View File

@@ -1,7 +1,6 @@
/**
* @file assets/themes/dark.css
* @description 主题样式文件-深色主题
* @since Beta v0.7.2
* 主题样式文件-深色主题
* @since v0.8.9
*/
/* dark mode */
@@ -30,6 +29,7 @@ html.dark {
--box-text-4: var(--tgc-white-4);
--box-text-5: var(--tgc-red-1);
--box-text-7: var(--tgc-white-5);
--box-text-8: var(--tgc-yellow-1);
/* button */
--btn-text: var(--tgc-yellow-1);

View File

@@ -1,7 +1,6 @@
/**
* @file assets/themes/default.css
* @description 主题样式文件-默认(浅色)主题
* @since Beta v0.7.2
* 主题样式文件-默认(浅色)主题
* @since v0.8.9
*/
/* default(light) theme */
@@ -30,6 +29,7 @@ html.default {
--box-text-4: var(--tgc-blue-3); /* subtitle */
--box-text-5: var(--tgc-pink-1); /* tag */
--box-text-7: var(--tgc-dark-7); /* quote */
--box-text-8: var(--tgc-od-orange); /* sth hint */
/* button */
--btn-text: var(--tgc-yellow-2); /* with tgc-btn-1 */

View File

@@ -1,6 +1,9 @@
<!-- 标题栏组件 -->
<template>
<div class="tsl-box">
<img src="@/assets/icons/arrow-right.svg" alt="right" />
<slot name="icon">
<img alt="right" class="tsl-icon" src="@/assets/icons/arrow-right.svg" />
</slot>
<slot></slot>
</div>
</template>
@@ -18,7 +21,7 @@
gap: 4px;
}
.tsl-box :first-child {
.tsl-icon {
width: 24px;
height: 24px;
box-sizing: border-box;
@@ -26,7 +29,7 @@
filter: invert(0.75);
}
.dark .tsl-box :first-child {
.dark .tsl-icon {
filter: none;
}
</style>

View File

@@ -0,0 +1,173 @@
<!-- 剧诗角色&敌人 -->
<template>
<div class="tuc-ae-box">
<div class="tuc-avatars">
<div class="tuc-ae-title">上场角色</div>
<div class="tuc-ae-grid">
<TItembox
v-for="(avatar, idx) in props.avatars"
:key="idx"
:model-value="getAvatarBox(avatar)"
/>
</div>
</div>
<div class="tuc-enemies">
<div class="tuc-ae-title">讨伐列表</div>
<div class="tuc-ae-flex">
<div v-for="(enemy, idx) in props.enemies" :key="idx" class="tuc-enemy">
<div class="tuc-enemy-icon">
<img :src="enemy.icon" alt="icon" />
</div>
<div class="tuc-enemy-info">
<span>{{ enemy.name }}</span>
<span>Lv.{{ enemy.level }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import TItembox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
import { getWikiBrief, getZhElement } from "@utils/toolFunc.js";
type TucAeBoxProps = {
/* 上场角色数据 */
avatars: Array<TGApp.Game.Combat.Avatar>;
/* 敌人数据 */
enemies: Array<TGApp.Game.Combat.Enemy>;
};
const props = defineProps<TucAeBoxProps>();
function getAvatarBox(item: TGApp.Game.Combat.Avatar): TItemBoxData {
const findAvatar = getWikiBrief(item.avatar_id);
let innerText = item.avatar_type === 2 ? "试用角色" : item.avatar_type === 3 ? "助演角色" : "";
let findWeapon;
if (findAvatar) {
findWeapon = findAvatar.weapon;
if (innerText === "") innerText = findAvatar.name;
}
return {
bg: `/icon/bg/${item.rarity === 105 ? 5 : item.rarity}-BGC.webp`,
clickable: false,
display: "inner",
height: "80px",
icon: `/WIKI/character/${item.avatar_id}.webp`,
innerHeight: innerText === "" ? 0 : 20,
innerText: innerText,
lt:
item.element === "None"
? findWeapon
? `/icon/weapon/${findWeapon}.webp`
: ""
: `/icon/element/${getZhElement(item.element)}元素.webp`,
ltSize: "20px",
innerBlur: "5px",
rt: "",
rtSize: "",
size: "80px",
};
}
</script>
<style lang="scss" scoped>
.tuc-ae-box {
position: relative;
display: flex;
width: 100%;
box-sizing: border-box;
align-items: stretch;
justify-content: space-between;
padding: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 4px;
background: var(--box-bg-2);
row-gap: 8px;
}
.tuc-avatars {
position: relative;
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
row-gap: 4px;
}
.tuc-ae-title {
color: var(--box-text-2);
font-family: var(--font-title);
}
.tuc-ae-grid {
position: relative;
display: grid;
margin: 0 auto;
gap: 8px;
grid-template-columns: repeat(4, 80px);
}
.tuc-enemies {
position: relative;
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: flex-start;
row-gap: 4px;
}
.tuc-ae-flex {
position: relative;
display: flex;
width: 100%;
height: 100%;
flex-wrap: wrap-reverse;
align-items: center;
justify-content: center;
gap: 4px 8px;
}
.tuc-enemy {
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 4px 8px 4px 4px;
border-radius: 40px;
background: var(--box-bg-3);
column-gap: 4px;
}
.tuc-enemy-icon {
position: relative;
overflow: hidden;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--box-bg-4);
img {
width: 100%;
height: 100%;
}
}
.tuc-enemy-info {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
font-size: 12px;
span {
line-height: 16px;
&:first-child {
font-family: var(--font-title);
}
}
}
</style>

View File

@@ -1,5 +1,6 @@
<!-- 剧诗角色列表 -->
<template>
<div class="tuca-box" :class="{ grid: props.detail }">
<div :class="{ grid: props.detail }" class="tuca-box">
<TItemBox v-for="(item, idx) in props.modelValue" :key="idx" :model-value="getItemBox(item)" />
</div>
</template>
@@ -52,7 +53,7 @@ function getItemBox(item: TGApp.Game.Combat.Avatar): TItemBoxData {
&.grid {
display: grid;
width: 100%;
grid-gap: 4px;
gap: 4px;
grid-template-columns: repeat(2, 1fr);
}
}

View File

@@ -0,0 +1,151 @@
<!-- 剧诗辉彩祝福 -->
<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="/source/UI/combatCrown.webp" />
</div>
<div class="tuc-buff-desc">
<span>辉彩祝福</span>
</div>
</div>
<div class="tuc-buff-total" v-html="parseHtmlText(props.modelValue.summary.desc)" />
</div>
<template v-for="(buff, idx) in props.modelValue.buffs" :key="idx">
<div v-if="buff.level > 1" class="tuc-buff-item">
<div class="tuc-buff-summary">
<div class="tuc-buff-icon">
<img :alt="buff.name" :src="buff.icon" />
</div>
<div class="tuc-buff-desc">
<span>{{ buff.name }}</span>
<span>Lv.{{ buff.level }}</span>
</div>
</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 class="tuc-effect-title">
<img :src="effect.icon" alt="icon" />
<span v-html="parseHtmlText(effect.name)" />
</div>
<span class="tuc-effect-desc" v-html="parseHtmlText(effect.desc)" />
</div>
</div>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import { parseHtmlText } from "@utils/toolFunc.js";
type TucBuffBoxProps = {
/* 辉彩祝福数据 */
modelValue: TGApp.Game.Combat.SplendourBuff;
};
const props = defineProps<TucBuffBoxProps>();
</script>
<style lang="css" scoped>
.tuc-buff-box {
position: relative;
display: flex;
width: 100%;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 4px;
background: var(--box-bg-2);
row-gap: 8px;
}
.tuc-buff-item {
position: relative;
display: flex;
width: 100%;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
column-gap: 8px;
}
.tuc-buff-summary {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
row-gap: 4px;
}
.tuc-buff-icon {
position: relative;
width: 60px;
height: 60px;
flex-shrink: 0;
padding: 4px;
border-radius: 4px;
background-color: var(--box-bg-3);
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.tuc-buff-desc {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
font-family: var(--font-title);
font-size: 10px;
}
.dark .tuc-buff-summary .tuc-buff-icon img.summary {
filter: invert(1);
}
.tuc-buff-detail {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.tuc-buff-total {
font-size: 12px;
white-space: pre-wrap;
}
.tuc-effect-title {
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
column-gap: 4px;
font-family: var(--font-title);
font-size: 12px;
img {
width: 16px;
height: 16px;
filter: invert(0.6);
}
}
.dark .tuc-effect-title img {
filter: unset;
}
.tuc-effect-desc {
position: relative;
display: block;
font-size: 10px;
}
</style>

View File

@@ -1,71 +0,0 @@
<template>
<div class="tuc-buff-box">
<div class="tuc-buff-item">
<img alt="total" src="/source/UI/combatCrown.webp" />
<span>{{ props.modelValue.summary.total_level }}</span>
</div>
<div
class="tuc-buff-item"
v-for="(item, idx) in props.modelValue.buffs"
:key="idx"
:title="item.name"
>
<img :alt="item.name" :src="item.icon" />
<span>{{ item.level }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
type TucBuffProps = { modelValue: TGApp.Game.Combat.SplendourBuff };
const props = defineProps<TucBuffProps>();
const columnCnt = computed<number>(() => {
const len = props.modelValue.buffs.length;
if ((len + 1) % 2 === 1) return len / 2 + 1;
return (len + 1) / 2;
});
</script>
<style lang="css" scoped>
.tuc-buff-box {
display: grid;
width: 100%;
grid-gap: 4px;
/* stylelint-disable value-keyword-case */
grid-template-columns: repeat(v-bind(columnCnt), 1fr);
/* stylelint-enable value-keyword-case */
}
.tuc-buff-item {
position: relative;
display: flex;
width: 80px;
height: 80px;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 4px;
background-color: var(--box-bg-3);
img {
width: 100%;
height: 100%;
object-fit: cover;
}
span {
position: absolute;
right: 0;
bottom: 0;
padding: 0 4px;
background: var(--common-shadow-2);
border-bottom-right-radius: 4px;
border-top-left-radius: 4px;
color: var(--tgc-white-2);
font-family: var(--font-title);
font-size: 14px;
text-shadow: 0 0 4px #00000033;
}
}
</style>

View File

@@ -0,0 +1,100 @@
<!-- 剧诗神秘收获 -->
<template>
<div v-if="props.modelValue.length > 0" class="tuc-card-box">
<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>
<div class="tuc-ci-info">
<div class="tuc-ci-title">{{ card.name }}</div>
<div class="tuc-ci-desc" v-html="parseHtmlText(card.desc)" />
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { parseHtmlText } from "@utils/toolFunc.js";
type TucCardBoxProps = {
/* 神秘收获数据 */
modelValue: Array<TGApp.Game.Combat.Card>;
};
const props = defineProps<TucCardBoxProps>();
</script>
<style lang="scss" scoped>
.tuc-card-box {
position: relative;
display: flex;
width: 300px;
box-sizing: border-box;
flex-direction: column;
flex-shrink: 0;
align-items: flex-start;
justify-content: flex-start;
padding: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 4px;
background: var(--box-bg-2);
row-gap: 8px;
}
.tuc-card-title {
color: var(--box-text-2);
font-family: var(--font-title);
}
.tuc-card-list {
position: relative;
display: flex;
width: 100%;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
row-gap: 8px;
}
.tuc-card-item {
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
column-gap: 8px;
}
.tuc-ci-icon {
position: relative;
width: 60px;
height: 60px;
flex-shrink: 0;
padding: 4px;
border-radius: 4px;
background-color: var(--box-bg-3);
img {
width: 100%;
height: 100%;
filter: invert(1);
object-fit: cover;
}
}
.dark .tuc-ci-icon img {
filter: unset;
}
.tuc-ci-title {
position: relative;
font-family: var(--font-title);
}
.tuc-ci-desc {
position: relative;
flex-shrink: 0;
font-size: 12px;
}
</style>

View File

@@ -1,41 +0,0 @@
<template>
<div class="tuc-cards-box">
<div class="tuc-cards-item" v-for="(card, idx) in modelValue" :key="idx" :title="card.name">
<img :src="card.icon" :alt="card.name" />
</div>
</div>
</template>
<script lang="ts" setup>
defineProps<{ modelValue: Array<TGApp.Game.Combat.Card> }>();
</script>
<style lang="css" scoped>
.tuc-cards-box {
display: flex;
width: 100%;
flex-wrap: wrap;
align-items: flex-start;
justify-content: flex-start;
gap: 5px;
}
.tuc-cards-item {
position: relative;
display: flex;
width: 50px;
align-items: center;
justify-content: center;
aspect-ratio: 1;
img {
max-width: 100%;
filter: invert(1);
object-fit: cover;
}
}
.dark .tuc-cards-item {
img {
filter: none;
}
}
</style>

View File

@@ -9,7 +9,7 @@
<div v-else-if="!Array.isArray(props.data)" class="tucfi-data">
<TItemBox :model-value="getBox(props.data)" />
</div>
<div class="tucfi-icons" v-else>
<div v-else class="tucfi-icons">
<div v-for="(item, idx) in props.data" :key="idx" class="tucfi-icon">
<TItemBox :model-value="getBox2(item)" />
</div>
@@ -68,17 +68,16 @@ function getBox2(item: TGApp.Game.Combat.AvatarMini): TItemBoxData {
padding: 10px;
border-radius: 5px;
background: var(--box-bg-1);
font-family: var(--font-title);
}
.tucfi-label {
color: var(--box-text-4);
font-family: var(--font-title);
font-size: 20px;
}
.tucfi-data {
color: var(--tgc-yellow-1);
font-family: var(--font-text);
color: var(--box-text-8);
font-size: 20px;
}

View File

@@ -1,16 +1,23 @@
<!-- 剧诗数据概览 -->
<template>
<div class="tuco-box">
<TucTile title="最佳记录" :val="getBestVal()" />
<TucTile :title="`获得星章-${props.data.medal_num}`" :val="props.data.get_medal_round_list" />
<TucTile :title="getRoundTitle()" :val="getRoundVal()" />
<TucTile title="消耗幻剧之花" :val="props.data.coin_num" />
<TucFight label="最快完成演出" :data="props.fights.shortest_avatar_list" />
<TucTile title="总耗时" :val="getTime()" />
<!-- <TucTile title="助演角色支援" :val="`${props.data.rent_cnt}次`" />-->
<!-- <TucTile title="场外声援" :val="`${props.data.avatar_bonus_num}次`" />-->
<TucFight label="击败最多敌人" :data="props.fights.max_defeat_avatar" />
<TucFight label="最高伤害输出" :data="props.fights.max_damage_avatar" />
<TucFight label="最高承受伤害" :data="props.fights.max_take_damage_avatar" />
<div class="tuco-line1">
<TucTile :val="getBestVal()" title="最佳记录" />
<TucTile :val="props.data.coin_num" title="消耗幻剧之花" />
<TucTile :val="getTime()" title="总耗时" />
<TucTile :title="getRoundTitle()" :val="getRoundVal()" />
</div>
<div class="tuco-line2">
<TucFight :data="props.fights.max_defeat_avatar" label="击败最多敌人" />
<TucFight :data="props.fights.max_take_damage_avatar" label="最高承受伤害" />
<TucFight :data="props.fights.shortest_avatar_list" label="最快完成演出" />
<TucFight :data="props.fights.max_damage_avatar" label="最高伤害输出" />
</div>
<div class="tuco-line3">
<TucTile :val="`${props.data.rent_cnt}次`" title="助演角色支援" />
<TucTile :title="`获得星章-${props.data.medal_num}`" :val="props.data.get_medal_round_list" />
<TucTile :val="`${props.data.avatar_bonus_num}次`" title="场外声援" />
</div>
</div>
</template>
<script lang="ts" setup>
@@ -58,9 +65,36 @@ function getTime(): string {
</script>
<style lang="css" scoped>
.tuco-box {
position: relative;
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
row-gap: 8px;
}
.tuco-line1 {
position: relative;
display: grid;
width: 100%;
gap: 8px;
column-gap: 8px;
grid-template-columns: 1fr 2fr 2fr 1fr;
}
.tuco-line2 {
position: relative;
display: grid;
width: 100%;
column-gap: 8px;
grid-template-columns: repeat(4, 1fr);
}
.tuco-line3 {
position: relative;
display: grid;
width: 100%;
column-gap: 8px;
grid-template-columns: repeat(3, 1fr);
}
</style>

View File

@@ -1,34 +1,31 @@
<!-- 真境剧诗单轮次卡片组件 -->
<!-- 剧诗单幕 -->
<template>
<div class="tucr-box">
<div class="tucr-title">
<img :src="`/icon/combat/${getIcon()}.webp`" alt="combat" />
<span class="main" v-if="props.round.is_tarot">
<img
:class="`stat_${props.round.is_get_medal}`"
:src="`/icon/combat/${getIcon()}.webp`"
alt="combat"
/>
<span v-if="props.round.is_tarot" class="main">
圣牌挑战·{{ props.round.tarot_serial_no }}
</span>
<span class="main" v-else>{{ props.round.round_id }}</span>
<span v-else class="main">{{ props.round.round_id }}</span>
<span class="sub">{{ timestampToDate(Number(props.round.finish_time) * 1000) }}</span>
</div>
<TucAeBox :avatars="props.round.avatars" :enemies="props.round.enemies" />
<div class="tucr-content">
<TucSub title="出演角色" class="main">
<TucAvatars :model-value="props.round.avatars" :detail="true" />
</TucSub>
<TucSub title="辉彩祝福" class="main">
<TucBuffs :model-value="props.round.splendour_buff" />
</TucSub>
<TucSub :title="`神秘收获(${props.round.choice_cards.length})`" class="sub">
<TucCards :model-value="props.round.choice_cards" />
</TucSub>
<TucBuffBox :model-value="props.round.splendour_buff" />
<TucCardBox :model-value="props.round.choice_cards" />
</div>
</div>
</template>
<script lang="ts" setup>
import { timestampToDate } from "@utils/toolFunc.js";
import TucAvatars from "./tuc-avatars.vue";
import TucBuffs from "./tuc-buffs.vue";
import TucCards from "./tuc-cards.vue";
import TucSub from "./tuc-sub.vue";
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 };
const props = defineProps<TucRoundProps>();
@@ -37,7 +34,7 @@ function getIcon(): string {
return `${props.round.is_tarot ? "tarot" : "star"}_${props.round.is_get_medal ? "1" : "0"}`;
}
</script>
<style lang="css" scoped>
<style lang="scss" scoped>
.tucr-box {
display: flex;
width: 100%;
@@ -47,7 +44,7 @@ function getIcon(): string {
padding: 8px;
border-radius: 4px;
background: var(--box-bg-1);
row-gap: 4px;
row-gap: 8px;
}
.tucr-title {
@@ -60,6 +57,10 @@ function getIcon(): string {
img {
height: 30px;
object-fit: contain;
&.stat_false {
filter: invert(0.5);
}
}
.main {
@@ -74,15 +75,11 @@ function getIcon(): string {
}
.tucr-content {
position: relative;
display: flex;
width: 100%;
flex-shrink: 0;
align-items: flex-start;
align-items: stretch;
justify-content: flex-start;
column-gap: 8px;
.sub {
width: 100%;
}
}
</style>

View File

@@ -1,30 +0,0 @@
<template>
<div class="tuc-sub-box">
<div class="tuc-sub-title">{{ title }}</div>
<slot name="default" />
</div>
</template>
<script lang="ts" setup>
defineProps<{ title: string }>();
</script>
<style lang="css" scoped>
.tuc-sub-box {
position: relative;
display: flex;
height: 205px;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 4px;
background: var(--box-bg-2);
}
.tuc-sub-title {
margin-right: auto;
color: var(--box-text-2);
font-family: var(--font-title);
}
</style>

View File

@@ -1,21 +1,28 @@
<!-- 真境剧诗概况卡片组件 -->
<!-- 真境剧诗概况卡片 -->
<template>
<div class="tuct-box">
<div class="tuct-title">
<slot name="title">{{ props.title }}</slot>
</div>
<div class="tuct-text" v-if="!Array.isArray(props.val)">
<div v-if="!Array.isArray(props.val)" class="tuct-text">
<slot name="text">{{ props.val }}</slot>
</div>
<div class="tuct-icons" v-else>
<div v-else class="tuct-icons">
<template v-for="(v, idx) in props.val" :key="idx">
<img
v-if="idx < 10"
:src="`/icon/combat/star_${v}.webp`"
:alt="`${v}`"
:class="`stat${v}`"
:src="`/icon/combat/star_${v}.webp`"
:title="`第${idx + 1}幕`"
/>
<img v-else :src="`/icon/combat/tarot_${v}.webp`" :alt="`${v}`" :title="`圣牌${idx - 9}`" />
<img
v-else
:alt="`${v}`"
:class="`stat${v}`"
:src="`/icon/combat/tarot_${v}.webp`"
:title="`圣牌${idx - 9}`"
/>
</template>
</div>
</div>
@@ -36,19 +43,17 @@ const props = defineProps<TucTileProps>();
padding: 8px;
border-radius: 4px;
background: var(--box-bg-1);
font-family: var(--font-title);
}
.tuct-title {
color: var(--box-text-4);
font-family: var(--font-title);
font-size: 20px;
}
.tuct-text {
color: var(--tgc-yellow-1);
font-family: var(--font-text);
color: var(--box-text-8);
font-size: 20px;
font-weight: bold;
}
.tuct-icons {
@@ -59,6 +64,14 @@ const props = defineProps<TucTileProps>();
img {
height: 30px;
object-fit: contain;
&.stat0 {
filter: invert(0.4);
}
}
}
.dark .tuct-icons img.stat0 {
filter: unset;
}
</style>

View File

@@ -1,4 +1,4 @@
<!-- 真境剧诗页面 TODO: 重构UI -->
<!-- 真境剧诗页面 -->
<template>
<v-app-bar>
<template #prepend>
@@ -6,19 +6,19 @@
<img alt="icon" src="/source/UI/userCombat.webp" />
<span>幻想真境剧诗</span>
<v-select
density="compact"
variant="outlined"
v-model="uidCur"
:items="uidList"
:hide-details="true"
:items="uidList"
density="compact"
label="游戏UID"
variant="outlined"
/>
<v-btn :rounded="true" class="uc-btn" @click="toAbyss()">
<img src="/source/UI/userAbyss.webp" alt="abyss" />
<img alt="abyss" src="/source/UI/userAbyss.webp" />
<span>深境螺旋</span>
</v-btn>
<v-btn :rounded="true" class="uc-btn" @click="toChallenge()">
<img src="/source/UI/userChallenge.webp" alt="challenge" />
<img alt="challenge" src="/source/UI/userChallenge.webp" />
<span>幽境危战</span>
</v-btn>
</div>
@@ -26,28 +26,28 @@
<template #append>
<div class="uct-right">
<v-btn
class="uc-btn"
@click="shareCombat()"
:rounded="true"
:disabled="localCombat.length === 0"
:rounded="true"
class="uc-btn"
prepend-icon="mdi-share"
@click="shareCombat()"
>
分享
</v-btn>
<v-btn class="uc-btn" @click="refreshCombat()" :rounded="true" prepend-icon="mdi-refresh">
<v-btn :rounded="true" class="uc-btn" prepend-icon="mdi-refresh" @click="refreshCombat()">
刷新
</v-btn>
<v-btn class="uc-btn" @click="tryReadCombat()" :rounded="true" prepend-icon="mdi-download">
<v-btn :rounded="true" class="uc-btn" prepend-icon="mdi-download" @click="tryReadCombat()">
导入
</v-btn>
<v-btn class="uc-btn" @click="deleteCombat()" :rounded="true" prepend-icon="mdi-delete">
<v-btn :rounded="true" class="uc-btn" prepend-icon="mdi-delete" @click="deleteCombat()">
删除
</v-btn>
</div>
</template>
</v-app-bar>
<div class="uc-box">
<v-tabs v-model="userTab" direction="vertical" class="uc-tabs-box" center-active>
<v-tabs v-model="userTab" center-active class="uc-tabs-box" direction="vertical">
<v-tab v-for="item in localCombat" :key="item.id" :value="item.id"> {{ item.id }}</v-tab>
</v-tabs>
<v-window v-model="userTab" class="uc-window">
@@ -64,15 +64,18 @@
<span>{{ item.id }}</span>
<span> UID</span>
<span>{{ uidCur }}</span>
<span>更新于</span>
<span>{{ item.updated }}</span>
</div>
<div class="ucw-share">真境剧诗 | Render by TeyvatGuide v{{ version }}</div>
</div>
<TSubLine>统计周期 {{ item.startTime }} ~ {{ item.endTime }}</TSubLine>
<TSubLine>
<div class="ucw-subtitle">
<span>统计周期 {{ item.startTime }} ~ {{ item.endTime }}</span>
<span>{{ item.updated }}更新</span>
</div>
</TSubLine>
<TucOverview :data="item.stat" :fights="item.detail.fight_statisic" />
<TSubLine>使用角色({{ item.detail.backup_avatars.length }})</TSubLine>
<TucAvatars :model-value="item.detail.backup_avatars" :detail="false" />
<TucAvatars :detail="false" :model-value="item.detail.backup_avatars" />
<div class="ucw-rounds">
<TucRound v-for="(round, idx) in item.detail.rounds_data" :key="idx" :round="round" />
</div>
@@ -80,7 +83,7 @@
</v-window-item>
</v-window>
<div v-show="localCombat.length === 0" class="user-empty">
<img src="/source/UI/empty.webp" alt="empty" />
<img alt="empty" src="/source/UI/empty.webp" />
<span>暂无数据请尝试刷新</span>
</div>
</div>
@@ -210,7 +213,7 @@ async function shareCombat(): Promise<void> {
return;
}
await showLoading.start("正在生成图片", fileName);
await generateShareImg(fileName, shareDom);
await generateShareImg(fileName, shareDom, 2.0);
await showLoading.end();
await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片成功`);
}
@@ -374,12 +377,12 @@ async function tryReadCombat(): Promise<void> {
align-items: center;
color: var(--common-text-title);
column-gap: 4px;
font-family: var(--font-title);
font-size: 20px;
}
font-size: 18px;
.ucw-title :nth-child(2n) {
color: var(--tgc-yellow-1);
:nth-child(2n) {
color: var(--tgc-od-orange);
font-family: var(--font-title);
}
}
.ucw-share {
@@ -388,6 +391,16 @@ async function tryReadCombat(): Promise<void> {
opacity: 0.8;
}
.ucw-subtitle {
position: relative;
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
padding-right: 8px;
padding-left: 4px;
}
.user-empty {
position: absolute;
top: calc(50vh - 200px);
@@ -406,8 +419,11 @@ async function tryReadCombat(): Promise<void> {
}
.ucw-rounds {
display: grid;
gap: 8px;
grid-template-columns: repeat(2, 1fr);
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
row-gap: 8px;
}
</style>

View File

@@ -115,7 +115,6 @@ export async function generateShareImg(
canvas,
x: -15,
y: -15,
dpi: 350,
};
let canvasData;
try {