This commit is contained in:
2026-04-20 00:00:48 +08:00
parent dbf33477d9
commit cda93965e3
33 changed files with 677 additions and 317 deletions
+31 -29
View File
@@ -2,11 +2,11 @@ import re
from api.core.Auth import createToken from api.core.Auth import createToken
from api.core.Gzip import register_gzip_request from api.core.Gzip import register_gzip_request
import json import json
from fastapi import APIRouter,Request, Depends, Body from fastapi import APIRouter, Request, Depends, Body
from typing import Annotated from typing import Annotated
from api.core.Response import Return, response from api.core.Response import Return, response
from api.core.Redis import get_redis_info from api.core.Redis import get_redis_info
from api.dp import RedisDep,AltersDep from api.dp import RedisDep, AltersDep
from core.character.adventure import get_adv_list from core.character.adventure import get_adv_list
from core.basic.character import createCharacter from core.basic.character import createCharacter
from core.open.helper import get_user_info_by_id from core.open.helper import get_user_info_by_id
@@ -17,6 +17,7 @@ router = APIRouter()
register_gzip_request(router) register_gzip_request(router)
def replace_placeholders(template): def replace_placeholders(template):
# 匹配 <int>、<float>、<float1>、<float2> 等 # 匹配 <int>、<float>、<float1>、<float2> 等
pattern = re.compile(r'<(int|float\d*)>') pattern = re.compile(r'<(int|float\d*)>')
@@ -30,44 +31,43 @@ def replace_placeholders(template):
return pattern.sub(repl, template) return pattern.sub(repl, template)
@router.get('/adventure') @router.get('/adventure')
async def get_adventure_info(request: Request,redis:RedisDep): async def get_adventure_info(request: Request, redis: RedisDep):
adventure_info = get_redis_info(redis, 'dcalc:adventure', get_adv_list) adventure_info = get_redis_info(redis, 'dcalc:adventure', get_adv_list)
return response(data=adventure_info) return response(data=adventure_info)
@router.get("/token/get/{alter}")
async def getToken(redis:RedisDep, @router.get('/token/get/{alter}')
alter: str, request: Request, version: str = None, equVersion: str = "0" async def getToken(redis: RedisDep, alter: str, request: Request, version: str = None, equVersion: str = '0'):
):
token = createToken(alter, equVersion, redis) token = createToken(alter, equVersion, redis)
return response(data=token) return response(data=token)
@router.get("/character")
async def get_character_info( @router.get('/character')
request: Request, state: AltersDep ,redis:RedisDep async def get_character_info(request: Request, state: AltersDep, redis: RedisDep):
):
character = createCharacter(state.alter, state.equVersion) character = createCharacter(state.alter, state.equVersion)
def get_character(): def get_character():
return character.getInfo() return character.getInfo()
info = get_redis_info(redis, f"dcalc:character:{state.alter}:{state.equVersion}", get_character)
info = get_redis_info(redis, f'dcalc:character:{state.alter}:{state.equVersion}', get_character)
# info = character.getInfo() # info = character.getInfo()
return response(data=info) return response(data=info)
@router.get("/skill/{skillId}/{level}")
async def get_skill_info( @router.get('/skill/{skillId}/{level}')
state: AltersDep, skillId: str, level: int, redis: RedisDep async def get_skill_info(state: AltersDep, skillId: str, level: int, redis: RedisDep):
):
""" """
获取技能信息 获取技能信息
""" """
alter = state.alter alter = state.alter
jobId = alter.split(".")[1] jobId = alter.split('.')[1]
jobGrowId = alter.split(".")[2] jobGrowId = alter.split('.')[2]
skill_info = {} skill_info = {}
key = f'openapi:{jobId}:{jobGrowId}:{skillId}' key = f'openapi:{jobId}:{jobGrowId}:{skillId}'
try: try:
def get_skill_info(): def get_skill_info():
# This function should retrieve the skill info based on job and jobGrow # This function should retrieve the skill info based on job and jobGrow
# For now, we return a placeholder dictionary # For now, we return a placeholder dictionary
@@ -97,25 +97,27 @@ async def get_skill_info(
return response(data=skill_info) return response(data=skill_info)
@router.get("/dnfhelper/{uid}/") @router.get('/dnfhelper/{uid}/')
async def get_info_by_dnfhelper(uid: str, state: AltersDep, redis: RedisDep): async def get_info_by_dnfhelper(uid: str, state: AltersDep, redis: RedisDep):
try: try:
result = await get_user_info_by_id(uid,state) result = await get_user_info_by_id(uid, state)
return response(data=result) return response(data=result)
except Exception as e: except Exception as e:
if isinstance(e, ResponseException): if isinstance(e, ResponseException):
return response(code=500, message=str(e), data=None) return response(code=500, message=str(e), data=None)
return response(code=500, message="Failed to fetch DNFHelper info", data=None) print(e)
return response(code=500, message='Failed to fetch DNFHelper info', data=None)
@router.post("/skillTree/")
async def get_skill_by_code(state: AltersDep, code = Body(None)): @router.post('/skillTree/')
async def get_skill_by_code(state: AltersDep, code=Body(None)):
try: try:
skills = get_skill_tree_info(code.get("code", "")) skills = get_skill_tree_info(code.get('code', ''))
if skills.get("job",{}).get("class","") != state.alter: if skills.get('job', {}).get('class', '') != state.alter:
return response(code=500, message="技能加点代码职业与登录职业不匹配", data=None) return response(code=500, message='技能加点代码职业与登录职业不匹配', data=None)
return response(data=skills) return response(data=skills)
except Exception as e: except Exception as e:
print(f"Error fetching skill tree info: {e}") print(f'Error fetching skill tree info: {e}')
if isinstance(e, ResponseException): if isinstance(e, ResponseException):
return response(code=500, message=str(e), data=None) return response(code=500, message=str(e), data=None)
return response(code=500, message="解析技能加点失败", data=None) return response(code=500, message='解析技能加点失败', data=None)
+1 -1
View File
@@ -101,7 +101,7 @@ class CalcEquipMixin(CharacterBase):
max_point_oath_suit = max((suit for suit in oathSuitInfo if oathSuitInfo[suit]['point'] > 0), key=lambda suit: oathSuitInfo[suit]['point'], default=None) max_point_oath_suit = max((suit for suit in oathSuitInfo if oathSuitInfo[suit]['point'] > 0), key=lambda suit: oathSuitInfo[suit]['point'], default=None)
zero_point_suits = [suit for suit in suitInfo if suitInfo[suit]['point'] == 0] zero_point_suits = [suit for suit in suitInfo if suitInfo[suit]['point'] == 0]
# 套装没有到太初的时候,优先把誓约套装的点数加到装备套装上,以达到更高的套装等级 # 套装没有到太初的时候,优先把誓约套装的点数加到装备套装上,以达到更高的套装等级
if 0 < suitInfo[max_point_suit]['point'] < 2550 and oathSuitInfo.get('max_point_oath_suit', None) is not None: if suitInfo is not None and 0 < suitInfo[max_point_suit]['point'] < 2550 and oathSuitInfo.get('max_point_oath_suit', None) is not None:
suitInfo[max_point_suit]['point'] += min(2550 - suitInfo[max_point_suit]['point'], oathSuitInfo[max_point_oath_suit]['point']) suitInfo[max_point_suit]['point'] += min(2550 - suitInfo[max_point_suit]['point'], oathSuitInfo[max_point_oath_suit]['point'])
oathSuitInfo[max_point_oath_suit]['point'] -= min(2550 - suitInfo[max_point_suit]['point'], oathSuitInfo[max_point_oath_suit]['point']) oathSuitInfo[max_point_oath_suit]['point'] -= min(2550 - suitInfo[max_point_suit]['point'], oathSuitInfo[max_point_oath_suit]['point'])
pass pass
+15 -12
View File
@@ -79,12 +79,12 @@ job_mapping = {
async def get_user_info_by_id(user_id: str, alter: AltersDep): async def get_user_info_by_id(user_id: str, alter: AltersDep):
if alter is None: if alter is None:
raise ResponseException("登录过期或无效Token,请刷新后重试") raise ResponseException('登录过期或无效Token,请刷新后重试')
name = next((k for k, v in job_mapping.items() if v == alter.origin), '') name = next((k for k, v in job_mapping.items() if v == alter.origin), '')
# 通过OCR助手获取用户信息 # 通过OCR助手获取用户信息
helper_info = await get_main_user_info_by_id(user_id, name) helper_info = await get_main_user_info_by_id(user_id, name)
if helper_info is None: if helper_info is None:
raise ResponseException("当前助手ID不存在或已隐藏,请确认后重试") raise ResponseException('当前助手ID不存在或已隐藏,请确认后重试')
else: else:
return trans_to_dcalc_set(helper_info, alter) return trans_to_dcalc_set(helper_info, alter)
pass pass
@@ -93,7 +93,7 @@ async def get_user_info_by_id(user_id: str, alter: AltersDep):
def trans_to_dcalc_set(detail, alter: AltersDep): def trans_to_dcalc_set(detail, alter: AltersDep):
job = job_mapping.get(detail.get('job', ''), '') job = job_mapping.get(detail.get('job', ''), '')
if job != alter.origin: if job != alter.origin:
raise ResponseException("未能匹配到当前职业对应的角色信息,请确认后重试") raise ResponseException('未能匹配到当前职业对应的角色信息,请确认后重试')
basic = get_equipment('0') basic = get_equipment('0')
basicAvatars = basic.funs.get_dress_list([]) basicAvatars = basic.funs.get_dress_list([])
equs = detail.get('equs', []) equs = detail.get('equs', [])
@@ -126,9 +126,9 @@ def trans_to_dcalc_set(detail, alter: AltersDep):
# 贴膜 # 贴膜
if dcalcEqu: if dcalcEqu:
info['id'] = dcalcEqu.id info['id'] = dcalcEqu.id
dcalcEquFusion = next((item for item in basic.stones if item.name == equ.get('upgrade', '')), None) # dcalcEquFusion = next((item for item in basic.stones if item.name == equ.get('upgrade', '')), None)
if dcalcEquFusion: # if dcalcEquFusion:
info['fusion'] = dcalcEquFusion.id # info['fusion'] = dcalcEquFusion.id
info['enchant'] = getEnchatIdByName(equ.get('enchant', ''), equ.get('posName', ''), basic.enchants) info['enchant'] = getEnchatIdByName(equ.get('enchant', ''), equ.get('posName', ''), basic.enchants)
emblems = [getEmblemIdByName(i.get('detail', ''), i.get('categorize', ''), i.get('rarity', ''), basic.emblems) for i in equ.get('emblems', [])] emblems = [getEmblemIdByName(i.get('detail', ''), i.get('categorize', ''), i.get('rarity', ''), basic.emblems) for i in equ.get('emblems', [])]
info['emblem_0'] = emblems[0] if len(emblems) > 0 else 0 info['emblem_0'] = emblems[0] if len(emblems) > 0 else 0
@@ -167,7 +167,7 @@ def trans_to_dcalc_set(detail, alter: AltersDep):
if item.get('posName', '') == '皮肤': if item.get('posName', '') == '皮肤':
match = re.search(r'\[(\d+)级\]', item.get('name', '')) match = re.search(r'\[(\d+)级\]', item.get('name', ''))
lv = int(match.group(1)) if match else None lv = int(match.group(1)) if match else None
if lv : if lv:
item['enchant'] = f'Lv{lv}主动+1|全属强(12)|四维(55)' item['enchant'] = f'Lv{lv}主动+1|全属强(12)|四维(55)'
if item.get('posName', '') == '光环': if item.get('posName', '') == '光环':
# 先默认这个了 不管了 # 先默认这个了 不管了
@@ -182,11 +182,14 @@ def trans_to_dcalc_set(detail, alter: AltersDep):
'equips': equips, 'equips': equips,
'avatars': avatar, 'avatars': avatar,
'avatarLink': detail.get('avatarLink', ''), 'avatarLink': detail.get('avatarLink', ''),
'dressInfo': [{ 'dressInfo': [
'dress': i.get('name', ''), {
'clone': i.get('clone', ''), 'dress': i.get('name', ''),
'part': i.get('posName', ''), 'clone': i.get('clone', ''),
} for i in detail.get('avatars', [])] 'part': i.get('posName', ''),
}
for i in detail.get('avatars', [])
],
} }
# 处理装备 # 处理装备
# 处理时装 # 处理时装
Binary file not shown.
+1
View File
@@ -21,6 +21,7 @@ export interface IResultSkill {
} }
export interface IResultUserInfo { export interface IResultUserInfo {
id: number
name: string name: string
value: string | number value: string | number
} }
+3 -3
View File
@@ -72,13 +72,13 @@ export interface IEquipment {
Buffer: number[] Buffer: number[]
detail: string detail: string
bufferDetail: string bufferDetail: string
}
export interface IOathInfo extends IEquipment {
position: string[] position: string[]
wearUrl: string[] wearUrl: string[]
oathPos: string
displayUrl?: string
} }
export interface ISkill { export interface ISkill {
icon: string icon: string
name: string name: string
@@ -11,6 +11,7 @@ import {
renderList, renderList,
watch, watch,
} from 'vue' } from 'vue'
import { getElementZoom } from '@/utils/position'
type HandleChangeFunction = (value: string | number) => string[] | Promise<string[]> type HandleChangeFunction = (value: string | number) => string[] | Promise<string[]>
@@ -55,10 +56,12 @@ export default defineComponent({
function onResize() { function onResize() {
if (triggerRef.value) { if (triggerRef.value) {
const { width, height, left, top } = triggerRef.value.getBoundingClientRect() const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
const zoom = getElementZoom('body')
dropdownPosition.value = { dropdownPosition.value = {
w: width, w: width / zoom,
x: left, x: left / zoom,
y: top + height + 2, y: (top + height + 2) / zoom,
} }
} }
} }
@@ -4,6 +4,7 @@ import type { CSSProperties, PropType } from 'vue'
import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, watch } from 'vue' import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, watch } from 'vue'
import { onClickOutside, useVModel } from '@vueuse/core' import { onClickOutside, useVModel } from '@vueuse/core'
import { getElementZoom } from '@/utils/position'
interface ICascaderItem { interface ICascaderItem {
label: string label: string
id?: string | number id?: string | number
@@ -74,18 +75,19 @@ export default defineComponent({
function onResize() { function onResize() {
if (triggerRef.value) { if (triggerRef.value) {
const { width, height, left, top } = triggerRef.value.getBoundingClientRect() const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
const zoom = getElementZoom('body')
if (window.innerHeight - top > 160) { if (window.innerHeight - top > 160) {
dropdownPosition.value = { dropdownPosition.value = {
w: width, w: width / zoom,
x: left, x: left / zoom,
y: top + height + 2, y: (top + height + 2) / zoom,
z: 0, z: 0,
} }
} else { } else {
dropdownPosition.value = { dropdownPosition.value = {
w: width, w: width / zoom,
x: left, x: left / zoom,
z: window.innerHeight - top + 2, z: (window.innerHeight - top + 2) / zoom,
y: 0, y: 0,
} }
} }
@@ -13,6 +13,7 @@ import {
} from 'vue' } from 'vue'
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
import { getElementZoom } from '@/utils/position'
export default defineComponent({ export default defineComponent({
name: 'IIconselect', name: 'IIconselect',
@@ -72,20 +73,24 @@ export default defineComponent({
const dropdownPosition = ref({ x: 0, y: 0, w: 0 }) const dropdownPosition = ref({ x: 0, y: 0, w: 0 })
// 下拉框位置 // 下拉框位置
const dropdownStyle = computed<CSSProperties>(() => { const dropdownStyle = computed<CSSProperties>(() => {
const zoom = getElementZoom(appendToRef.value)
return { return {
left: `${dropdownPosition.value.x}px`, left: `${dropdownPosition.value.x}px`,
top: `${dropdownPosition.value.y}px`, top: `${dropdownPosition.value.y}px`,
width: `${columnNum.value * 32 + 11}px`, width: `${(columnNum.value * 32 + 11) / zoom}px`,
} }
}) })
function onResize() { function onResize() {
if (triggerRef.value) { if (triggerRef.value) {
const { width, height, left, top } = triggerRef.value.getBoundingClientRect() const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
const zoom = getElementZoom(appendToRef.value)
dropdownPosition.value = { dropdownPosition.value = {
w: width, w: width / zoom,
x: left - 2, x: (left - 2) / zoom,
y: (top + height + 2), y: (top + height + 2) / zoom,
} }
} }
} }
@@ -15,6 +15,7 @@ import {
import { onClickOutside, useVModel } from '@vueuse/core' import { onClickOutside, useVModel } from '@vueuse/core'
import { classPropType, labelPropType } from '@/components/hooks/types' import { classPropType, labelPropType } from '@/components/hooks/types'
import { getElementZoom } from '@/utils/position'
export default defineComponent({ export default defineComponent({
name: 'ISelect', name: 'ISelect',
@@ -98,18 +99,19 @@ export default defineComponent({
function onResize() { function onResize() {
if (triggerRef.value) { if (triggerRef.value) {
const { width, height, left, top } = triggerRef.value.getBoundingClientRect() const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
const zoom = getElementZoom(appendToRef.value)
if (window.innerHeight - top > 160) { if (window.innerHeight - top > 160) {
dropdownPosition.value = { dropdownPosition.value = {
w: width, w: width / zoom,
x: left, x: left / zoom,
y: (top + height + 2), y: (top + height + 2) / zoom,
z: 0, z: 0,
} }
} else { } else {
dropdownPosition.value = { dropdownPosition.value = {
w: width, w: width / zoom,
x: left, x: left / zoom,
z: (window.innerHeight - top + 2), z: (window.innerHeight - top + 2) / zoom,
y: 0, y: 0,
} }
} }
@@ -3,7 +3,9 @@
<div class="relative" :class="{ inactive: props.inactive }"> <div class="relative" :class="{ inactive: props.inactive }">
<div <div
class="w-28px h-28px" class="w-28px h-28px"
:style="{ backgroundImage: `url(${getImageURL(props.equipment?.imageUrl)})` }" :style="{
backgroundImage: `url(${getImageURL((useDisplayUrl ? props.equipment?.displayUrl : props.equipment?.imageUrl) || '')})`,
}"
> >
<img :src="getLocalImageURL(rarity)" /> <img :src="getLocalImageURL(rarity)" />
</div> </div>
@@ -21,6 +23,7 @@ import { getImageURL } from '@/utils/images'
const props = defineProps<{ const props = defineProps<{
equipment?: IEquipment equipment?: IEquipment
inactive?: boolean inactive?: boolean
useDisplayUrl?: boolean
}>() }>()
const rarity = computed(() => { const rarity = computed(() => {
@@ -38,16 +38,12 @@
</div> </div>
<!-- 装备属性 --> <!-- 装备属性 -->
<div class="divide"></div> <div class="divide"></div>
<div <div class="flex flex-col items-start my-1px whitespace-pre-wrap leading-15px">
class="flex flex-col items-start my-1px whitespace-pre-wrap leading-15px"
>
<div class="text-#DED29A py-4px whitespace-pre-wrap" v-html="detail"></div> <div class="text-#DED29A py-4px whitespace-pre-wrap" v-html="detail"></div>
</div> </div>
<template v-if="bufferDetail"> <template v-if="bufferDetail">
<div class="divide"></div> <div class="divide"></div>
<div <div class="flex flex-col items-start my-1px whitespace-pre-wrap leading-15px">
class="flex flex-col items-start my-1px whitespace-pre-wrap leading-15px"
>
<div class="text-#A48B2E mb-10px pt-4px">辅助职业专属属性</div> <div class="text-#A48B2E mb-10px pt-4px">辅助职业专属属性</div>
<div class="text-#DED29A pb-10px whitespace-pre-wrap" v-html="bufferDetail"></div> <div class="text-#DED29A pb-10px whitespace-pre-wrap" v-html="bufferDetail"></div>
</div> </div>
@@ -95,16 +91,18 @@ const mainAttr = computed(() => {
// a.match(/\[(?!.*?技能).*?]/g) // a.match(/\[(?!.*?技能).*?]/g)
const detail = computed(() => const detail = computed(() =>
props.equipment?.detail.replace( props.equipment?.detail?.replace(
/\[(?![^\]]*技能)[^\[\]]+\]/g, match => `<span style="color:#3d9147">${match}</span>` /\[(?![^\]]*技能)[^\[\]]+\]/g,
) (match) => `<span style="color:#3d9147">${match}</span>`,
),
) )
const bufferDetail = computed(() => { const bufferDetail = computed(() => {
const detail = props.equipment?.bufferDetail const detail = props.equipment?.bufferDetail
if (!detail || detail == '-') return undefined if (!detail || detail == '-') return undefined
return props.equipment?.bufferDetail.replace( return props.equipment?.bufferDetail.replace(
/\[(?![^\]]*技能)[^\[\]]+\]/g, match => `<span style="color:#3d9147">${match}</span>` /\[(?![^\]]*技能)[^\[\]]+\]/g,
(match) => `<span style="color:#3d9147">${match}</span>`,
) )
}) })
</script> </script>
@@ -96,7 +96,7 @@ const equ = (id: string) => {
const infoStyle = (part: string) => { const infoStyle = (part: string) => {
let x = 10 let x = 10
let y = 5 let y = 10
let index = infoStore.parts.findIndex((p) => p == part) let index = infoStore.parts.findIndex((p) => p == part)
if (index == 13) { if (index == 13) {
index -= 7 index -= 7
@@ -0,0 +1,182 @@
<template>
<div
class="pos-relative w-264px h-full"
:style="{ backgroundImage: withBg ? `url(${getImageURL('/common/oath/head.png')})` : 'none' }"
>
<template v-for="part in parts" :key="part">
<div
class="pos-absolute text-white/50 w-26px h-26px border-1px border-solid border-#7a7a7a/50 flex items-center justify-center"
:style="infoStyle(part)"
@click="choosePart(part)"
>
<template v-if="detail?.[part]?.id">
<EquipmentIcon :equipment="equ(detail[part].id, part)" useDisplayUrl />
</template>
<template v-else-if="!withBg">
{{ part != '11' ? '星蕴' : '誓约' }}
</template>
</div>
<!-- <div v-if="detail?.[part]?.fusion" class="pos-absolute" :style="fusionStyle(part)">
<EquipmentIcon :equipment="oaths(detail[part].fusion)" />
</div> -->
</template>
<!-- <img
v-if="props.avatar"
:src="props.avatar"
alt="Avatar"
class="pos-absolute w-70% left-1/2 translate-x--1/2 -bottom-10px"
/> -->
</div>
</template>
<script lang="ts" setup name="EquList">
import { getImageURL } from '@/utils/images'
import EquipmentIcon from '@/components/dnf/Equipment/Icon/index.vue'
import { useInfoStore, type IConfigOath } from '@/stores'
const props = defineProps({
withBg: {
type: Boolean,
default: true,
},
detail: {
type: Object as PropType<Record<string, IConfigOath>>,
},
})
const partModelValue = defineModel('part', { default: '0' })
const infoStore = useInfoStore()
const parts = computed(() => {
const parts = infoStore.oathParts
return parts
})
const equ = (id: string, part: string) => {
return infoStore.oaths.find((e) => e.id == id && e.oathPos == part)
}
const position = [
{
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: 131.5,
},
{
x: 118,
y: 145.5,
},
{
x: 162,
y: 131.5,
},
{
x: 118,
y: 111,
},
]
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) => {
if (props.withBg)
return {
left: `${position[parseInt(part)].x}px`,
top: `${position[parseInt(part)].y}px`,
zIndex: 6,
// fontWeight: 900,
}
else
return {
left: `${positionSimple[parseInt(part)].x}px`,
top: `${positionSimple[parseInt(part)].y}px`,
zIndex: 6,
// fontWeight: 900,
}
}
const choosePart = (part: string) => {
partModelValue.value = part
}
</script>
<style lang="scss" scoped></style>
@@ -1,60 +0,0 @@
`<!-- 每排总计6+4+4,共计14个排列 -->
<template>
<div class="flex flex-wrap gap-4px content-start" :style="{ width: `${rowCount * 32}px` }">
<template v-for="(equip, _) in equs" :key="_">
<EquipmentIcon
:inactive="configStore.config.equips[equip?.itemDetailType ?? '']?.fusion != equip?.id"
:equipment="equip"
@click="configStore.chooseEqu(equip, true)"
/>
</template>
</div>
</template>
<script lang="ts" setup StoneUniversal>
import type { IEquipment } from '@/api/info/type'
import { useConfigStore, useInfoStore } from '@/stores'
import { getImageURL } from '@/utils/images'
import EquipmentIcon from '@/components/dnf/Equipment/Icon/index.vue'
const props = defineProps<{
rowCount?: number
}>()
const rowCount = Math.max(props.rowCount ?? 0, 14)
const infoStore = useInfoStore()
const configStore = useConfigStore()
const equs = computed(() => {
const items = infoStore.stones.filter((equip) => equip.categorize.includes('神界'))
const res: (IEquipment | undefined)[] = []
res.push(
...items.filter((a) => a.rarity === '太初'),
...Array(Math.max(0, rowCount - items.filter((a) => a.rarity === '太初').length)).fill(
undefined,
),
)
;['手镯', '项链', '戒指', '', '辅助装备', '魔法石', '耳环', ''].forEach((cat) => {
const equ: (IEquipment | undefined)[] = items.filter(
(a) => a.rarity === '史诗' && a.itemDetailType == cat,
)
res.push(...equ, ...Array(Math.max(0, 7 - equ.length)).fill(undefined))
})
// const categorize = Array.from(new Set(items.map((item) => item.categorize)))
// raritiyList.forEach((rarity) => {
// categorize.forEach((cat) => {
// const equ : (IEquipment | undefined)[] = items.filter((a) => a.rarity === rarity && a.categorize === cat)
// equ.splice(5,0,undefined)
// res.push(
// ...equ,
// ...Array(Math.max(0,rowCount - equ.length)).fill(undefined),
// )
// })
// })
return res
})
</script>
`
@@ -15,7 +15,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { IEquipment } from '@/api/info/type' import type { IEquipment } from '@/api/info/type'
import { useConfigStore, useInfoStore } from '@/stores' import { useConfigStore, useInfoStore } from '@/stores'
import { getImageURL } from '@/utils/images'
import EquipmentIcon from '@/components/dnf/Equipment/Icon/index.vue' import EquipmentIcon from '@/components/dnf/Equipment/Icon/index.vue'
const props = defineProps<{ const props = defineProps<{
@@ -126,7 +126,7 @@ import PartSelect from '@/components/dnf/Equipment/PartSelect/index.vue'
import EquipmentRaritySelect from '@/components/dnf/Equipment/RaritySelect/index.vue' import EquipmentRaritySelect from '@/components/dnf/Equipment/RaritySelect/index.vue'
import { getImageURL } from '@/utils/images' import { getImageURL } from '@/utils/images'
import { rarityClass } from '@/utils' import { rarityClass } from '@/utils'
import type { IEquipment, IOathInfo } from '@/api/info/type' import type { IEquipment } from '@/api/info/type'
import EquipmentIcon from '@/components/dnf/Equipment/Icon/index.vue' import EquipmentIcon from '@/components/dnf/Equipment/Icon/index.vue'
import Info from '@/components/dnf/Equipment/Info/index.vue' import Info from '@/components/dnf/Equipment/Info/index.vue'
import SuitInfo from '@/components/dnf/Equipment/SuitInfo/index.vue' import SuitInfo from '@/components/dnf/Equipment/SuitInfo/index.vue'
@@ -259,7 +259,7 @@ const equs = computed(() => {
) )
} else { } else {
return formatOath( return formatOath(
total as unknown as IOathInfo[], total as unknown as IEquipment[],
suitList.value.find((a) => a.value == curSuit.value), suitList.value.find((a) => a.value == curSuit.value),
) )
} }
@@ -275,7 +275,7 @@ const isActive = computed(() => {
return configStore.config.equips[item.itemDetailType].id == item.id return configStore.config.equips[item.itemDetailType].id == item.id
} }
} else { } else {
// return configStore.config.equips[item.itemDetailType].fusion == item.id return configStore.config.oaths[item.oathPos].id == item.id
} }
} }
}) })
@@ -288,8 +288,8 @@ const chooseEqu = (item?: IEquipment) => {
? configStore.config.equips[ ? configStore.config.equips[
item.itemType.includes('武器') ? item.itemType : item.itemDetailType item.itemType.includes('武器') ? item.itemType : item.itemDetailType
] ]
: configStore.config.equips[item.itemDetailType] : configStore.config.oaths[item.oathPos]
target[type.value === 'equ' ? 'id' : 'fusion'] = isActive.value(item) ? '' : item.id target['id'] = isActive.value(item) ? '' : item.id
} }
</script> </script>
@@ -1,4 +1,4 @@
import type { IEquipment, IOathInfo } from '@/api/info/type' import type { IEquipment } from '@/api/info/type'
export const rowCount = 10 export const rowCount = 10
@@ -293,7 +293,7 @@ export const formatStone = (total: IEquipment[], curSuit?: string) => {
} }
export const formatOath = ( export const formatOath = (
total: IOathInfo[], total: IEquipment[],
curSuit?: { name: string; value: number; imageUrl: string }, curSuit?: { name: string; value: number; imageUrl: string },
) => { ) => {
// oath // oath
@@ -303,7 +303,7 @@ export const formatOath = (
const primer = [] const primer = []
const primerItem = total.filter((equip) => equip.itemDetailType == '星蕴石') const primerItem = total.filter((equip) => equip.itemDetailType == '星蕴石')
for (let i = 0; i < 11; i++) { for (let i = 0; i < 11; i++) {
const temp = primerItem.filter((a) => a.position.includes(i.toString())) const temp = primerItem.filter((a) => a.oathPos == i.toString())
primer.push( primer.push(
...temp, ...temp,
...Array(temp.length == 0 ? rowCount : rowCount - (temp.length % rowCount)).fill(undefined), ...Array(temp.length == 0 ? rowCount : rowCount - (temp.length % rowCount)).fill(undefined),
@@ -5,14 +5,12 @@ import { useInfoStore, useConfigStore } from '@/stores'
import ClothList from '@/components/dnf/Avatar/List/index.vue' import ClothList from '@/components/dnf/Avatar/List/index.vue'
export default defineComponent({ export default defineComponent({
name: 'Clothes', name: 'Clothes',
setup(props) { setup() {
const configStore = useConfigStore() const configStore = useConfigStore()
const part = ref('武器装扮') const part = ref('武器装扮')
// const globalStore = useGlobalStore() // const globalStore = useGlobalStore()
/** 是否有徽章 */
const has_socket = computed(() => ['武器装扮', '光环', '皮肤'].includes(part.value as string))
const basicInfoStore = useInfoStore() const basicInfoStore = useInfoStore()
const has_enchant = computed(() => { const has_enchant = computed(() => {
return !['头部', '帽子', '脸部', '胸部', '上衣', '腰带', '下装', '鞋'].includes( return !['头部', '帽子', '脸部', '胸部', '上衣', '腰带', '下装', '鞋'].includes(
@@ -26,9 +24,9 @@ export default defineComponent({
.sort((a, b) => (b.fame ?? 0) - (a.fame ?? 0)) .sort((a, b) => (b.fame ?? 0) - (a.fame ?? 0))
}) })
const emblem_list = computed<IEnchantingInfo[] | undefined>(() => { // const emblem_list = computed<IEnchantingInfo[] | undefined>(() => {
return basicInfoStore.emblems.filter((item) => item.position.includes(part.value)) // return basicInfoStore.emblems.filter((item) => item.position.includes(part.value))
}) // })
const currentInfo = function <T>(name: string, defaultValue?: T) { const currentInfo = function <T>(name: string, defaultValue?: T) {
return computed<string | number>({ return computed<string | number>({
@@ -36,7 +34,7 @@ export default defineComponent({
let key: 'avatar' | 'equips' = 'avatar' let key: 'avatar' | 'equips' = 'avatar'
if (part.value == '宠物') key = 'equips' if (part.value == '宠物') key = 'equips'
return ( return (
(configStore.config[key]?.[part.value] as Record<string, any>)?.[name] ?? (configStore.config[key]?.[part.value] as Record<string, string | number>)?.[name] ??
defaultValue ?? defaultValue ??
0 0
) )
@@ -48,10 +46,13 @@ export default defineComponent({
let key: 'avatar' | 'equips' = 'avatar' let key: 'avatar' | 'equips' = 'avatar'
if (part.value == '宠物') key = 'equips' if (part.value == '宠物') key = 'equips'
if (!configStore.config[key][part.value]) { if (!configStore.config[key][part.value]) {
configStore.config[key][part.value] = key === 'avatar' ? { ...configStore.defaultAvatarConfig } : { ...configStore.defaultEqusConfig } configStore.config[key][part.value] =
key === 'avatar'
? { ...configStore.defaultAvatarConfig }
: { ...configStore.defaultEqusConfig }
} }
if (configStore.config[key][part.value]) { if (configStore.config[key][part.value]) {
;(configStore.config[key][part.value] as Record<string, any>)[name] = val ;(configStore.config[key][part.value] as Record<string, string | number>)[name] = val
} }
}, },
}) })
@@ -60,11 +61,11 @@ export default defineComponent({
// 附魔 // 附魔
const enchant = currentInfo<string | number>('enchant') const enchant = currentInfo<string | number>('enchant')
// 镶嵌栏1 // // 镶嵌栏1
const emblem_0 = currentInfo<string | number>('emblem_0', 0) // const emblem_0 = currentInfo<string | number>('emblem_0', 0)
// 镶嵌栏2 // // 镶嵌栏2
const emblem_1 = currentInfo<string | number>('emblem_1') // const emblem_1 = currentInfo<string | number>('emblem_1')
// 品级 // 品级
const id = currentInfo<string | number>('id') const id = currentInfo<string | number>('id')
@@ -76,9 +77,9 @@ export default defineComponent({
* 同步徽章1到徽章2 * 同步徽章1到徽章2
* @param val * @param val
*/ */
function changeSocket(val: number) { // function changeSocket(val: number) {
emblem_1.value = val // emblem_1.value = val
} // }
return () => { return () => {
return ( return (
@@ -127,33 +128,6 @@ export default defineComponent({
</div> </div>
</> </>
)} )}
{has_socket.value ? (
<div class="equ-profile-item">
<div class="row-name">徽章</div>
<calc-select
onChange={changeSocket}
v-model={emblem_0.value}
class="flex-1 !h-20px"
>
<calc-option value={0}>无</calc-option>
{renderList(emblem_list.value ?? [], (item) => (
<calc-option
value={item.id}
>{`${item.rarity}${item.categorize}徽章[${item.detail}]`}</calc-option>
))}
</calc-select>
<calc-select v-model={emblem_1.value} class="flex-1 !h-20px">
<calc-option value={0}>无</calc-option>
{renderList(emblem_list.value ?? [], (item) => (
<calc-option
value={item.id}
>{`${item.rarity}${item.categorize}徽章[${item.detail}]`}</calc-option>
))}
</calc-select>
</div>
) : (
<div></div>
)}
</div> </div>
</> </>
) )
@@ -0,0 +1,5 @@
<template>
<div></div>
</template>
<script lang="ts" setup name="Emblems"></script>
@@ -6,7 +6,7 @@ import EquList from '@/components/dnf/Equipment/List/index.vue'
import { getImageURL } from '@/utils/images' import { getImageURL } from '@/utils/images'
export default defineComponent({ export default defineComponent({
name: 'Equip', name: 'Equip',
setup(props) { setup() {
const configStore = useConfigStore() const configStore = useConfigStore()
const part = ref('头肩') const part = ref('头肩')
const can_upgrade = computed(() => { const can_upgrade = computed(() => {
@@ -15,14 +15,6 @@ export default defineComponent({
// const globalStore = useGlobalStore() // const globalStore = useGlobalStore()
/** 是否有徽章 */
const has_socket = computed(
() => !['称号', '宠物', '耳环', '武器'].includes(part.value as string),
)
/** 是否有右侧徽章 */
const has_emblem_1 = computed(
() => has_socket.value && !['辅助装备', '魔法石'].includes(part.value as string),
)
/** 是否可以设置秘宝精度 */ /** 是否可以设置秘宝精度 */
const can_set_secret = computed(() => { const can_set_secret = computed(() => {
return ['武器', '辅助装备', '魔法石', '耳环'].includes(part.value as string) return ['武器', '辅助装备', '魔法石', '耳环'].includes(part.value as string)
@@ -40,9 +32,9 @@ export default defineComponent({
.sort((a, b) => (b.fame ?? 0) - (a.fame ?? 0)) .sort((a, b) => (b.fame ?? 0) - (a.fame ?? 0))
}) })
const emblem_list = computed<IEnchantingInfo[] | undefined>(() => { // const emblem_list = computed<IEnchantingInfo[] | undefined>(() => {
return basicInfoStore.emblems.filter((item) => item.position.includes(part.value)) // return basicInfoStore.emblems.filter((item) => item.position.includes(part.value))
}) // })
const global_change = ref(false) const global_change = ref(false)
@@ -50,6 +42,7 @@ export default defineComponent({
return computed<string | number>({ return computed<string | number>({
get() { get() {
return ( return (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(configStore.config.equips?.[part.value] as Record<string, any>)?.[name] ?? (configStore.config.equips?.[part.value] as Record<string, any>)?.[name] ??
defaultValue ?? defaultValue ??
0 0
@@ -59,10 +52,10 @@ export default defineComponent({
if (val == undefined && name !== 'weaponFusion') { if (val == undefined && name !== 'weaponFusion') {
return return
} }
if (name == 'emblem_1' && !has_emblem_1.value) { // if (name == 'emblem_1' && !has_emblem_1.value) {
// configStore.setForge(part.value, name, 0) // // configStore.setForge(part.value, name, 0)
return // return
} // }
let parts: string[] = [] let parts: string[] = []
@@ -75,16 +68,16 @@ export default defineComponent({
parts = enchant.position parts = enchant.position
} }
} }
if (['emblem_0', 'emblem_1'].includes(name)) { // if (['emblem_0', 'emblem_1'].includes(name)) {
const emblem = basicInfoStore?.emblems?.find( // const emblem = basicInfoStore?.emblems?.find(
(item) => item.id.toString() == val.toString(), // (item) => item.id.toString() == val.toString(),
) as IEnchantingInfo | undefined // ) as IEnchantingInfo | undefined
if (emblem?.position) { // if (emblem?.position) {
parts = emblem.position.filter( // parts = emblem.position.filter(
(item) => !['皮肤', '武器装扮', '光环'].includes(item), // (item) => !['皮肤', '武器装扮', '光环'].includes(item),
) // )
} // }
} // }
if (['reinforceType', 'reinforce', 'adaptation'].includes(name)) { if (['reinforceType', 'reinforce', 'adaptation'].includes(name)) {
parts = basicInfoStore.parts.filter((e) => !['称号', '宠物'].includes(e)) parts = basicInfoStore.parts.filter((e) => !['称号', '宠物'].includes(e))
} }
@@ -96,6 +89,7 @@ export default defineComponent({
configStore.config.equips[part] = { ...configStore.defaultEqusConfig } configStore.config.equips[part] = { ...configStore.defaultEqusConfig }
} }
if (configStore.config.equips[part]) { if (configStore.config.equips[part]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(configStore.config.equips[part] as Record<string, any>)[name] = val ;(configStore.config.equips[part] as Record<string, any>)[name] = val
} }
} }
@@ -106,11 +100,11 @@ export default defineComponent({
// 附魔 // 附魔
const enchant = currentInfo<string | number>('enchant') const enchant = currentInfo<string | number>('enchant')
// 镶嵌栏1 // // 镶嵌栏1
const emblem_0 = currentInfo<string | number>('emblem_0', 0) // const emblem_0 = currentInfo<string | number>('emblem_0', 0)
// 镶嵌栏2 // // 镶嵌栏2
const emblem_1 = currentInfo<string | number>('emblem_1') // const emblem_1 = currentInfo<string | number>('emblem_1')
// 增幅 // 增幅
const reinforce_type = currentInfo<string | number>('reinforceType', 1) const reinforce_type = currentInfo<string | number>('reinforceType', 1)
@@ -133,11 +127,11 @@ export default defineComponent({
* 同步徽章1到徽章2 * 同步徽章1到徽章2
* @param val * @param val
*/ */
function changeSocket(val: number) { // function changeSocket(val: number) {
if (has_emblem_1.value) { // if (has_emblem_1.value) {
emblem_1.value = val // emblem_1.value = val
} // }
} // }
const setWeaponFusion = (val: number) => { const setWeaponFusion = (val: number) => {
if (val == weaponFusion.value) { if (val == weaponFusion.value) {
@@ -240,37 +234,6 @@ export default defineComponent({
))} ))}
</calc-select> </calc-select>
</div> </div>
{has_socket.value ? (
<div class="equ-profile-item">
<div class="row-name">徽章</div>
<calc-select
onChange={changeSocket}
v-model={emblem_0.value}
class="flex-1 !h-20px"
>
<calc-option value={''}>无</calc-option>
{renderList(emblem_list.value ?? [], (item) => (
<calc-option
value={item.id}
key={item.id}
>{`${item.rarity}${item.categorize}徽章[${item.detail}]`}</calc-option>
))}
</calc-select>
{has_emblem_1.value && (
<calc-select v-model={emblem_1.value} class="flex-1 !h-20px">
<calc-option value={''}>无</calc-option>
{renderList(emblem_list.value ?? [], (item) => (
<calc-option
value={item.id}
key={item.id}
>{`${item.rarity}${item.categorize}徽章[${item.detail}]`}</calc-option>
))}
</calc-select>
)}
</div>
) : (
<div></div>
)}
{can_upgrade.value ? ( {can_upgrade.value ? (
<div class="equ-profile-item"> <div class="equ-profile-item">
@@ -4,7 +4,7 @@
<div class="flex gap-2 mt-5px"> <div class="flex gap-2 mt-5px">
<calc-select class="flex-1 !h-20px" v-model="configStore.config.jades[key].id"> <calc-select class="flex-1 !h-20px" v-model="configStore.config.jades[key].id">
<calc-option :value="0">无</calc-option> <calc-option :value="0">无</calc-option>
<calc-option v-for="(item, index) in infoStore.jades" :key="item.id" :value="item.id"> <calc-option v-for="item in infoStore.jades" :key="item.id" :value="item.id">
{{ item.name }} {{ item.name }}
</calc-option> </calc-option>
</calc-select> </calc-select>
@@ -17,7 +17,7 @@
></calc-slider> --> ></calc-slider> -->
<calc-select class="flex-1 !h-20px" v-model="configStore.config.jades[key].value"> <calc-select class="flex-1 !h-20px" v-model="configStore.config.jades[key].value">
<calc-option <calc-option
v-for="(item, index) in valueList(configStore.config.jades[key].id)" v-for="item in valueList(configStore.config.jades[key].id)"
:key="item.id" :key="item.id"
:value="item.id" :value="item.id"
> >
@@ -38,26 +38,25 @@ import { getFloat } from '@/utils'
const configStore = useConfigStore() const configStore = useConfigStore()
const infoStore = useInfoStore() const infoStore = useInfoStore()
const currentJade = computed(() => { // const currentJade = computed(() => {
return function (id: number = -1) { // return function (id: number = -1) {
if (id == -1) { // if (id == -1) {
return undefined // return undefined
} else { // } else {
const res = [] // const cur = infoStore.jades?.find((e) => e.id == id) as IJade
const cur = infoStore.jades?.find((e) => e.id == id) as IJade // return cur
return cur // }
} // }
} // })
})
const formatValue = (id: number) => { // const formatValue = (id: number) => {
return (value: number) => { // return (value: number) => {
if (currentJade.value(id)?.unit.includes('等级')) { // if (currentJade.value(id)?.unit.includes('等级')) {
return `$等级 +${value}` // return `$等级 +${value}`
} // }
return `${getFloat(value, 1)}${currentJade.value(id)?.unit}` // return `${getFloat(value, 1)}${currentJade.value(id)?.unit}`
} // }
} // }
const valueList = computed(() => { const valueList = computed(() => {
return function (id: number = -1) { return function (id: number = -1) {
@@ -0,0 +1,77 @@
<template>
<div class="h-150px flex justify-center items-center">
<Oath v-model:part="part" :withBg="false" />
</div>
<div class="flex flex-wrap equ-profile">
<div class="equ-profile-item">
<div class="mr-10px row-name">当前部位</div>
<span>{{ part == '11' ? '誓约' : `星蕴石-${parseInt(part) + 1}` }}</span>
<calc-checkbox
style="margin-left: auto"
v-model="global_change"
label="全局修改"
></calc-checkbox>
</div>
<div class="equ-profile-item">
<div class="row-name">调适</div>
<calc-select v-model="adaptation" class="flex-1 !h-20px">
<template v-for="i in Array.from({ length: 4 }, (_, i) => i)" :key="i">
<calc-option :value="i">{{ i }}</calc-option>
</template>
</calc-select>
</div>
</div>
</template>
<script lang="ts" setup name="Oaths">
import Oath from '@/components/dnf/Equipment/Oath/index.vue'
import { useConfigStore, useInfoStore } from '@/stores'
const part = ref('0')
const global_change = ref(false)
const configStore = useConfigStore()
const infoStore = useInfoStore()
const currentInfo = function <T>(name: string, defaultValue?: T) {
return computed<string | number>({
get() {
return (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(configStore.config.oaths?.[part.value] as Record<string, any>)?.[name] ?? defaultValue ?? 0
)
},
set(val) {
if (val == undefined) {
return
}
// if (name == 'emblem_1' && !has_emblem_1.value) {
// // configStore.setForge(part.value, name, 0)
// return
// }
let parts: string[] = []
if (global_change.value) {
parts = infoStore.oathParts
} else {
parts = [part.value]
}
for (const part of parts) {
if (!configStore.config.oaths[part]) {
configStore.config.oaths[part] = { ...configStore.defaultOathConfig }
}
if (configStore.config.oaths[part]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(configStore.config.oaths[part] as Record<string, any>)[name] = val
}
}
},
})
}
// 调适
const adaptation = currentInfo<string | number>('adaptation')
</script>
@@ -12,7 +12,7 @@
class="flex-1 !h-20px" class="flex-1 !h-20px"
/> />
</div> </div>
<div class="equ-profile-item"> <!-- <div class="equ-profile-item">
<div class="row-name">勋章</div> <div class="row-name">勋章</div>
<calc-select <calc-select
v-for="(key, index) in ['medal_rarity', 'medal_reinforce']" v-for="(key, index) in ['medal_rarity', 'medal_reinforce']"
@@ -37,7 +37,7 @@
{{ item.name }} {{ item.name }}
</calc-option> </calc-option>
</calc-select> </calc-select>
</div> </div> -->
<div class="equ-profile-item"> <div class="equ-profile-item">
<div class="row-name">冒险团</div> <div class="row-name">冒险团</div>
<calc-slider <calc-slider
+8 -2
View File
@@ -2,11 +2,15 @@
<div class="w-420px overflow-auto"> <div class="w-420px overflow-auto">
<calc-collapse class="w-100%" title="装备/时装打造(点击部位切换)"> <calc-collapse class="w-100%" title="装备/时装打造(点击部位切换)">
<calc-tabs v-model="partTab" class="!mb-2"> <calc-tabs v-model="partTab" class="!mb-2">
<calc-tab :value="0">装备栏</calc-tab> <calc-tab :value="0">装备</calc-tab>
<calc-tab :value="1">时装栏</calc-tab> <calc-tab :value="2">誓约</calc-tab>
<calc-tab :value="1">时装</calc-tab>
<calc-tab :value="3">徽章</calc-tab>
</calc-tabs> </calc-tabs>
<equipment v-if="partTab === 0"></equipment> <equipment v-if="partTab === 0"></equipment>
<clothes v-if="partTab === 1"></clothes> <clothes v-if="partTab === 1"></clothes>
<oaths v-if="partTab === 2"></oaths>
<emblems v-if="partTab === 3"></emblems>
</calc-collapse> </calc-collapse>
<calc-collapse class="w-100%" title="辟邪玉"> <calc-collapse class="w-100%" title="辟邪玉">
<jade></jade> <jade></jade>
@@ -19,6 +23,8 @@
<script setup lang="ts"> <script setup lang="ts">
import equipment from './components/Equipments.vue' import equipment from './components/Equipments.vue'
import clothes from './components/Avatars.vue' import clothes from './components/Avatars.vue'
import emblems from './components/Emblems.vue'
import oaths from './components/Oaths.vue'
import jade from './components/Jades.vue' import jade from './components/Jades.vue'
import sundry from './components/Sundry.vue' import sundry from './components/Sundry.vue'
const partTab = ref(0) const partTab = ref(0)
+60 -2
View File
@@ -5,11 +5,30 @@
<calc-button class="flex-1" @click="setStandard">设为基准</calc-button> <calc-button class="flex-1" @click="setStandard">设为基准</calc-button>
<calc-button class="flex-1" @click="clearStandard">清空基准</calc-button> <calc-button class="flex-1" @click="clearStandard">清空基准</calc-button>
</div> </div>
<div class="flex bg-#101012 h-40px text-#6C5E4A font-bold z-10">
<div
class="flex-1 text-15px flex items-center justify-center"
@click="chooseCategory('equ')"
:class="{ active: type === 'equ' }"
>
装备
</div>
<div class="w-auto text-18px flex items-center justify-center !font-normal">|</div>
<div
class="flex-1 text-15px flex items-center justify-center"
@click="chooseCategory('oath')"
:class="{ active: type === 'oath' }"
>
誓约
</div>
</div>
<Info <Info
:suits="configStore.result.state.suits" :suits="configStore.result.state.suits"
:info="configStore.result.state.info" :info="configStore.result.state.info"
:equs="configStore.config.equips" :equs="configStore.config.equips"
:avatar="configStore.config.avatarLink" :avatar="configStore.config.avatarLink"
:oaths="configStore.config.oaths"
:display="type"
></Info> ></Info>
</div> </div>
<Skill <Skill
@@ -31,10 +50,10 @@ import { useConfigStore, useInfoStore } from '@/stores'
import Info from './components/Info.vue' import Info from './components/Info.vue'
import Skill from './components/Skill.vue' import Skill from './components/Skill.vue'
import BufferSkill from './components/BufferSkill.vue' import BufferSkill from './components/BufferSkill.vue'
import type { IResultSkill } from '@/api/calc/type'
const configStore = useConfigStore() const configStore = useConfigStore()
const infoStore = useInfoStore() const infoStore = useInfoStore()
const type = ref<'equ' | 'oath'>('equ')
const setStandard = () => { const setStandard = () => {
if (!configStore?.result?.state?.uuid) return if (!configStore?.result?.state?.uuid) return
@@ -44,6 +63,45 @@ const setStandard = () => {
const clearStandard = () => { const clearStandard = () => {
infoStore.setStandard(undefined) infoStore.setStandard(undefined)
} }
const chooseCategory = (cat: 'equ' | 'oath') => {
type.value = cat
console.log(type.value)
}
</script> </script>
<style scoped lang="scss"></style> <style lang="scss" scoped>
.active {
color: #ccc088;
position: relative;
&::before {
content: '';
position: absolute;
width: 60px;
height: 15px;
opacity: 0.35;
background: #bf8d20;
filter: blur(7px);
z-index: 2;
}
&::after {
content: '';
position: absolute;
background: url('@/assets/img/active.png') no-repeat;
width: 148px;
height: 16px;
bottom: 2px;
transform: translate(0, 50%);
z-index: 4;
}
}
.item-head {
background: linear-gradient(#2b2817, #171407);
border-top: 1px solid #423d2c;
border-bottom: 1px solid #211d15;
text-align: center;
}
</style>
@@ -5,7 +5,9 @@
:detail="props.equs" :detail="props.equs"
:with-sub-weapon="(infoStore.infos?.subweapons?.length ?? 0) > 0" :with-sub-weapon="(infoStore.infos?.subweapons?.length ?? 0) > 0"
:avatar="props.avatar" :avatar="props.avatar"
v-show="display == 'equ'"
></EquList> ></EquList>
<Oath :detail="props.oaths" v-show="display == 'oath'"></Oath>
</div> </div>
<div class="flex flex-wrap border-y-1px my-1px border-y-solid border-#FFFF/10 gap-1% px-1%"> <div class="flex flex-wrap border-y-1px my-1px border-y-solid border-#FFFF/10 gap-1% px-1%">
<template v-for="info in props.info" :key="info.id"> <template v-for="info in props.info" :key="info.id">
@@ -37,37 +39,71 @@
<div class="border-y-1px border-y-solid my-1px border-#FFFF/10"> <div class="border-y-1px border-y-solid my-1px border-#FFFF/10">
<div class="flex"> <div class="flex">
<div class="flex-1 flex items-center justify-center bg-#221A08 text-#9A7E4A"> <div class="flex-1 flex items-center justify-center bg-#221A08 text-#9A7E4A">
穿戴装备信息 穿戴{{ display == 'equ' ? '装备' : '誓约' }}信息
</div> </div>
</div> </div>
<template v-for="part in partOrder" :key="part"> <template v-if="display == 'equ'">
<div class="flex p-2px partItem gap-5px"> <template v-for="part in partOrder" :key="part">
<img <div class="flex p-2px partItem gap-5px">
:src="getImageURL(`/equipment/part/${part}.png`)"
alt=""
class="w-20px h-20px object-contain"
/>
<div class="flex items-center justify-center" v-if="adaptation(part) > 0">
<img <img
:src="getImageURL(`/equipment/adaptation/${adaptation(part)}.png`)" :src="getImageURL(`/equipment/part/${part}.png`)"
alt="" alt=""
class="w-11px h-10px object-contain" class="w-20px h-20px object-contain"
/> />
<div class="flex items-center justify-center" v-if="adaptation(part) > 0">
<img
:src="getImageURL(`/equipment/adaptation/${adaptation(part)}.png`)"
alt=""
class="w-11px h-10px object-contain"
/>
</div>
<div
class="flex items-center"
v-if="props.equs[part]?.reinforce > 0"
:class="props.equs[part]?.reinforceType == 1 ? 'artifact' : 'advanced'"
>
+{{ props.equs[part]?.reinforce }}
</div>
<div class="rare" v-if="props.equs[part]?.refine > 0">
({{ props.equs[part]?.refine }})
</div>
<div :class="rarityClass(equ(props.equs?.[part]?.id)?.rarity ?? '')">
{{ equ(props.equs?.[part]?.id)?.name }}
</div>
</div> </div>
<div </template>
class="flex items-center" </template>
v-if="props.equs[part]?.reinforce > 0" <template v-else>
:class="props.equs[part]?.reinforceType == 1 ? 'artifact' : 'advanced'" <template v-for="part in oathOrder" :key="part">
> <div class="flex p-2px partItem gap-5px">
+{{ props.equs[part]?.reinforce }} <img
:src="getImageURL(`/equipment/part/${part == '11' ? '誓约' : '星蕴石'}.png`)"
alt=""
class="w-20px h-20px object-contain"
/>
<div class="flex items-center justify-center" v-if="adaptation(part) > 0">
<img
:src="getImageURL(`/equipment/adaptation/${adaptation(part)}.png`)"
alt=""
class="w-11px h-10px object-contain"
/>
</div>
<!-- <div
class="flex items-center"
v-if="props.equs[part]?.reinforce > 0"
:class="props.equs[part]?.reinforceType == 1 ? 'artifact' : 'advanced'"
>
+{{ props.equs[part]?.reinforce }}
</div> -->
<!-- <div class="rare" v-if="props.equs[part]?.refine > 0">
({{ props.equs[part]?.refine }})
</div> -->
{{ equ(props.equs?.[part]?.id) }}
<div :class="rarityClass(equ(props.oaths?.[part]?.id)?.rarity ?? '')">
{{ equ(props.oaths?.[part]?.id)?.name }}
</div>
</div> </div>
<div class="rare" v-if="props.equs[part]?.refine > 0"> </template>
({{ props.equs[part]?.refine }})
</div>
<div :class="rarityClass(equ(props.equs?.[part]?.id)?.rarity ?? '')">
{{ equ(props.equs?.[part]?.id)?.name }}
</div>
</div>
</template> </template>
</div> </div>
<div class="h-40px w-full"></div> <div class="h-40px w-full"></div>
@@ -79,14 +115,17 @@ import { getImageURL } from '@/utils/images'
import { rarityClass } from '@/utils' import { rarityClass } from '@/utils'
import type { IResultUserInfo, IResultSuit } from '@/api/calc/type' import type { IResultUserInfo, IResultSuit } from '@/api/calc/type'
import EquList from '@/components/dnf/Equipment/List/index.vue' import EquList from '@/components/dnf/Equipment/List/index.vue'
import type { IConfigEquip } from '@/stores/config' import Oath from '@/components/dnf/Equipment/Oath/index.vue'
import type { IConfigEquip, IConfigOath } from '@/stores/config'
import { useInfoStore } from '@/stores' import { useInfoStore } from '@/stores'
const props = defineProps<{ const props = defineProps<{
info: IResultUserInfo[] info: IResultUserInfo[]
suits: IResultSuit[] suits: IResultSuit[]
equs: Record<string, IConfigEquip> equs: Record<string, IConfigEquip>
oaths: Record<string, IConfigOath>
avatar: string avatar: string
display: 'oath' | 'equ'
}>() }>()
const partOrder = [ const partOrder = [
@@ -103,9 +142,15 @@ const partOrder = [
'魔法石', '魔法石',
'耳环', '耳环',
] ]
const oathOrder = Array.from({ length: 12 }, (_, i) => (11 - i).toString())
const infoStore = useInfoStore() const infoStore = useInfoStore()
const equ = (id: string) => { const equ = (id: string) => {
if (props.display == 'oath') {
console.log(props.oaths, id)
return infoStore.oaths.find((e) => e.id == id)
}
return infoStore.equips.find((e) => e.id == id) return infoStore.equips.find((e) => e.id == id)
} }
+18 -2
View File
@@ -9,8 +9,24 @@
<script lang="ts" name="index" setup> <script lang="ts" name="index" setup>
const zoom = computed(() => { const zoom = computed(() => {
const DPI = Math.max(0.85,Math.min(/macintosh|mac os x/i.test(navigator.userAgent.toLowerCase()) ? 1 : window.devicePixelRatio || 1,2)) * 0.95 const DPI =
return Math.max(1,/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent || navigator.vendor || (window as any).opera) ? 1 :(window.screen.width / 1800 / DPI)) Math.max(
0.85,
Math.min(
/macintosh|mac os x/i.test(navigator.userAgent.toLowerCase())
? 1
: window.devicePixelRatio || 1,
2,
),
) * 0.95
return Math.max(
1,
/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
navigator.userAgent || navigator.vendor || (window as any).opera,
)
? 1
: window.screen.width / 1800 / DPI,
)
}) })
import { useDialog } from '@/components/hooks/dialog' import { useDialog } from '@/components/hooks/dialog'
+29 -5
View File
@@ -31,9 +31,15 @@ export interface IConfigEquip {
weaponFusion: number weaponFusion: number
} }
export interface IConfigOath {
id: string
adaptation: number
}
export interface IConfig { export interface IConfig {
skills: Record<string, { lv: number ,vp:number, up: number}> skills: Record<string, { lv: number; vp: number; up: number }>
equips: Record<string, IConfigEquip> equips: Record<string, IConfigEquip>
oaths: Record<string, IConfigOath>
jades: Record< jades: Record<
string, string,
{ {
@@ -84,17 +90,23 @@ const defaultAvatarConfig = {
option: '0', option: '0',
} }
const defaultOathConfig = {
id: '',
adaptation: 0,
}
export const useConfigStore = defineStore('configStore', () => { export const useConfigStore = defineStore('configStore', () => {
const config = ref<IConfig>({ const config = ref<IConfig>({
skills: {}, skills: {},
equips: {}, equips: {},
oaths: {},
jades: {}, jades: {},
avatar: {}, avatar: {},
sundry: {}, sundry: {},
bindAwake: 50, bindAwake: 50,
DSB: false, DSB: false,
BUFF: false, BUFF: false,
options:{}, options: {},
avatarLink: '', avatarLink: '',
}) })
@@ -114,19 +126,21 @@ export const useConfigStore = defineStore('configStore', () => {
skillCountConfig.value = JSON.parse(localSkillConfig) skillCountConfig.value = JSON.parse(localSkillConfig)
} }
} }
if (!config.value) if (!config.value) {
config.value = { config.value = {
skills: {}, skills: {},
equips: {}, equips: {},
oaths: {},
jades: {}, jades: {},
avatar: {}, avatar: {},
sundry: {}, sundry: {},
bindAwake: 50, bindAwake: 50,
DSB: false, DSB: false,
BUFF: false, BUFF: false,
options:{}, options: {},
avatarLink: '', avatarLink: '',
} }
}
if (!config.value?.equips) config.value.equips = {} if (!config.value?.equips) config.value.equips = {}
if (!config.value?.skills) config.value.skills = {} if (!config.value?.skills) config.value.skills = {}
if (!config.value?.jades) config.value.jades = {} if (!config.value?.jades) config.value.jades = {}
@@ -134,6 +148,8 @@ export const useConfigStore = defineStore('configStore', () => {
if (!config.value?.sundry) config.value.sundry = {} if (!config.value?.sundry) config.value.sundry = {}
if (!config.value?.bindAwake) config.value.bindAwake = 50 if (!config.value?.bindAwake) config.value.bindAwake = 50
if (!config.value?.options) config.value.options = {} if (!config.value?.options) config.value.options = {}
if (!config.value?.oaths) config.value.oaths = {}
console.log(config.value.oaths)
const part = [...infoStore.parts, '宠物', '副武器'].filter( const part = [...infoStore.parts, '宠物', '副武器'].filter(
(a) => !config.value?.equips.hasOwnProperty(a), (a) => !config.value?.equips.hasOwnProperty(a),
@@ -156,8 +172,15 @@ export const useConfigStore = defineStore('configStore', () => {
config.value && (config.value.avatar[p] = { ...defaultAvatarConfig }) config.value && (config.value.avatar[p] = { ...defaultAvatarConfig })
}) })
infoStore.oathParts.forEach((p) => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
config.value &&
!config.value?.oaths.hasOwnProperty(p) &&
(config.value.oaths[p.toString()] = { ...defaultOathConfig })
})
infoStore.options.forEach((option) => { infoStore.options.forEach((option) => {
if(Object.keys(config.value.options).includes(option.id.toString())) return if (Object.keys(config.value.options).includes(option.id.toString())) return
config.value.options[option.id.toString()] = 0 config.value.options[option.id.toString()] = 0
}) })
@@ -238,5 +261,6 @@ export const useConfigStore = defineStore('configStore', () => {
skillCountConfig, skillCountConfig,
defaultEqusConfig, defaultEqusConfig,
defaultAvatarConfig, defaultAvatarConfig,
defaultOathConfig,
} }
}) })
+23 -2
View File
@@ -1,5 +1,5 @@
import api from '@/api' import api from '@/api'
import type { ICharacterInfo } from '@/api/info/type' import type { ICharacterInfo, IEquipment } from '@/api/info/type'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { useConfigStore } from './config' import { useConfigStore } from './config'
import type { IResult, IResultSkill, IResultSkillCount } from '@/api/calc/type' import type { IResult, IResultSkill, IResultSkillCount } from '@/api/calc/type'
@@ -28,7 +28,22 @@ export const useInfoStore = defineStore('infoStore', () => {
const options = computed(() => infos.value?.options ?? []) const options = computed(() => infos.value?.options ?? [])
const oaths = computed(() => infos.value?.oaths ?? []) const oaths = computed(() => {
const origin = infos.value?.oaths ?? []
const result: IEquipment[] = []
origin.forEach((o) => {
o.position.forEach((p, i) => {
result.push({
...o,
oathPos: p,
displayUrl: o.wearUrl?.[parseInt(p)]
? `/equipment/icon/oathprimer/primer_equip/${o.wearUrl?.[parseInt(p)]}`
: o.imageUrl,
})
})
})
return result
})
const standardUUID = ref<string>() const standardUUID = ref<string>()
@@ -93,6 +108,10 @@ export const useInfoStore = defineStore('infoStore', () => {
'宠物装备-蓝', '宠物装备-蓝',
] ]
const oathParts = Array.from({ length: 12 }, (_, i) => i.toString())
const emblemParts = ['彩色', '红色', '蓝色', '绿色', '黄色']
const createCharacter = async (alter: string, equVersion?: string) => { const createCharacter = async (alter: string, equVersion?: string) => {
const time = new Date().getTime() const time = new Date().getTime()
alter_token.value = btoa(JSON.stringify({ alter, equVersion, time })) alter_token.value = btoa(JSON.stringify({ alter, equVersion, time }))
@@ -112,6 +131,8 @@ export const useInfoStore = defineStore('infoStore', () => {
parts, parts,
oaths, oaths,
avatarParts, avatarParts,
oathParts,
emblemParts,
avatars, avatars,
jades, jades,
setStandard, setStandard,
+32
View File
@@ -0,0 +1,32 @@
export function resolveTeleportTarget(target?: string | Element | null) {
if (typeof window === 'undefined') {
return null
}
if (typeof target === 'string') {
return document.querySelector(target)
}
return target ?? document.body
}
export function getElementZoom(target?: string | Element | null) {
if (typeof window === 'undefined') {
return 1
}
let zoom = 1
let current = resolveTeleportTarget(target) as HTMLElement | null
while (current) {
const currentZoom = Number.parseFloat(window.getComputedStyle(current).zoom)
if (Number.isFinite(currentZoom) && currentZoom > 0) {
zoom *= currentZoom
}
current = current.parentElement
}
return zoom || 1
}
+1 -1
Submodule images updated: 2dc49b5e8f...965adcfe7e