CP增加解除费用

This commit is contained in:
zhx 2026-06-08 19:40:13 +08:00
parent ae8ae7db55
commit 4ad96f07be
4 changed files with 32 additions and 2 deletions

View File

@ -16,6 +16,7 @@ export interface CPRelationConfigDto {
displayName: string; displayName: string;
maxCountPerUser: number; maxCountPerUser: number;
applicationExpireHours: number; applicationExpireHours: number;
breakupCostCoins: number;
status: string; status: string;
levels: CPLevelDto[]; levels: CPLevelDto[];
updatedAtMs?: number; updatedAtMs?: number;
@ -32,6 +33,7 @@ export interface CPConfigPayload {
relationType: CPRelationType; relationType: CPRelationType;
maxCountPerUser: number; maxCountPerUser: number;
applicationExpireHours: number; applicationExpireHours: number;
breakupCostCoins: number;
status: string; status: string;
levels: Array<{ levels: Array<{
level: number; level: number;
@ -74,6 +76,7 @@ function normalizeRelation(raw: unknown): CPRelationConfigDto {
displayName: stringValue(item.displayName ?? item.display_name) || relationLabel(relationType), displayName: stringValue(item.displayName ?? item.display_name) || relationLabel(relationType),
maxCountPerUser: numberValue(item.maxCountPerUser ?? item.max_count_per_user), maxCountPerUser: numberValue(item.maxCountPerUser ?? item.max_count_per_user),
applicationExpireHours: numberValue(item.applicationExpireHours ?? item.application_expire_hours), applicationExpireHours: numberValue(item.applicationExpireHours ?? item.application_expire_hours),
breakupCostCoins: numberValue(item.breakupCostCoins ?? item.breakup_cost_coins),
status: stringValue(item.status) || "active", status: stringValue(item.status) || "active",
levels: arrayValue(item.levels).map(normalizeLevel), levels: arrayValue(item.levels).map(normalizeLevel),
updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms),

View File

@ -214,7 +214,7 @@
.relationControls { .relationControls {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(220px, 1fr)); grid-template-columns: repeat(3, minmax(180px, 1fr));
gap: var(--space-3); gap: var(--space-3);
} }

View File

@ -149,6 +149,7 @@ export function defaultRelations() {
displayName: cpRelationLabels[relationType], displayName: cpRelationLabels[relationType],
maxCountPerUser: relationType === "cp" ? 1 : 5, maxCountPerUser: relationType === "cp" ? 1 : 5,
applicationExpireHours: defaultExpireHours, applicationExpireHours: defaultExpireHours,
breakupCostCoins: 0,
status: "active", status: "active",
levels: defaultLevels(), levels: defaultLevels(),
updatedAtMs: 0, updatedAtMs: 0,
@ -194,6 +195,7 @@ function relationToForm(relation) {
displayName: relation.displayName || cpRelationLabels[relation.relationType], displayName: relation.displayName || cpRelationLabels[relation.relationType],
maxCountPerUser: relation.relationType === "cp" ? "1" : String(relation.maxCountPerUser || 5), maxCountPerUser: relation.relationType === "cp" ? "1" : String(relation.maxCountPerUser || 5),
applicationExpireHours: String(relation.applicationExpireHours || defaultExpireHours), applicationExpireHours: String(relation.applicationExpireHours || defaultExpireHours),
breakupCostCoins: String(relation.breakupCostCoins || 0),
status: relation.status || "active", status: relation.status || "active",
levels: completeLevels(relation.levels || []).map(levelToForm), levels: completeLevels(relation.levels || []).map(levelToForm),
}; };
@ -214,6 +216,7 @@ function payloadFromForm(form) {
const relationType = relation.relationType; const relationType = relation.relationType;
const maxCountPerUser = relationType === "cp" ? 1 : Number(relation.maxCountPerUser || 0); const maxCountPerUser = relationType === "cp" ? 1 : Number(relation.maxCountPerUser || 0);
const applicationExpireHours = Number(relation.applicationExpireHours || 0); const applicationExpireHours = Number(relation.applicationExpireHours || 0);
const breakupCostCoins = Number(relation.breakupCostCoins || 0);
const status = relation.status === "disabled" ? "disabled" : "active"; const status = relation.status === "disabled" ? "disabled" : "active";
if (!Number.isInteger(maxCountPerUser) || maxCountPerUser <= 0) { if (!Number.isInteger(maxCountPerUser) || maxCountPerUser <= 0) {
throw new Error(`${cpRelationLabels[relationType]}数量必须大于 0`); throw new Error(`${cpRelationLabels[relationType]}数量必须大于 0`);
@ -221,10 +224,14 @@ function payloadFromForm(form) {
if (!Number.isInteger(applicationExpireHours) || applicationExpireHours <= 0) { if (!Number.isInteger(applicationExpireHours) || applicationExpireHours <= 0) {
throw new Error(`${cpRelationLabels[relationType]}申请过期小时必须大于 0`); throw new Error(`${cpRelationLabels[relationType]}申请过期小时必须大于 0`);
} }
if (!Number.isInteger(breakupCostCoins) || breakupCostCoins < 0) {
throw new Error(`${cpRelationLabels[relationType]}解除费用不能小于 0`);
}
return { return {
relationType, relationType,
maxCountPerUser, maxCountPerUser,
applicationExpireHours, applicationExpireHours,
breakupCostCoins,
status, status,
levels: completeFormLevels(relation.levels || [], relationType), levels: completeFormLevels(relation.levels || [], relationType),
}; };

View File

@ -27,6 +27,7 @@ export function CPConfigPage() {
<SummaryItem label="启用类型">{page.activeRelationCount}</SummaryItem> <SummaryItem label="启用类型">{page.activeRelationCount}</SummaryItem>
<SummaryItem label="兄弟上限">{relationMax(relations, "brother")}</SummaryItem> <SummaryItem label="兄弟上限">{relationMax(relations, "brother")}</SummaryItem>
<SummaryItem label="姐妹上限">{relationMax(relations, "sister")}</SummaryItem> <SummaryItem label="姐妹上限">{relationMax(relations, "sister")}</SummaryItem>
<SummaryItem label="CP解除费">{relationBreakupCost(relations, "cp")}</SummaryItem>
</div> </div>
<div className={styles.summaryActions}> <div className={styles.summaryActions}>
<Button <Button
@ -154,6 +155,14 @@ function RelationEditor({ disabled, relation, resourceGroups, updateLevel, updat
value={relation.applicationExpireHours} value={relation.applicationExpireHours}
onChange={(event) => updateRelation(relationType, { applicationExpireHours: event.target.value })} onChange={(event) => updateRelation(relationType, { applicationExpireHours: event.target.value })}
/> />
<TextField
disabled={disabled}
inputProps={{ min: 0, step: 1 }}
label="解除费用金币"
type="number"
value={relation.breakupCostCoins}
onChange={(event) => updateRelation(relationType, { breakupCostCoins: event.target.value })}
/>
</div> </div>
<div className={styles.levelEditorList}> <div className={styles.levelEditorList}>
{(relation.levels || []).map((level) => ( {(relation.levels || []).map((level) => (
@ -254,10 +263,16 @@ function cpConfigColumns() {
width: "minmax(130px, 0.45fr)", width: "minmax(130px, 0.45fr)",
render: (item) => `${formatNumber(item.applicationExpireHours)} 小时`, render: (item) => `${formatNumber(item.applicationExpireHours)} 小时`,
}, },
{
key: "breakupCost",
label: "解除费用",
width: "minmax(130px, 0.45fr)",
render: (item) => `${formatNumber(item.breakupCostCoins)} 金币`,
},
{ {
key: "levels", key: "levels",
label: "等级亲密值", label: "等级亲密值",
width: "minmax(360px, 1.3fr)", width: "minmax(360px, 1.2fr)",
render: (item) => <LevelPreview levels={item.levels || []} />, render: (item) => <LevelPreview levels={item.levels || []} />,
}, },
{ {
@ -310,6 +325,11 @@ function relationMax(relations, relationType) {
return relation ? formatNumber(relation.maxCountPerUser) : "-"; return relation ? formatNumber(relation.maxCountPerUser) : "-";
} }
function relationBreakupCost(relations, relationType) {
const relation = relations.find((item) => item.relationType === relationType);
return relation ? `${formatNumber(relation.breakupCostCoins)} 金币` : "-";
}
function resourceGroupLabel(group, value) { function resourceGroupLabel(group, value) {
if (group) { if (group) {
return group.name || group.groupCode || `资源组 #${group.groupId}`; return group.name || group.groupCode || `资源组 #${group.groupId}`;