/* === 产品维护页面 (基于 wrtb_info_product 表) === */ /* global React, useToast, ConfirmModal */ const { useState, useEffect } = React; function PageOperationProduct() { const toast = useToast(); const [products, setProducts] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [filters, setFilters] = useState({ category: '', manager_id: '', featured_tag: '' }); const [categories, setCategories] = useState([]); const [managers, setManagers] = useState([]); const [featuredTags, setFeaturedTags] = useState([]); const [ratingOptions, setRatingOptions] = useState([]); const [showDrawer, setShowDrawer] = useState(false); const [drawerMode, setDrawerMode] = useState('view'); const [currentProduct, setCurrentProduct] = useState(null); const [formData, setFormData] = useState({}); const [confirmModalVisible, setConfirmModalVisible] = useState(false); const [confirmConfig, setConfirmConfig] = useState({ title: '', message: '', onConfirm: () => {} }); const fetchProducts = async () => { try { const params = new URLSearchParams({ page, page_size: pageSize, ...filters }); const data = await window.apiFetch(`/api/info-products/?${params}`); setProducts(data.data || []); setTotal(data.total || 0); } catch (error) { console.error('获取产品列表失败:', error); toast.push('获取产品列表失败', 'error'); } }; const fetchOptions = async () => { try { const [catRes, mgrRes, tagRes, ratingRes] = await Promise.all([ window.apiFetch('/api/info-products/categories/list'), window.apiFetch('/api/info-products/managers/list'), window.apiFetch('/api/info-products/featured-tags/list'), window.apiFetch('/api/dict/product_level') ]); setCategories(catRes.data || []); setManagers(mgrRes.data || []); setFeaturedTags(tagRes.data || []); setRatingOptions(ratingRes.data || []); } catch (error) { console.error('获取选项失败:', error); } }; useEffect(() => { fetchProducts(); fetchOptions(); }, [page, pageSize, filters]); const handleFilterChange = (key, value) => { setFilters(prev => ({ ...prev, [key]: value })); setPage(1); }; const handleView = async (productId) => { try { const data = await window.apiFetch(`/api/info-products/${productId}`); setCurrentProduct(data); setDrawerMode('view'); setShowDrawer(true); } catch (error) { console.error('获取产品详情失败:', error); toast.push('获取产品详情失败', 'error'); } }; const handleCreate = () => { setFormData({}); setDrawerMode('create'); setShowDrawer(true); }; const handleEdit = async (productId) => { try { const data = await window.apiFetch(`/api/info-products/${productId}`); setFormData(data); setDrawerMode('edit'); setShowDrawer(true); } catch (error) { console.error('获取产品详情失败:', error); toast.push('获取产品详情失败', 'error'); } }; const handleDelete = (productId, productName) => { setConfirmConfig({ title: '确认删除', message: `确定要删除产品 "${productName}" 吗?`, onConfirm: async () => { try { const options = { method: 'DELETE' }; await window.apiFetch(`/api/info-products/${productId}`, options); toast.push('删除成功', 'success'); fetchProducts(); } catch (error) { toast.push('删除失败: ' + (error.message || '未知错误'), 'error'); } } }); setConfirmModalVisible(true); }; const handleSave = async () => { try { const options = { method: drawerMode === 'create' ? 'POST' : 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }; const url = drawerMode === 'create' ? '/api/info-products/' : `/api/info-products/${formData.productId}`; await window.apiFetch(url, options); toast.push(drawerMode === 'create' ? '创建成功' : '更新成功', 'success'); setShowDrawer(false); fetchProducts(); } catch (error) { toast.push('保存失败: ' + (error.message || '未知错误'), 'error'); } }; const basicFields = [ { key: 'productId', label: '产品ID', english: 'product_id', required: true }, { key: 'productName', label: '产品名称', english: 'product_name', required: true }, { key: 'productCode', label: '产品代码', english: 'product_code', required: true }, { key: 'managerId', label: '管理人ID', english: 'manager_id' }, { key: 'managerName', label: '管理人名称', english: 'manager_name' }, { key: 'category', label: '产品分类', english: 'category', type: 'select', options: categories }, { key: 'rating', label: '评级', english: 'rating', type: 'select', options: ratingOptions }, { key: 'featuredTag', label: '特色标签', english: 'featured_tag', type: 'multiselect', options: featuredTags } ]; const indicatorFields = [ { key: 'latestNav', label: '最新净值', english: 'latest_nav', type: 'number' }, { key: 'latestDate', label: '最新日期', english: 'latest_date' }, { key: 'annualReturn', label: '年化收益', english: 'annual_return', type: 'number' }, { key: 'maxDrawdown', label: '最大回撤', english: 'max_drawdown', type: 'number' }, { key: 'currentDrawdown', label: '当前回撤', english: 'current_drawdown', type: 'number' }, { key: 'sharpe', label: '夏普比率', english: 'sharpe', type: 'number' }, { key: 'calmar', label: '卡玛比率', english: 'calmar', type: 'number' }, { key: 'annualVolatility', label: '年化波动率', english: 'annual_volatility', type: 'number' } ]; const evaluationFields = [ { key: 'conclusion', label: '系统结论', english: 'conclusion', type: 'textarea' }, { key: 'risk', label: '主要风险', english: 'risk', type: 'textarea' } ]; const listFields = [ { key: 'productId', label: '产品ID' }, { key: 'productName', label: '产品名称' }, { key: 'productCode', label: '产品代码' }, { key: 'category', label: '分类' }, { key: 'managerName', label: '管理人' }, { key: 'latestNav', label: '最新净值' }, { key: 'annualReturn', label: '年化收益' }, { key: 'sharpe', label: '夏普' } ]; const fmtNum = (v, decimals = 4) => { if (v == null) return '-'; return typeof v === 'number' ? v.toFixed(decimals) : v; }; return (

产品明细

共 {total} 条记录

{listFields.map(field => ( ))} {products.length === 0 ? ( ) : ( products.map((product, index) => ( {listFields.map(field => { let value = product[field.key]; if (field.key === 'category' && typeof value === 'object' && value !== null) { value = value.name || '-'; } else if (field.key === 'featured_tag') { if (Array.isArray(value)) { value = value.length > 0 ? value.join(', ') : '-'; } else if (typeof value === 'object' && value !== null) { value = value.name || '-'; } else { value = value || '-'; } } else if (field.key === 'latestNav' || field.key === 'annualReturn' || field.key === 'sharpe') { value = fmtNum(value); } else { value = value || '-'; } return ; })} )) )}
{field.label}操作
暂无数据
{value}
共 {total} 条记录,当前第 {page} / {Math.ceil(total / pageSize) || 1} 页
第 {page} 页
{showDrawer && (
setShowDrawer(false)}>
e.stopPropagation()}>
{drawerMode === 'view' ? '产品详情' : drawerMode === 'create' ? '新增产品' : '修改产品'}
{drawerMode === 'view' ? ( <>
基础信息
{basicFields.map(field => (
{currentProduct && (currentProduct[field.key] || '-')}
))}
核心指标
{indicatorFields.map(field => (
{currentProduct && ( field.type === 'number' ? fmtNum(currentProduct[field.key]) : (currentProduct[field.key] || '-') )}
))}
评估结论
{evaluationFields.map(field => (
{currentProduct && (currentProduct[field.key] || '-')}
))}
) : ( <>
基础信息
{basicFields.map(field => (
{field.type === 'select' ? ( ) : field.type === 'multiselect' ? ( ) : ( setFormData(prev => ({ ...prev, [field.key]: e.target.value }))} disabled={field.key === 'productId' && drawerMode === 'edit'} /> )}
))}
评估结论
{evaluationFields.map(field => (