dev
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import type { IEquipment } from "@/api/info/type";
|
||||
import { getImageURL } from "@/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
equipment?: IEquipment;
|
||||
inactive?: boolean;
|
||||
useDisplayUrl?: boolean;
|
||||
}>();
|
||||
|
||||
const rarity = computed(() => {
|
||||
switch (props.equipment?.rarity) {
|
||||
case "稀有":
|
||||
return "rare";
|
||||
case "神器":
|
||||
return "artifact";
|
||||
case "传说":
|
||||
return "legend";
|
||||
case "史诗":
|
||||
return "epic";
|
||||
case "太初":
|
||||
return "primeval";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
});
|
||||
|
||||
const imagePath = computed(() => {
|
||||
if (!props.equipment) return "";
|
||||
const path = props.useDisplayUrl ? props.equipment.displayUrl : props.equipment.imageUrl;
|
||||
return path ? getImageURL(path) : "";
|
||||
});
|
||||
|
||||
const getRarityFrame = (value: string) => {
|
||||
if (!value) return "";
|
||||
return new URL(`./rarity/${value}.png`, import.meta.url).href;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="equipment">
|
||||
<div class="legacy-icon" :class="{ inactive }">
|
||||
<div class="legacy-icon-core" :style="{ backgroundImage: `url(${imagePath})` }">
|
||||
<img v-if="rarity" :src="getRarityFrame(rarity)" :alt="rarity" class="legacy-icon-frame" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="legacy-icon-empty" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.legacy-icon {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.legacy-icon-core {
|
||||
width: calc(100% + 2px);
|
||||
height: calc(100% + 2px);
|
||||
margin-left: -1px;
|
||||
margin-top: -1px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.legacy-icon-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.legacy-icon-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid rgba(122, 122, 122, 0.5);
|
||||
}
|
||||
|
||||
.inactive::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.56);
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 300 B |
Binary file not shown.
|
After Width: | Height: | Size: 304 B |
Binary file not shown.
|
After Width: | Height: | Size: 293 B |
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
Binary file not shown.
|
After Width: | Height: | Size: 316 B |
@@ -0,0 +1,187 @@
|
||||
<script setup lang="ts" name="EquList">
|
||||
import { computed } from "vue";
|
||||
import { useInfoStore, type IConfigEquip } from "@/stores";
|
||||
import { getImageURL } from "@/utils";
|
||||
import LegacyEquipmentIcon from "@/components/dnf/Equipment/Icon/index.vue";
|
||||
|
||||
const baseWidth = 264;
|
||||
const baseHeight = 172;
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
withBg?: boolean;
|
||||
withPet?: boolean;
|
||||
detail?: Record<string, IConfigEquip>;
|
||||
withSubWeapon?: boolean;
|
||||
avatar?: string;
|
||||
}>(),
|
||||
{
|
||||
withBg: true,
|
||||
withPet: true,
|
||||
withSubWeapon: false,
|
||||
avatar: "",
|
||||
},
|
||||
);
|
||||
|
||||
const partModelValue = defineModel<string>("part", { default: "" });
|
||||
const infoStore = useInfoStore();
|
||||
|
||||
const parts = computed(() => {
|
||||
return infoStore.parts.filter((part) => {
|
||||
if (part === "宠物" && !props.withPet) return false;
|
||||
return !!props.detail?.[part];
|
||||
});
|
||||
});
|
||||
|
||||
const equ = (id: string) => infoStore.equips.find((item) => item.id === id);
|
||||
|
||||
const infoStyle = (part: string) => {
|
||||
let x = 10;
|
||||
let y = 10;
|
||||
let index = infoStore.parts.findIndex((current) => current === part);
|
||||
|
||||
if (index === 13) {
|
||||
index -= 7;
|
||||
} else if (index >= 5 && index <= 12) {
|
||||
x += 179;
|
||||
index -= 5;
|
||||
}
|
||||
|
||||
x += (index % 2) * 39;
|
||||
y += Math.floor(index / 2) * 34;
|
||||
|
||||
return {
|
||||
left: `${(x / baseWidth) * 100}%`,
|
||||
top: `${(y / baseHeight) * 100}%`,
|
||||
zIndex: 6,
|
||||
};
|
||||
};
|
||||
|
||||
const choosePart = (part: string) => {
|
||||
partModelValue.value = part;
|
||||
};
|
||||
|
||||
const partName = (part: string) => {
|
||||
if (part === "辅助装备") return "左槽";
|
||||
if (part === "魔法石") return "右槽";
|
||||
return part;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="equ-list"
|
||||
:class="{ 'equ-list--plain': !withBg }"
|
||||
:style="{
|
||||
backgroundImage: withBg ? `url(${getImageURL('/common/head.png')})` : 'none',
|
||||
}"
|
||||
>
|
||||
<template v-for="part in parts" :key="part">
|
||||
<button class="equ-slot" :style="infoStyle(part)" type="button" @click="choosePart(part)">
|
||||
<template v-if="detail?.[part]?.id">
|
||||
<LegacyEquipmentIcon :equipment="equ(detail[part].id)" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="equ-slot-text">{{ partName(part) }}</span>
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<button
|
||||
v-if="withSubWeapon"
|
||||
class="equ-slot"
|
||||
:style="{ left: `${(150 / baseWidth) * 100}%`, top: `${(5 / baseHeight) * 100}%`, zIndex: 8 }"
|
||||
type="button"
|
||||
@click="choosePart('副武器')"
|
||||
>
|
||||
<template v-if="detail?.['副武器']?.id">
|
||||
<LegacyEquipmentIcon :equipment="equ(detail['副武器'].id)" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="equ-slot-text">副武</span>
|
||||
</template>
|
||||
</button>
|
||||
|
||||
<img v-if="avatar" :src="avatar" alt="Avatar" class="equ-avatar" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.equ-list {
|
||||
position: relative;
|
||||
width: min(100%, 264px);
|
||||
aspect-ratio: 264 / 172;
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid rgba(85, 72, 46, 0.55);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(24, 20, 14, 0.8),
|
||||
0 8px 20px rgba(0, 0, 0, 0.32);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.equ-list::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background:
|
||||
radial-gradient(circle at 50% 8%, rgba(88, 161, 255, 0.24), transparent 35%),
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.28));
|
||||
}
|
||||
|
||||
.equ-list--plain {
|
||||
background:
|
||||
linear-gradient(180deg, rgba(19, 21, 24, 0.96), rgba(10, 10, 12, 0.98)),
|
||||
radial-gradient(circle at 50% 0, rgba(55, 95, 138, 0.24), transparent 45%);
|
||||
}
|
||||
|
||||
.equ-slot {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
width: 9.848%;
|
||||
height: 15.116%;
|
||||
border: 1px solid rgba(122, 122, 122, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgb(255 255 255 / 0.5);
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
padding: 0;
|
||||
transition:
|
||||
border-color 0.18s ease,
|
||||
box-shadow 0.18s ease,
|
||||
background-color 0.18s ease;
|
||||
}
|
||||
|
||||
.equ-slot:hover {
|
||||
border-color: rgba(229, 196, 113, 0.72);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(229, 196, 113, 0.38),
|
||||
inset 0 0 8px rgba(255, 210, 122, 0.22);
|
||||
background: rgba(47, 35, 14, 0.28);
|
||||
}
|
||||
|
||||
.equ-slot:focus-visible {
|
||||
outline: none;
|
||||
border-color: rgba(235, 211, 141, 0.88);
|
||||
box-shadow:
|
||||
0 0 0 2px rgba(235, 211, 141, 0.35),
|
||||
inset 0 0 8px rgba(255, 210, 122, 0.25);
|
||||
}
|
||||
|
||||
.equ-slot-text {
|
||||
font-size: 0.6rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.equ-avatar {
|
||||
position: absolute;
|
||||
width: 70%;
|
||||
left: 50%;
|
||||
bottom: -5.814%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,164 @@
|
||||
<script setup lang="ts" name="OathList">
|
||||
import { computed } from "vue";
|
||||
import { useInfoStore, type IConfigOath } from "@/stores";
|
||||
import { getImageURL } from "@/utils";
|
||||
import LegacyEquipmentIcon from "@/components/dnf/Equipment/Icon/index.vue";
|
||||
|
||||
const baseWidth = 264;
|
||||
const baseHeight = 172;
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
withBg?: boolean;
|
||||
detail?: Record<string, IConfigOath>;
|
||||
}>(),
|
||||
{
|
||||
withBg: true,
|
||||
},
|
||||
);
|
||||
|
||||
const partModelValue = defineModel<string>("part", { default: "0" });
|
||||
const infoStore = useInfoStore();
|
||||
|
||||
const parts = computed(() => infoStore.oathParts);
|
||||
|
||||
const equ = (id: string, part: string) =>
|
||||
infoStore.oaths.find((item) => item.id === id && item.oathPos === part);
|
||||
|
||||
const position = [
|
||||
{ x: 31, y: 13 },
|
||||
{ x: 31, y: 46 },
|
||||
{ x: 31, y: 79 },
|
||||
{ x: 31, y: 112 },
|
||||
{ x: 207, y: 13 },
|
||||
{ x: 207, y: 46 },
|
||||
{ x: 207, y: 79 },
|
||||
{ x: 207, y: 112 },
|
||||
{ x: 75, y: 128.5 },
|
||||
{ x: 119, y: 141.5 },
|
||||
{ x: 163, y: 128.5 },
|
||||
{ x: 119, y: 109 },
|
||||
];
|
||||
|
||||
const positionSimple = [
|
||||
{ x: 30, y: 12 },
|
||||
{ x: 30, y: 46 },
|
||||
{ x: 30, y: 80.5 },
|
||||
{ x: 30, y: 114 },
|
||||
{ x: 206, y: 12 },
|
||||
{ x: 206, y: 46 },
|
||||
{ x: 206, y: 80.5 },
|
||||
{ x: 206, y: 114 },
|
||||
{ x: 74, y: 114 },
|
||||
{ x: 118, y: 114 },
|
||||
{ x: 162, y: 114 },
|
||||
{ x: 118, y: 80.5 },
|
||||
];
|
||||
|
||||
const infoStyle = (part: string) => {
|
||||
const index = Number.parseInt(part, 10);
|
||||
const target = props.withBg ? position[index] : positionSimple[index];
|
||||
return {
|
||||
left: `${((target?.x ?? 0) / baseWidth) * 100}%`,
|
||||
top: `${((target?.y ?? 0) / baseHeight) * 100}%`,
|
||||
zIndex: 6,
|
||||
};
|
||||
};
|
||||
|
||||
const choosePart = (part: string) => {
|
||||
partModelValue.value = part;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="oath-list"
|
||||
:class="{ 'oath-list--plain': !withBg }"
|
||||
:style="{
|
||||
backgroundImage: withBg ? `url(${getImageURL('/common/oath/head.png')})` : 'none',
|
||||
}"
|
||||
>
|
||||
<template v-for="part in parts" :key="part">
|
||||
<button class="oath-slot" :style="infoStyle(part)" type="button" @click="choosePart(part)">
|
||||
<template v-if="detail?.[part]?.id">
|
||||
<LegacyEquipmentIcon :equipment="equ(detail[part].id, part)" use-display-url />
|
||||
</template>
|
||||
<template v-else-if="!withBg">
|
||||
<span class="oath-slot-text">{{ part !== "11" ? "星蕴" : "誓约" }}</span>
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.oath-list {
|
||||
position: relative;
|
||||
width: min(100%, 264px);
|
||||
aspect-ratio: 264 / 172;
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid rgba(85, 72, 46, 0.55);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(24, 20, 14, 0.8),
|
||||
0 8px 20px rgba(0, 0, 0, 0.32);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.oath-list::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background:
|
||||
radial-gradient(circle at 50% 8%, rgba(88, 161, 255, 0.24), transparent 35%),
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.28));
|
||||
}
|
||||
|
||||
.oath-list--plain {
|
||||
background:
|
||||
linear-gradient(180deg, rgba(19, 21, 24, 0.96), rgba(10, 10, 12, 0.98)),
|
||||
radial-gradient(circle at 50% 0, rgba(55, 95, 138, 0.24), transparent 45%);
|
||||
}
|
||||
|
||||
.oath-slot {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
width: 9.848%;
|
||||
height: 15.116%;
|
||||
border: 1px solid rgba(122, 122, 122, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgb(255 255 255 / 0.5);
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
padding: 0;
|
||||
transition:
|
||||
border-color 0.18s ease,
|
||||
box-shadow 0.18s ease,
|
||||
background-color 0.18s ease;
|
||||
}
|
||||
|
||||
.oath-slot:hover {
|
||||
border-color: rgba(229, 196, 113, 0.72);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(229, 196, 113, 0.38),
|
||||
inset 0 0 8px rgba(255, 210, 122, 0.22);
|
||||
background: rgba(47, 35, 14, 0.28);
|
||||
}
|
||||
|
||||
.oath-slot:focus-visible {
|
||||
outline: none;
|
||||
border-color: rgba(235, 211, 141, 0.88);
|
||||
box-shadow:
|
||||
0 0 0 2px rgba(235, 211, 141, 0.35),
|
||||
inset 0 0 8px rgba(255, 210, 122, 0.25);
|
||||
}
|
||||
|
||||
.oath-slot-text {
|
||||
font-size: 0.6rem;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
+4
-5
@@ -14,6 +14,7 @@ const props = defineProps<{
|
||||
defineEmits<{
|
||||
click: [];
|
||||
preview: [];
|
||||
rightClick: [];
|
||||
}>();
|
||||
|
||||
const rarityClass = computed(() => {
|
||||
@@ -35,11 +36,8 @@ const rarityClass = computed(() => {
|
||||
|
||||
const imageUrl = computed(() => {
|
||||
if (!props.equipment) return "";
|
||||
return getImageURL(
|
||||
props.useDisplayUrl
|
||||
? props.equipment.displayUrl || props.equipment.imageUrl
|
||||
: props.equipment.imageUrl,
|
||||
);
|
||||
const path = props.useDisplayUrl ? props.equipment.displayUrl : props.equipment.imageUrl;
|
||||
return path ? getImageURL(path) : "";
|
||||
});
|
||||
|
||||
const cellStyle = {
|
||||
@@ -65,6 +63,7 @@ const cellStyle = {
|
||||
:style="cellStyle"
|
||||
@click="$emit('click')"
|
||||
@mouseenter="$emit('preview')"
|
||||
@contextmenu.prevent="$emit('rightClick')"
|
||||
>
|
||||
<img :src="imageUrl" :alt="equipment.name" class="size-full object-contain" />
|
||||
<div v-if="inactive" class="absolute inset-0 bg-black/45" />
|
||||
|
||||
Reference in New Issue
Block a user