🌱 经典视图适配,1/3

#121
This commit is contained in:
目棃
2024-08-24 18:41:28 +08:00
parent 4be13dc857
commit 1fe33ba4fd
13 changed files with 560 additions and 524 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div class="tua-ab-box">
<div class="tua-ab-box" title="点击查看详情">
<div class="tua-ab-top">
<TItembox v-model="avatarBox" />
<div class="tua-abt-right">

View File

@@ -0,0 +1,121 @@
<template>
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="20px">
<div class="tdo-box">
<div class="tdo-tabs-container">
<v-tabs v-model="modeTab" class="tdo-tabs" :rounded="true">
<v-tab value="classic">经典视图</v-tab>
<v-tab value="card">卡片视图简略</v-tab>
<v-tab value="dev">卡片视图详细</v-tab>
</v-tabs>
</div>
<div class="tdo-container">
<div class="tdo-box-arrow left" @click="handleClick('left')">
<img alt="left" src="../../assets/icons/arrow-right.svg" />
</div>
<v-window class="tdo-box-container" v-model="modeTab">
<v-window-item value="classic">
<TucDetailCard :data-val="avatar" />
</v-window-item>
<v-window-item value="card"> </v-window-item>
<v-window-item value="dev"> </v-window-item>
</v-window>
<div class="tdo-box-arrow right" @click="handleClick('right')">
<img alt="right" src="../../assets/icons/arrow-right.svg" />
</div>
</div>
</div>
</TOverlay>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import TOverlay from "../main/t-overlay.vue";
import TucDetailCard from "../userAvatarOld/tuc-detail-card.vue";
interface TuaDetailOverlayProps {
modelValue: boolean;
avatar: TGApp.Sqlite.Character.UserRole;
mode: "classic" | "card" | "dev";
}
interface TuaDetailOverlayEmits {
(e: "update:modelValue", val: boolean): void;
(e: "update:mode", val: "classic" | "card" | "dev"): void;
(e: "toNext", val: boolean): void;
}
const props = defineProps<TuaDetailOverlayProps>();
const emits = defineEmits<TuaDetailOverlayEmits>();
const visible = computed<boolean>({
get: () => props.modelValue,
set: (val) => emits("update:modelValue", val),
});
const modeTab = computed<"classic" | "card" | "dev">({
get: () => props.mode,
set: (val) => emits("update:mode", val),
});
function onCancel(): void {
visible.value = false;
}
function handleClick(pos: "left" | "right"): void {
if (pos === "left") emits("toNext", false);
else emits("toNext", true);
}
</script>
<style lang="css" scoped>
.tdo-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 120px;
row-gap: 10px;
}
.tdo-tabs-container {
background: var(--box-bg-1);
box-shadow: 0 0 10px var(--common-shadow-2);
color: var(--box-text-1);
}
.tdo-container {
display: flex;
align-items: center;
justify-content: center;
column-gap: 10px;
}
.tdo-box-arrow {
position: relative;
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
cursor: pointer;
}
.dark .tdo-box-arrow {
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
}
.tdo-box-arrow.left img {
width: 100%;
height: 100%;
transform: rotate(180deg);
}
.tdo-box-arrow.right img {
width: 100%;
height: 100%;
}
.tdo-box-container {
position: relative;
}
</style>

View File

@@ -0,0 +1,324 @@
<template>
<div class="tuc-do-box">
<img :src="bg" alt="role" class="tuc-do-bg" />
<div v-if="props.dataVal.costumes.length > 0" class="tuc-do-costume">
<v-switch v-model="showCostumeSwitch" color="#fb7299" @click="switchBg">
<template #label>
<v-icon>mdi-tshirt-crew-outline</v-icon>
</template>
</v-switch>
</div>
<div v-if="showCostumeSwitch" class="tuc-do-costume-name">
{{ props.dataVal.costumes[0].name }}
</div>
<div class="tuc-do-show">
<div class="tuc-do-main">
<div class="tuc-do-left">
<div
class="tuc-dol-item"
:style="`opacity: ${selected.pos === 0 ? '1' : '0.5'}`"
@click="showDetail(props.dataVal.weapon, '武器', 0)"
>
<TucDetailItemBox :model-value="weaponBox" />
</div>
<div
v-for="(item, index) in relicList"
:key="index"
class="tuc-dol-item"
:style="{
cursor: item ? 'pointer' : 'default',
opacity: selected.pos === index + 1 ? '1' : item ? '0.5' : '1',
}"
@click="showDetail(item, '圣遗物', index + 1)"
>
<TucDetailRelic :model-value="item" :pos="index + 1" />
</div>
</div>
<!-- 右侧环状排列6个命座 -->
<div class="tuc-do-right">
<div class="tuc-dor-box">
<TucDetailConstellation
v-for="item in props.dataVal.constellations"
:key="item.pos"
class="tuc-dor-item"
:model-value="item"
:style="{
border: selected.pos === item.pos + 5 ? '2px solid var(--tgc-yellow-1)' : '',
}"
@click="showDetail(item, '命座', item.pos + 5)"
/>
</div>
</div>
</div>
<!-- 底部说明 -->
<div class="tuc-do-bottom">
<TucDetailDescWeapon v-if="selected.type === '武器'" :model-value="props.dataVal.weapon" />
<TucDetailDescConstellation
v-if="selected.type === '命座' && selectConstellation"
:model-value="selectConstellation"
/>
<TucDetailDescRelic
v-if="selected.type === '圣遗物' && selectRelic"
:model-value="selectRelic"
/>
</div>
<div class="tuc-do-quote">* 所有数据以游戏内为准此处仅供参考</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, watch } from "vue";
import TucDetailConstellation from "./tuc-detail-constellation.vue";
import TucDetailDescConstellation from "./tuc-detail-desc-constellation.vue";
import TucDetailDescRelic from "./tuc-detail-desc-relic.vue";
import TucDetailDescWeapon from "./tuc-detail-desc-weapon.vue";
import TucDetailItemBox from "./tuc-detail-itembox.vue";
import TucDetailRelic from "./tuc-detail-relic.vue";
interface ToUcDetailProps {
dataVal: TGApp.Sqlite.Character.UserRole;
}
interface ToUcDetailSelect {
type: "命座" | "武器" | "圣遗物";
pos: number;
}
type fixedLenArray<T, N extends number> = [T, ...T[]] & { length: N };
type RelicList = fixedLenArray<TGApp.Game.Avatar.Relic | false, 5>;
const props = defineProps<ToUcDetailProps>();
const relicList = computed<RelicList>(() => {
return [
props.dataVal.relics.find((item) => item.pos === 1) || false,
props.dataVal.relics.find((item) => item.pos === 2) || false,
props.dataVal.relics.find((item) => item.pos === 3) || false,
props.dataVal.relics.find((item) => item.pos === 4) || false,
props.dataVal.relics.find((item) => item.pos === 5) || false,
];
});
const weaponBox = computed(() => {
const weapon = props.dataVal.weapon;
return {
icon: `/WIKI/weapon/${weapon.id}.webp`,
bg: `/icon/bg/${weapon.rarity}-Star.webp`,
};
});
const showCostumeSwitch = ref(false);
const selectConstellation = ref<TGApp.Game.Avatar.Constellation>();
const selectRelic = ref<TGApp.Game.Avatar.Relic>();
const selected = ref<ToUcDetailSelect>({ type: "武器", pos: 0 });
const bg = computed<string>(() => {
return showCostumeSwitch.value ? props.dataVal.costumes[0].icon : props.dataVal.avatar.image;
});
const bgTransY = computed<string>(() => {
return showCostumeSwitch.value ? "0" : "10px";
});
const bgFit = computed<string>(() => {
return showCostumeSwitch.value ? "cover" : "contain";
});
// 加载数据
function loadData(): void {
selected.value = { type: "武器", pos: 0 };
selectConstellation.value = undefined;
selectRelic.value = undefined;
showCostumeSwitch.value = false;
}
onMounted(() => {
loadData();
});
watch(
() => props.dataVal,
() => {
loadData();
},
);
function showDetail(
item:
| TGApp.Game.Avatar.Constellation
| TGApp.Game.Avatar.WeaponDetail
| TGApp.Game.Avatar.Relic
| false,
selectType: "命座" | "武器" | "圣遗物",
selectPos: number,
): void {
if (!item) return;
switch (selectType) {
case "命座":
selectConstellation.value = <TGApp.Game.Avatar.Constellation>item;
break;
case "武器":
break;
case "圣遗物":
selectRelic.value = <TGApp.Game.Avatar.Relic>item;
break;
default:
break;
}
selected.value = {
type: selectType,
pos: selectPos,
};
}
function switchBg(): void {
showCostumeSwitch.value = !showCostumeSwitch.value;
}
</script>
<style lang="css" scoped>
.tuc-do-box {
position: relative;
overflow: hidden;
width: 450px;
height: 600px;
border-radius: 5px;
background: var(--box-bg-1);
}
.tuc-do-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
max-height: 100%;
border-radius: 5px;
margin: 0 auto;
object-fit: v-bind(bgFit);
transform: translateY(v-bind(bgTransY));
}
.tuc-do-costume {
position: absolute;
z-index: 1;
top: 5px;
right: 10px;
color: var(--tgc-pink-1);
}
.tuc-do-costume-name {
position: absolute;
top: 10px;
left: calc(50% - 80px);
width: 160px;
border-radius: 5px;
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
background: var(--tgc-white-1);
color: var(--tgc-yellow-1);
font-family: var(--font-text);
font-size: 16px;
opacity: 0.8;
text-align: center;
}
.tuc-do-show {
position: relative;
display: flex;
height: 100%;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
.tuc-do-main {
position: relative;
display: flex;
width: 100%;
height: 360px;
align-items: center;
justify-content: space-between;
}
.tuc-do-left {
position: relative;
width: 150px;
height: 100%;
padding: 10px;
column-count: 2;
column-gap: 10px;
}
.tuc-do-right {
width: 175px;
height: 100%;
margin-right: 20px;
}
.tuc-do-bottom {
width: 100%;
padding: 0 10px;
}
.tuc-do-quote {
display: flex;
align-items: center;
justify-content: center;
padding: 2px 5px;
margin-top: auto;
margin-left: auto;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background: var(--common-shadow-2);
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
box-shadow: 2px 2px 5px var(--tgc-dark-1);
color: var(--tgc-white-1);
font-size: 14px;
text-shadow: 0 0 5px var(--tgc-dark-1);
}
/* 左侧显示区域 */
.tuc-dol-item {
position: relative;
border-radius: 5px;
margin-top: 50px;
cursor: pointer;
}
/* 右侧显示区域 */
.tuc-dor-box {
position: relative;
width: 100%;
height: 100%;
}
.tuc-dor-item {
position: absolute;
cursor: pointer;
}
/* 环状排列6个命座 */
.tuc-dor-item:nth-child(1) {
top: 10px;
left: 10px;
}
.tuc-dor-item:nth-child(2) {
top: 45px;
left: 75px;
}
.tuc-dor-item:nth-child(3) {
top: 110px;
right: 0;
}
.tuc-dor-item:nth-child(4) {
right: 0;
bottom: 110px;
}
.tuc-dor-item:nth-child(5) {
bottom: 45px;
left: 75px;
}
.tuc-dor-item:nth-child(6) {
bottom: 10px;
left: 10px;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div class="tuc-dc-box">
<div v-if="!modelValue.active" class="tuc-dc-lock">
<div v-if="!modelValue.is_actived" class="tuc-dc-lock">
<v-icon color="white"> mdi-lock </v-icon>
</div>
<div class="tuc-dc-icon">
@@ -10,7 +10,7 @@
</template>
<script lang="ts" setup>
interface TucDetailConstellationProps {
modelValue: TGApp.Sqlite.Character.RoleConstellation;
modelValue: TGApp.Game.Avatar.Constellation;
}
defineProps<TucDetailConstellationProps>();

View File

@@ -19,7 +19,7 @@
</template>
<template #desc>
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="parseHtmlText(props.modelValue.description)"></span>
<span v-html="parseHtmlText(props.modelValue.effect)"></span>
</template>
</TucDetailDesc>
</template>
@@ -30,7 +30,7 @@ import TucDetailConstellation from "./tuc-detail-constellation.vue";
import TucDetailDesc from "./tuc-detail-desc.vue";
interface TucDetailDescConstellationProps {
modelValue: TGApp.Sqlite.Character.RoleConstellation;
modelValue: TGApp.Game.Avatar.Constellation;
}
const props = defineProps<TucDetailDescConstellationProps>();

View File

@@ -12,26 +12,25 @@
<span>{{ props.modelValue.level }}</span>
</div>
<div class="tuc-ddrc-bottom">
<img :src="`/icon/star/${props.modelValue.star}.webp`" alt="star" />
<img :src="`/icon/star/${props.modelValue.rarity}.webp`" alt="star" />
</div>
</div>
</template>
<template #desc>
<div class="tuc-ddrd-title">{{ props.modelValue.set.name }}</div>
<div v-for="(desc, index) in props.modelValue.set.effect" :key="index" class="tuc-ddrc-desc">
<span>{{ desc.active }}件套</span>
<span>{{ desc.description }}</span>
<div v-for="(desc, index) in props.modelValue.set.affixes" :key="index" class="tuc-ddrc-desc">
<span>{{ desc.activation_number }}件套</span>
<span>{{ desc.effect }}</span>
</div>
</template>
</TucDetailDesc>
</template>
<script lang="ts" setup>
// vue
import TucDetailDesc from "./tuc-detail-desc.vue";
import TucDetailRelic from "./tuc-detail-relic.vue";
interface TucDetailDescRelicProps {
modelValue: TGApp.Sqlite.Character.RoleReliquary;
modelValue: TGApp.Game.Avatar.Relic;
}
const props = defineProps<TucDetailDescRelicProps>();

View File

@@ -10,19 +10,16 @@
<span>{{ props.modelValue.name }}</span>
<span>Lv.{{ props.modelValue.level }}</span>
<span>精炼</span>
<span>{{ props.modelValue.affix }}</span>
<span>{{ props.modelValue.affix_level }}</span>
<span></span>
</div>
<div class="tuc-ddwc-bottom">
<img :src="`/icon/star/${props.modelValue.star}.webp`" alt="star" />
<img :src="`/icon/star/${props.modelValue.rarity}.webp`" alt="star" />
</div>
</div>
</template>
<template #desc>
<span
v-if="props.modelValue.description"
v-html="parseHtmlText(props.modelValue.description)"
></span>
<span v-if="props.modelValue.desc" v-html="parseHtmlText(props.modelValue.desc)"></span>
</template>
</TucDetailDesc>
</template>
@@ -35,13 +32,13 @@ import TucDetailDesc from "./tuc-detail-desc.vue";
import TucDetailItemBox from "./tuc-detail-itembox.vue";
interface TucDetailDescWeaponProps {
modelValue: TGApp.Sqlite.Character.RoleWeapon;
modelValue: TGApp.Game.Avatar.WeaponDetail;
}
const props = defineProps<TucDetailDescWeaponProps>();
const box = computed(() => {
return {
bg: `/icon/bg/${props.modelValue.star}-Star.webp`,
bg: `/icon/bg/${props.modelValue.rarity}-Star.webp`,
icon: `/WIKI/weapon/${props.modelValue.id}.webp`,
};
});

View File

@@ -14,6 +14,7 @@
</template>
<style lang="css" scoped>
.tuc-dd-box {
width: 100%;
padding: 10px;
border: 1px solid var(--common-shadow-2);
border-radius: 5px;
@@ -45,7 +46,7 @@
.tuc-dd-desc {
width: 100%;
max-height: 50px;
height: 80px;
margin-top: 5px;
color: var(--box-text-4);
font-family: var(--font-text);
@@ -54,9 +55,4 @@
text-align: left;
word-break: break-all;
}
/* 隐藏 desc 侧面滚动条 */
.tuc-dd-desc::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -4,7 +4,7 @@
<img :src="`/icon/relic/${props.pos}.webp`" alt="relic" />
</div>
<div v-if="props.modelValue" class="tuc-dr-bg">
<img :src="`/icon/bg/${props.modelValue.star}-Star.webp`" alt="bg" />
<img :src="`/icon/bg/${props.modelValue.rarity}-Star.webp`" alt="bg" />
</div>
<div v-if="props.modelValue" class="tuc-dr-icon">
<img :src="props.modelValue.icon" alt="relic" />
@@ -13,7 +13,7 @@
</template>
<script lang="ts" setup>
interface TucDetailRelicProps {
modelValue: TGApp.Sqlite.Character.RoleReliquary | false;
modelValue: TGApp.Game.Avatar.Relic | false;
pos: number;
}

View File

@@ -1,488 +0,0 @@
<template>
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="20px">
<div class="tuc-do-div">
<!-- 左侧箭头 -->
<div class="tuc-arrow-left" @click="handleClick('left')">
<img src="../../assets/icons/arrow-right.svg" alt="left" />
</div>
<!-- 中间内容 -->
<div class="tuc-do-box">
<div class="tuc-do-bg">
<img
:src="data.bg"
alt="role"
:style="{
objectFit: data.bgFit,
}"
/>
</div>
<div class="tuc-do-quote">* 所有数据以游戏内为准此处仅供参考</div>
<!-- 衣装 -->
<div v-if="data.costume.length > 0" class="tuc-do-costume">
<v-switch v-model="showCostumeSwitch" color="#fb7299" @click="switchBg">
<template #label>
<v-icon>mdi-tshirt-crew-outline</v-icon>
</template>
</v-switch>
</div>
<div v-if="showCostumeSwitch" class="tuc-do-costume-name">
{{ data.costume[0].name }}
</div>
<div v-if="data" class="tuc-do-show">
<!-- 左侧武器跟圣遗物 -->
<div class="tuc-do-left">
<div
class="tuc-dol-item"
:style="{
opacity: selected.pos === 0 ? '1' : '0.5',
}"
@click="showDetail(data.weapon, '武器', 0)"
>
<TucDetailItemBox v-model="weaponBox" />
</div>
<div
v-for="(item, index) in data.reliquary"
:key="index"
class="tuc-dol-item"
:style="{
cursor: item ? 'pointer' : 'default',
opacity: selected.pos === index + 1 ? '1' : item ? '0.5' : '1',
}"
@click="showDetail(item, '圣遗物', index + 1)"
>
<TucDetailRelic :model-value="item" :pos="index + 1" />
</div>
</div>
<!-- 右侧环状排列6个命座 -->
<div class="tuc-do-right">
<div class="tuc-dor-box">
<TucDetailConstellation
v-for="item in data.constellation"
:key="item.pos"
class="tuc-dor-item"
:model-value="item"
:style="{
border: selected.pos === item.pos + 5 ? '2px solid var(--tgc-yellow-1)' : '',
}"
@click="showDetail(item, '命座', item.pos + 5)"
/>
</div>
</div>
<!-- 底部说明 -->
<div class="tuc-do-bottom">
<TucDetailDescWeapon v-if="selected.type === '武器'" v-model="selectWeapon" />
<TucDetailDescConstellation
v-if="selected.type === '命座'"
v-model="selectConstellation"
/>
<TucDetailDescRelic v-if="selected.type === '圣遗物'" v-model="selectRelic" />
</div>
</div>
</div>
<!-- 右侧箭头 -->
<div class="tuc-arrow-right" @click="handleClick('right')">
<img src="../../assets/icons/arrow-right.svg" alt="right" />
</div>
</div>
</TOverlay>
</template>
<script lang="ts" setup>
import { computed, onMounted, onUpdated, ref } from "vue";
import TOverlay from "../main/t-overlay.vue";
import TucDetailConstellation from "./tuc-detail-constellation.vue";
import TucDetailDescConstellation from "./tuc-detail-desc-constellation.vue";
import TucDetailDescRelic from "./tuc-detail-desc-relic.vue";
import TucDetailDescWeapon from "./tuc-detail-desc-weapon.vue";
import TucDetailItemBox from "./tuc-detail-itembox.vue";
import TucDetailRelic from "./tuc-detail-relic.vue";
interface ToUcDetailProps {
modelValue: boolean;
dataVal: TGApp.Sqlite.Character.UserRole;
}
interface ToUcDetailEmits {
(e: "update:modelValue", value: boolean): void;
(e: "clickL"): void;
(e: "clickR"): void;
}
type fixedLenArray<T, N extends number> = [T, ...T[]] & { length: N };
type relicsInfo = fixedLenArray<TGApp.Sqlite.Character.RoleReliquary | false, 5>;
interface ToUcDetailData {
weapon: TGApp.Sqlite.Character.RoleWeapon;
constellation: TGApp.Sqlite.Character.RoleConstellation[];
reliquary: relicsInfo;
costume: TGApp.Sqlite.Character.RoleCostume[];
bg: string;
bgFit: "contain" | "cover";
}
interface ToUcDetailSelect {
type: "命座" | "武器" | "圣遗物";
pos: number;
}
const emits = defineEmits<ToUcDetailEmits>();
const props = defineProps<ToUcDetailProps>();
const visible = computed({
get: () => props.modelValue,
set: (value) => {
emits("update:modelValue", value);
},
});
const showCostumeSwitch = ref(false);
// data
const data = ref<ToUcDetailData>({
weapon: <TGApp.Sqlite.Character.RoleWeapon>{},
constellation: [],
reliquary: [false, false, false, false, false],
costume: [],
bg: "",
bgFit: "contain",
});
const selectConstellation = ref<TGApp.Sqlite.Character.RoleConstellation>(
<TGApp.Sqlite.Character.RoleConstellation>{},
);
const selectWeapon = ref<TGApp.Sqlite.Character.RoleWeapon>(<TGApp.Sqlite.Character.RoleWeapon>{});
const selectRelic = ref<TGApp.Sqlite.Character.RoleReliquary>(
<TGApp.Sqlite.Character.RoleReliquary>{},
);
const selected = ref<ToUcDetailSelect>({
type: "武器",
pos: 0,
});
// 重置数据
function resetData(): void {
data.value = {
weapon: <TGApp.Sqlite.Character.RoleWeapon>{},
constellation: [],
reliquary: [false, false, false, false, false],
costume: [],
bg: "",
bgFit: "contain",
};
selected.value = {
type: "武器",
pos: 0,
};
}
// 加载数据
function loadData(): void {
if (!props.modelValue) return;
resetData();
data.value.weapon = JSON.parse(props.dataVal.weapon);
data.value.constellation = JSON.parse(props.dataVal.constellation);
if (props.dataVal.reliquary !== "") {
const relics = <TGApp.Sqlite.Character.RoleReliquary[]>JSON.parse(props.dataVal.reliquary);
const relicTemp: relicsInfo = [false, false, false, false, false];
relics.forEach((item) => {
switch (item.pos) {
case 1:
relicTemp[0] = item;
break;
case 2:
relicTemp[1] = item;
break;
case 3:
relicTemp[2] = item;
break;
case 4:
relicTemp[3] = item;
break;
case 5:
relicTemp[4] = item;
break;
default:
break;
}
});
data.value.reliquary = relicTemp;
}
data.value.costume = JSON.parse(props.dataVal.costume);
data.value.bg = props.dataVal.img;
data.value.bgFit = "contain";
showCostumeSwitch.value = false;
selectWeapon.value = data.value.weapon;
}
onMounted(() => {
loadData();
});
// 监听外部数据变化
onUpdated(() => {
loadData();
});
const weaponBox = computed(() => {
const weapon = data.value.weapon;
return {
icon: `/WIKI/weapon/${weapon.id}.webp`,
bg: `/icon/bg/${weapon.star}-Star.webp`,
};
});
const onCancel = (): void => {
visible.value = false;
emits("update:modelValue", false);
};
function handleClick(pos: "left" | "right") {
pos === "left" ? emits("clickL") : emits("clickR");
}
function showDetail(
item:
| TGApp.Sqlite.Character.RoleConstellation
| TGApp.Sqlite.Character.RoleWeapon
| TGApp.Sqlite.Character.RoleReliquary
| false,
selectType: "命座" | "武器" | "圣遗物",
selectPos: number,
): void {
if (!item) return;
switch (selectType) {
case "命座":
selectConstellation.value = <TGApp.Sqlite.Character.RoleConstellation>item;
break;
case "武器":
selectWeapon.value = <TGApp.Sqlite.Character.RoleWeapon>item;
break;
case "圣遗物":
selectRelic.value = <TGApp.Sqlite.Character.RoleReliquary>item;
break;
default:
break;
}
selected.value = {
type: selectType,
pos: selectPos,
};
}
function switchBg(): void {
if (!showCostumeSwitch.value) {
data.value.bg = data.value.costume[0].icon;
data.value.bgFit = "cover";
showCostumeSwitch.value = true;
} else {
data.value.bg = props.dataVal.img;
data.value.bgFit = "contain";
showCostumeSwitch.value = false;
}
}
</script>
<style lang="css" scoped>
.tuc-do-div {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
}
.tuc-arrow-left,
.tuc-arrow-right {
position: relative;
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
cursor: pointer;
}
.dark .tuc-arrow-left,
.dark .tuc-arrow-right {
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
}
.tuc-arrow-left img {
width: 100%;
height: 100%;
transform: rotate(180deg);
}
.tuc-arrow-right img {
width: 100%;
height: 100%;
}
.tuc-do-box {
position: relative;
width: 500px;
height: 620px;
padding: 10px;
border-radius: 5px;
background: var(--box-bg-1);
}
.tuc-do-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 5px;
margin: 0 auto;
}
.tuc-do-bg img {
width: 100%;
height: 100%;
border-radius: 5px;
}
.tuc-do-quote {
position: absolute;
right: 0;
bottom: 0;
padding: 2px 5px;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background: var(--common-shadow-2);
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
color: var(--tgc-white-1);
font-size: 14px;
}
.tuc-do-costume {
position: absolute;
z-index: 1;
top: 5px;
right: 10px;
color: var(--tgc-pink-1);
}
.tuc-do-costume-name {
position: absolute;
top: 10px;
left: calc(50% - 80px);
width: 160px;
border-radius: 5px;
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
background: var(--tgc-white-1);
color: var(--tgc-yellow-1);
font-family: var(--font-text);
font-size: 16px;
opacity: 0.8;
text-align: center;
}
.tuc-do-show {
position: absolute;
display: flex;
width: calc(100% - 20px);
flex-wrap: wrap;
align-items: start;
justify-content: space-around;
}
.tuc-do-left {
position: relative;
width: 50%;
height: 400px;
}
.tuc-do-right {
width: 50%;
height: 400px;
}
.tuc-do-bottom {
width: 480px;
height: 140px;
}
/* 左侧显示区域 */
.tuc-dol-item {
position: absolute;
border-radius: 5px;
cursor: pointer;
}
/* 排列武器跟5个圣遗物 */
.tuc-dol-item:nth-child(1) {
top: 40px;
left: 10px;
}
.tuc-dol-item:nth-child(2) {
top: 90px;
left: 80px;
}
.tuc-dol-item:nth-child(3) {
top: 140px;
left: 10px;
}
.tuc-dol-item:nth-child(4) {
top: 190px;
left: 80px;
}
.tuc-dol-item:nth-child(5) {
top: 240px;
left: 10px;
}
.tuc-dol-item:nth-child(6) {
top: 290px;
left: 80px;
}
/* 右侧显示区域 */
.tuc-dor-box {
position: relative;
width: 100%;
height: 100%;
}
.tuc-dor-item {
position: absolute;
cursor: pointer;
}
/* 环状排列6个命座 */
.tuc-dor-item:nth-child(1) {
top: 0;
right: 100px;
}
.tuc-dor-item:nth-child(2) {
top: 50px;
right: 40px;
}
.tuc-dor-item:nth-child(3) {
top: 130px;
right: 10px;
}
.tuc-dor-item:nth-child(4) {
right: 10px;
bottom: 130px;
}
.tuc-dor-item:nth-child(5) {
right: 40px;
bottom: 50px;
}
.tuc-dor-item:nth-child(6) {
right: 100px;
bottom: 0;
}
</style>

View File

@@ -13,7 +13,13 @@
</template>
刷新
</v-btn>
<v-btn @click="share()" rounded variant="outlined" v-model:loading="loadShare">
<v-btn
:disabled="showOverlay"
@click="share()"
rounded
variant="outlined"
v-model:loading="loadShare"
>
<template #prepend>
<v-icon>mdi-share</v-icon>
</template>
@@ -25,10 +31,21 @@
<div class="uc-select">
<v-btn variant="outlined" @click="showSelect = true">筛选角色</v-btn>
<v-btn variant="outlined" @click="resetSelect = true">重置筛选</v-btn>
<v-select
v-model="showMode"
:items="modeList"
label="详情视图模式"
hide-details
item-title="label"
item-value="value"
variant="outlined"
class="uc-select-mode"
density="compact"
/>
</div>
<!-- todo 展示模式切换 -->
</template>
</v-app-bar>
<TwoSelectC v-model="showSelect" @select-c="handleSelect" v-model:reset="resetSelect" />
<div class="uc-box">
<div class="uc-top">
<div class="uc-top-title">
@@ -39,7 +56,7 @@
<span v-else> 暂无数据 </span>
</div>
</div>
<div class="uc-grid">
<div class="uc-grid" v-if="!isEmpty">
<TuaAvatarBox
v-for="(role, index) in selectedList"
:key="index"
@@ -47,8 +64,16 @@
@click="selectRole(role)"
/>
</div>
<div class="uc-empty" v-else>
<img src="/source/UI/empty.webp" alt="empty" />
</div>
</div>
<TwoSelectC v-model="showSelect" @select-c="handleSelect" v-model:reset="resetSelect" />
<TuaDetailOverlay
v-model="showOverlay"
:avatar="dataVal"
v-model:mode="showMode"
@to-next="handleSwitch"
/>
</template>
<script lang="ts" setup>
import { storeToRefs } from "pinia";
@@ -57,6 +82,7 @@ import { onBeforeMount, onMounted, ref, watch } from "vue";
import showSnackbar from "../../components/func/snackbar.js";
import ToLoading from "../../components/overlay/to-loading.vue";
import TuaAvatarBox from "../../components/userAvatar/tua-avatar-box.vue";
import TuaDetailOverlay from "../../components/userAvatar/tua-detail-overlay.vue";
import TwoSelectC, { SelectedCValue } from "../../components/wiki/two-select-c.vue";
import { AppCharacterData } from "../../data/index.js";
import TSUserAvatar from "../../plugins/Sqlite/modules/userAvatar.js";
@@ -77,17 +103,23 @@ const loadingTitle = ref<string>();
const loadingSub = ref<string>();
// data
const isEmpty = ref(true);
const isEmpty = ref<boolean>(true);
const roleList = ref<TGApp.Sqlite.Character.UserRole[]>([]);
const selectedList = ref<TGApp.Sqlite.Character.UserRole[]>([]);
// overlay
const visible = ref(false);
const dataVal = ref<TGApp.Sqlite.Character.UserRole>(<TGApp.Sqlite.Character.UserRole>{});
const selectIndex = ref(0);
const showOverlay = ref<boolean>(false);
const selectIndex = ref<number>(0);
const showSelect = ref<boolean>(false);
const showMode = ref<"classic" | "card" | "dev">("classic");
const resetSelect = ref<boolean>(false);
const modeList = [
{ label: "经典视图", value: "classic" },
{ label: "卡片视图(简略)", value: "card" },
{ label: "卡片视图(详细)", value: "dev" },
];
onBeforeMount(() => {
if (userStore.account.value) user.value = userStore.account.value;
@@ -112,6 +144,24 @@ watch(resetSelect, (val) => {
});
}
});
watch(showMode, (val) => {
if (val === "classic") {
showSnackbar({
text: "已切换至经典视图",
color: "success",
});
} else if (val === "card") {
showSnackbar({
text: "已切换至卡片视图(简略)",
color: "success",
});
} else {
showSnackbar({
text: "已切换至卡片视图(详细)",
color: "success",
});
}
});
function getOrderedList(
data: TGApp.Sqlite.Character.UserRole[],
@@ -144,6 +194,10 @@ async function load(): Promise<void> {
async function refresh(): Promise<void> {
if (!user.value) return;
if (showOverlay.value) {
showOverlay.value = false;
await new Promise((resolve) => setTimeout(resolve, 500));
}
await TGLogger.Info(`[Character][refreshRoles][${user.value.gameUid}] 正在更新角色数据`);
loadingTitle.value = "正在获取角色列表";
loading.value = true;
@@ -205,7 +259,13 @@ async function refresh(): Promise<void> {
}
async function share(): Promise<void> {
if (!user.value) return;
if (!user.value || isEmpty.value) {
showSnackbar({
text: "暂无数据",
color: "error",
});
return;
}
await TGLogger.Info(`[Character][shareRoles][${user.value.gameUid}] 正在生成分享图片`);
const rolesBox = <HTMLElement>document.querySelector(".uc-box");
const fileName = `【角色列表】-${user.value.gameUid}`;
@@ -234,7 +294,7 @@ function getUpdateTime(): string {
function selectRole(role: TGApp.Sqlite.Character.UserRole): void {
dataVal.value = role;
selectIndex.value = roleList.value.indexOf(role);
visible.value = true;
showOverlay.value = true;
}
function handleSelect(val: SelectedCValue) {
@@ -260,6 +320,17 @@ function handleSelect(val: SelectedCValue) {
const selectedId = filterC.map((item) => item.id);
selectedList.value = roleList.value.filter((role) => selectedId.includes(role.avatar.id));
}
function handleSwitch(next: boolean): void {
if (next) {
selectIndex.value += 1;
if (selectIndex.value >= roleList.value.length) selectIndex.value = 0;
} else {
selectIndex.value -= 1;
if (selectIndex.value < 0) selectIndex.value = roleList.value.length - 1;
}
dataVal.value = roleList.value[selectIndex.value];
}
</script>
<style lang="css" scoped>
.uc-select {
@@ -270,6 +341,15 @@ function handleSelect(val: SelectedCValue) {
gap: 10px;
}
.uc-select-mode {
display: flex;
width: 200px;
height: 40px;
align-items: center;
justify-content: flex-start;
font-size: 14px;
}
.uc-box {
display: flex;
flex-direction: column;
@@ -308,4 +388,11 @@ function handleSelect(val: SelectedCValue) {
grid-gap: 15px;
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.uc-empty {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
</style>