diff --git a/app/components/ZzzCharacterAvatar.vue b/app/components/ZzzCharacterAvatar.vue new file mode 100644 index 0000000..05f17b5 --- /dev/null +++ b/app/components/ZzzCharacterAvatar.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/app/components/ZzzPanelDamage.vue b/app/components/ZzzPanelDamage.vue new file mode 100644 index 0000000..4ceebbc --- /dev/null +++ b/app/components/ZzzPanelDamage.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/app/components/ZzzPanelDetail.vue b/app/components/ZzzPanelDetail.vue new file mode 100644 index 0000000..2f81925 --- /dev/null +++ b/app/components/ZzzPanelDetail.vue @@ -0,0 +1,238 @@ + + + + + diff --git a/app/components/ZzzPanelDriveDiscs.vue b/app/components/ZzzPanelDriveDiscs.vue new file mode 100644 index 0000000..c9ccb4a --- /dev/null +++ b/app/components/ZzzPanelDriveDiscs.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/app/components/ZzzPanelProperties.vue b/app/components/ZzzPanelProperties.vue new file mode 100644 index 0000000..5989b54 --- /dev/null +++ b/app/components/ZzzPanelProperties.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/app/components/ZzzPanelSwiper.vue b/app/components/ZzzPanelSwiper.vue new file mode 100644 index 0000000..24c4384 --- /dev/null +++ b/app/components/ZzzPanelSwiper.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/app/components/ZzzPanelWeapon.vue b/app/components/ZzzPanelWeapon.vue new file mode 100644 index 0000000..9e44db1 --- /dev/null +++ b/app/components/ZzzPanelWeapon.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/app/composables/useApi.ts b/app/composables/useApi.ts index 6ba333e..deef9a0 100644 --- a/app/composables/useApi.ts +++ b/app/composables/useApi.ts @@ -21,6 +21,7 @@ import type { ExamSubmitResult, MihoyoBinding, NsfwStatus, + ZzzPanelsResult, Post, PostCategory, ArticleFeed, @@ -788,6 +789,24 @@ export function useApi() { return { success: data.success === true }; }; + // ── 绝区零角色面板 ──────────────────────────────── + const getZzzPanels = async (uid: string): Promise => { + const response = await cachedRead( + ["zzz-panels", uid], + async () => + $api("/api/zzz/panels", { + method: "GET", + query: { uid }, + }) as Promise, + STALE_LIST, + ); + return response as ZzzPanelsResult; + }; + + const getMyZzzPanels = async (): Promise => { + const response = await $api("/api/zzz/panels/me", { method: "GET" }); + return response as ZzzPanelsResult; + }; const getSelfUser = async (): Promise => { return cachedRead( qk.me.self, @@ -2342,6 +2361,9 @@ export function useApi() { pollMihoyoQr, getMihoyoBinding, unbindMihoyo, + // 绝区零角色面板 + getZzzPanels, + getMyZzzPanels, }; } diff --git a/app/pages/profile/[id].vue b/app/pages/profile/[id].vue index 4a53e76..09955b9 100644 --- a/app/pages/profile/[id].vue +++ b/app/pages/profile/[id].vue @@ -532,9 +532,14 @@ onBeforeUnmount(() => {

{{ profile.name || profile.login || "匿名用户" }}

- + {{ profile.zzz.regionName || "绝区零" }} - +
@@ -971,12 +976,17 @@ onBeforeUnmount(() => { font-size: 13px; font-weight: 700; letter-spacing: 0.5px; + text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } +.ik-banner__zzz-badge:hover { + background: rgba(191, 255, 9, 0.15); +} + /* Hidden profile badge */ .ik-banner__hidden-badge { position: relative; diff --git a/app/pages/zzz-panel/[[uid]].vue b/app/pages/zzz-panel/[[uid]].vue new file mode 100644 index 0000000..eab2c51 --- /dev/null +++ b/app/pages/zzz-panel/[[uid]].vue @@ -0,0 +1,147 @@ + + + + + diff --git a/app/types/entities.ts b/app/types/entities.ts index 1d922a0..e4141a6 100644 --- a/app/types/entities.ts +++ b/app/types/entities.ts @@ -331,6 +331,167 @@ export interface ZzzRoleBadge { regionName?: string; } +export interface ZzzPanelProperty { + propertyId: number; + name: string; + statKey: string; + isPercentage: boolean; + base: number; + add: number; + final: number; + baseDisplay: string; + addDisplay: string; + finalDisplay: string; + weightClass: string; +} + +export interface ZzzPanelSkill { + index: number; + name: string; + level: number; +} + +export interface ZzzPanelWeaponStat { + propertyId: number; + name: string; + isPercentage: boolean; + value: number; + displayValue: string; +} + +export interface ZzzPanelWeapon { + id: number; + name: string; + level: number; + rarity: number; + rarityName: string; + star: number; + iconUrl: string; + mainStat: ZzzPanelWeaponStat[]; + subStat: ZzzPanelWeaponStat[]; +} + +export interface ZzzPanelDiscMainStat { + propertyId: number; + name: string; + isPercentage: boolean; + level: number; + value: number; + displayValue: string; +} + +export interface ZzzPanelDiscSubStat { + propertyId: number; + name: string; + isPercentage: boolean; + level: number; + value: number; + displayValue: string; +} + +export interface ZzzPanelDisc { + slot: number; + id: number; + level: number; + rarity: number; + suitId: number; + suitName: string; + suitIconUrl: string; + score: number; + comment: string; + mainStats: ZzzPanelDiscMainStat[]; + subStats: ZzzPanelDiscSubStat[]; +} + +export interface ZzzPanelScore { + total: number; + max: number; + comment: string; + weightRule: string; + propertyStats: Array<{ + propertyId: number; + name: string; + weight: number; + count: number; + value: string; + }>; + proficiencyScore: number; + discScores: Array<{ + slot: number; + score: number; + maxScore: number; + comment?: string; + mainStat?: { propertyId: number; statKey: string; weight: number }; + subStats: Array<{ + propertyId: number; + statKey: string; + level: number; + weight: number; + contribution: number; + }>; + }>; +} + +export interface ZzzPanelDamageDetails { + damageIndex: number; + selectedSkill?: { name: string; type: string }; + skills: Array<{ name: string; type: string; critDMG: number; expectDMG: number }>; + areas: Record; + buffs: Array<{ name: string; source?: string; type: string; value: number }>; + panelBuffs: Array<{ name: string; source?: string; type: string; value: number; max: number }>; +} + +export interface ZzzPanelDamage { + damageIndex: number; + finalAtk: number; + finalHp: number; + finalDef: number; + critRate: number; + critDmg: number; + damageBonus: number; + anomalyProficiency: number; + anomalyControl: number; + impact: number; + energyRegen: number; + details?: ZzzPanelDamageDetails; +} + +export interface ZzzPanelAvatar { + id: number; + name: string; + level: number; + promotionLevel: number; + mindscape: number; + coreSkill: number; + rank: number; + profession: string; + element: string; + rarity: number; + rarityName: string; + iconUrls: { portrait: string; circle: string; square: string; select: string }; + skills: ZzzPanelSkill[]; + properties: ZzzPanelProperty[]; + weapon: ZzzPanelWeapon | null; + discs: ZzzPanelDisc[]; + baseStats: unknown; + buildStats: unknown; + score: ZzzPanelScore; + damage: ZzzPanelDamage; +} + +export interface ZzzPanel { + uid: string; + region: string; + avatar: ZzzPanelAvatar; +} + +export interface ZzzPanelsResult { + uid: string; + region: string; + count: number; + avatars: ZzzPanelAvatar[]; +} + /** 当前用户的米游社绑定信息 */ export interface MihoyoBinding { aid: string;