From 456bbac8c674e903e6a8b39da672a483df08234d Mon Sep 17 00:00:00 2001 From: tikkhun Date: Fri, 28 Nov 2025 16:32:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20=E6=B7=BB=E5=8A=A0=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=E7=AE=A1=E7=90=86=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=92=8C=E7=B1=BB=E5=9E=8B=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(Dict): 优化字典项表单使用Form实例并添加下载模板功能 feat(Product): 新增产品编辑功能并优化创建表单 --- src/pages/Dict/List/index.tsx | 145 +++++++++++++-------- src/pages/Product/List/index.tsx | 212 ++++++++++++++++++++++++++++--- src/servers/api/area.ts | 90 +++++++++++++ src/servers/api/index.ts | 2 + src/servers/api/typings.d.ts | 87 ++++++++++--- 5 files changed, 449 insertions(+), 87 deletions(-) create mode 100644 src/servers/api/area.ts diff --git a/src/pages/Dict/List/index.tsx b/src/pages/Dict/List/index.tsx index e931673..527da54 100644 --- a/src/pages/Dict/List/index.tsx +++ b/src/pages/Dict/List/index.tsx @@ -36,11 +36,7 @@ const DictPage: React.FC = () => { // 控制字典项模态框的显示 const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false); const [editingDictItem, setEditingDictItem] = useState(null); - const [dictItemForm, setDictItemForm] = useState({ - name: '', - title: '', - value: '', - }); + const [dictItemForm] = Form.useForm(); // 获取字典列表 const fetchDicts = async (title?: string) => { @@ -126,36 +122,32 @@ const DictPage: React.FC = () => { // 打开添加字典项模态框 const handleAddDictItem = () => { setEditingDictItem(null); - setDictItemForm({ name: '', title: '', value: '' }); + dictItemForm.resetFields(); setIsDictItemModalVisible(true); }; // 打开编辑字典项模态框 const handleEditDictItem = (item: any) => { setEditingDictItem(item); - setDictItemForm(item); + dictItemForm.setFieldsValue(item); setIsDictItemModalVisible(true); }; // 处理字典项表单提交(添加/编辑) - const handleDictItemFormSubmit = async () => { - if (!dictItemForm.name || !dictItemForm.title) { - message.warning('请输入名称和标题'); - return; - } + const handleDictItemFormSubmit = async (values: any) => { try { if (editingDictItem) { // 编辑 await request(`/dict/item/${editingDictItem.id}`, { method: 'PUT', - data: dictItemForm, + data: values, }); message.success('更新成功'); } else { // 添加 await request('/dict/item', { method: 'POST', - data: { ...dictItemForm, dictId: selectedDict.id }, + data: { ...values, dictId: selectedDict.id }, }); message.success('添加成功'); } @@ -193,6 +185,50 @@ const DictPage: React.FC = () => { } }; + /** + * @description 下载字典模板(走认证请求) + */ + const handleDownloadDictTemplate = async () => { + try { + // 使用带有认证拦截的 request 发起下载请求(后端鉴权通过) + const blob = await request('/dict/template', { responseType: 'blob' }); + // 创建临时链接并触发下载 + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'dict-template.xlsx'; + document.body.appendChild(a); + a.click(); + a.remove(); + window.URL.revokeObjectURL(url); + } catch (error) { + // 错误处理:认证失败或网络错误 + message.error('下载模板失败'); + } + }; + + /** + * @description 下载字典项模板(走认证请求) + */ + const handleDownloadDictItemTemplate = async () => { + try { + // 使用带有认证拦截的 request 发起下载请求(后端鉴权通过) + const blob = await request('/dict/item/template', { responseType: 'blob' }); + // 创建临时链接并触发下载 + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'dict-item-template.xlsx'; + document.body.appendChild(a); + a.click(); + a.remove(); + window.URL.revokeObjectURL(url); + } catch (error) { + // 错误处理:认证失败或网络错误 + message.error('下载模板失败'); + } + }; + // 左侧字典列表的列定义 const dictColumns = [ { @@ -211,6 +247,7 @@ const DictPage: React.FC = () => { render: (_: any, record: any) => ( - @@ -336,14 +379,14 @@ const DictPage: React.FC = () => { - - +
+ { - +
{ dictItemForm.submit()} onCancel={() => setIsDictItemModalVisible(false)} + destroyOnClose > -
- - - setDictItemForm({ ...dictItemForm, name: e.target.value }) - } - /> + + + - - - setDictItemForm({ ...dictItemForm, title: e.target.value }) - } - /> + + - - - setDictItemForm({ ...dictItemForm, value: e.target.value }) - } - /> + + + + +
diff --git a/src/pages/Product/List/index.tsx b/src/pages/Product/List/index.tsx index 79bc93e..d94269b 100644 --- a/src/pages/Product/List/index.tsx +++ b/src/pages/Product/List/index.tsx @@ -6,6 +6,7 @@ import { productcontrollerGetproductlist, productcontrollerGetstrengthall, productcontrollerUpdateproductnamecn, + productcontrollerUpdateproduct, } from '@/servers/api/product'; import { templatecontrollerRendertemplate } from '@/servers/api/template'; import { PlusOutlined } from '@ant-design/icons'; @@ -23,6 +24,7 @@ import { } from '@ant-design/pro-components'; import { App, Button, Popconfirm } from 'antd'; import React, { useRef, useState } from 'react'; +const capitalize = (s: string) => s.charAt(0).toLocaleUpperCase() + s.slice(1); // TODO interface DictItem { id: number; @@ -54,7 +56,7 @@ const NameCn: React.FC<{ if (!success) { return message.error(errMsg); } - tableRef?.current?.reload(); + tableRef?.current?.reloadAndRest?.(); }, }} /> @@ -67,6 +69,10 @@ const List: React.FC = () => { const { message } = App.useApp(); const columns: ProColumns[] = [ + { + title: 'sku', + dataIndex: 'sku', + }, { title: '名称', dataIndex: 'name', @@ -80,6 +86,21 @@ const List: React.FC = () => { ); }, }, + { + title: '价格', + dataIndex: 'price', + hideInSearch: true, + }, + { + title: '促销价', + dataIndex: 'promotionPrice', + hideInSearch: true, + }, + { + title: '库存', + dataIndex: 'stock', + hideInSearch: true, + }, { title: '产品描述', dataIndex: 'description', @@ -101,10 +122,7 @@ const List: React.FC = () => { title: '湿度', dataIndex: 'humidity', }, - { - title: 'sku', - dataIndex: 'sku', - }, + { title: '更新时间', dataIndex: 'updatedAt', @@ -123,6 +141,7 @@ const List: React.FC = () => { valueType: 'option', render: (_, record) => ( <> + { - try { - const { success, message: errMsg } = - await productcontrollerCreateproduct(values); - if (!success) { - throw new Error(errMsg); - } - tableRef.current?.reload(); + const { success, message: errMsg } = + await productcontrollerCreateproduct(values); + if (success) { message.success('提交成功'); + tableRef.current?.reloadAndRest?.(); return true; - } catch (error: any) { - message.error(error.message); } + message.error(errMsg); + return false; }} > @@ -381,6 +397,20 @@ const CreateForm: React.FC<{ 自动生成 + + ; + record: API.Product; +}> = ({ tableRef, record }) => { + const { message } = App.useApp(); + const formRef = useRef(); + const [brandOptions, setBrandOptions] = useState([]); + const [strengthOptions, setStrengthOptions] = useState([]); + const [flavorOptions, setFlavorOptions] = useState([]); + + const setInitialIds = () => { + const brand = brandOptions.find((item) => item.title === record.brandName); + const strength = strengthOptions.find((item) => item.title === record.strengthName); + const flavor = flavorOptions.find((item) => item.title === record.flavorsName); + formRef.current?.setFieldsValue({ + brandId: brand?.id, + strengthId: strength?.id, + flavorsId: flavor?.id, + }); + }; + + React.useEffect(() => { + if (brandOptions.length && strengthOptions.length && flavorOptions.length) { + setInitialIds(); + } + }, [brandOptions, strengthOptions, flavorOptions]); + + return ( + + formRef={formRef} + title="编辑" + trigger={} + initialValues={{ + name: record.name, + sku: record.sku, + description: record.description, + humidity: record.humidity, + price: (record as any).price, + promotionPrice: (record as any).promotionPrice, + }} + onFinish={async (values) => { + const { success, message: errMsg } = await productcontrollerUpdateproduct( + { id: record.id }, + values as any, + ); + if (success) { + message.success('更新成功'); + tableRef.current?.reloadAndRest?.(); + return true; + } + message.error(errMsg); + return false; + }} + > + { + const { data = [] } = await productcontrollerGetbrandall(); + setBrandOptions(data); + return data.map((item: DictItem) => ({ + label: item.name, + value: item.id, + })); + }} + rules={[{ required: true, message: '请选择产品品牌' }]} + /> + { + const { data = [] } = await productcontrollerGetstrengthall(); + setStrengthOptions(data); + return data.map((item: DictItem) => ({ + label: item.name, + value: item.id, + })); + }} + rules={[{ required: true, message: '请选择强度' }]} + /> + { + const { data = [] } = await productcontrollerGetflavorsall(); + setFlavorOptions(data); + return data.map((item: DictItem) => ({ + label: item.name, + value: item.id, + })); + }} + rules={[{ required: true, message: '请选择口味' }]} + /> + + + + + + + + + + ); +}; diff --git a/src/servers/api/area.ts b/src/servers/api/area.ts new file mode 100644 index 0000000..60ed939 --- /dev/null +++ b/src/servers/api/area.ts @@ -0,0 +1,90 @@ +// @ts-ignore +/* eslint-disable */ +import { request } from 'umi'; + +/** 获取区域列表(分页) GET /api/area/ */ +export async function areacontrollerGetarealist( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.areacontrollerGetarealistParams, + options?: { [key: string]: any }, +) { + return request('/api/area/', { + method: 'GET', + params: { + ...params, + }, + ...(options || {}), + }); +} + +/** 创建区域 POST /api/area/ */ +export async function areacontrollerCreatearea( + body: API.CreateAreaDTO, + options?: { [key: string]: any }, +) { + return request('/api/area/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }); +} + +/** 根据ID获取区域详情 GET /api/area/${param0} */ +export async function areacontrollerGetareabyid( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.areacontrollerGetareabyidParams, + options?: { [key: string]: any }, +) { + const { id: param0, ...queryParams } = params; + return request(`/api/area/${param0}`, { + method: 'GET', + params: { ...queryParams }, + ...(options || {}), + }); +} + +/** 更新区域 PUT /api/area/${param0} */ +export async function areacontrollerUpdatearea( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.areacontrollerUpdateareaParams, + body: API.UpdateAreaDTO, + options?: { [key: string]: any }, +) { + const { id: param0, ...queryParams } = params; + return request(`/api/area/${param0}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + params: { ...queryParams }, + data: body, + ...(options || {}), + }); +} + +/** 删除区域 DELETE /api/area/${param0} */ +export async function areacontrollerDeletearea( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.areacontrollerDeleteareaParams, + options?: { [key: string]: any }, +) { + const { id: param0, ...queryParams } = params; + return request(`/api/area/${param0}`, { + method: 'DELETE', + params: { ...queryParams }, + ...(options || {}), + }); +} + +/** 获取所有区域 GET /api/area/all */ +export async function areacontrollerGetallareas(options?: { + [key: string]: any; +}) { + return request('/api/area/all', { + method: 'GET', + ...(options || {}), + }); +} diff --git a/src/servers/api/index.ts b/src/servers/api/index.ts index 042fa0e..618def7 100644 --- a/src/servers/api/index.ts +++ b/src/servers/api/index.ts @@ -2,6 +2,7 @@ /* eslint-disable */ // API 更新时间: // API 唯一标识: +import * as area from './area'; import * as customer from './customer'; import * as dict from './dict'; import * as locales from './locales'; @@ -17,6 +18,7 @@ import * as user from './user'; import * as webhook from './webhook'; import * as wpProduct from './wpProduct'; export default { + area, customer, dict, locales, diff --git a/src/servers/api/typings.d.ts b/src/servers/api/typings.d.ts index bc88ecc..0ecd7f2 100644 --- a/src/servers/api/typings.d.ts +++ b/src/servers/api/typings.d.ts @@ -7,6 +7,37 @@ declare namespace API { postal_code?: string; }; + type Area = { + id?: number; + /** 区域名称 */ + name?: string; + /** 创建时间 */ + createdAt: string; + /** 更新时间 */ + updatedAt: string; + }; + + type areacontrollerDeleteareaParams = { + id: number; + }; + + type areacontrollerGetareabyidParams = { + id: number; + }; + + type areacontrollerGetarealistParams = { + /** 当前页码 */ + currentPage?: number; + /** 每页数量 */ + pageSize?: number; + /** 区域名称 */ + name?: string; + }; + + type areacontrollerUpdateareaParams = { + id: number; + }; + type BatchSetSkuDTO = { /** sku 数据列表 */ skus?: SkuItemDTO[]; @@ -34,6 +65,11 @@ declare namespace API { items?: Dict[]; }; + type CreateAreaDTO = { + /** 区域名称 */ + name?: string; + }; + type CreateBrandDTO = { /** 品牌名称 */ title: string; @@ -64,12 +100,12 @@ declare namespace API { description?: string; /** 产品 SKU */ sku?: string; - /** 品牌 */ - brand?: DictItemDTO; - /** 规格 */ - strength?: DictItemDTO; - /** 口味 */ - flavor?: DictItemDTO; + /** 品牌 ID */ + brandId?: number; + /** 规格 ID */ + strengthId?: number; + /** 口味 ID */ + flavorsId?: number; humidity?: string; /** 价格 */ price?: number; @@ -177,13 +213,6 @@ declare namespace API { id: number; }; - type DictItemDTO = { - /** 显示名称 */ - title?: string; - /** 唯一标识 */ - name: string; - }; - type DisableSiteDTO = {}; type localecontrollerGetlocaleParams = { @@ -705,6 +734,12 @@ declare namespace API { sku?: string; /** 价格 */ price?: number; + /** 促销价格 */ + promotionPrice?: number; + /** 库存 */ + stock?: number; + /** 来源 */ + source?: number; /** 创建时间 */ createdAt: string; /** 更新时间 */ @@ -906,6 +941,15 @@ declare namespace API { items?: PurchaseOrderDTO[]; }; + type QueryAreaDTO = { + /** 当前页码 */ + currentPage?: number; + /** 每页数量 */ + pageSize?: number; + /** 区域名称 */ + name?: string; + }; + type QueryBrandDTO = { /** 页码 */ current?: number; @@ -1569,6 +1613,11 @@ declare namespace API { minute?: string; }; + type UpdateAreaDTO = { + /** 区域名称 */ + name?: string; + }; + type UpdateBrandDTO = { /** 品牌名称 */ title?: string; @@ -1594,12 +1643,12 @@ declare namespace API { description?: string; /** 产品 SKU */ sku?: string; - /** 品牌 */ - brand?: DictItemDTO; - /** 规格 */ - strength?: DictItemDTO; - /** 口味 */ - flavor?: DictItemDTO; + /** 品牌 ID */ + brandId?: number; + /** 规格 ID */ + strengthId?: number; + /** 口味 ID */ + flavorsId?: number; humidity?: string; /** 价格 */ price?: number;