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
+26 -24
View File
@@ -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,7 +97,7 @@ 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)
@@ -105,17 +105,19 @@ async def get_info_by_dnfhelper(uid: str, state: AltersDep, redis: RedisDep):
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/")
@router.post('/skillTree/')
async def get_skill_by_code(state: AltersDep, code=Body(None)): 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
+11 -8
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
@@ -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', ''), 'dress': i.get('name', ''),
'clone': i.get('clone', ''), 'clone': i.get('clone', ''),
'part': i.get('posName', ''), 'part': i.get('posName', ''),
} for i in detail.get('avatars', [])] }
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,9 +39,10 @@
<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-if="display == 'equ'">
<template v-for="part in partOrder" :key="part"> <template v-for="part in partOrder" :key="part">
<div class="flex p-2px partItem gap-5px"> <div class="flex p-2px partItem gap-5px">
<img <img
@@ -69,6 +72,39 @@
</div> </div>
</div> </div>
</template> </template>
</template>
<template v-else>
<template v-for="part in oathOrder" :key="part">
<div class="flex p-2px partItem gap-5px">
<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>
</template>
</template>
</div> </div>
<div class="h-40px w-full"></div> <div class="h-40px w-full"></div>
</div> </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'
+26 -2
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,10 +90,16 @@ 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: {},
@@ -114,10 +126,11 @@ 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: {},
@@ -127,6 +140,7 @@ export const useConfigStore = defineStore('configStore', () => {
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,6 +172,13 @@ 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