refactor: add store mixin into city regions & refactor buying action
This commit is contained in:
@@ -5,9 +5,8 @@ from typing import TYPE_CHECKING, Tuple, Any
|
|||||||
from src.classes.action import InstantAction
|
from src.classes.action import InstantAction
|
||||||
from src.classes.event import Event
|
from src.classes.event import Event
|
||||||
from src.classes.region import CityRegion
|
from src.classes.region import CityRegion
|
||||||
from src.classes.elixir import Elixir, get_elixirs_by_realm
|
from src.classes.elixir import Elixir
|
||||||
from src.classes.prices import prices
|
from src.classes.prices import prices
|
||||||
from src.classes.cultivation import Realm
|
|
||||||
from src.classes.weapon import Weapon
|
from src.classes.weapon import Weapon
|
||||||
from src.classes.auxiliary import Auxiliary
|
from src.classes.auxiliary import Auxiliary
|
||||||
from src.classes.material import Material
|
from src.classes.material import Material
|
||||||
@@ -28,8 +27,7 @@ class Buy(InstantAction):
|
|||||||
|
|
||||||
ACTION_NAME = "购买"
|
ACTION_NAME = "购买"
|
||||||
EMOJI = "💸"
|
EMOJI = "💸"
|
||||||
elixir_names_str = ", ".join([e.name for e in get_elixirs_by_realm(Realm.Qi_Refinement)])
|
DESC = f"在城镇购买物品/装备/丹药。"
|
||||||
DESC = f"在城镇购买物品/装备(丹药购买后将立即服用)。可选丹药:{elixir_names_str}"
|
|
||||||
DOABLES_REQUIREMENTS = "在城镇且金钱足够"
|
DOABLES_REQUIREMENTS = "在城镇且金钱足够"
|
||||||
PARAMS = {"target_name": "str"}
|
PARAMS = {"target_name": "str"}
|
||||||
|
|
||||||
@@ -42,6 +40,15 @@ class Buy(InstantAction):
|
|||||||
if not res.is_valid:
|
if not res.is_valid:
|
||||||
return False, f"未知物品: {target_name}"
|
return False, f"未知物品: {target_name}"
|
||||||
|
|
||||||
|
# 检查商店是否售卖
|
||||||
|
# 必须是 StoreMixin (CityRegion 混入了 StoreMixin)
|
||||||
|
if hasattr(region, "is_selling"):
|
||||||
|
if not region.is_selling(res.obj.name):
|
||||||
|
return False, f"{region.name} 不出售 {res.obj.name}"
|
||||||
|
else:
|
||||||
|
# 如果不是商店区域(虽然前面已经检查了 CityRegion,但为了安全)
|
||||||
|
return False, "该区域没有商店"
|
||||||
|
|
||||||
# 核心逻辑委托给 Avatar
|
# 核心逻辑委托给 Avatar
|
||||||
return self.avatar.can_buy_item(res.obj)
|
return self.avatar.can_buy_item(res.obj)
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class Auxiliary(Item):
|
|||||||
full_desc = f"{full_desc} (已吞噬魂魄:{souls})"
|
full_desc = f"{full_desc} (已吞噬魂魄:{souls})"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"desc": full_desc,
|
"desc": full_desc,
|
||||||
"grade": self.realm.value,
|
"grade": self.realm.value,
|
||||||
|
|||||||
@@ -185,10 +185,6 @@ class InventoryMixin:
|
|||||||
|
|
||||||
# 2. 丹药特殊检查
|
# 2. 丹药特殊检查
|
||||||
if isinstance(obj, Elixir):
|
if isinstance(obj, Elixir):
|
||||||
# 商店业务规则:当前仅开放练气期丹药购买
|
|
||||||
if obj.realm != Realm.Qi_Refinement:
|
|
||||||
return False, "当前仅开放练气期丹药购买"
|
|
||||||
|
|
||||||
# 境界限制
|
# 境界限制
|
||||||
if obj.realm > self.cultivation_progress.realm:
|
if obj.realm > self.cultivation_progress.realm:
|
||||||
return False, f"境界不足,无法承受药力 ({obj.realm.value})"
|
return False, f"境界不足,无法承受药力 ({obj.realm.value})"
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class Elixir(Item):
|
|||||||
|
|
||||||
def get_structured_info(self) -> dict:
|
def get_structured_info(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"desc": self.desc,
|
"desc": self.desc,
|
||||||
"grade": self.realm.value,
|
"grade": self.realm.value,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from src.classes.animal import Animal, animals_by_id
|
|||||||
from src.classes.plant import Plant, plants_by_id
|
from src.classes.plant import Plant, plants_by_id
|
||||||
from src.classes.lode import Lode, lodes_by_id
|
from src.classes.lode import Lode, lodes_by_id
|
||||||
from src.classes.sect import sects_by_name
|
from src.classes.sect import sects_by_name
|
||||||
|
from src.classes.store import StoreMixin
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from src.classes.avatar import Avatar
|
from src.classes.avatar import Avatar
|
||||||
@@ -212,18 +213,48 @@ class CultivateRegion(Region):
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(eq=False)
|
@dataclass(eq=False)
|
||||||
class CityRegion(Region):
|
class CityRegion(Region, StoreMixin):
|
||||||
"""城市区域"""
|
"""城市区域"""
|
||||||
|
sell_items: str = field(default="[]")
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
super().__post_init__()
|
||||||
|
try:
|
||||||
|
import ast
|
||||||
|
items_list = ast.literal_eval(self.sell_items)
|
||||||
|
if isinstance(items_list, list):
|
||||||
|
self.init_store(items_list)
|
||||||
|
else:
|
||||||
|
self.init_store([])
|
||||||
|
except Exception:
|
||||||
|
self.init_store([])
|
||||||
|
|
||||||
def get_region_type(self) -> str:
|
def get_region_type(self) -> str:
|
||||||
return "city"
|
return "city"
|
||||||
|
|
||||||
def _get_desc(self) -> str:
|
def _get_desc(self) -> str:
|
||||||
|
store_info = self.get_store_info()
|
||||||
|
if store_info:
|
||||||
|
return f"({store_info})"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"城市区域:{self.name} - {self.desc}"
|
store_info = self.get_store_info()
|
||||||
|
desc_part = f" | {store_info}" if store_info else ""
|
||||||
|
return f"城市区域:{self.name} - {self.desc}{desc_part}"
|
||||||
|
|
||||||
def get_structured_info(self) -> dict:
|
def get_structured_info(self) -> dict:
|
||||||
info = super().get_structured_info()
|
info = super().get_structured_info()
|
||||||
info["type_name"] = "城市区域"
|
info["type_name"] = "城市区域"
|
||||||
|
|
||||||
|
store_items_info = []
|
||||||
|
if hasattr(self, 'store_items'):
|
||||||
|
from src.classes.prices import prices
|
||||||
|
for item in self.store_items:
|
||||||
|
item_info = item.get_structured_info()
|
||||||
|
# Inject price
|
||||||
|
item_info["price"] = prices.get_buying_price(item, None)
|
||||||
|
store_items_info.append(item_info)
|
||||||
|
|
||||||
|
info["store_items"] = store_items_info
|
||||||
return info
|
return info
|
||||||
|
|||||||
72
src/classes/store.py
Normal file
72
src/classes/store.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
from src.utils.resolution import resolve_query
|
||||||
|
from src.classes.elixir import Elixir
|
||||||
|
from src.classes.weapon import Weapon
|
||||||
|
from src.classes.auxiliary import Auxiliary
|
||||||
|
from src.classes.prices import prices
|
||||||
|
|
||||||
|
class StoreMixin:
|
||||||
|
"""
|
||||||
|
商店功能混入类
|
||||||
|
赋予区域售卖物品的能力
|
||||||
|
"""
|
||||||
|
|
||||||
|
def init_store(self, item_names: list[str]):
|
||||||
|
"""
|
||||||
|
初始化商店物品
|
||||||
|
:param item_names: 物品名称列表
|
||||||
|
"""
|
||||||
|
self.store_items = []
|
||||||
|
if not item_names:
|
||||||
|
return
|
||||||
|
|
||||||
|
for name in item_names:
|
||||||
|
# 期望类型:丹药、武器、辅助
|
||||||
|
res = resolve_query(name, expected_types=[Elixir, Weapon, Auxiliary])
|
||||||
|
if res.is_valid and res.obj:
|
||||||
|
self.store_items.append(res.obj)
|
||||||
|
|
||||||
|
def get_store_info(self) -> str:
|
||||||
|
"""
|
||||||
|
获取商店信息描述
|
||||||
|
例如:交易:练气剑、练气刀(100灵石);练气破境丹(50灵石)
|
||||||
|
"""
|
||||||
|
# 如果没有初始化或者没有物品
|
||||||
|
if not hasattr(self, 'store_items') or not self.store_items:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# 按价格分组
|
||||||
|
items_by_price = defaultdict(list)
|
||||||
|
for item in self.store_items:
|
||||||
|
# 获取该物品的标准购买价格(作为标价,买家为 None)
|
||||||
|
price = prices.get_buying_price(item, None)
|
||||||
|
items_by_price[price].append(item.name)
|
||||||
|
|
||||||
|
if not items_by_price:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# 格式化输出
|
||||||
|
parts = []
|
||||||
|
# 按价格从低到高排序
|
||||||
|
for price in sorted(items_by_price.keys()):
|
||||||
|
names = items_by_price[price]
|
||||||
|
# 去重并保持顺序 (Python 3.7+ dict key insertion order)
|
||||||
|
unique_names = list(dict.fromkeys(names))
|
||||||
|
names_str = "、".join(unique_names)
|
||||||
|
parts.append(f"{names_str}({price}灵石)")
|
||||||
|
|
||||||
|
return "交易:" + ";".join(parts)
|
||||||
|
|
||||||
|
def is_selling(self, item_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
检查商店是否出售该物品
|
||||||
|
"""
|
||||||
|
if not hasattr(self, 'store_items'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 简单的名字匹配 (Assuming item.name is what we look for)
|
||||||
|
# 如果需要更严格的匹配(如 normalized name),需要在这里处理,
|
||||||
|
# 但通常 resolve_query 解析出的 obj.name 是标准名。
|
||||||
|
return any(item.name == item_name for item in self.store_items)
|
||||||
@@ -48,6 +48,7 @@ class Weapon(Item):
|
|||||||
|
|
||||||
def get_structured_info(self) -> dict:
|
def get_structured_info(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"desc": self.desc,
|
"desc": self.desc,
|
||||||
"grade": self.realm.value,
|
"grade": self.realm.value,
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ def _load_and_assign_regions(game_map: Map, region_coords: dict[int, list[tuple[
|
|||||||
elif type_tag == "cultivate":
|
elif type_tag == "cultivate":
|
||||||
params["essence_type"] = EssenceType.from_str(get_str(row, "root_type"))
|
params["essence_type"] = EssenceType.from_str(get_str(row, "root_type"))
|
||||||
params["essence_density"] = get_int(row, "root_density")
|
params["essence_density"] = get_int(row, "root_density")
|
||||||
|
elif type_tag == "city":
|
||||||
|
sell_items_str = get_str(row, "sell_items")
|
||||||
|
if sell_items_str:
|
||||||
|
params["sell_items"] = sell_items_str
|
||||||
elif type_tag == "sect":
|
elif type_tag == "sect":
|
||||||
sect_id = get_int(row, "sect_id")
|
sect_id = get_int(row, "sect_id")
|
||||||
params["sect_id"] = sect_id
|
params["sect_id"] = sect_id
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
id,name,desc
|
id,name,desc,sell_items
|
||||||
ID必须以3开头,,
|
ID必须以3开头,,,
|
||||||
301,青云城,繁华都市,人烟稠密,商贾云集。此地是交易天材地宝、寻找机缘的重要场所。
|
301,青云城,繁华都市,人烟稠密,商贾云集。此地是交易天材地宝、寻找机缘的重要场所。,"['练气破境丹', '练气长生丹']"
|
||||||
302,沙月城,沙漠绿洲中的贸易重镇,各路商队在此集结,是修士补给和交流的重要据点。
|
302,沙月城,沙漠绿洲中的贸易重镇,各路商队在此集结,是修士补给和交流的重要据点。,"['练气燃血丹', '练气回春丹']"
|
||||||
303,翠林城,森林深处的修仙重镇,众多修士在此栖居,是修炼和炼宝的理想之地。
|
303,翠林城,森林深处的修仙重镇,众多修士在此栖居,是修炼和炼宝的理想之地。,"['练气剑', '练气刀', '练气枪']"
|
||||||
304,沧澜城,坐落于大河入海口的三角洲,百川归海,水运昌隆,是水系修士往来最为频繁的宝地。
|
304,沧澜城,坐落于大河入海口的三角洲,百川归海,水运昌隆,是水系修士往来最为频繁的宝地。,"['练气棍', '练气扇', '练气鞭']"
|
||||||
305,揽月城,屹立于连绵群山之巅,终年云雾缭绕,灵气纯净,是苦修之士感悟天道的绝佳场所。
|
305,揽月城,屹立于连绵群山之巅,终年云雾缭绕,灵气纯净,是苦修之士感悟天道的绝佳场所。,"['练气琴', '练气笛', '练气暗器']"
|
||||||
|
|||||||
|
@@ -12,6 +12,13 @@ def test_buy_item_success(avatar_in_city, mock_item_data):
|
|||||||
materials_mock = mock_item_data["materials"]
|
materials_mock = mock_item_data["materials"]
|
||||||
test_material = mock_item_data["obj_material"]
|
test_material = mock_item_data["obj_material"]
|
||||||
|
|
||||||
|
# 模拟 CityRegion.is_selling 方法
|
||||||
|
# 直接在 avatar_in_city.tile.region 上 mock 或者动态添加属性
|
||||||
|
# 由于 avatar_in_city 使用了 mock_region,我们需要确保它有 is_selling
|
||||||
|
# 假设 conftest 中 avatar_in_city.tile.region 是一个 CityRegion 实例或者 Mock
|
||||||
|
|
||||||
|
# 我们这里动态 patch is_selling
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=True) as mock_is_selling:
|
||||||
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
patch("src.utils.resolution.materials_by_name", materials_mock):
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
@@ -20,6 +27,7 @@ def test_buy_item_success(avatar_in_city, mock_item_data):
|
|||||||
# 1. 检查是否可购买
|
# 1. 检查是否可购买
|
||||||
can_start, reason = action.can_start("铁矿石")
|
can_start, reason = action.can_start("铁矿石")
|
||||||
assert can_start is True
|
assert can_start is True
|
||||||
|
mock_is_selling.assert_called_with("铁矿石")
|
||||||
|
|
||||||
# 2. 执行购买
|
# 2. 执行购买
|
||||||
initial_money = avatar_in_city.magic_stone
|
initial_money = avatar_in_city.magic_stone
|
||||||
@@ -38,6 +46,7 @@ def test_buy_elixir_success(avatar_in_city, mock_item_data):
|
|||||||
materials_mock = mock_item_data["materials"]
|
materials_mock = mock_item_data["materials"]
|
||||||
test_elixir = mock_item_data["obj_elixir"]
|
test_elixir = mock_item_data["obj_elixir"]
|
||||||
|
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=True):
|
||||||
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
patch("src.utils.resolution.materials_by_name", materials_mock):
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
@@ -59,6 +68,22 @@ def test_buy_elixir_success(avatar_in_city, mock_item_data):
|
|||||||
assert len(avatar_in_city.elixirs) == 1
|
assert len(avatar_in_city.elixirs) == 1
|
||||||
assert avatar_in_city.elixirs[0].elixir.name == "聚气丹"
|
assert avatar_in_city.elixirs[0].elixir.name == "聚气丹"
|
||||||
|
|
||||||
|
def test_buy_fail_item_not_sold(avatar_in_city, mock_item_data):
|
||||||
|
"""测试商品不在商店售卖列表中"""
|
||||||
|
elixirs_mock = mock_item_data["elixirs"]
|
||||||
|
materials_mock = mock_item_data["materials"]
|
||||||
|
|
||||||
|
# Mock is_selling 返回 False
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=False):
|
||||||
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
|
action = Buy(avatar_in_city, avatar_in_city.world)
|
||||||
|
can_start, reason = action.can_start("铁矿石")
|
||||||
|
|
||||||
|
assert can_start is False
|
||||||
|
assert "不出售" in reason
|
||||||
|
|
||||||
def test_buy_fail_not_in_city(dummy_avatar, mock_item_data):
|
def test_buy_fail_not_in_city(dummy_avatar, mock_item_data):
|
||||||
"""测试不在城市无法购买"""
|
"""测试不在城市无法购买"""
|
||||||
elixirs_mock = mock_item_data["elixirs"]
|
elixirs_mock = mock_item_data["elixirs"]
|
||||||
@@ -83,6 +108,7 @@ def test_buy_fail_no_money(avatar_in_city, mock_item_data):
|
|||||||
|
|
||||||
avatar_in_city.magic_stone = 0 # 没钱
|
avatar_in_city.magic_stone = 0 # 没钱
|
||||||
|
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=True):
|
||||||
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
patch("src.utils.resolution.materials_by_name", materials_mock):
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
@@ -119,6 +145,7 @@ def test_buy_elixir_fail_high_level_restricted(avatar_in_city, mock_item_data):
|
|||||||
assert avatar_in_city.cultivation_progress.realm == Realm.Qi_Refinement
|
assert avatar_in_city.cultivation_progress.realm == Realm.Qi_Refinement
|
||||||
assert high_level_elixir.realm == Realm.Foundation_Establishment
|
assert high_level_elixir.realm == Realm.Foundation_Establishment
|
||||||
|
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=True):
|
||||||
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
patch("src.utils.resolution.materials_by_name", materials_mock):
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
@@ -126,7 +153,8 @@ def test_buy_elixir_fail_high_level_restricted(avatar_in_city, mock_item_data):
|
|||||||
can_start, reason = action.can_start("筑基丹")
|
can_start, reason = action.can_start("筑基丹")
|
||||||
|
|
||||||
assert can_start is False
|
assert can_start is False
|
||||||
assert "当前仅开放练气期丹药购买" in reason
|
# 错误信息变了,现在是通用的境界限制
|
||||||
|
assert "境界不足" in reason
|
||||||
|
|
||||||
def test_buy_elixir_fail_duplicate_active(avatar_in_city, mock_item_data):
|
def test_buy_elixir_fail_duplicate_active(avatar_in_city, mock_item_data):
|
||||||
"""测试药效尚存无法重复购买"""
|
"""测试药效尚存无法重复购买"""
|
||||||
@@ -139,6 +167,7 @@ def test_buy_elixir_fail_duplicate_active(avatar_in_city, mock_item_data):
|
|||||||
|
|
||||||
avatar_in_city.elixirs.append(consumed)
|
avatar_in_city.elixirs.append(consumed)
|
||||||
|
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=True):
|
||||||
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
patch("src.utils.resolution.materials_by_name", materials_mock):
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
@@ -158,17 +187,6 @@ def test_buy_weapon_trade_in(avatar_in_city, mock_item_data):
|
|||||||
materials_mock = mock_item_data["materials"]
|
materials_mock = mock_item_data["materials"]
|
||||||
new_weapon = mock_item_data["obj_weapon"]
|
new_weapon = mock_item_data["obj_weapon"]
|
||||||
|
|
||||||
# 手动添加武器到 materials_mock (Buy logic looks up weapons in materials too? Or just assumes unique names?)
|
|
||||||
# Buy code checks `get_item_by_name` which checks all dicts.
|
|
||||||
# In test_buy_action we only mocked elixirs and materials.
|
|
||||||
# Let's ensure '青云剑' is findable. Ideally it should be in weapons_by_name but maybe Buy logic is flexible?
|
|
||||||
# Original test put it in materials_mock["青云剑"] = new_weapon. Let's follow that pattern for now or better: mock weapons too.
|
|
||||||
|
|
||||||
# Wait, original test: materials_mock["青云剑"] = new_weapon
|
|
||||||
# But `src.utils.resolution.get_item_by_name` checks materials, weapons, auxiliaries.
|
|
||||||
# Let's do it properly by mocking weapons_by_name as well if possible, or just stick to materials for simplicity if Buy allows.
|
|
||||||
# Buy uses `get_item_by_name`.
|
|
||||||
|
|
||||||
materials_mock["青云剑"] = new_weapon
|
materials_mock["青云剑"] = new_weapon
|
||||||
|
|
||||||
# 构造旧武器
|
# 构造旧武器
|
||||||
@@ -190,6 +208,7 @@ def test_buy_weapon_trade_in(avatar_in_city, mock_item_data):
|
|||||||
|
|
||||||
expected_money = initial_money - buy_cost + sell_refund
|
expected_money = initial_money - buy_cost + sell_refund
|
||||||
|
|
||||||
|
with patch.object(avatar_in_city.tile.region, 'is_selling', return_value=True):
|
||||||
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
with patch("src.utils.resolution.elixirs_by_name", elixirs_mock), \
|
||||||
patch("src.utils.resolution.materials_by_name", materials_mock):
|
patch("src.utils.resolution.materials_by_name", materials_mock):
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,21 @@ function jumpToAvatar(id: string) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Store Items -->
|
||||||
|
<div class="section" v-if="data.store_items?.length">
|
||||||
|
<div class="section-title">坊市交易</div>
|
||||||
|
<div class="list">
|
||||||
|
<EntityRow
|
||||||
|
v-for="item in data.store_items"
|
||||||
|
:key="item.id || item.name"
|
||||||
|
:item="item"
|
||||||
|
:meta="`${item.price}灵石`"
|
||||||
|
compact
|
||||||
|
@click="showDetail(item)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const typeMap: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const displayType = computed(() => {
|
const displayType = computed(() => {
|
||||||
|
if (props.item?.type_name) return props.item.type_name; // 优先使用后端传回的中文类型名
|
||||||
if (!props.item?.type) return '';
|
if (!props.item?.type) return '';
|
||||||
return typeMap[props.item.type] || props.item.type;
|
return typeMap[props.item.type] || props.item.type;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface EffectEntity extends EntityBase {
|
|||||||
grade?: string;
|
grade?: string;
|
||||||
rarity?: string; // e.g., 'SSR', 'R', '上品'
|
rarity?: string; // e.g., 'SSR', 'R', '上品'
|
||||||
type?: string;
|
type?: string;
|
||||||
|
type_name?: string; // 新增:中文类型名,如"丹药"、"破境"等
|
||||||
color?: string | number[]; // 某些实体自带颜色
|
color?: string | number[]; // 某些实体自带颜色
|
||||||
drops?: EffectEntity[];
|
drops?: EffectEntity[];
|
||||||
hq_name?: string;
|
hq_name?: string;
|
||||||
@@ -167,6 +168,7 @@ export interface RegionDetail extends EntityBase {
|
|||||||
animals: EffectEntity[];
|
animals: EffectEntity[];
|
||||||
plants: EffectEntity[];
|
plants: EffectEntity[];
|
||||||
lodes: EffectEntity[];
|
lodes: EffectEntity[];
|
||||||
|
store_items?: (EffectEntity & { price: number })[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 天地灵机 ---
|
// --- 天地灵机 ---
|
||||||
|
|||||||
Reference in New Issue
Block a user