From f039ebc542f8aaaec045fa55280db886120de5f9 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sun, 11 Oct 2026 14:42:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(09-08):=20toolbox=20=E9=81=93=E5=85=B7?= =?UTF-8?q?=E6=A0=8F=E4=B8=89=E5=88=97=E5=B8=83=E5=B1=80=E4=B8=8E=E9=81=93?= =?UTF-8?q?=E5=85=B7=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 toolbox.tsx:永久道具 / 可消耗道具 / 道具信息三列布局 - 以 heroState.items.iterateItems() 枚举背包,过滤 hideInToolbox - 各组按数量降序、数量相同按图块数字升序 - 行渲染 + raw.name + core.formatBigNumber(count) - 右列展示 raw.name 与 raw.text;列表 / 说明纵向 - 右下退出按钮(本任务仅视觉占位) --- .../client-modules/src/ui/toolbox.tsx | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 packages-user/client-modules/src/ui/toolbox.tsx diff --git a/packages-user/client-modules/src/ui/toolbox.tsx b/packages-user/client-modules/src/ui/toolbox.tsx new file mode 100644 index 0000000..0fadfa0 --- /dev/null +++ b/packages-user/client-modules/src/ui/toolbox.tsx @@ -0,0 +1,261 @@ +import { GameUI, SetupComponentOptions } from '@motajs/system'; +import { computed, defineComponent, ref } from 'vue'; +import { Font } from '@motajs/render'; +import { Scroll } from '@user/client-base'; +import { IHeroItemState, IHeroState } from '@user/data-base'; +import { IHeroAttr } from '@user/data-common'; +import { FULL_LOC, MAIN_HEIGHT, MAIN_WIDTH } from '../shared'; +import { IUIPropsBase } from './types'; + +export interface ToolBoxProps extends IUIPropsBase { + /** 勇士状态对象 */ + heroState: IHeroState; +} + +const toolBoxProps = { + props: ['controller', 'instance', 'state', 'heroState'] +} satisfies SetupComponentOptions; + +/** 单列宽度,画面等分为永久道具、可消耗道具、道具信息三列 */ +const COLUMN_WIDTH = MAIN_WIDTH / 3; +/** 列表标题占用的高度 */ +const HEADER_HEIGHT = 28; +/** 列表区域起始纵坐标 */ +const LIST_TOP = HEADER_HEIGHT + 8; +/** 列表区域高度,底部为退出按钮留出空间 */ +const LIST_HEIGHT = MAIN_HEIGHT - LIST_TOP - 36; +/** 每行道具的高度 */ +const ROW_HEIGHT = 46; +/** 行内图标的尺寸 */ +const ROW_ICON_SIZE = 32; +/** 道具信息列起始横坐标 */ +const INFO_LEFT = COLUMN_WIDTH * 2; +/** 道具说明区域起始纵坐标 */ +const DESC_TOP = 40; +/** 道具说明区域高度,底部为退出按钮留出空间 */ +const DESC_HEIGHT = MAIN_HEIGHT - DESC_TOP - 36; + +/** 列表标题字体 */ +const titleFont = Font.defaults({ size: 14, weight: 700 }); +/** 道具名称与数量字体 */ +const itemFont = Font.defaults({ size: 13 }); +/** 道具说明字体 */ +const descFont = Font.defaults({ size: 12 }); + +export const ToolBox = defineComponent(props => { + const hero = props.heroState; + + /** 当前选中的道具图块数字,-1 表示未选中 */ + const selected = ref(-1); + + /** + * 枚举背包内该消耗类型的道具,过滤掉在道具栏隐藏的道具, + * 并按数量从多到少排序,数量相同时按图块数字升序 + * @param consumable 保留的道具是否为可消耗道具 + */ + const collect = (consumable: boolean) => { + const list: IHeroItemState[] = []; + for (const pair of hero.items.iterateItems()) { + const item = pair[1]; + if (item.raw.hideInToolbox === true) continue; + if (item.consumable === consumable) list.push(item); + } + return list.sort((a, b) => + a.count !== b.count ? b.count - a.count : a.num - b.num + ); + }; + + /** 永久道具列表 */ + const permItems = computed(() => collect(false)); + /** 可消耗道具列表 */ + const consItems = computed(() => collect(true)); + + /** 当前选中的道具状态,未选中时为 null */ + const selectedItem = computed | null>(() => { + if (selected.value < 0) return null; + for (const item of permItems.value) { + if (item.num === selected.value) return item; + } + for (const item of consItems.value) { + if (item.num === selected.value) return item; + } + return null; + }); + + /** + * 点击道具行,将点击的道具设为当前选中项 + * @param item 被点击的道具状态 + */ + const clickItem = (item: IHeroItemState) => { + selected.value = item.num; + }; + + return () => { + const current = selectedItem.value; + return ( + + + + + {permItems.value.map((item, i) => { + const isSelected = item.num === selected.value; + return ( + clickItem(item)} + > + + + + + + ); + })} + + + {consItems.value.map((item, i) => { + const isSelected = item.num === selected.value; + return ( + clickItem(item)} + > + + + + + + ); + })} + + + + {current === null && ( + + )} + {current !== null && ( + + )} + + + + ); + }; +}, toolBoxProps); + +export const ToolBoxUI = new GameUI('toolbox', ToolBox);