From 83153a4300856f2687b2db642d933190e91ad2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B4=E5=9E=9A=20=E8=8C=83?= <2664438031@qq.com> Date: Wed, 24 Jun 2026 00:01:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=B3=E4=BE=A7=E9=85=8D=E4=BB=B6=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modification-form/index.tsx | 768 ++++++++++++++------- src/index.css | 18 + src/page/firearms/index.tsx | 512 +++++++------- 3 files changed, 786 insertions(+), 512 deletions(-) diff --git a/src/components/modification-form/index.tsx b/src/components/modification-form/index.tsx index 75271bb..1ebb90f 100644 --- a/src/components/modification-form/index.tsx +++ b/src/components/modification-form/index.tsx @@ -1,289 +1,289 @@ -import { useEffect, useMemo, useState } from "react" -import { FirearmApi } from "@/api" -import slotNames from "@/constant/slots.json" -import tuningNames from "@/constant/tunings.json" -import { Firearm, ModificationRequest } from "@/types" -import { AutoComplete, Button, Card, Form, Input, InputNumber, Select, Space, Tag, message } from "antd" -import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons" -import AccessoryFormModal from "@/components/AccessoryFormModal" +// import { useEffect, useMemo, useState } from "react" +// import { FirearmApi } from "@/api" +// import slotNames from "@/constant/slots.json" +// import tuningNames from "@/constant/tunings.json" +// import { Firearm, ModificationRequest } from "@/types" +// import { AutoComplete, Button, Card, Form, Input, InputNumber, Select, Space, Tag, message } from "antd" +// import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons" +// import AccessoryFormModal from "@/components/AccessoryFormModal" -const slotOptions = slotNames.map((slotName) => ({ value: slotName })) -const tuningOptions = tuningNames.map((tuningName) => ({ value: tuningName })) +// const slotOptions = slotNames.map((slotName) => ({ value: slotName })) +// const tuningOptions = tuningNames.map((tuningName) => ({ value: tuningName })) -interface ModificationFormProps { - form: ReturnType>[0] - onFinish: (values: ModificationRequest) => void - lockFirearmId?: number -} +// interface ModificationFormProps { +// form: ReturnType>[0] +// onFinish: (values: ModificationRequest) => void +// lockFirearmId?: number +// } -interface AccessoryType { - slotName: string - accessoryName: string - tunings: Array<{ tuningName: string; tuningValue: number }> -} +// interface AccessoryType { +// slotName: string +// accessoryName: string +// tunings: Array<{ tuningName: string; tuningValue: number }> +// } -export default function ModificationForm({ form, onFinish, lockFirearmId }: ModificationFormProps) { - const [firearmOptions, setFirearmOptions] = useState>([]) - const [firearmLoading, setFirearmLoading] = useState(false) - - // 配件弹窗相关状态 - const [accessoryModalOpen, setAccessoryModalOpen] = useState(false) - const [editingAccessory, setEditingAccessory] = useState(null) - const [editingAccessoryIndex, setEditingAccessoryIndex] = useState(null) +// export default function ModificationForm({ form, onFinish, lockFirearmId }: ModificationFormProps) { +// const [firearmOptions, setFirearmOptions] = useState>([]) +// const [firearmLoading, setFirearmLoading] = useState(false) - useEffect(() => { - let active = true +// // 配件弹窗相关状态 +// const [accessoryModalOpen, setAccessoryModalOpen] = useState(false) +// const [editingAccessory, setEditingAccessory] = useState(null) +// const [editingAccessoryIndex, setEditingAccessoryIndex] = useState(null) - async function loadAllFirearms() { - setFirearmLoading(true) - try { - const allFirearms: Firearm[] = [] - let page = 0 - let totalPages = 1 +// useEffect(() => { +// let active = true - while (page < totalPages) { - const paged = await FirearmApi.getFirearms({ - page, - size: 100, - sortBy: "id", - direction: "ASC", - }) +// async function loadAllFirearms() { +// setFirearmLoading(true) +// try { +// const allFirearms: Firearm[] = [] +// let page = 0 +// let totalPages = 1 - allFirearms.push(...paged.items) - totalPages = paged.totalPages - page += 1 - } +// while (page < totalPages) { +// const paged = await FirearmApi.getFirearms({ +// page, +// size: 100, +// sortBy: "id", +// direction: "ASC", +// }) - if (!active) { - return - } +// allFirearms.push(...paged.items) +// totalPages = paged.totalPages +// page += 1 +// } - setFirearmOptions( - allFirearms.map((firearm) => ({ - value: firearm.id, - label: `${firearm.name}`, - })) - ) - } finally { - if (active) { - setFirearmLoading(false) - } - } - } +// if (!active) { +// return +// } - void loadAllFirearms() +// setFirearmOptions( +// allFirearms.map((firearm) => ({ +// value: firearm.id, +// label: `${firearm.name}`, +// })) +// ) +// } finally { +// if (active) { +// setFirearmLoading(false) +// } +// } +// } - return () => { - active = false - } - }, []) +// void loadAllFirearms() - const mergedFirearmOptions = useMemo(() => { - if ( - lockFirearmId === undefined || - firearmOptions.some((option) => option.value === lockFirearmId) - ) { - return firearmOptions - } +// return () => { +// active = false +// } +// }, []) - return [{ value: lockFirearmId, label: `武器 ID: ${lockFirearmId}` }, ...firearmOptions] - }, [firearmOptions, lockFirearmId]) +// const mergedFirearmOptions = useMemo(() => { +// if ( +// lockFirearmId === undefined || +// firearmOptions.some((option) => option.value === lockFirearmId) +// ) { +// return firearmOptions +// } - // 打开添加配件弹窗 - const handleAddAccessory = () => { - setEditingAccessory(null) - setEditingAccessoryIndex(null) - setAccessoryModalOpen(true) - } +// return [{ value: lockFirearmId, label: `武器 ID: ${lockFirearmId}` }, ...firearmOptions] +// }, [firearmOptions, lockFirearmId]) - // 打开编辑配件弹窗 - const handleEditAccessory = (index: number, accessory: AccessoryType) => { - setEditingAccessory(accessory) - setEditingAccessoryIndex(index) - setAccessoryModalOpen(true) - } +// // 打开添加配件弹窗 +// const handleAddAccessory = () => { +// setEditingAccessory(null) +// setEditingAccessoryIndex(null) +// setAccessoryModalOpen(true) +// } - // 删除配件 - const handleDeleteAccessory = (remove: (index: number) => void, index: number) => { - remove(index) - message.success("删除成功") - } +// // 打开编辑配件弹窗 +// const handleEditAccessory = (index: number, accessory: AccessoryType) => { +// setEditingAccessory(accessory) +// setEditingAccessoryIndex(index) +// setAccessoryModalOpen(true) +// } - // 配件弹窗确认回调 - const handleAccessoryModalOk = (values: AccessoryType) => { - const accessories = form.getFieldValue("accessories") || [] - - if (editingAccessoryIndex !== null) { - // 编辑模式:更新指定索引的数据 - const updatedAccessories = [...accessories] - updatedAccessories[editingAccessoryIndex] = values - form.setFieldsValue({ accessories: updatedAccessories }) - message.success("修改成功") - } else { - // 新增模式:添加新数据 - form.setFieldsValue({ accessories: [...accessories, values] }) - message.success("添加成功") - } - - setAccessoryModalOpen(false) - setEditingAccessory(null) - setEditingAccessoryIndex(null) - } +// // 删除配件 +// const handleDeleteAccessory = (remove: (index: number) => void, index: number) => { +// remove(index) +// message.success("删除成功") +// } - // 配件弹窗取消回调 - const handleAccessoryModalCancel = () => { - setAccessoryModalOpen(false) - setEditingAccessory(null) - setEditingAccessoryIndex(null) - } +// // 配件弹窗确认回调 +// const handleAccessoryModalOk = (values: AccessoryType) => { +// const accessories = form.getFieldValue("accessories") || [] - return ( - <> - - form={form} - layout="vertical" - onFinish={() => { - const allValues = form.getFieldsValue(true); - console.log('完整数据:', allValues); - onFinish(allValues); - }} - requiredMark={false}> - - name="firearmId" - label="武器" - rules={[{ required: true, message: "请输入武器" }]}> - - className="w-full" - placeholder="请选择武器" - options={mergedFirearmOptions} - loading={firearmLoading} - disabled={lockFirearmId !== undefined} - showSearch={{ - filterOption: (input, option) => { - const labelText = String(option?.label ?? "") - return labelText.toLowerCase().includes(input.toLowerCase()) - }, - }} - /> - +// if (editingAccessoryIndex !== null) { +// // 编辑模式:更新指定索引的数据 +// const updatedAccessories = [...accessories] +// updatedAccessories[editingAccessoryIndex] = values +// form.setFieldsValue({ accessories: updatedAccessories }) +// message.success("修改成功") +// } else { +// // 新增模式:添加新数据 +// form.setFieldsValue({ accessories: [...accessories, values] }) +// message.success("添加成功") +// } - - name="name" - label="改装名称" - rules={[{ required: true, message: "请输入改装名称" }]}> - - +// setAccessoryModalOpen(false) +// setEditingAccessory(null) +// setEditingAccessoryIndex(null) +// } - - name="code" - label="改枪码" - rules={[{ required: true, message: "请输入改枪码" }]}> - - +// // 配件弹窗取消回调 +// const handleAccessoryModalCancel = () => { +// setAccessoryModalOpen(false) +// setEditingAccessory(null) +// setEditingAccessoryIndex(null) +// } - name="tags" label="标签"> - - +// +// name="name" +// label="改装名称" +// rules={[{ required: true, message: "请输入改装名称" }]}> +// +// - name="videoUrl" label="视频链接"> - - +// +// name="code" +// label="改枪码" +// rules={[{ required: true, message: "请输入改枪码" }]}> +// +// - name="note" label="备注"> - - +// name="tags" label="标签"> +// +// - label="配件配置"> - - {(accessoryFields, { add, remove }) => ( -
- {/* 配件标签列表 */} -
- {accessoryFields.length === 0 ? ( -
暂无配件,点击下方按钮添加
- ) : ( - accessoryFields.map((accessoryField, index) => { - const accessory = form.getFieldValue(["accessories", index]) as AccessoryType - return ( - { - e.preventDefault() - handleDeleteAccessory(remove, accessoryField.name) - }} - closeIcon={} - className="px-3 py-1.5 text-sm border hover:shadow-sm transition-all" - style={{ marginRight: 8, marginBottom: 8 }} - > - - - {accessory?.slotName || "?"} - - · - - {accessory?.accessoryName || "未命名"} - - {accessory?.tunings && accessory.tunings.length > 0 && ( - - {accessory.tunings.length}项精校 - - )} -
+// name="videoUrl" label="视频链接"> +// +// - {/* 添加按钮 */} - -
- )} -
- - +// name="note" label="备注"> +// +// - +// {/* 改造后的配件列表 */} +//
- {/* 配件编辑弹窗 */} - - - ) -} +// label="配件配置"> +// +// {(accessoryFields, { add, remove }) => ( +//
+// {/* 配件标签列表 */} +//
+// {accessoryFields.length === 0 ? ( +//
暂无配件,点击下方按钮添加
+// ) : ( +// accessoryFields.map((accessoryField, index) => { +// const accessory = form.getFieldValue(["accessories", index]) as AccessoryType +// return ( +// { +// e.preventDefault() +// handleDeleteAccessory(remove, accessoryField.name) +// }} +// closeIcon={} +// className="px-3 py-1.5 text-sm border hover:shadow-sm transition-all " +// style={{ marginRight: 8, marginBottom: 8 }} +// > +// +// +// {accessory?.slotName || "?"} +// +// · +// +// {accessory?.accessoryName || "未命名"} +// +// {accessory?.tunings && accessory.tunings.length > 0 && ( +// +// {accessory.tunings.length}项精校 +// +// )} +//
+ +// {/* 添加按钮 */} +// +//
+// )} +//
+// +//
+ +// + +// {/* 配件编辑弹窗 */} +// +// +// ) +// } // import { useEffect, useMemo, useState } from "react" // import { FirearmApi } from "@/api" // import slotNames from "@/constant/slots.json" @@ -500,4 +500,260 @@ export default function ModificationForm({ form, onFinish, lockFirearmId }: Modi // // // ) -// } \ No newline at end of file +// } +import { useEffect, useMemo, useState } from "react"; +import { FirearmApi } from "@/api"; +import slotNames from "@/constant/slots.json"; +import tuningNames from "@/constant/tunings.json"; +import { Firearm, ModificationRequest } from "@/types"; +import { AutoComplete, Button, Card, Form, Input, InputNumber, Select, Space } from "antd"; + +interface ModificationFormProps { + form: ReturnType>[0]; + onFinish: (values: ModificationRequest) => void; + lockFirearmId?: number; +} + +const slotOptions = slotNames.map((slotName) => ({ value: slotName })); +const tuningOptions = tuningNames.map((tuningName) => ({ value: tuningName })); + +export default function ModificationForm({ form, onFinish, lockFirearmId }: ModificationFormProps) { + const [firearmOptions, setFirearmOptions] = useState>([]); + const [firearmLoading, setFirearmLoading] = useState(false); + + useEffect(() => { + let active = true; + + async function loadAllFirearms() { + setFirearmLoading(true); + try { + const allFirearms: Firearm[] = []; + let page = 0; + let totalPages = 1; + + while (page < totalPages) { + const paged = await FirearmApi.getFirearms({ + page, + size: 100, + sortBy: "id", + direction: "ASC", + }); + + allFirearms.push(...paged.items); + totalPages = paged.totalPages; + page += 1; + } + + if (!active) return; + + setFirearmOptions( + allFirearms.map((firearm) => ({ + value: firearm.id, + label: `${firearm.name}`, + })) + ); + } finally { + if (active) { + setFirearmLoading(false); + } + } + } + + void loadAllFirearms(); + + return () => { + active = false; + }; + }, []); + + const mergedFirearmOptions = useMemo(() => { + if ( + lockFirearmId === undefined || + firearmOptions.some((option) => option.value === lockFirearmId) + ) { + return firearmOptions; + } + + return [{ value: lockFirearmId, label: `武器 ID: ${lockFirearmId}` }, ...firearmOptions]; + }, [firearmOptions, lockFirearmId]); + + return ( + + form={form} + layout="vertical" + onFinish={onFinish} + requiredMark={false} + className="max-w-full mx-auto" + > + {/* 强制不换行,左右并排 */} +
+ {/* 左侧:主要字段,固定宽度,控件宽度收窄 */} +
+ + name="firearmId" + label="武器" + rules={[{ required: true, message: "请输入武器" }]} + > + + className="w-[580px]" + placeholder="请选择武器" + options={mergedFirearmOptions} + loading={firearmLoading} + disabled={lockFirearmId !== undefined} + showSearch={{ + filterOption: (input, option) => { + const labelText = String(option?.label ?? ""); + return labelText.toLowerCase().includes(input.toLowerCase()); + }, + }} + /> + + + + name="name" + label="改装名称" + rules={[{ required: true, message: "请输入改装名称" }]} + > + + + + + name="code" + label="改枪码" + rules={[{ required: true, message: "请输入改枪码" }]} + > + + + + name="tags" label="标签"> + + + + name="videoUrl" label="视频链接"> + + + + name="note" label="备注"> + + +
+ + {/* 右侧:配件列表,固定宽度,卡片缩小,高度限制滚动 */} +
+
+ + {(accessoryFields, { add: addAccessory, remove: removeAccessory }) => ( +
+ {accessoryFields.map((accessoryField) => ( + removeAccessory(accessoryField.name)} + > + 删除配件 + + } + className="w-full [&_.ant-card-body]:!p-3" // 卡片内边距由默认24px缩小到12px + > + {/* 用容器控制表单项间距,由默认大间距缩小为4px */} +
+ + + + + + + + + + {(tuningFields, { add: addTuning, remove: removeTuning }) => ( +
{/* 精校列表间距由3(12px)缩小为2(8px) */} + {tuningFields.map((tuningField) => ( + + + + + + {/* 完全保留 */} + + + + ))} + +
+ )} +
+
+
+ ))} + {/* 添加配件按钮,保持在左侧(默认左对齐) */} + +
+ )} +
+
+
+
+ + ); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index 9447624..78091b9 100644 --- a/src/index.css +++ b/src/index.css @@ -201,4 +201,22 @@ body { .ant-collapse-body{ background: #1e1e1e; +} + +.ant-modal-container{ + width: 620px; +} + +.hide-scrollbar { + max-height: 500px; /* 限制高度,触发滚动 */ + overflow-y: auto; /* 允许滚动 */ + + /* 隐藏滚动条(Firefox 和 IE) */ + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE/Edge */ +} + +/* 隐藏滚动条(Chrome/Safari/Edge 新版) */ +.hide-scrollbar::-webkit-scrollbar { + display: none; } \ No newline at end of file diff --git a/src/page/firearms/index.tsx b/src/page/firearms/index.tsx index 89c897a..926c168 100644 --- a/src/page/firearms/index.tsx +++ b/src/page/firearms/index.tsx @@ -172,118 +172,105 @@ export default function FirearmsPage() {
- {loading?(
- -
):( - - {firearms.map((firearm) => ( - + {loading ? (
+ +
) : ( + + {firearms.map((firearm) => ( + - - - handleDelete(firearm)}> - - -
- ) : null - } - variant="outlined" - style={{ - height: '100%', display: 'flex', flexDirection: 'column' - }} - styles={{ - root: { - border: '1px solid #313131' - }, - header: { - borderBottom: '1px solid #303030', - }, - body: { - flex: 1, - overflow: 'auto', - padding: '12px', - - }, - actions: { - flexShrink: 0, - background: '#1e1e1e', - display: 'flex' + handleDelete(firearm)}> + + + + ) : null } - }} - actions={[ -
- null} - styles={ - { - root: { - background: '#1e1e1e', - width: '100%' + }, + actions: { + flexShrink: 0, + background: '#1e1e1e', + display: 'flex' + } + }} + actions={[ +
+ null} + styles={ + { + root: { + background: '#1e1e1e', + width: '100%' + } } } - } - items={[{ - key: '1', - label: ( - - ), - children: - }]} - /> -
, - ]}> -
-
-
-
- + 查看改枪码 + + ), + children: + }]} + /> +
, + ]}> +
+
+
+
- {firearm.name} - - - {firearmTypeText[firearm.type]} - -
-
-
-
-
- {/* 武器输出等级 */} -
-
+ + {firearmTypeText[firearm.type]} + +
+
+
+
+
+ {/* 武器输出等级 */} +
- 武器输出等级 +
+ 武器输出等级 +
+
+ {firearm.level} +
+ + {/* 子弹口径 */}
- {firearm.level} +
+ 子弹口径 +
+
+ {firearm.calibre} +
+
+ + {/* 每秒甲伤 */} +
+
+ 每秒甲伤 +
+
+ {asDps(firearm.fireRate, firearm.armourDamage)} +
+
+ + {/* 每秒肉伤 */} +
+
+ 每秒肉伤 +
+
+ {asDps(firearm.fireRate, firearm.bodyDamage)} +
- {/* 子弹口径 */} -
-
- 子弹口径 -
-
- {firearm.calibre} -
-
- - {/* 每秒甲伤 */} -
-
- 每秒甲伤 -
-
- {asDps(firearm.fireRate, firearm.armourDamage)} -
-
- - {/* 每秒肉伤 */} -
-
- 每秒肉伤 -
-
- {asDps(firearm.fireRate, firearm.bodyDamage)} -
-
- +
+
+ {firearm.review}
, + placement: "topLeft", + } + : false, + }} + className="whitespace-pre-line"> + {firearm.review || "暂无描述"} +
-
- {firearm.review}
, - placement: "topLeft", - } - : false, - }} - className="whitespace-pre-line"> - {firearm.review || "暂无描述"} - -
-
-
- - - ))} - {firearms.length === 0 && ( - - - 暂无武器数据 - - - )} - )} +
+ + + ))} + {firearms.length === 0 && ( + + + 暂无武器数据 + + + )} + )}