add relationship in front

This commit is contained in:
bridge
2025-12-01 01:16:02 +08:00
parent a5d2b192fe
commit 1aaa4d4094
4 changed files with 100 additions and 7 deletions

View File

@@ -694,6 +694,7 @@ class CreateAvatarRequest(BaseModel):
auxiliary_id: Optional[int] = None
alignment: Optional[str] = None
appearance: Optional[int] = None
relations: Optional[List[dict]] = None
class DeleteAvatarRequest(BaseModel):
avatar_id: str
@@ -883,7 +884,8 @@ def create_avatar(req: CreateAvatarRequest):
technique=req.technique_id,
weapon=req.weapon_id,
auxiliary=req.auxiliary_id,
appearance=req.appearance
appearance=req.appearance,
relations=req.relations
)
if req.pic_id is not None:
@@ -899,10 +901,9 @@ def create_avatar(req: CreateAvatarRequest):
if req.appearance is not None:
avatar.appearance = get_appearance_by_level(req.appearance)
# 清空系统自动生成的关系,保持用户自定义角色独立
existing_relations = list(getattr(avatar, "relations", {}).keys())
for other in existing_relations:
avatar.clear_relation(other)
# 关系已经在 create_avatar_from_request 中根据参数设置好了,
# 且该函数内部调用 MortalPlanner 时已经指定 allow_relations=False不会生成随机关系。
# 因此这里不需要再清空关系,否则会把自己选的关系删掉。
if req.alignment:
avatar.alignment = Alignment.from_str(req.alignment)

View File

@@ -859,6 +859,7 @@ def create_avatar_from_request(
auxiliary: Union[str, int, Auxiliary, None] = None,
personas: Union[str, int, Persona, List[Union[str, int, Persona]], None] = None,
appearance: Optional[int] = None,
relations: Optional[List[Dict[str, str]]] = None,
) -> Avatar:
"""
供前端使用的角色创建入口:支持字符串/ID 参数,且默认不生成亲友关系。
@@ -927,4 +928,28 @@ def create_avatar_from_request(
overrides=overrides if overrides else None,
)
if relations:
for rel_item in relations:
target_id = rel_item.get('target_id')
rel_type = rel_item.get('relation')
if not target_id or not rel_type:
continue
# 尝试转为字符串ID
t_id_str = str(target_id)
target = world.avatar_manager.avatars.get(t_id_str)
if not target:
continue
# 解析关系
rel_enum = None
for r in Relation:
if r.value == rel_type:
rel_enum = r
break
if rel_enum:
avatar.set_relation(target, rel_enum)
return avatar

View File

@@ -47,6 +47,7 @@ export interface CreateAvatarParams {
auxiliary_id?: number;
alignment?: string;
appearance?: number;
relations?: Array<{ target_id: string; relation: string }>;
}
export interface PhenomenonDTO {

View File

@@ -38,9 +38,21 @@ const createForm = ref<CreateAvatarParams>({
weapon_id: undefined,
auxiliary_id: undefined,
alignment: undefined,
appearance: 7
appearance: 7,
relations: []
})
const relationOptions = [
{ label: '父母', value: 'parent' },
{ label: '子女', value: 'child' },
{ label: '兄弟姐妹', value: 'sibling' },
{ label: '师傅', value: 'master' },
{ label: '徒弟', value: 'apprentice' },
{ label: '道侣', value: 'lovers' },
{ label: '朋友', value: 'friend' },
{ label: '仇人', value: 'enemy' }
]
// --- Delete Avatar State ---
const avatarList = ref<SimpleAvatarDTO[]>([])
const avatarSearch = ref('')
@@ -114,6 +126,24 @@ const alignmentOptions = computed(() => {
}))
})
const avatarOptions = computed(() => {
return avatarList.value.map(a => ({
label: `[${a.sect_name}] ${a.name}`,
value: a.id
}))
})
function addRelation() {
if (!createForm.value.relations) {
createForm.value.relations = []
}
createForm.value.relations.push({ target_id: '', relation: 'friend' })
}
function removeRelation(index: number) {
createForm.value.relations?.splice(index, 1)
}
// --- Actions ---
async function fetchSaves() {
@@ -214,7 +244,8 @@ async function handleCreateAvatar() {
weapon_id: undefined,
auxiliary_id: undefined,
alignment: undefined,
appearance: 7
appearance: 7,
relations: []
}
} catch (e) {
message.error('创建失败: ' + String(e))
@@ -247,6 +278,7 @@ function switchTab(tab: typeof activeTab.value) {
fetchSaves()
} else if (tab === 'create') {
fetchGameData()
fetchAvatarList()
} else if (tab === 'delete') {
fetchAvatarList()
}
@@ -395,6 +427,27 @@ onMounted(() => {
<n-form-item label="辅助装备">
<n-select v-model:value="createForm.auxiliary_id" :options="auxiliaryOptions" placeholder="选择辅助装备 (可留空)" clearable />
</n-form-item>
<n-form-item label="人际关系">
<div class="relations-container">
<div v-for="(rel, index) in createForm.relations" :key="index" class="relation-row">
<n-select
v-model:value="rel.target_id"
:options="avatarOptions"
placeholder="选择角色"
filterable
style="width: 160px"
/>
<n-select
v-model:value="rel.relation"
:options="relationOptions"
placeholder="关系"
style="width: 100px"
/>
<n-button @click="removeRelation(index)" circle size="small" type="error">-</n-button>
</div>
<n-button @click="addRelation" size="small" dashed style="width: 100%">+ 添加关系</n-button>
</div>
</n-form-item>
<div class="actions">
<n-button type="primary" @click="handleCreateAvatar" block>创建角色</n-button>
</div>
@@ -753,4 +806,17 @@ onMounted(() => {
text-align: right;
color: #ddd;
}
.relations-container {
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
}
.relation-row {
display: flex;
gap: 8px;
align-items: center;
}
</style>