feat(frontend): 优化配装页面布局和功能

-调整顶部角色和选项区的布局,增加角色头像和神器信息
- 优化属性区、属性过滤区和指定套装区的样式,采用纵向布局
- 添加角色信息编辑弹窗,可编辑神器、阵型等信息
- 移除按钮区的固定宽度样式,使其自适应布局
- 调整配装结果列表和详情区的样式,优化间距和对齐
This commit is contained in:
hu xiaotong
2025-07-04 17:04:04 +08:00
parent 721770f70a
commit 41814a2bc8
2 changed files with 219 additions and 120 deletions

View File

@@ -48,10 +48,6 @@ button {
button:hover { button:hover {
border-color: #646cff; border-color: #646cff;
} }
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {
:root { :root {

View File

@@ -1,139 +1,242 @@
import React from 'react'; 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'; import {AppstoreOutlined, FilterOutlined, UserOutlined} from '@ant-design/icons';
const { Content } = Layout; const {Content} = Layout;
// 静态数据示例 // 静态数据示例
const heroList = [ const heroList = [
{ id: 1, name: '雅娜凯', avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=1' }, {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: 2, name: '艾莉丝', avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2'},
]; ];
const hero = heroList[0]; const hero = heroList[0];
const attributes = [ const attributes = [
{ label: '攻击', value: 1567 }, {label: '攻击', value: 1567},
{ label: '防御', value: 1654 }, {label: '防御', value: 1654},
{ label: '生命', value: 24447 }, {label: '生命', value: 24447},
{ label: '速度', value: 188 }, {label: '速度', value: 188},
{ label: '暴击', value: 49 }, {label: '暴击', value: 49},
{ label: '爆伤', value: 166 }, {label: '爆伤', value: 166},
{ label: '命中', value: 46 }, {label: '命中', value: 46},
{ label: '抵抗', value: 52 }, {label: '抵抗', value: 52},
]; ];
const setOptions = [ const setOptions = [
{ label: '任意套装', value: 'any' }, {label: '任意套装', value: 'any'},
{ label: '暴击套', value: 'crit' }, {label: '暴击套', value: 'crit'},
{ label: '速度套', value: 'speed' }, {label: '速度套', value: 'speed'},
]; ];
const filterOptions = [ const filterOptions = [
{ label: '攻击', value: 'atk' }, {label: '攻击', value: 'atk'},
{ label: '防御', value: 'def' }, {label: '防御', value: 'def'},
{ label: '生命', value: 'hp' }, {label: '生命', value: 'hp'},
{ label: '速度', value: 'spd' }, {label: '速度', value: 'spd'},
{ label: '暴击', value: 'cr' }, {label: '暴击', value: 'cr'},
{ label: '爆伤', value: 'cd' }, {label: '爆伤', value: 'cd'},
{ label: '命中', value: 'acc' }, {label: '命中', value: 'acc'},
{ label: '抵抗', value: 'res' }, {label: '抵抗', value: 'res'},
]; ];
const resultColumns = [ const resultColumns = [
{ title: '套装', dataIndex: 'set', key: 'set', render: (v: string) => <Tag>{v}</Tag> }, {title: '套装', dataIndex: 'set', key: 'set', render: (v: string) => <Tag>{v}</Tag>},
{ title: '攻击', dataIndex: 'atk', key: 'atk' }, {title: '攻击', dataIndex: 'atk', key: 'atk'},
{ title: '防御', dataIndex: 'def', key: 'def' }, {title: '防御', dataIndex: 'def', key: 'def'},
{ title: '生命', dataIndex: 'hp', key: 'hp' }, {title: '生命', dataIndex: 'hp', key: 'hp'},
{ title: '速度', dataIndex: 'spd', key: 'spd' }, {title: '速度', dataIndex: 'spd', key: 'spd'},
{ title: '暴击', dataIndex: 'cr', key: 'cr' }, {title: '暴击', dataIndex: 'cr', key: 'cr'},
{ title: '爆伤', dataIndex: 'cd', key: 'cd' }, {title: '爆伤', dataIndex: 'cd', key: 'cd'},
{ title: '命中', dataIndex: 'acc', key: 'acc' }, {title: '命中', dataIndex: 'acc', key: 'acc'},
{ title: '抵抗', dataIndex: 'res', key: 'res' }, {title: '抵抗', dataIndex: 'res', key: 'res'},
]; ];
const resultData = [ const resultData = [
{ key: 1, set: '暴击套', atk: 2000, def: 1500, hp: 20000, spd: 200, cr: 100, cd: 250, acc: 30, res: 20 }, {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: 2, set: '速度套', atk: 1800, def: 1400, hp: 21000, spd: 220, cr: 80, cd: 200, acc: 40, res: 30},
]; ];
export default function OptimizerPage() { export default function OptimizerPage() {
const [editVisible, setEditVisible] = React.useState(false);
const [editData, setEditData] = React.useState({
artifact: '',
artifactLevel: 0,
formation: '',
exclusive: '',
star: 5,
});
return ( return (
<Layout style={{ minHeight: '100vh' }}> <Layout style={{minHeight: '100vh'}}>
<Content style={{ padding: 16 }}> <Content style={{padding: 16}}>
{/* 顶部角色和选项区 */} {/* 顶部角色和选项区 */}
<Card style={{ marginBottom: 16 }}> <Card style={{marginBottom: 16, position: 'relative'}}>
<Row gutter={16}> <Row gutter={16} align="top">
{/* 角色头像与选择 */} {/* 角色头像与神器区 */}
<Col span={4} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> <Col span={6} style={{display: 'flex', alignItems: 'flex-start'}}>
<Avatar size={64} src={hero.avatar} icon={<UserOutlined />} /> <Avatar size={64} src={hero.avatar} icon={<UserOutlined/>} style={{marginRight: 16}}/>
<div style={{ marginTop: 8, fontWeight: 500 }}>{hero.name}</div> <div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
<Select style={{ width: '100%', marginTop: 8 }} defaultValue={hero.id}> <div style={{fontWeight: 500}}>{hero.name}</div>
{heroList.map(h => <Select.Option key={h.id} value={h.id}>{h.name}</Select.Option>)} <div style={{display: 'flex', alignItems: 'center', gap: 8}}>
</Select> <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>
{/* 角色属性 */} {/* 属性区(纵向) */}
<Col span={6}> <Col span={6}>
<div style={{ fontWeight: 500, marginBottom: 8 }}></div> <div style={{fontWeight: 500, marginBottom: 8}}></div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}> <div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
{attributes.map(attr => ( {attributes.map(attr => (
<div key={attr.label} style={{ minWidth: 60 }}>{attr.label}: <b>{attr.value}</b></div> <div key={attr.label}>{attr.label}: <b>{attr.value}</b></div>
))} ))}
</div> </div>
</Col> </Col>
{/* 属性过滤 */} {/* 属性过滤区(纵向,最小最大值) */}
<Col span={7}> <Col span={6}>
<div style={{ fontWeight: 500, marginBottom: 8 }}></div> <div style={{fontWeight: 500, marginBottom: 8}}></div>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}> <div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
{filterOptions.map(opt => ( {filterOptions.map(opt => (
<Input key={opt.value} addonBefore={opt.label} placeholder="最小值" style={{ width: 100 }} /> <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> </div>
</Col> </Col>
{/* 套装选择与操作 */} {/* 指定套装区(纵向,多选) */}
<Col span={7}> <Col span={6} style={{display: 'flex', flexDirection: 'column', justifyContent: 'flex-start'}}>
<div style={{ fontWeight: 500, marginBottom: 8 }}></div> <div style={{fontWeight: 500, marginBottom: 8}}></div>
<Select style={{ width: '100%', marginBottom: 8 }} defaultValue={setOptions[0].value}> <Select
{setOptions.map(opt => <Select.Option key={opt.value} value={opt.value}>{opt.label}</Select.Option>)} 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> </Select>
<div style={{ display: 'flex', gap: 8 }}>
<Button type="primary" icon={<AppstoreOutlined />}></Button>
<Button icon={<FilterOutlined />}></Button>
</div>
</Col> </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> </Row>
</Card> </Card>
{/* 配装结果列表区 */} {/* 配装结果列表区 */}
<Card title="配装结果" style={{ marginBottom: 16 }}> <Card title="配装结果" style={{marginBottom: 16}}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}> <div style={{display: 'flex', justifyContent: 'space-between', marginBottom: 8}}>
<div><b>123,456</b> | <b>2</b> </div> <div><b>123,456</b> | <b>2</b> </div>
<Pagination size="small" total={2} pageSize={10} current={1} showSizeChanger={false} /> <Pagination size="small" total={2} pageSize={10} current={1} showSizeChanger={false}/>
</div> </div>
<Table <Table
dataSource={resultData} dataSource={resultData}
columns={resultColumns} columns={resultColumns}
pagination={false} pagination={false}
scroll={{ x: true }} scroll={{x: true}}
rowKey="key" rowKey="key"
/> />
</Card> </Card>
{/* 单个配装详情区 */} {/* 单个配装详情区 */}
<Card title="配装详情"> <Card title="配装详情">
<div style={{ display: 'flex', gap: 24 }}> <div style={{display: 'flex', gap: 24}}>
<div> <div>
<Avatar size={48} src={hero.avatar} /> <Avatar size={48} src={hero.avatar}/>
<div style={{ marginTop: 8 }}>{hero.name}</div> <div style={{marginTop: 8}}>{hero.name}</div>
</div> </div>
<Divider type="vertical" style={{ height: 80 }} /> <Divider type="vertical" style={{height: 80}}/>
<div> <div>
<div><Tag color="blue"></Tag></div> <div><Tag color="blue"></Tag></div>
<div>2000150020000200</div> <div>2000150020000200</div>
<div>100%250%30%20%</div> <div>100%250%30%20%</div>
</div> </div>
<Divider type="vertical" style={{ height: 80 }} /> <Divider type="vertical" style={{height: 80}}/>
<div> <div>
<Button type="primary"></Button> <Button type="primary"></Button>
<Button style={{ marginLeft: 8 }}></Button> <Button style={{marginLeft: 8}}></Button>
</div> </div>
</div> </div>
</Card> </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> </Content>
</Layout> </Layout>
); );