移除分类图片
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Button, Image, Tag, Typography} from 'antd';
|
||||
import React, {useEffect, useMemo, useState} from 'react';
|
||||
import {Button, Form, Tag, TreeSelect, Typography} from 'antd';
|
||||
import type {FormInstance} from 'antd';
|
||||
import {NodeExpandOutlined} from '@ant-design/icons';
|
||||
import XinTable from '@/components/XinTable';
|
||||
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
||||
@@ -10,6 +11,45 @@ import {getCategoryTable, getCategoryTree} from '@/api/product/category.ts';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
/**
|
||||
* 上级分类选择(分类最多二级):
|
||||
* - 二级分类禁选(不能作为上级,否则出现三级)
|
||||
* - 编辑时禁选自身
|
||||
* - 编辑的分类含子分类时禁选所有一级分类(只能保持顶级,否则其子分类会变成三级)
|
||||
*/
|
||||
const ParentCategorySelect: React.FC<{
|
||||
form: FormInstance;
|
||||
tree: IProductCategoryTree[];
|
||||
value?: number;
|
||||
onChange?: (value: number) => void;
|
||||
}> = ({ form, tree, value, onChange }) => {
|
||||
// id/children 由 XinTable 编辑时 setFieldsValue(record) 写入(非表单项,需传 form 监听)
|
||||
const editingId = Form.useWatch('id', form);
|
||||
const children = Form.useWatch('children', form);
|
||||
const hasChildren = Array.isArray(children) && children.length > 0;
|
||||
|
||||
const treeData = useMemo(() => {
|
||||
const walk = (nodes: IProductCategoryTree[], depth: number): IProductCategoryTree[] =>
|
||||
nodes.map((node) => ({
|
||||
...node,
|
||||
disabled: depth >= 2 || node.id === editingId || (hasChildren && depth >= 1),
|
||||
children: node.children?.length ? walk(node.children, depth + 1) : node.children,
|
||||
}));
|
||||
return [{ id: 0, name: '顶级分类', children: walk(tree, 1) }];
|
||||
}, [tree, editingId, hasChildren]);
|
||||
|
||||
return (
|
||||
<TreeSelect
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
treeData={treeData}
|
||||
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
|
||||
placeholder="默认顶级分类"
|
||||
treeDefaultExpandAll
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 递归收集全部节点 id(用于展开整棵树)
|
||||
*/
|
||||
@@ -52,16 +92,10 @@ const ProductCategoryPage: React.FC = () => {
|
||||
{
|
||||
title: '上级分类',
|
||||
dataIndex: 'parent_id',
|
||||
valueType: 'treeSelect',
|
||||
hideInTable: true,
|
||||
hideInSearch: true,
|
||||
initialValue: 0,
|
||||
fieldProps: {
|
||||
treeData: [{ id: 0, name: '顶级分类', children: categoryTree }],
|
||||
fieldNames: { label: 'name', value: 'id', children: 'children' },
|
||||
placeholder: '默认顶级分类',
|
||||
treeDefaultExpandAll: true,
|
||||
},
|
||||
fieldRender: (form) => <ParentCategorySelect form={form} tree={categoryTree} />,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
@@ -90,31 +124,6 @@ const ProductCategoryPage: React.FC = () => {
|
||||
},
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '分类图标',
|
||||
dataIndex: 'icon_id',
|
||||
valueType: 'image',
|
||||
fieldProps: {
|
||||
action: '/product/category/upload',
|
||||
mode: 'single',
|
||||
maxCount: 1,
|
||||
changeType: "id"
|
||||
},
|
||||
render: (_, record: IProductCategory) => {
|
||||
const url = record.icon_url
|
||||
if (!url) return '-';
|
||||
return (
|
||||
<Image
|
||||
src={url}
|
||||
width={32}
|
||||
height={32}
|
||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||
/>
|
||||
);
|
||||
},
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'updated_at',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Button, Card,
|
||||
Drawer,
|
||||
@@ -41,6 +41,22 @@ const PRICE_TYPE_OPTIONS = [
|
||||
/** 四舍五入保留两位 */
|
||||
const round2 = (v: number) => Math.round(v * 100) / 100;
|
||||
|
||||
/** 商品表单用分类树:有子分类的节点禁选(商品只能挂在末级分类上) */
|
||||
const markParentDisabled = (nodes: IProductCategoryTree[]): IProductCategoryTree[] =>
|
||||
nodes.map((node) => ({
|
||||
...node,
|
||||
disabled: !!node.children?.length,
|
||||
children: node.children?.length ? markParentDisabled(node.children) : node.children,
|
||||
}));
|
||||
|
||||
/** 侧栏用分类树:有子分类的节点不可选中(仅供展开,筛选按末级分类) */
|
||||
const markParentUnselectable = (nodes: IProductCategoryTree[]): IProductCategoryTree[] =>
|
||||
nodes.map((node) => ({
|
||||
...node,
|
||||
selectable: !node.children?.length,
|
||||
children: node.children?.length ? markParentUnselectable(node.children) : node.children,
|
||||
}));
|
||||
|
||||
/**
|
||||
* 等级价格表单:每个等级 = 计价类型(固定价/成本百分比)+ 对应输入框;
|
||||
* 切换计价类型时按成本价自动换算(固定→百分比:percent=(price/cost-1)*100;百分比→固定:price=cost*(1+percent/100))
|
||||
@@ -159,6 +175,10 @@ const ProductGoodsPage: React.FC = () => {
|
||||
const [levels, setLevels] = useState<ICustomerLevel[]>([]);
|
||||
const [suppliers, setSuppliers] = useState<ISupplier[]>([]);
|
||||
const [categoryTree, setCategoryTree] = useState<IProductCategoryTree[]>([]);
|
||||
// 商品表单专用分类树:父分类禁选,仅末级可选(侧栏筛选/价格矩阵仍用原始树)
|
||||
const formCategoryTree = useMemo(() => markParentDisabled(categoryTree), [categoryTree]);
|
||||
// 侧栏分类树:父分类不可选中,仅作展开归组
|
||||
const sidebarCategoryTree = useMemo(() => markParentUnselectable(categoryTree), [categoryTree]);
|
||||
|
||||
// ===== 分类侧栏 =====
|
||||
const [activeCategory, setActiveCategory] = useState<number | undefined>(undefined);
|
||||
@@ -476,12 +496,12 @@ const ProductGoodsPage: React.FC = () => {
|
||||
align: "center",
|
||||
rules: [{ required: true, message: '请选择分类' }],
|
||||
fieldProps: {
|
||||
treeData: categoryTree,
|
||||
treeData: formCategoryTree,
|
||||
fieldNames: { label: 'name', value: 'id', children: 'children' },
|
||||
treeDefaultExpandAll: true,
|
||||
showSearch: true,
|
||||
treeNodeFilterProp: 'name',
|
||||
placeholder: '选择分类',
|
||||
placeholder: '选择末级分类',
|
||||
},
|
||||
hideInSearch: true,
|
||||
render: (_, record) =>
|
||||
@@ -647,7 +667,7 @@ const ProductGoodsPage: React.FC = () => {
|
||||
showLine
|
||||
blockNode
|
||||
onSelect={(selectedKeys) => setActiveCategory(Number(selectedKeys[0]))}
|
||||
treeData={categoryTree}
|
||||
treeData={sidebarCategoryTree}
|
||||
selectedKeys={activeCategory ? [activeCategory] : undefined}
|
||||
fieldNames={{title: 'name', key: 'id', children: 'children'}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user