@@ -50,7 +50,7 @@ export default function ModificationCreateModal({
|
|||||||
|
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
firearmId: lockedFirearmId ?? defaultFirearmId,
|
firearmId: lockedFirearmId ?? defaultFirearmId,
|
||||||
accessories: [{ slotName: "", accessoryName: "", tunings: [] }],
|
accessories: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
})
|
})
|
||||||
}, [open, defaultFirearmId, lockedFirearmId, form])
|
}, [open, defaultFirearmId, lockedFirearmId, form])
|
||||||
@@ -95,6 +95,3 @@ export default function ModificationCreateModal({
|
|||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { FirearmApi } from "@/api"
|
import { FirearmApi } from "@/api"
|
||||||
import slotNames from "@/constant/slots.json"
|
import slotNames from "@/constant/slots.json"
|
||||||
|
import tuningNames from "@/constant/tunings.json"
|
||||||
import { Firearm, ModificationRequest } from "@/types"
|
import { Firearm, ModificationRequest } from "@/types"
|
||||||
import { AutoComplete, Button, Card, Form, Input, InputNumber, Select, Space } from "antd"
|
import { AutoComplete, Button, Card, Form, Input, InputNumber, Select, Space } from "antd"
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ interface ModificationFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const slotOptions = slotNames.map((slotName) => ({ value: slotName }))
|
const slotOptions = slotNames.map((slotName) => ({ value: slotName }))
|
||||||
|
const tuningOptions = tuningNames.map((tuningName) => ({ value: tuningName }))
|
||||||
|
|
||||||
export default function ModificationForm({ form, onFinish, lockFirearmId }: ModificationFormProps) {
|
export default function ModificationForm({ form, onFinish, lockFirearmId }: ModificationFormProps) {
|
||||||
const [firearmOptions, setFirearmOptions] = useState<Array<{ value: number; label: string }>>([])
|
const [firearmOptions, setFirearmOptions] = useState<Array<{ value: number; label: string }>>([])
|
||||||
@@ -64,7 +66,10 @@ export default function ModificationForm({ form, onFinish, lockFirearmId }: Modi
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const mergedFirearmOptions = useMemo(() => {
|
const mergedFirearmOptions = useMemo(() => {
|
||||||
if (lockFirearmId === undefined || firearmOptions.some((option) => option.value === lockFirearmId)) {
|
if (
|
||||||
|
lockFirearmId === undefined ||
|
||||||
|
firearmOptions.some((option) => option.value === lockFirearmId)
|
||||||
|
) {
|
||||||
return firearmOptions
|
return firearmOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,14 +169,18 @@ export default function ModificationForm({ form, onFinish, lockFirearmId }: Modi
|
|||||||
<Space key={tuningField.key} align="start" className="w-full" wrap>
|
<Space key={tuningField.key} align="start" className="w-full" wrap>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[tuningField.name, "tuningName"]}
|
name={[tuningField.name, "tuningName"]}
|
||||||
label="调校项"
|
label="精校属性"
|
||||||
rules={[{ required: true, message: "请输入调校项" }]}>
|
rules={[{ required: true, message: "请选择或输入精校属性" }]}>
|
||||||
<Input placeholder="例如:后坐控制" className="w-44" />
|
<AutoComplete
|
||||||
|
options={tuningOptions}
|
||||||
|
placeholder="例如:后坐控制"
|
||||||
|
className="w-44"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[tuningField.name, "tuningValue"]}
|
name={[tuningField.name, "tuningValue"]}
|
||||||
label="调校值"
|
label="精校值"
|
||||||
rules={[{ required: true, message: "请输入调校值" }]}>
|
rules={[{ required: true, message: "请输入精校值" }]}>
|
||||||
<InputNumber className="w-32" placeholder="例如:0.35" />
|
<InputNumber className="w-32" placeholder="例如:0.35" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Button
|
<Button
|
||||||
@@ -185,8 +194,9 @@ export default function ModificationForm({ form, onFinish, lockFirearmId }: Modi
|
|||||||
))}
|
))}
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
|
disabled={tuningFields.length >= 2}
|
||||||
onClick={() => addTuning({ tuningName: "", tuningValue: 0 })}>
|
onClick={() => addTuning({ tuningName: "", tuningValue: 0 })}>
|
||||||
添加调校
|
添加精校
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -194,7 +204,8 @@ export default function ModificationForm({ form, onFinish, lockFirearmId }: Modi
|
|||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
variant="solid"
|
||||||
|
color="lime"
|
||||||
onClick={() => addAccessory({ slotName: "", accessoryName: "", tunings: [] })}>
|
onClick={() => addAccessory({ slotName: "", accessoryName: "", tunings: [] })}>
|
||||||
添加配件
|
添加配件
|
||||||
</Button>
|
</Button>
|
||||||
@@ -204,6 +215,3 @@ export default function ModificationForm({ form, onFinish, lockFirearmId }: Modi
|
|||||||
</Form>
|
</Form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
["安装位置", "厚度", "缩放倍率", "长度", "瞳距", "配重", "托腮板安装位置"]
|
||||||
+18
-12
@@ -36,21 +36,20 @@ export default function FirearmsPage() {
|
|||||||
const [editingFirearm, setEditingFirearm] = useState<Firearm | null>(null)
|
const [editingFirearm, setEditingFirearm] = useState<Firearm | null>(null)
|
||||||
const [deletingId, setDeletingId] = useState<number | null>(null)
|
const [deletingId, setDeletingId] = useState<number | null>(null)
|
||||||
|
|
||||||
const loadFirearms = useCallback(() => {
|
const loadFirearms = useCallback(async () => {
|
||||||
return FirearmApi.getFirearms({
|
const pagedData = await FirearmApi.getFirearms({
|
||||||
page: page - 1,
|
page: page - 1,
|
||||||
size: 12,
|
size: 12,
|
||||||
sortBy: "id",
|
sortBy: "id",
|
||||||
direction: "ASC",
|
direction: "ASC",
|
||||||
type: typeFilter === allTypeValue ? undefined : typeFilter,
|
type: typeFilter === allTypeValue ? undefined : typeFilter,
|
||||||
}).then((pagedData) => {
|
|
||||||
setFirearms(pagedData.items)
|
|
||||||
setTotal(pagedData.totalElements)
|
|
||||||
})
|
})
|
||||||
|
setFirearms(pagedData.items)
|
||||||
|
setTotal(pagedData.totalElements)
|
||||||
}, [page, typeFilter])
|
}, [page, typeFilter])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadFirearms()
|
void loadFirearms()
|
||||||
}, [loadFirearms])
|
}, [loadFirearms])
|
||||||
|
|
||||||
async function handleDelete(firearm: Firearm) {
|
async function handleDelete(firearm: Firearm) {
|
||||||
@@ -61,7 +60,7 @@ export default function FirearmsPage() {
|
|||||||
if (firearms.length === 1 && page > 1) {
|
if (firearms.length === 1 && page > 1) {
|
||||||
setPage(page - 1)
|
setPage(page - 1)
|
||||||
} else {
|
} else {
|
||||||
loadFirearms()
|
void loadFirearms()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
message.error("武器删除失败,请稍后重试")
|
message.error("武器删除失败,请稍后重试")
|
||||||
@@ -114,8 +113,7 @@ export default function FirearmsPage() {
|
|||||||
okText="删除"
|
okText="删除"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
okButtonProps={{ danger: true, loading: deletingId === firearm.id }}
|
okButtonProps={{ danger: true, loading: deletingId === firearm.id }}
|
||||||
onConfirm={() => handleDelete(firearm)}
|
onConfirm={() => handleDelete(firearm)}>
|
||||||
>
|
|
||||||
<Button type="link" danger size="small" loading={deletingId === firearm.id}>
|
<Button type="link" danger size="small" loading={deletingId === firearm.id}>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
@@ -155,7 +153,15 @@ export default function FirearmsPage() {
|
|||||||
<Typography.Paragraph
|
<Typography.Paragraph
|
||||||
style={{ marginBottom: 0 }}
|
style={{ marginBottom: 0 }}
|
||||||
type="secondary"
|
type="secondary"
|
||||||
ellipsis={{ rows: 3 }}
|
ellipsis={{
|
||||||
|
rows: 3,
|
||||||
|
tooltip: firearm.review
|
||||||
|
? {
|
||||||
|
title: <div style={{ whiteSpace: "pre-line" }}>{firearm.review}</div>,
|
||||||
|
placement: "topLeft",
|
||||||
|
}
|
||||||
|
: false,
|
||||||
|
}}
|
||||||
className="whitespace-pre-line">
|
className="whitespace-pre-line">
|
||||||
{firearm.review || "暂无描述"}
|
{firearm.review || "暂无描述"}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
@@ -190,7 +196,7 @@ export default function FirearmsPage() {
|
|||||||
onCancel={() => setCreateModalOpen(false)}
|
onCancel={() => setCreateModalOpen(false)}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setCreateModalOpen(false)
|
setCreateModalOpen(false)
|
||||||
loadFirearms()
|
void loadFirearms()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -200,7 +206,7 @@ export default function FirearmsPage() {
|
|||||||
onCancel={() => setEditingFirearm(null)}
|
onCancel={() => setEditingFirearm(null)}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setEditingFirearm(null)
|
setEditingFirearm(null)
|
||||||
loadFirearms()
|
void loadFirearms()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
import { App, Button, Card, Col, Pagination, Popconfirm, Row, Select, Space, Tag, Typography } from "antd"
|
import {
|
||||||
|
App,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Col,
|
||||||
|
Pagination,
|
||||||
|
Popconfirm,
|
||||||
|
Row,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Tag,
|
||||||
|
Typography,
|
||||||
|
} from "antd"
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import { Link, useSearchParams } from "react-router-dom"
|
import { Link, useSearchParams } from "react-router-dom"
|
||||||
import { ModificationApi, TagApi } from "@/api"
|
import { ModificationApi, TagApi } from "@/api"
|
||||||
@@ -39,7 +51,7 @@ export default function ModCodesPage() {
|
|||||||
})
|
})
|
||||||
}, [firearmId])
|
}, [firearmId])
|
||||||
|
|
||||||
const loadModifications = useCallback(() => {
|
const loadModifications = useCallback(async () => {
|
||||||
return ModificationApi.getModifications({
|
return ModificationApi.getModifications({
|
||||||
page: page - 1,
|
page: page - 1,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
@@ -54,7 +66,7 @@ export default function ModCodesPage() {
|
|||||||
}, [page, firearmId, selectedTags])
|
}, [page, firearmId, selectedTags])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadModifications()
|
void loadModifications()
|
||||||
}, [loadModifications])
|
}, [loadModifications])
|
||||||
|
|
||||||
async function handleDelete(modification: Modification) {
|
async function handleDelete(modification: Modification) {
|
||||||
@@ -69,7 +81,7 @@ export default function ModCodesPage() {
|
|||||||
if (modifications.length === 1 && page > 1) {
|
if (modifications.length === 1 && page > 1) {
|
||||||
setPage(page - 1)
|
setPage(page - 1)
|
||||||
} else {
|
} else {
|
||||||
loadModifications()
|
void loadModifications()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
message.error("改枪码删除失败,请稍后重试")
|
message.error("改枪码删除失败,请稍后重试")
|
||||||
@@ -114,8 +126,7 @@ export default function ModCodesPage() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedTags([])
|
setSelectedTags([])
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
清除筛选
|
清除筛选
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -138,7 +149,10 @@ export default function ModCodesPage() {
|
|||||||
extra={
|
extra={
|
||||||
user ? (
|
user ? (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Button type="link" size="small" onClick={() => setEditingModification(modification)}>
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => setEditingModification(modification)}>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
@@ -147,9 +161,12 @@ export default function ModCodesPage() {
|
|||||||
okText="删除"
|
okText="删除"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
okButtonProps={{ danger: true, loading: deletingId === modification.id }}
|
okButtonProps={{ danger: true, loading: deletingId === modification.id }}
|
||||||
onConfirm={() => handleDelete(modification)}
|
onConfirm={() => handleDelete(modification)}>
|
||||||
>
|
<Button
|
||||||
<Button type="link" danger size="small" loading={deletingId === modification.id}>
|
type="link"
|
||||||
|
danger
|
||||||
|
size="small"
|
||||||
|
loading={deletingId === modification.id}>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
@@ -195,15 +212,23 @@ export default function ModCodesPage() {
|
|||||||
<div className="mt-2 overflow-x-auto">
|
<div className="mt-2 overflow-x-auto">
|
||||||
<div className="grid min-w-275 grid-cols-5 gap-2">
|
<div className="grid min-w-275 grid-cols-5 gap-2">
|
||||||
{(modification.accessories || []).map((accessory, accessoryIndex) => (
|
{(modification.accessories || []).map((accessory, accessoryIndex) => (
|
||||||
<div key={`${modification.id}-accessory-${accessoryIndex}`} className="rounded border border-gray-100 p-2">
|
<div
|
||||||
|
key={`${modification.id}-accessory-${accessoryIndex}`}
|
||||||
|
className="rounded border border-gray-100 p-2">
|
||||||
<div className="flex items-center justify-between gap-2 rounded bg-gray-50 px-2 py-1">
|
<div className="flex items-center justify-between gap-2 rounded bg-gray-50 px-2 py-1">
|
||||||
<Tag color="blue" className="mr-0">{accessory.slotName || "未填写槽位"}</Tag>
|
<Typography color="blue" className="mr-0">
|
||||||
<Tag className="mr-0">{accessory.accessoryName || "未填写配件"}</Tag>
|
{accessory.slotName || "未填写槽位"}
|
||||||
|
</Typography>
|
||||||
|
<Typography className="mr-0 text-[#4C1D95]">
|
||||||
|
{accessory.accessoryName || "未填写配件"}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
{(accessory.tunings?.length || 0) > 0 ? (
|
{(accessory.tunings?.length || 0) > 0 ? (
|
||||||
<div className="mt-2 flex flex-wrap gap-1">
|
<div className="mt-2 flex flex-wrap gap-1">
|
||||||
{accessory.tunings.map((tuning, tuningIndex) => (
|
{accessory.tunings.map((tuning, tuningIndex) => (
|
||||||
<Tag key={`${modification.id}-${accessoryIndex}-tuning-${tuningIndex}`} color="geekblue">
|
<Tag
|
||||||
|
key={`${modification.id}-${accessoryIndex}-tuning-${tuningIndex}`}
|
||||||
|
color="geekblue">
|
||||||
{tuning.tuningName || "未命名"}: {tuning.tuningValue ?? "-"}
|
{tuning.tuningName || "未命名"}: {tuning.tuningValue ?? "-"}
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user