UPDATE: new ui
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 47s

This commit is contained in:
2026-03-20 16:00:07 +07:00
parent b69a5261ec
commit 09ca305b60
10 changed files with 104 additions and 72 deletions

View File

@@ -274,6 +274,8 @@
"detailHiddenUi": "开启后将隐藏游戏界面。", "detailHiddenUi": "开启后将隐藏游戏界面。",
"detailDisableCensorship": "开启后将关闭游戏内的审查。", "detailDisableCensorship": "开启后将关闭游戏内的审查。",
"detailMultipathCharacter": "允许更改部分角色的命途。", "detailMultipathCharacter": "允许更改部分角色的命途。",
"trailblazer": "开拓者" "trailblazer": "开拓者",
"listExtraEffect": "额外效果列表",
"extra": "额外"
} }
} }

View File

@@ -275,6 +275,8 @@
"detailHiddenUi": "Enabling this feature will hide the game UI.", "detailHiddenUi": "Enabling this feature will hide the game UI.",
"detailDisableCensorship": "Enabling this feature will disable in-game censorship.", "detailDisableCensorship": "Enabling this feature will disable in-game censorship.",
"detailMultipathCharacter": "Allows changing the Path of certain characters.", "detailMultipathCharacter": "Allows changing the Path of certain characters.",
"trailblazer": "Trailblazer" "trailblazer": "Trailblazer",
"listExtraEffect": "List Extra Effect",
"extra": "Extra"
} }
} }

View File

@@ -274,6 +274,8 @@
"detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。", "detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。",
"detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。", "detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。",
"detailMultipathCharacter": "一部キャラクターの運命を変更できます。", "detailMultipathCharacter": "一部キャラクターの運命を変更できます。",
"trailblazer": "開拓者" "trailblazer": "開拓者",
"listExtraEffect": "追加効果一覧",
"extra": "追加"
} }
} }

View File

@@ -274,6 +274,8 @@
"detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.", "detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.",
"detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.", "detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.",
"detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.", "detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.",
"trailblazer": "개척자" "trailblazer": "개척자",
"listExtraEffect": "추가 효과 목록",
"extra": "추가"
} }
} }

View File

@@ -274,6 +274,8 @@
"detailHiddenUi": "Khi bật tính năng này sẽ ẩn giao diện của game.", "detailHiddenUi": "Khi bật tính năng này sẽ ẩn giao diện của game.",
"detailDisableCensorship": "Khi bật tính năng này sẽ tắt kiểm duyệt của game.", "detailDisableCensorship": "Khi bật tính năng này sẽ tắt kiểm duyệt của game.",
"detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.", "detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.",
"trailblazer": "Nhà khai phá" "trailblazer": "Nhà khai phá",
"listExtraEffect": "Danh sách hiệu ứng bổ sung",
"extra": "Bổ sung"
} }
} }

View File

@@ -274,6 +274,8 @@
"detailHiddenUi": "开启后将隐藏游戏界面。", "detailHiddenUi": "开启后将隐藏游戏界面。",
"detailDisableCensorship": "开启后将关闭游戏内的审查。", "detailDisableCensorship": "开启后将关闭游戏内的审查。",
"detailMultipathCharacter": "允许更改部分角色的命途。", "detailMultipathCharacter": "允许更改部分角色的命途。",
"trailblazer": "开拓者" "trailblazer": "开拓者",
"listExtraEffect": "额外效果列表",
"extra": "额外"
} }
} }

View File

@@ -3,6 +3,7 @@
import { useState } from "react" import { useState } from "react"
import { replaceByParam, getLocaleName } from "@/helper" import { replaceByParam, getLocaleName } from "@/helper"
import { ExtraEffect } from "@/types" import { ExtraEffect } from "@/types"
import { useTranslations } from "next-intl"
type Props = { type Props = {
extras: Record<string, ExtraEffect> | undefined extras: Record<string, ExtraEffect> | undefined
@@ -12,7 +13,7 @@ type Props = {
export default function ExtraEffectList({ extras, locale }: Props) { export default function ExtraEffectList({ extras, locale }: Props) {
const [openList, setOpenList] = useState(false) const [openList, setOpenList] = useState(false)
const [openId, setOpenId] = useState<number | null>(null) const [openId, setOpenId] = useState<number | null>(null)
const transI18n = useTranslations("DataPage")
if (!extras || Object.keys(extras).length === 0) return null if (!extras || Object.keys(extras).length === 0) return null
return ( return (
@@ -22,7 +23,7 @@ export default function ExtraEffectList({ extras, locale }: Props) {
onClick={() => setOpenList(!openList)} onClick={() => setOpenList(!openList)}
> >
<span className="text-sm font-semibold text-primary"> <span className="text-sm font-semibold text-primary">
List Extra Effect ({Object.keys(extras).length}) {transI18n("listExtraEffect")} ({Object.keys(extras).length})
</span> </span>
<span <span
@@ -36,7 +37,7 @@ export default function ExtraEffectList({ extras, locale }: Props) {
<div <div
className={`overflow-hidden transition-all duration-300 ${ className={`overflow-hidden transition-all duration-300 ${
openList ? "max-h-[500px] mt-2" : "max-h-0" openList ? "max-h-125 mt-2" : "max-h-0"
}`} }`}
> >
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
@@ -56,7 +57,7 @@ export default function ExtraEffectList({ extras, locale }: Props) {
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-[10px] uppercase font-bold bg-primary/50 px-1.5 py-0.5 rounded"> <span className="text-[10px] uppercase font-bold bg-primary/50 px-1.5 py-0.5 rounded">
Extra {transI18n("extra")}
</span> </span>
<span className="text-sm font-medium text-primary/80"> <span className="text-sm font-medium text-primary/80">

View File

@@ -10,7 +10,7 @@ import { useMemo, useState } from "react"
import useDetailDataStore from "@/stores/detailDataStore" import useDetailDataStore from "@/stores/detailDataStore"
export default function ExtraSettingBar() { export default function ExtraSettingBar() {
const { extraData, setExtraData } = useGlobalStore() const { extraData, setExtraData, isEnableChangePath, setIsEnableChangePath } = useGlobalStore()
const transI18n = useTranslations("DataPage") const transI18n = useTranslations("DataPage")
const { mapAvatar, mapPeak, stage, baseType } = useDetailDataStore() const { mapAvatar, mapPeak, stage, baseType } = useDetailDataStore()
const { locale } = useLocaleStore() const { locale } = useLocaleStore()
@@ -160,7 +160,7 @@ export default function ExtraSettingBar() {
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
setIsChildClick(true) setIsChildClick(true)
if (extraData?.theory_craft?.stage_id !== Number(stage.ID)) { if (extraData?.theory_craft?.stage_id !== Number(stage.ID)) {
setExtraData({ setExtraData({
...extraData, ...extraData,
@@ -228,66 +228,75 @@ export default function ExtraSettingBar() {
<div className="tooltip tooltip-info tooltip-bottom" data-tip={transI18n("detailMultipathCharacter")}> <div className="tooltip tooltip-info tooltip-bottom" data-tip={transI18n("detailMultipathCharacter")}>
<Info className="text-primary" size={20} /> <Info className="text-primary" size={20} />
</div> </div>
<input
type="checkbox"
className="toggle toggle-primary"
checked={isEnableChangePath}
onChange={(e) => setIsEnableChangePath(e.target.checked)}
/>
</h3> </h3>
{isEnableChangePath && (
<>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
<label className="flex items-center gap-3">
<User className="text-warning" size={20} />
<span className="label-text font-semibold">{transI18n("mainPath")}</span>
<SelectCustomImage
customSet={Object.values(mapAvatar).filter((it) => extraData?.multi_path?.multi_path_main?.includes(Number(it.ID))).map((it) => ({
value: it.ID.toString(),
label: getNameChar(locale, transI18n, it),
imageUrl: `${process.env.CDN_URL}/${baseType?.[it.BaseType].Icon}`
}))}
excludeSet={[]}
selectedCustomSet={extraData?.multi_path?.main?.toString() || ""}
placeholder={transI18n("selectAMainStat")}
setSelectedCustomSet={(it) => {
if (!it) return
setExtraData({
...extraData,
multi_path: {
march_7: extraData?.multi_path?.march_7 || 1001,
main: Number(it) || 8001,
multi_path_main: extraData?.multi_path?.multi_path_main || [],
multi_path_march_7: extraData?.multi_path?.multi_path_march_7 || []
}
})
}}
/>
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow"> </label>
<label className="flex items-center gap-3"> </motion.div>
<User className="text-warning" size={20} />
<span className="label-text font-semibold">{transI18n("mainPath")}</span>
<SelectCustomImage
customSet={Object.values(mapAvatar).filter((it) => extraData?.multi_path?.multi_path_main?.includes(Number(it.ID))).map((it) => ({
value: it.ID.toString(),
label: getNameChar(locale, transI18n, it),
imageUrl: `${process.env.CDN_URL}/${baseType?.[it.BaseType].Icon}`
}))}
excludeSet={[]}
selectedCustomSet={extraData?.multi_path?.main?.toString() || ""}
placeholder={transI18n("selectAMainStat")}
setSelectedCustomSet={(it) => {
if (!it) return
setExtraData({
...extraData,
multi_path: {
march_7: extraData?.multi_path?.march_7 || 1001,
main: Number(it) || 8001,
multi_path_main: extraData?.multi_path?.multi_path_main || [],
multi_path_march_7: extraData?.multi_path?.multi_path_march_7 || []
}
})
}}
/>
</label> <motion.div className="form-control bg-base-200 p-4 rounded-xl shadow">
</motion.div> <label className="flex items-center gap-3">
<BowArrow className="text-info" size={20} />
<motion.div className="form-control bg-base-200 p-4 rounded-xl shadow"> <span className="label-text font-semibold">{transI18n("march7Path")}</span>
<label className="flex items-center gap-3"> <SelectCustomImage
<BowArrow className="text-info" size={20} /> customSet={Object.values(mapAvatar).filter((it) => extraData?.multi_path?.multi_path_march_7?.includes(Number(it.ID))).map((it) => ({
<span className="label-text font-semibold">{transI18n("march7Path")}</span> value: it.ID.toString(),
<SelectCustomImage label: getNameChar(locale, transI18n, it),
customSet={Object.values(mapAvatar).filter((it) => extraData?.multi_path?.multi_path_march_7?.includes(Number(it.ID))).map((it) => ({ imageUrl: `${process.env.CDN_URL}/${baseType?.[it.BaseType].Icon}`
value: it.ID.toString(), }))}
label: getNameChar(locale, transI18n, it), excludeSet={[]}
imageUrl: `${process.env.CDN_URL}/${baseType?.[it.BaseType].Icon}` selectedCustomSet={extraData?.multi_path?.march_7?.toString() || ""}
}))} placeholder={transI18n("selectAMainStat")}
excludeSet={[]} setSelectedCustomSet={(it) => {
selectedCustomSet={extraData?.multi_path?.march_7?.toString() || ""} if (!it) return
placeholder={transI18n("selectAMainStat")} setExtraData({
setSelectedCustomSet={(it) => { ...extraData,
if (!it) return multi_path: {
setExtraData({ march_7: Number(it) || 1001,
...extraData, main: extraData?.multi_path?.main || 8001,
multi_path: { multi_path_main: extraData?.multi_path?.multi_path_main || [],
march_7: Number(it) || 1001, multi_path_march_7: extraData?.multi_path?.multi_path_march_7 || []
main: extraData?.multi_path?.main || 8001, }
multi_path_main: extraData?.multi_path?.multi_path_main || [], })
multi_path_march_7: extraData?.multi_path?.multi_path_march_7 || [] }}
} />
}) </label>
}} </motion.div>
/> </>
</label> )}
</motion.div>
</div> </div>
{/* CHALLENGE */} {/* CHALLENGE */}

View File

@@ -14,7 +14,7 @@ export const connectToPS = async (): Promise<{ success: boolean, message: string
username, username,
password password
} = useConnectStore.getState() } = useConnectStore.getState()
const {setExtraData, setIsConnectPS} = useGlobalStore.getState() const { setExtraData, setIsConnectPS } = useGlobalStore.getState()
let urlQuery = serverUrl let urlQuery = serverUrl
if (!urlQuery.startsWith("http://") && !urlQuery.startsWith("https://")) { if (!urlQuery.startsWith("http://") && !urlQuery.startsWith("https://")) {
@@ -59,7 +59,7 @@ export const syncDataToPS = async (): Promise<{ success: boolean, message: strin
password password
} = useConnectStore.getState() } = useConnectStore.getState()
const {extraData, setIsConnectPS, setExtraData} = useGlobalStore.getState() const {extraData, setIsConnectPS, setExtraData, isEnableChangePath} = useGlobalStore.getState()
const {avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config} = useUserDataStore.getState() const {avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config} = useUserDataStore.getState()
@@ -85,7 +85,13 @@ export const syncDataToPS = async (): Promise<{ success: boolean, message: strin
return { success: true, message: "" } return { success: true, message: "" }
} }
} }
const response = await SendDataToServer(username, password, urlQuery, data, extraData) const newExtra = structuredClone(extraData)
if (newExtra && !isEnableChangePath) {
newExtra.multi_path = undefined
}
const response = await SendDataToServer(username, password, urlQuery, data, newExtra)
if (typeof response === "string") { if (typeof response === "string") {
setIsConnectPS(false) setIsConnectPS(false)
return { success: false, message: response } return { success: false, message: response }

View File

@@ -4,6 +4,8 @@ import { ExtraData } from '@/types'
interface GlobalState { interface GlobalState {
isConnectPS: boolean; isConnectPS: boolean;
extraData?: ExtraData; extraData?: ExtraData;
isEnableChangePath: boolean
setIsEnableChangePath: (newIsEnableChangePath: boolean) => void;
setExtraData: (newExtraData: ExtraData | undefined) => void; setExtraData: (newExtraData: ExtraData | undefined) => void;
setIsConnectPS: (newIsConnectPS: boolean) => void; setIsConnectPS: (newIsConnectPS: boolean) => void;
} }
@@ -11,6 +13,8 @@ interface GlobalState {
const useGlobalStore = create<GlobalState>((set) => ({ const useGlobalStore = create<GlobalState>((set) => ({
isConnectPS: false, isConnectPS: false,
extraData: undefined, extraData: undefined,
isEnableChangePath: false,
setIsEnableChangePath: (newIsEnableChangePath: boolean) => set({ isEnableChangePath: newIsEnableChangePath }),
setExtraData: (newExtraData: ExtraData | undefined) => set({ extraData: newExtraData }), setExtraData: (newExtraData: ExtraData | undefined) => set({ extraData: newExtraData }),
setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }), setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }),
})); }));