feat(frontend): 优化配装页面布局和功能
-调整顶部角色和选项区的布局,增加角色头像和神器信息 - 优化属性区、属性过滤区和指定套装区的样式,采用纵向布局 - 添加角色信息编辑弹窗,可编辑神器、阵型等信息 - 移除按钮区的固定宽度样式,使其自适应布局 - 调整配装结果列表和详情区的样式,优化间距和对齐
This commit is contained in:
@@ -48,10 +48,6 @@ button {
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
|
||||
@@ -1,140 +1,243 @@
|
||||
import React from 'react';
|
||||
import {Avatar, Button, Card, Col, Divider, Input, Layout, Pagination, Row, Select, Table, Tag} from 'antd';
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
Layout,
|
||||
Modal,
|
||||
Pagination,
|
||||
Row,
|
||||
Select,
|
||||
Table,
|
||||
Tag
|
||||
} from 'antd';
|
||||
import {AppstoreOutlined, FilterOutlined, UserOutlined} from '@ant-design/icons';
|
||||
|
||||
const { Content } = Layout;
|
||||
const {Content} = Layout;
|
||||
|
||||
// 静态数据示例
|
||||
const heroList = [
|
||||
{ id: 1, name: '雅娜凯', avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=1' },
|
||||
{ id: 2, name: '艾莉丝', avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2' },
|
||||
{id: 1, name: '雅娜凯', avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=1'},
|
||||
{id: 2, name: '艾莉丝', avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2'},
|
||||
];
|
||||
const hero = heroList[0];
|
||||
const attributes = [
|
||||
{ label: '攻击', value: 1567 },
|
||||
{ label: '防御', value: 1654 },
|
||||
{ label: '生命', value: 24447 },
|
||||
{ label: '速度', value: 188 },
|
||||
{ label: '暴击', value: 49 },
|
||||
{ label: '爆伤', value: 166 },
|
||||
{ label: '命中', value: 46 },
|
||||
{ label: '抵抗', value: 52 },
|
||||
{label: '攻击', value: 1567},
|
||||
{label: '防御', value: 1654},
|
||||
{label: '生命', value: 24447},
|
||||
{label: '速度', value: 188},
|
||||
{label: '暴击', value: 49},
|
||||
{label: '爆伤', value: 166},
|
||||
{label: '命中', value: 46},
|
||||
{label: '抵抗', value: 52},
|
||||
];
|
||||
const setOptions = [
|
||||
{ label: '任意套装', value: 'any' },
|
||||
{ label: '暴击套', value: 'crit' },
|
||||
{ label: '速度套', value: 'speed' },
|
||||
{label: '任意套装', value: 'any'},
|
||||
{label: '暴击套', value: 'crit'},
|
||||
{label: '速度套', value: 'speed'},
|
||||
];
|
||||
const filterOptions = [
|
||||
{ label: '攻击', value: 'atk' },
|
||||
{ label: '防御', value: 'def' },
|
||||
{ label: '生命', value: 'hp' },
|
||||
{ label: '速度', value: 'spd' },
|
||||
{ label: '暴击', value: 'cr' },
|
||||
{ label: '爆伤', value: 'cd' },
|
||||
{ label: '命中', value: 'acc' },
|
||||
{ label: '抵抗', value: 'res' },
|
||||
{label: '攻击', value: 'atk'},
|
||||
{label: '防御', value: 'def'},
|
||||
{label: '生命', value: 'hp'},
|
||||
{label: '速度', value: 'spd'},
|
||||
{label: '暴击', value: 'cr'},
|
||||
{label: '爆伤', value: 'cd'},
|
||||
{label: '命中', value: 'acc'},
|
||||
{label: '抵抗', value: 'res'},
|
||||
];
|
||||
|
||||
const resultColumns = [
|
||||
{ title: '套装', dataIndex: 'set', key: 'set', render: (v: string) => <Tag>{v}</Tag> },
|
||||
{ title: '攻击', dataIndex: 'atk', key: 'atk' },
|
||||
{ title: '防御', dataIndex: 'def', key: 'def' },
|
||||
{ title: '生命', dataIndex: 'hp', key: 'hp' },
|
||||
{ title: '速度', dataIndex: 'spd', key: 'spd' },
|
||||
{ title: '暴击', dataIndex: 'cr', key: 'cr' },
|
||||
{ title: '爆伤', dataIndex: 'cd', key: 'cd' },
|
||||
{ title: '命中', dataIndex: 'acc', key: 'acc' },
|
||||
{ title: '抵抗', dataIndex: 'res', key: 'res' },
|
||||
{title: '套装', dataIndex: 'set', key: 'set', render: (v: string) => <Tag>{v}</Tag>},
|
||||
{title: '攻击', dataIndex: 'atk', key: 'atk'},
|
||||
{title: '防御', dataIndex: 'def', key: 'def'},
|
||||
{title: '生命', dataIndex: 'hp', key: 'hp'},
|
||||
{title: '速度', dataIndex: 'spd', key: 'spd'},
|
||||
{title: '暴击', dataIndex: 'cr', key: 'cr'},
|
||||
{title: '爆伤', dataIndex: 'cd', key: 'cd'},
|
||||
{title: '命中', dataIndex: 'acc', key: 'acc'},
|
||||
{title: '抵抗', dataIndex: 'res', key: 'res'},
|
||||
];
|
||||
const resultData = [
|
||||
{ key: 1, set: '暴击套', atk: 2000, def: 1500, hp: 20000, spd: 200, cr: 100, cd: 250, acc: 30, res: 20 },
|
||||
{ key: 2, set: '速度套', atk: 1800, def: 1400, hp: 21000, spd: 220, cr: 80, cd: 200, acc: 40, res: 30 },
|
||||
{key: 1, set: '暴击套', atk: 2000, def: 1500, hp: 20000, spd: 200, cr: 100, cd: 250, acc: 30, res: 20},
|
||||
{key: 2, set: '速度套', atk: 1800, def: 1400, hp: 21000, spd: 220, cr: 80, cd: 200, acc: 40, res: 30},
|
||||
];
|
||||
|
||||
export default function OptimizerPage() {
|
||||
return (
|
||||
<Layout style={{ minHeight: '100vh' }}>
|
||||
<Content style={{ padding: 16 }}>
|
||||
{/* 顶部角色和选项区 */}
|
||||
<Card style={{ marginBottom: 16 }}>
|
||||
<Row gutter={16}>
|
||||
{/* 角色头像与选择 */}
|
||||
<Col span={4} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<Avatar size={64} src={hero.avatar} icon={<UserOutlined />} />
|
||||
<div style={{ marginTop: 8, fontWeight: 500 }}>{hero.name}</div>
|
||||
<Select style={{ width: '100%', marginTop: 8 }} defaultValue={hero.id}>
|
||||
{heroList.map(h => <Select.Option key={h.id} value={h.id}>{h.name}</Select.Option>)}
|
||||
</Select>
|
||||
</Col>
|
||||
{/* 角色属性 */}
|
||||
<Col span={6}>
|
||||
<div style={{ fontWeight: 500, marginBottom: 8 }}>属性</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
||||
{attributes.map(attr => (
|
||||
<div key={attr.label} style={{ minWidth: 60 }}>{attr.label}: <b>{attr.value}</b></div>
|
||||
))}
|
||||
</div>
|
||||
</Col>
|
||||
{/* 属性过滤 */}
|
||||
<Col span={7}>
|
||||
<div style={{ fontWeight: 500, marginBottom: 8 }}>属性过滤</div>
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||
{filterOptions.map(opt => (
|
||||
<Input key={opt.value} addonBefore={opt.label} placeholder="最小值" style={{ width: 100 }} />
|
||||
))}
|
||||
</div>
|
||||
</Col>
|
||||
{/* 套装选择与操作 */}
|
||||
<Col span={7}>
|
||||
<div style={{ fontWeight: 500, marginBottom: 8 }}>指定套装</div>
|
||||
<Select style={{ width: '100%', marginBottom: 8 }} defaultValue={setOptions[0].value}>
|
||||
{setOptions.map(opt => <Select.Option key={opt.value} value={opt.value}>{opt.label}</Select.Option>)}
|
||||
</Select>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Button type="primary" icon={<AppstoreOutlined />}>开始配装</Button>
|
||||
<Button icon={<FilterOutlined />}>重置筛选</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
const [editVisible, setEditVisible] = React.useState(false);
|
||||
const [editData, setEditData] = React.useState({
|
||||
artifact: '',
|
||||
artifactLevel: 0,
|
||||
formation: '',
|
||||
exclusive: '',
|
||||
star: 5,
|
||||
});
|
||||
|
||||
{/* 配装结果列表区 */}
|
||||
<Card title="配装结果" style={{ marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||
<div>全部排列组合:<b>123,456</b> 条 | 筛选结果:<b>2</b> 条</div>
|
||||
<Pagination size="small" total={2} pageSize={10} current={1} showSizeChanger={false} />
|
||||
</div>
|
||||
<Table
|
||||
dataSource={resultData}
|
||||
columns={resultColumns}
|
||||
pagination={false}
|
||||
scroll={{ x: true }}
|
||||
rowKey="key"
|
||||
/>
|
||||
</Card>
|
||||
return (
|
||||
<Layout style={{minHeight: '100vh'}}>
|
||||
<Content style={{padding: 16}}>
|
||||
{/* 顶部角色和选项区 */}
|
||||
<Card style={{marginBottom: 16, position: 'relative'}}>
|
||||
<Row gutter={16} align="top">
|
||||
{/* 角色头像与神器区 */}
|
||||
<Col span={6} style={{display: 'flex', alignItems: 'flex-start'}}>
|
||||
<Avatar size={64} src={hero.avatar} icon={<UserOutlined/>} style={{marginRight: 16}}/>
|
||||
<div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
|
||||
<div style={{fontWeight: 500}}>{hero.name}</div>
|
||||
<div style={{display: 'flex', alignItems: 'center', gap: 8}}>
|
||||
<span>神器:</span>
|
||||
<span style={{cursor: 'pointer', color: '#1677ff'}}
|
||||
onClick={() => setEditVisible(true)}>
|
||||
{/* 假设有artifactUrl则显示图片,否则暂无 */}
|
||||
{editData.artifact ? <img src={editData.artifact} alt="神器"
|
||||
style={{width: 32, height: 32}}/> : '暂无'}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{display: 'flex', alignItems: 'center', gap: 8}}>
|
||||
<span>专属神器:</span>
|
||||
<span style={{cursor: 'pointer', color: '#1677ff'}}
|
||||
onClick={() => setEditVisible(true)}>
|
||||
{editData.exclusive ? editData.exclusive : '暂无'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
{/* 属性区(纵向) */}
|
||||
<Col span={6}>
|
||||
<div style={{fontWeight: 500, marginBottom: 8}}>属性</div>
|
||||
<div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
|
||||
{attributes.map(attr => (
|
||||
<div key={attr.label}>{attr.label}: <b>{attr.value}</b></div>
|
||||
))}
|
||||
</div>
|
||||
</Col>
|
||||
{/* 属性过滤区(纵向,最小最大值) */}
|
||||
<Col span={6}>
|
||||
<div style={{fontWeight: 500, marginBottom: 8}}>属性过滤</div>
|
||||
<div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
|
||||
{filterOptions.map(opt => (
|
||||
<div key={opt.value} style={{display: 'flex', alignItems: 'center', gap: 4}}>
|
||||
<span style={{minWidth: 36}}>{opt.label}</span>
|
||||
<InputNumber size="small" placeholder="最小值" style={{width: 70}}/>
|
||||
<span>~</span>
|
||||
<InputNumber size="small" placeholder="最大值" style={{width: 70}}/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Col>
|
||||
{/* 指定套装区(纵向,多选) */}
|
||||
<Col span={6} style={{display: 'flex', flexDirection: 'column', justifyContent: 'flex-start'}}>
|
||||
<div style={{fontWeight: 500, marginBottom: 8}}>指定套装</div>
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{width: '100%', marginBottom: 8}}
|
||||
defaultValue={[setOptions[0].value]}
|
||||
>
|
||||
{setOptions.map(opt => <Select.Option key={opt.value}
|
||||
value={opt.value}>{opt.label}</Select.Option>)}
|
||||
</Select>
|
||||
</Col>
|
||||
{/* 按钮区,绝对定位到Card右下角 */}
|
||||
<div style={{position: 'absolute', right: 24, bottom: 16, display: 'flex', gap: 8}}>
|
||||
<Button type="primary" icon={<AppstoreOutlined/>}>开始配装</Button>
|
||||
<Button icon={<FilterOutlined/>}>重置筛选</Button>
|
||||
</div>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
{/* 单个配装详情区 */}
|
||||
<Card title="配装详情">
|
||||
<div style={{ display: 'flex', gap: 24 }}>
|
||||
<div>
|
||||
<Avatar size={48} src={hero.avatar} />
|
||||
<div style={{ marginTop: 8 }}>{hero.name}</div>
|
||||
</div>
|
||||
<Divider type="vertical" style={{ height: 80 }} />
|
||||
<div>
|
||||
<div>套装:<Tag color="blue">暴击套</Tag></div>
|
||||
<div>攻击:2000,防御:1500,生命:20000,速度:200</div>
|
||||
<div>暴击:100%,爆伤:250%,命中:30%,抵抗:20%</div>
|
||||
</div>
|
||||
<Divider type="vertical" style={{ height: 80 }} />
|
||||
<div>
|
||||
<Button type="primary">保存配置</Button>
|
||||
<Button style={{ marginLeft: 8 }}>删除配置</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
{/* 配装结果列表区 */}
|
||||
<Card title="配装结果" style={{marginBottom: 16}}>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom: 8}}>
|
||||
<div>全部排列组合:<b>123,456</b> 条 | 筛选结果:<b>2</b> 条</div>
|
||||
<Pagination size="small" total={2} pageSize={10} current={1} showSizeChanger={false}/>
|
||||
</div>
|
||||
<Table
|
||||
dataSource={resultData}
|
||||
columns={resultColumns}
|
||||
pagination={false}
|
||||
scroll={{x: true}}
|
||||
rowKey="key"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 单个配装详情区 */}
|
||||
<Card title="配装详情">
|
||||
<div style={{display: 'flex', gap: 24}}>
|
||||
<div>
|
||||
<Avatar size={48} src={hero.avatar}/>
|
||||
<div style={{marginTop: 8}}>{hero.name}</div>
|
||||
</div>
|
||||
<Divider type="vertical" style={{height: 80}}/>
|
||||
<div>
|
||||
<div>套装:<Tag color="blue">暴击套</Tag></div>
|
||||
<div>攻击:2000,防御:1500,生命:20000,速度:200</div>
|
||||
<div>暴击:100%,爆伤:250%,命中:30%,抵抗:20%</div>
|
||||
</div>
|
||||
<Divider type="vertical" style={{height: 80}}/>
|
||||
<div>
|
||||
<Button type="primary">保存配置</Button>
|
||||
<Button style={{marginLeft: 8}}>删除配置</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 角色信息编辑弹窗 */}
|
||||
<Modal
|
||||
title="角色信息编辑"
|
||||
open={editVisible}
|
||||
onCancel={() => setEditVisible(false)}
|
||||
onOk={() => setEditVisible(false)}
|
||||
>
|
||||
<div style={{display: 'flex', flexDirection: 'column', gap: 16}}>
|
||||
<div>
|
||||
<span>神器:</span>
|
||||
<Input
|
||||
value={editData.artifact}
|
||||
onChange={e => setEditData({...editData, artifact: e.target.value})}
|
||||
placeholder="神器图片URL或名称"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>神器等级:</span>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={20}
|
||||
value={editData.artifactLevel}
|
||||
onChange={v => setEditData({...editData, artifactLevel: v || 0})}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>阵型:</span>
|
||||
<Input
|
||||
value={editData.formation}
|
||||
onChange={e => setEditData({...editData, formation: e.target.value})}
|
||||
placeholder="如:前排/后排"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>专属装备:</span>
|
||||
<Input
|
||||
value={editData.exclusive}
|
||||
onChange={e => setEditData({...editData, exclusive: e.target.value})}
|
||||
placeholder="如:+9速度"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>星数:</span>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={6}
|
||||
value={editData.star}
|
||||
onChange={v => setEditData({...editData, star: v || 1})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user