Commit 2bc5a6d5 authored by 许佳敏's avatar 许佳敏

Merge branch 'feat-material-1' into 'v2-220418'

新增物料功能 See merge request linkseeks-design/pro-platform!123
parents 617fdfeb 25b814b6
......@@ -17,9 +17,9 @@ import MemberRoute from './memberRoute' // 会员能力路由
// import DealAbilityRoute from './dealAbilityRoute'; //
// import marketingRoute from './marketingRoute';
// import BalancedRoute from './balanceRoute';
import OrderRoute from './orderRoute'; // 订单能力
// import OrderRoute from './orderRoute'; // 订单能力
import asyncRoutes from '../router.config.json';
import ProcurementRoute from './procurementRoute'; // todo wuting
// import ProcurementRoute from './procurementRoute'; // todo wuting
// import { callForBidsRoute } from './procurementRoute/callForBids';
// import { purchaseInquiryRoute } from './procurementRoute/purchaseInquiry';
......@@ -34,8 +34,8 @@ const homeRoute = {
key: 'home',
component: '@/pages/home',
};
// isDev ? [ homeRoute, OrderRoute ] :
const routes = isDev ? [ homeRoute, OrderRoute ] : asyncRoutes;
const routes = isDev ? [ homeRoute ] : asyncRoutes;
// const routes = isDev ? [ CommodityRoute ] : asyncRoutes;
const memberCenterRoute = {
path: '/memberCenter',
......
import { RouterChild } from '../utils/index';
const MaterialRoute: RouterChild = {
path: '/memberCenter/material',
name: '物料',
routes: [
{
path: '/memberCenter/material/group',
name: '物料组',
component: '@/pages/commodity/material/materialGroup',
},
// {
// path: '/memberCenter/material/props',
// name: '物料属性',
// component: '@/pages/commodity/material/materialProps'
// },
{
path: '/memberCenter/material/query',
name: '物料查询',
component: '@/pages/commodity/material/materialQuery'
},
{
path: '/memberCenter/material/query/detail',
name: '物料详情',
component: '@/pages/commodity/material/materialQuery/detail',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/query/sourceList',
name: '货源清单',
component: '@/pages/commodity/material/materialQuery/sourceList',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/pendingAdd',
name: '待新增物料',
component: '@/pages/commodity/material/materialPendingAdd',
},
{
path: '/memberCenter/material/pendingAdd/add',
name: '新增物料',
noMargin: true,
component: '@/pages/commodity/material/materialPendingAdd/add',
hideInMenu: true,
},
{
path: '/memberCenter/material/pendingAdd/edit',
name: '修改物料',
noMargin: true,
component: '@/pages/commodity/material/materialPendingAdd/add',
hideInMenu: true,
},
{
path: '/memberCenter/material/pendingAdd/detail',
name: '查看物料详情',
noMargin: true,
component: '@/pages/commodity/material/materialPendingAdd/add',
hideInMenu: true,
},
{
path: '/memberCenter/material/pendingExamI',
name: '待审核一级',
component: '@/pages/commodity/material/materialPendingExamI'
},
{
path: '/memberCenter/material/pendingExamI/detail',
name: '物料详情',
component: '@/pages/commodity/material/materialPendingExamI/detail',
noMargin: true,
},
{
path: '/memberCenter/material/pendingExamII',
name: '待审核二级',
component: '@/pages/commodity/material/materialPendingExamII'
},
{
path: '/memberCenter/material/pendingExamII/detail',
name: '待审核二级详情',
component: '@/pages/commodity/material/materialPendingExamII/detail',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/pendingExamChangeI',
name: '待审核变更一级',
component: '@/pages/commodity/material/materialPendingExamChangeI'
},
{
path: '/memberCenter/material/pendingExamChangeI/detail',
name: '待审核二级详情',
component: '@/pages/commodity/material/materialPendingExamChangeI/detail',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/pendingExamChangeII',
name: '待审核变更二级',
component: '@/pages/commodity/material/materialPendingExamChangeII'
},
{
path: '/memberCenter/material/pendingExamChangeII/detail',
name: '待审核二级详情',
component: '@/pages/commodity/material/materialPendingExamChangeII/detail',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/materialAuditProcessConfig',
name: '物料审核流程规则配置',
component: '@/pages/commodity/material/materialAuditProcessConfig'
},
{
path: '/memberCenter/material/materialAuditProcessConfig/add',
name: '新增物料审核流程规则配置',
component: '@/pages/commodity/material/materialAuditProcessConfig/add',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/materialAuditProcessConfig/edit',
name: '修改物料审核流程规则配置',
component: '@/pages/commodity/material/materialAuditProcessConfig/add',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/material/materialAuditProcessConfig/detail',
name: '查看物料审核流程规则配置',
component: '@/pages/commodity/material/materialAuditProcessConfig/add',
noMargin: true,
hideInMenu: true
}
]
}
export default MaterialRoute
......@@ -59,7 +59,14 @@ registerVirtualBox('flex-layout', (_props) => {
return (
<RowLayout style={rowStyle}>
{
children.map((v, i) => <Col style={colStyle} key={i}>{v}</Col>)
children.map((v, i) => {
const visible = v?.props.schema?.visible ?? true
console.log(v.props)
if (!visible) {
return null;
}
return <Col style={colStyle} key={i}>{v}</Col>
})
}
</RowLayout>
)
......
/**
* 物料内部状态枚举
*/
/** 已冻结 */
export const FROZEN = 0;
/** 待新增物料 */
export const PENDING_ADD_MATERIAL = 1;
/** 待审核物料(一级) */
export const PENDING_EXAM_I = 2;
/** 一级审核不通过 */
export const EXAM_I_FAIL = 3;
/** 待审核物料(二级) */
export const PENDING_EXAM_II = 4;
/** 待审核物料(二级)不通过 */
export const EXAM_II_FAIL = 5;
/**
* 6: {status: 51, name: "待提交审核"}
7: {status: 52, name: "待审核物料(一级)"}
8: {status: 53, name: "一级审核不通过"}
9: {status: 54, name: "待审核物料(二级)"}
10: {status: 55, name: "二级审核不通过"}
11: {status: 99, name: "已确认"}
*/
/** 待提交审核 */
export const PENDING_SUBMIT_EXAM = 51;
/** 待审核变更一级 */
export const PENDING_EXAM_CHANGE_I = 52
/** 待审核变更一级 */
export const PENDING_EXAM_CHANGE_I_FAIL = 53;
/** 待提交变更二级 */
export const PENDING_EXAM_CHANGE_II = 54;
/** 待审核变更二级不通过 */
export const PENDING_EXAM_CHANGE_II_FAIL = 55
/** 已确认 */
export const HAS_CONFIRM = 99
\ No newline at end of file
import { Link } from "umi"
type Params = {
detailUrl: string,
extraColumn: any[]
}
export function getColumn(params: Params) {
const columns = [
{
title: '物料编号',
dataIndex: 'code',
render: (text, record) => {
return <Link to={`${params.detailUrl}?id=${record.id}`}>{text}</Link>
}
},
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '物料组',
dataIndex: 'materialGroup'
},
{
title: '规格型号',
dataIndex: 'type'
},
{
title: '品类',
dataIndex: 'category',
render: (text, record) => {
return (
<div>{record.customerCategory?.name}</div>
)
}
},
{
title: '品牌',
dataIndex: 'brand',
render: (text, record) => {
return (
<div>{record.brand?.name}</div>
)
}
},
{
title: '单位',
dataIndex: 'unitName'
},
{
title: '目录价',
dataIndex: 'costPrice'
},
{
title: '内部状态',
dataIndex: 'interiorStateName',
}
]
return columns.concat(params?.extraColumn || []);
}
export const columns = []
\ No newline at end of file
import { getIntl } from 'umi';
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
const intl = getIntl();
type Options = {
showStatus: boolean
}
export const getSchema = (options: Options) => {
const schema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
properties: {
topLayout: {
type: 'object',
'x-component': 'Mega-Layout',
'x-component-props': {
grid: true,
},
properties: {
ctl: {
type: 'object',
"x-component": 'controllerBtns'
},
code: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '物料编号',
},
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
colStyle: {
marginLeft: 20,
},
},
properties: {
name: {
type: 'string',
'x-component-props': {
placeholder: '物料名称',
allowClear: true,
},
},
materialGroupId: {
type: 'string',
'x-component': 'Cascader',
'x-component-props': {
placeholder: '物料组',
allowClear: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
style: { width: '150px' },
showSearch: true
},
},
brandId: {
type: 'string',
enum: [],
'x-component-props': {
placeholder: '品牌',
allowClear: true,
showSearch: true,
style: { width: '150px' },
},
},
categoryId: {
type: 'string',
'x-component': 'Cascader',
'x-component-props': {
placeholder: '品类',
allowClear: true,
style: { width: '150px' },
showSearch: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
},
},
status: {
type: 'string',
visible: options.showStatus,
'x-component-props': {
placeholder: '内部状态',
allowClear: true,
enum: [],
},
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: intl.formatMessage({id: 'handling.chaxun'}),
},
},
},
},
},
},
},
};
return schema;
}
import StatusTag from '@/components/StatusTag';
import { GetProductGoodsGetGoodsProcessDetailResponse } from '@/services/ProductV2Api';
import { findLastIndexFlowState } from '@/utils';
import { useMemo, useState } from 'react';
import FileItem from '../components/fileItem';
type Options<T> = {
initialValue: T
}
/**
* 该hook 作为获取详情页进本信息
* @param options
*/
function useGetDetailCommon<T extends GetProductGoodsGetGoodsProcessDetailResponse>(options: Options<T>) {
const { initialValue } = options;
const anchorHeader = useMemo(() => [
{
key: 'process',
name: '流转记录'
},
{
key: 'basic',
name: '基本信息'
},
{
key: 'properties',
name: '物料规格'
},
{
key: 'images',
name: '物料图片'
},
{
key: 'files',
name: '物料图片'
},
{
key: 'log',
name: '物料图片'
}
], []);
const basicInfoList = useMemo(() => {
return [
{
title: '物料编号',
value: initialValue?.code
},
{
title: '单位',
value: initialValue?.unitName
},
{
title: '物料名称',
value: initialValue?.name
},
{
title: '目录价',
value: initialValue?.costPrice
},
{
title: '规格型号',
value: 'M20型号'
},
{
title: '品牌',
value: initialValue?.brand?.name
},
{
title: '所属物料组',
value: initialValue?.materialGroup?.name
},
{
title: '备注',
value: '备注唔'
},
{
title: '品类',
value: (
<div>
{
initialValue?.customerCategory?.name
}
</div>
)
},
{
title: '物料状态',
value: (
<StatusTag title={initialValue?.interiorStateName} type="primary" />
)
}
]
}, [initialValue])
/** 动态物料属性 */
const properties = useMemo(() => {
if (!initialValue?.materialAttributeList) {
return [];
}
const result = initialValue?.materialAttributeList.map((_item) => {
return {
title: _item.customerAttribute.name,
value: _item.customerAttributeValueList.map((_row) => {
return _row.value
}).join('')
}
})
return result
}, [initialValue?.materialAttributeList])
/**
* 获取当前工作流
*/
const auditProcess = useMemo(() => {
const innerVerifySteps: {
step: number,
stepName: string,
roleName: string,
status: 'finish' | 'wait',
}[] = initialValue?.simpleProcessDefVO ?
initialValue?.simpleProcessDefVO?.tasks?.map(item => ({
step: item.taskStep,
stepName: item.taskName,
roleName: item.roleName,
status: initialValue?.simpleProcessDefVO?.currentStep > item.taskStep ? 'finish' : 'wait',
})) :
[]
console.log(innerVerifySteps)
const innerVerifyCurrent = findLastIndexFlowState(initialValue?.simpleProcessDefVO.tasks)
const outerVerifyCurrent = 0
const outerVerifySteps = null
return {
innerVerifySteps,
outerVerifySteps,
innerVerifyCurrent,
outerVerifyCurrent
}
}, [initialValue])
/** 附件column */
const tableColumn = useMemo(() => {
return [
{
title: '附件',
render: (text, record) => {
const value = { name: '设计图纸pdf', url: '' };
return (
<FileItem value={value} />
)
}
},
{
title: '备注',
dataIndex: 'desc'
}
]
}, [initialValue])
/**
* 内部单据流转记录
*/
const recordColumn = useMemo(() => {
return [
{
title: '流转记录',
dataIndex: 'id',
render: (text, record, index) => {
return (
<div>{index}</div>
)
}
},
{
title: '操作人',
dataIndex: 'roleName'
},
{
title: '部门',
dataIndex: 'department'
},
{
title: '职位',
dataIndex: 'position'
},
{
title: '状态',
dataIndex: 'state'
},
{
title: '操作',
dataIndex: 'operation'
},
{
title: '操作时间',
dataIndex: 'createTime'
},
{
title: '备注',
dataIndex: 'auditOpinion'
}
]
}, [])
return { anchorHeader: anchorHeader, auditProcess, basicInfoList, tableColumn, recordColumn, properties }
}
export default useGetDetailCommon
\ No newline at end of file
import { useEffect, useState } from "react";
type ResponseType<T> = {
data: T,
code: number,
message: string
}
type Options<Q, R> = {
id: string;
api: (params: { id: string }) => Promise<ResponseType<Q>>
logApi: (params: { goodsId: string }) => Promise<ResponseType<R>>
}
function useGetInitialValueDetail<T, R extends any[]>(options: Options<T, R>) {
const [initialValue, setInitialValue] = useState<T>(null);
const [record, setRecord] = useState<R>([] as R);
useEffect(() => {
async function getInitialValue() {
const { data, code } = await options.api({id: options.id})
if (code === 1000) {
setInitialValue(data)
}
}
getInitialValue();
}, [])
useEffect(() => {
if (!options.logApi) {
return;
}
async function getLogger() {
const { data, code } = await options.logApi({goodsId: options.id})
if (code === 1000) {
setRecord(data)
}
}
getLogger();
}, [])
return { initialValue, record };
}
export default useGetInitialValueDetail
\ No newline at end of file
import { getProductCustomerGetCustomerCategoryTree, getProductGoodsGetInnerStatus, getProductMaterialGroupTree, getProductSelectGetSelectBrand, getProductSelectGetSelectCnUnit } from "@/services/ProductV2Api"
import { createFormActions, FormEffectHooks, FormPath } from "@formily/antd"
const { onFormMount$ } = FormEffectHooks
const EMPTY_ARRAY = []
export const EMPTY = {
totalCount: 0,
data: [],
}
export const useAsyncCascader = async (name, service: () => Promise<any[]>) => {
const { setFieldState } = createFormActions()
onFormMount$().subscribe(() => {
service().then(res => {
setFieldState(name, state => {
FormPath.setIn(state, 'props.x-component-props.options', res)
})
}).catch(err => {
setFieldState(name, state => {
FormPath.setIn(state, 'props.x-component-props.options', [])
})
})
})
}
export const fetchTreeData = async () => {
try {
const { data, code } = await getProductMaterialGroupTree({ rootNodeId: '0' });
if (code === 1000) {
return data;
}
return EMPTY_ARRAY
} catch {
return EMPTY_ARRAY
}
}
export const fetchCategoryData = async () => {
try {
const { data, code } = await getProductCustomerGetCustomerCategoryTree();
if (code === 1000) {
return data;
}
return EMPTY_ARRAY
} catch {
return EMPTY_ARRAY
}
}
export const fetchBrand = async () => {
try {
const { data, code } = await getProductSelectGetSelectBrand();
if (code === 1000) {
return data;
}
return EMPTY_ARRAY
} catch {
return EMPTY_ARRAY
}
}
export const fetchStatus = async () => {
try {
const { data, code } = await getProductGoodsGetInnerStatus();
if (code === 1000) {
return data
}
return EMPTY_ARRAY
} catch {
return EMPTY_ARRAY
}
}
export const fetchUnit = async () => {
try {
const { data, code } = await getProductSelectGetSelectCnUnit();
if (code === 1000) {
return data.map((_item) => ({ name: _item.name, id: _item.id}))
}
return EMPTY_ARRAY
} catch {
return EMPTY_ARRAY
}
}
\ No newline at end of file
import React from 'react';
interface Iprops {
value: {
url: string,
name: string
}
}
const Files: React.FC<Iprops> & { isFieldComponent: boolean } = (props: Iprops) => {
// console.log()
const { value } = props
return (
<a href={value?.url || ''}>{value?.name || ''} </a>
)
}
Files.isFieldComponent = true
export default Files;
\ No newline at end of file
import React from 'react';
import { TreeSelect } from 'antd';
const { TreeNode } = TreeSelect;
interface Iprops {
props: {
enum: any[]
['x-component-props']: {
currentId: number
}
},
value: string,
mutators: {
change: (params: any) => void
remove: (index: number) => void
},
}
const FormilyTreeSelect: React.FC<Iprops> & { isFieldComponent: boolean } = (props: Iprops) => {
const { mutators, value } = props;
const options = props.props.enum || [];
const xComponentProps = props.props['x-component-props'];
const onChange = (value) => {
mutators.change(value);
}
const subTreeNode = (subTree) => {
return (
subTree.map((_item) => {
if (_item.id === xComponentProps.currentId) {
return subTreeNode(_item.children)
}
return (
<TreeNode value={_item.id} title={_item.title} key={_item.id}>
{
subTreeNode(_item.children)
}
</TreeNode>
)
})
)
}
return (
<TreeSelect
showSearch
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
allowClear
treeDefaultExpandAll
onChange={onChange}
value={value}
>
{
subTreeNode(options)
}
</TreeSelect>
)
}
FormilyTreeSelect.isFieldComponent = true
export default FormilyTreeSelect;
\ No newline at end of file
import UploadFiles from '@/components/UploadFiles/UploadFiles';
import { PlusOutlined } from '@ant-design/icons';
import { FormItemShallowProvider, FormPath, ISchema, Schema, SchemaField } from '@formily/antd';
import { isArr, toArr } from '@formily/antd/esm/shared';
import { Table, Form } from 'antd';
import { UploadChangeParam } from 'antd/lib/upload';
import React from 'react';
interface Iprops {
value: any;
editable: boolean,
schema: Schema,
path: any;
props: {
['x-component-props']: {
/**
* ☹
* 这里表现相对奇怪,当这个组件放在了table 下切这个组件是必填的话,会出现两个错误情况,我通过外部传值干掉其中一个
*/
showError?: boolean,
} & {
[key: string]: any
},
},
mutators: {
change: (params: any) => void
remove: (index: number) => void
},
}
const FormilyUploadEnclosure: React.FC<Iprops> & { isFieldComponent: boolean } = (props: Iprops) => {
const { value, schema, path, editable, mutators } = props;
const {
renderAddition,
renderRemove,
// renderEmpty,
// renderExtraOperations,
operationsWidth,
operations,
...componentProps
} = schema.getExtendsComponentProps() || {}
const renderColumns = (items: Schema) => {
return items.mapProperties((props, key) => {
const itemProps = {
...props.getExtendsItemProps(),
...props.getExtendsProps()
}
return {
title: props.title,
...itemProps,
key,
dataIndex: key,
render: (value: any, record: any, index: number) => {
const newPath = FormPath.parse(path).concat(index, key)
return (
<FormItemShallowProvider
key={newPath.toString()}
label={undefined}
labelCol={undefined}
wrapperCol={undefined}
>
<SchemaField path={newPath} schema={props} />
</FormItemShallowProvider>
)
}
}
})
}
let columns = []
if (schema.items) {
columns = isArr(schema.items)
? schema.items.reduce((buf, items) => {
return buf.concat(renderColumns(items))
}, [])
: renderColumns(schema.items)
}
if (editable && operations !== false) {
columns.push({
...operations,
key: 'operations',
dataIndex: 'operations',
width: operationsWidth || 200,
render: (value: any, record: any, index: number) => {
return (
<Form.Item>
<div className="array-item-operator">
<a
onClick={() => mutators.remove(index)}
>
删除
</a>
</div>
</Form.Item>
)
}
})
}
console.log(toArr(value));
const renderTable = () => {
return (
<Table
rowKey={record => {
return toArr(value).indexOf(record)
}}
pagination={false}
columns={columns}
dataSource={toArr(value)}
/>
)
}
return (
<div>
{
renderTable()
}
{renderAddition}
</div>
)
}
FormilyUploadEnclosure.isFieldComponent = true;
export default FormilyUploadEnclosure;
/**
* 详情页审核
* 用于详情页,一级审核, 二级审核,
* 这里本来想写根据schema,渲染table的。。。但感觉好像有问题
*/
import React, { useEffect, useMemo } from 'react';
import { createAsyncFormActions, createFormActions, ISchema, FormEffectHooks } from '@formily/antd';
import { Modal, Cascader } from 'antd';
import NiceForm from '@/components/NiceForm';
import { getIntl } from 'umi';
const formActions = createFormActions();
const intl = getIntl();
export type AddressOptionType = {
label: string,
value: string | number,
}
interface Iprops {
/**
* 显示/隐藏
*/
visible: boolean,
/**
* 模态框标题
*/
title: string,
/**
* value, 表单值
*/
value?: { [key: string]: any },
/**
* 是否显示label
*/
showLabel?: boolean
/**
* align: label 显示方式
*/
align?: "left" | "top",
/**
* 提交
*/
onSubmit: (value: {[key: string]: any}) => void,
onCancel: () => void
}
export type SubmitDataTypes = {
/**
* 当审核不通过时,才有审核不通过原因填写
*/
reason?: string,
}
const FrozonMadal: React.FC<Iprops> = (props: Iprops) => {
const { title, visible, onSubmit, onCancel, value, showLabel, align } = props;
const schema: ISchema = useMemo(() => ({
type: 'object',
properties: {
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
// labelCol: showLabel ? 5 : 0,
labelAlign: align,
full: true,
// labelAlign: 'left'
},
properties: {
reason: {
title: '冻结',
type: 'textarea',
'x-rules': [
{
required: true,
message: intl.formatMessage({id: 'components.qingtianxieshenhebutongguo'}),
},
],
},
}
}
}
}), [showLabel, align])
const handleOk = () => {
formActions.submit()
}
const handleFormSubmit = (values: SubmitDataTypes) => {
const {...res } = values;
const postData = values
onSubmit?.(postData)
}
const handleCancel = () => {
onCancel?.();
}
// useEffect(() => {
// if (!visible) {
// return ;
// }
// if (!value) {
// return ;
// }
// if (value.status === 0) {
// formActions.setFieldState('status', state => {
// state.editable = false;
// })
// }
// }, [visible, value])
return (
<Modal
title={title}
visible={visible}
onOk={handleOk}
onCancel={handleCancel}
>
<NiceForm
value={value}
actions={formActions}
schema={schema}
onSubmit={handleFormSubmit}
/>
</Modal>
)
}
FrozonMadal.defaultProps = {
// schema: defaultSchema,
value: {},
showLabel: true,
align: "top",
}
export default FrozonMadal
\ No newline at end of file
import React from 'react';
interface Iprops {
imageUrls: string[];
}
const ImageList: React.FC<Iprops> = (props: Iprops) => {
const { imageUrls } = props;
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
{
imageUrls?.map((key) => {
return (
<div
key={key}
style= {{
paddingRight: '16px',
marginBottom: '8px'
}}
>
<img
style={{
width: '120px',
height: '120px',
border: '1px solid #F4F5F7',
borderRadius: '4px',
}}
src={key}
/>
</div>
)
})
}
</div>
)
}
export default ImageList;
\ No newline at end of file
import React, { useReducer, Reducer, useEffect } from 'react';
import { Tree } from 'antd';
import TreeNodeTitle from './treeNodeTitle';
type TreeProps = React.ComponentProps<typeof Tree>
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
export type onSelectParameters = Parameters<TreeProps['onSelect']>
export type onDropStartParameters = Parameters<TreeProps['onDragStart']>
type TreeDataType = {
title: string,
key: string,
children?: TreeDataType[]
}
interface Iprops extends TreeProps {
treeData: TreeDataType[],
onSortEnd?: (treeData: TreeDataType[]) => void,
draggable?: boolean,
onAdd?: (params: { parentKey: string; depth: number}) => void,
onDragEnd?: ({ startNode, endNode }: any) => void,
}
interface State {
selectNode: null | onSelectParameters[1]['node'],
selectedNodesKey: onSelectParameters[0],
treeData: TreeDataType[],
dragNode: null
}
interface SelectAction {
type: 'select',
payload: {
selectedKeys: onSelectParameters[0],
event: onSelectParameters[1]
},
}
interface SortAction {
type: 'sort',
payload: {
treeData: TreeDataType[]
}
}
/**
* 初始化treeData
*/
interface InitTreeData {
type: 'init',
payload: {
treeData: TreeDataType[]
}
}
type Action = SelectAction | SortAction | InitTreeData ;
const reducer: Reducer<State, Action> = (state, action) => {
switch (action.type) {
case 'select': {
return {
...state,
selectNode: !action.payload.event.selected ? null : action.payload.event.node,
selectedNodesKey: action.payload.selectedKeys
}
}
case 'sort': {
return {
...state,
treeData: action.payload.treeData
}
}
case 'init': {
return {
...state,
treeData: action.payload.treeData
}
}
default:
throw new Error()
}
}
const MaterialTree: React.FC<Iprops> = (props: Iprops) => {
const { onSelect, onSortEnd, draggable = false } = props
const [state, dispatch] = useReducer(reducer, {
selectNode: null,
selectedNodesKey: [],
treeData: props.treeData,
dragNode: null
});
useEffect(() => {
dispatch({
type: 'init',
payload: {
treeData: props.treeData
}
})
}, [props.treeData])
const onAdd = (params: { parentKey: string; depth: number }, e) => {
e.stopPropagation();
props.onAdd?.(params)
}
const onAddChildNode = (params: { parentKey: string; depth: number }, e) => {
e.stopPropagation();
props.onAdd?.(params)
}
const actions = {
onAdd: onAdd,
onAddChildNode: onAddChildNode,
}
/**
* 将title 转成带有 Icon 的dom
* @param treeData
*/
const transferData = (treeData: TreeDataType[], depth: number, parentKey: string | null) => {
let data = [];
for (let i = 0; i < treeData.length; i++) {
const current = treeData[i];
const isSelected = state.selectedNodesKey?.includes(current.key)
const title = (
<TreeNodeTitle
title={current.title}
currentKey={current.key}
actions={actions}
depth={depth}
isSelected={isSelected}
parentKey={parentKey}
/>
)
const children = current.children ? transferData(current.children, depth + 1, current.key) : [];
const withChildren = children.length > 0 ? { children: children } : {}
data[i] = {
...current,
title: title,
...withChildren,
}
}
return data;
}
const generateTreeData = transferData(state.treeData, 0, null);
const onDrop = info => {
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split('-');
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
const loop = (data, key, callback) => {
for (let i = 0; i < data.length; i++) {
if (data[i].key === key) {
return callback(data[i], i, data);
}
if (data[i].children) {
loop(data[i].children, key, callback);
}
}
};
const data = [...state.treeData];
// Find dragObject
let dragObj;
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, item => {
item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj);
});
} else if (
(info.node.props.children || []).length > 0 && // Has children
info.node.props.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, item => {
item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj);
// in previous version, we use item.children.push(dragObj) to insert the
// item to the tail of the children
});
} else {
let ar;
let i;
loop(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
// onSortEnd?.(data)
console.log(data);
dispatch({
type: 'sort',
payload: {
treeData: data
}
})
};
const handleSelect = (selectedKeys: onSelectParameters[0], event: onSelectParameters[1]) => {
onSelect?.(selectedKeys, event)
dispatch({
type: 'select',
payload: {
selectedKeys,
event
}
})
}
return (
<Tree
draggable={draggable}
treeData={generateTreeData}
selectedKeys={state.selectedNodesKey}
onSelect={handleSelect}
onDrop={onDrop}
// onDragStart={onDragStart}
// onDragEnd={onDragEnd}
defaultExpandAll
blockNode
/>
)
}
export default MaterialTree;
\ No newline at end of file
@main-color: #00B37A;
:global {
#root {
.ant-tree {
.ant-tree-treenode {
width: 100%;
border: 1px solid transparent;
background: #fff;
}
.ant-tree-treenode-selected {
border: 1px solid @main-color;
}
}
}
}
.tree-node {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
.tree-node-title {
position: relative;
margin-left: 12px;
&::before {
content: "";
width: 4px;
height: 4px;
border-radius: 50%;
position: absolute;
top: 50%;
left: -12px;
margin-top: -2px;
background: #C0C4CC;
}
}
.isSelected {
color: @main-color;
&::before {
background: @main-color;
}
}
.tree-node-actions {
opacity: 0;
}
&:hover {
.tree-node-actions {
opacity: 1;
}
}
}
import React from 'react';
import { Space, Tooltip } from 'antd';
import { PlusCircleOutlined, HolderOutlined } from '@ant-design/icons';
import styles from './treeNodeTitle.less';
import cx from 'classnames';
interface Iprops {
title: string;
/** 当前元素key */
currentKey: string,
/** 深度 */
depth: number,
/** 父级key */
parentKey: string | null,
/** 是否选中 */
isSelected: boolean,
actions?: {
onAdd?: ({ parentKey, depth }: { parentKey: string; depth: number}, e) => void;
onAddChildNode?: ({ parentKey, depth }: { parentKey: string; depth: number}, e) => void;
onSort?: () => void;
}
}
const TreeNodeTitle: React.FC<Iprops> = (props: Iprops) => {
const { title, currentKey, actions, depth, parentKey, isSelected } = props;
const onAdd = (e) => {
actions.onAdd?.({ parentKey: parentKey, depth: depth }, e)
}
const onAddChildNode = (e) => {
actions.onAddChildNode?.({ parentKey: currentKey, depth: depth + 1 }, e)
}
return (
<div className={cx(styles['tree-node'])}>
<span className={cx(styles['tree-node-title'], {[styles.isSelected]: isSelected})}>{title}</span>
<div className={styles['tree-node-actions']}>
<Space>
<Tooltip
title="新增同级节点"
>
<PlusCircleOutlined onClick={onAdd} />
</Tooltip>
<Tooltip title="新增子节点">
<PlusCircleOutlined onClick={onAddChildNode} />
</Tooltip>
{/* <Tooltip title="排序">
<HolderOutlined />
</Tooltip> */}
</Space>
</div>
</div>
)
}
export default TreeNodeTitle;
\ No newline at end of file
import React from 'react';
const UploadFileTip = () => {
return (
<div>
<p>1. 一次可以选择 6 张图片</p>
<p>2. 图片尺寸为 800*800,单张大小不超过 600K,仅支持JPEG/JPG/PNG格式</p>
<p>3. 图片质量要清晰,不要虚化,建议主图为白色背景正面图</p>
</div>
)
}
UploadFileTip.isVirtualFieldComponent = true;
export default UploadFileTip;
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import AnchorPage from '@/components/AnchorPage';
import NiceForm from '@/components/NiceForm';
import { createFormActions, FormPath } from '@formily/antd';
import { ArrayTable } from '@formily/antd-components'
import { addSchema } from './schemas/add';
import ProcessRadio from './components/processRadio';
import ApplicableMaterial from './components/applicableMaterials';
import SelectMaterial from './components/selectMaterial';
import { Button } from 'antd';
import { getProductMaterialGroupTree, getProductMaterialProcessBaseList, getProductMaterialProcessGet, getProductMaterialProcessPageRelMaterial, postProductMaterialProcessSave, postProductMaterialProcessUpdate } from '@/services/ProductV2Api';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import FormilyTransfer from './components/formilyTransfer';
import { usePageStatus } from '@/hooks/usePageStatus';
const formActions = createFormActions();
type SubmitDataType = {
name: string,
/** 适用物料类型枚举值,1-所有物料,2-选择部分物料组,3-选择部分物料 */
suitableMaterialType: string | number,
/** 基础物料流程id */
baseProcessId: number,
/**
* 选择部分物料,适用物料类型是3时,
*/
materials?: {
id: number
}[],
/** 选择部分物料分组,适用物料类型是2时 */
materialGroups?: {
id: number
}[]
}
/**
* 新增物料审核流程规则
*/
const anchorHeader = [
{
name: '流程规则',
key: 'config'
},
{
name: '物料流程',
key: 'type',
},
{
name: '适用物料组/物料',
key: 'apply'
}
]
const Add = () => {
const { id, lastTypeParams } = usePageStatus();
console.log(lastTypeParams);
const isAdd = lastTypeParams === '/add' && !id
const isEdit = lastTypeParams === '/edit' && id
const isEditable = isAdd || isEdit;
const [initialValue, setInitialValue] = useState(null);
useEffect(() => {
if (isAdd) {
setInitialValue({
suitableMaterialType: 1
})
return;
}
async function getInitialValue() {
const { data, code } = await getProductMaterialProcessGet({ processId: id });
console.log(data);
if (code === 1000) {
/** 选择部分物料 */
let materials = [];
if (data.suitableMaterialType === 3) {
const listData = await getProductMaterialProcessPageRelMaterial({ processId: id })
materials = [...listData.data];
}
setInitialValue({
...data,
materials: materials,
// materialGroups: ['15']
});
}
}
getInitialValue();
}, [])
const fetchProcess = async () => {
const { data, code } = await getProductMaterialProcessBaseList();
if (code === 1000) {
return data
}
return [];
}
const handleSubmit = async (values: SubmitDataType) => {
let tempData = {}
if (values.suitableMaterialType === 2) {
tempData = {
materialGroups: values.materialGroups?.map((_item) => {
return {
materialGroupId: _item
}
}),
}
} else if (values.suitableMaterialType === 2) {
tempData = {
materials: values.materials?.map((_item) => {
return {
materialId: _item,
}
})
}
}
const { materials, materialGroups, ...rest } = values;
const defaultPostData = {
...rest,
...tempData
};
const postData = isAdd
? defaultPostData
: {
...defaultPostData,
id: id
}
const service = isAdd
? postProductMaterialProcessSave
: postProductMaterialProcessUpdate
const { data, code } = await service(postData);
if (code === 1000) {
history.back();
}
}
return (
<AnchorPage
title={"新增物料"}
anchors={anchorHeader}
extra={
<Button onClick={() => formActions.submit()}>保存</Button>
}
>
<NiceForm
onSubmit={handleSubmit}
schema={addSchema}
actions={formActions}
value={initialValue}
editable={isEditable as boolean}
components={{
ProcessRadio,
ApplicableMaterial,
ArrayTable,
SelectMaterial,
FormilyTransfer
}}
effects={($, actions) => {
useAsyncSelect('baseProcessId', fetchProcess);
$('onFieldInputChange', 'baseProcessId').subscribe((fieldState) => {
actions.setFieldState('selectMaterials', (state) => {
FormPath.setIn(state, 'props.x-component-props', {
processId: fieldState.value
});
})
});
$('onFieldMount', 'materialGroups').subscribe((fieldState) => {
// console.log("挂载")
getProductMaterialGroupTree({ rootNodeId: '0' })
.then(({data}) => {
console.log(data);
actions.setFieldState('materialGroups', (state) => {
FormPath.setIn(state, 'props.enum', data)
})
})
})
}}
/>
</AnchorPage>
)
}
export default Add;
.container {
display: flex;
flex-direction: row;
.item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 16px;
background: #F5F6F7;
border-radius: 4px;
padding: 8px 16px;
color: #5C626A;
cursor: pointer;
border: 1px solid transparent;
}
.active {
border: 1px solid #00A98F;
color: #00A98F;
}
}
\ No newline at end of file
import { Radio } from 'antd';
import React from 'react';
import styles from './index.less'
import className from 'classnames';
type EnumType = {
title: string,
id: string | number,
}
interface Iprops {
props: {
enum: EnumType[],
},
value: EnumType,
editable: boolean,
mutators: {
change: (id: number | string) => void
},
}
const ApplicableMaterial: React.FC<Iprops> & { isFieldComponent: boolean } = (props: Iprops) => {
const { value, editable, mutators } = props;
const options = props.props?.enum || [];
const handleChange = (_item: EnumType) => {
if (!editable) {
return;
}
mutators.change(_item.id)
}
return (
<div className={styles.container}>
{
options.map((_item) => {
const isChecked = _item.id === +value
return (
<div
key={_item.id}
className={
className(
styles.item,
{ [styles.active]: isChecked }
)
}
onClick={() => handleChange(_item)}
>
<Radio checked={isChecked} />
{_item.title}
</div>
)
})
}
</div>
)
}
ApplicableMaterial.isFieldComponent = true
export default ApplicableMaterial
\ No newline at end of file
import { Transfer, Tree } from 'antd';
import React, { useRef, useState } from 'react';
interface Iprops {
dataSource: any[],
targetKeys: string[],
value: string[],
props: {
enum: any[],
}
mutators: {
change: (data: string[]) => void,
}
}
/** 如果有其中一个未选,那么都选中 */
// const isChecked = (selectedKeys, eventKey) => selectedKeys.some((_item) => !eventKey.includes(_item))
const FormilyTransfer: React.FC<Iprops> & { isFieldComponent: boolean } = (props: Iprops) => {
const { value, mutators, ...restProps } = props;
const dataSource = props.props?.enum || [];
const transferDataSource = [];
function flatten(list = []) {
list.forEach(item => {
transferDataSource.push({...item, key: item.id});
flatten(item.children);
});
}
flatten(dataSource);
const generateTree = (treeNodes = [], checkedKeys = [], parentKey = '') => {
return treeNodes.map(({ children, ...props }) => {
const { checked, ...rest } = props
return {
...rest,
key: `${props.id}`,
title: props.title,
fullKey: `${parentKey}${props.id}`,
disabled: checkedKeys.includes(props.id),
children: generateTree(children, checkedKeys, `${parentKey}${props.id}-`),
}
});
}
const onChange = (datas) => {
mutators.change(datas)
}
return (
<Transfer
{...restProps}
targetKeys={value}
dataSource={transferDataSource}
className="tree-transfer"
render={item => item.title}
showSelectAll={false}
onChange={onChange}
>
{({ direction, onItemSelect, selectedKeys, onItemSelectAll }) => {
if (direction === 'left') {
const checkedKeys = [...selectedKeys, ...value];
console.log("selectedKeys", selectedKeys, checkedKeys)
const treeData = generateTree(dataSource, value || [])
return (
<Tree
blockNode
checkable
defaultExpandAll
// checkedKeys={checkedKeys}
treeData={treeData}
onCheck={(checkedKeysValue, e) => {
const isChecked = e.checked;
const halfChecked = e.halfCheckedKeys;
const newCheckedKeys = checkedKeysValue.filter((_item) => !halfChecked.includes(_item));
onItemSelectAll(newCheckedKeys, isChecked);
}}
/>
);
}
}}
</Transfer>
);
}
FormilyTransfer.isFieldComponent = true
export default FormilyTransfer
.panel {
position: relative;
.more {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -24px;
cursor: pointer;
}
}
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-right: -16px;
.item {
width: 50%;
padding-right: 16px;
margin-bottom: 8px;
// border: 1px solid #00A98F;
.itemContainer {
background: #F5F6F7;
border: 1px solid transparent;
border-radius: 4px;
padding: 16px;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
height: 100%;
}
.active {
border: 1px solid #00A98F;
}
.section {
flex: 1;
.info {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
.description {
margin-top: 8px;
color: #91959B;
font-size: 12px;
}
}
}
\ No newline at end of file
import StatusTag from '@/components/StatusTag';
import { Radio } from 'antd';
import React, { useState } from 'react';
import styles from './index.less';
import className from 'classnames'
import { CaretDownOutlined } from '@ant-design/icons';
type EnumType = {
baseProcessId: number,
processName: string,
processType: string | number,
processTypeName: string,
description: string,
}
interface Iprops {
props: {
enum: EnumType[],
},
value: number | string ,
editable: boolean,
mutators: {
change: (id: string | number) => void
},
}
/**
* 选择物料流程
* @returns
*/
const PAGE_SIZE = 6;
const ProcessRadio: React.FC<Iprops> & { isFieldComponent: boolean } = (props: Iprops) => {
const { value, editable, mutators } = props;
const [page, setPage] = useState<number>(1);
const options = props.props?.enum;
const dataSource = options.slice(0, page * PAGE_SIZE);
const hasMore = dataSource.length < options.length;
console.log(options);
const onChange = (_item: EnumType) => {
if (!editable) {
return;
}
mutators.change(_item.baseProcessId)
}
const handleLoadMore = () => {
setPage(page + 1)
}
return (
<div className={styles.panel}>
<div className={styles.container}>
{
dataSource.map((_item) => {
return (
<div
className={styles.item}
key={_item.baseProcessId}
>
<div
className={
className(
styles.itemContainer,
{ [styles.active]: _item.baseProcessId === value }
)
}
key={_item.baseProcessId}
onClick={() => onChange(_item)}
>
{
editable && (
<Radio
checked={_item.baseProcessId === value}
/>
)
}
<div className={styles.section}>
<div className={styles.info}>
<span>{_item.processName}</span>
<StatusTag type={'primary'} title={_item.processTypeName} />
</div>
<span className={styles.description}>{_item.description}</span>
</div>
</div>
</div>
)
})
}
</div>
{
hasMore && (
<div className={styles.more} onClick={handleLoadMore}>
加载更多
<CaretDownOutlined />
</div>
) || null
}
</div>
)
}
ProcessRadio.isFieldComponent = true
export default ProcessRadio;
\ No newline at end of file
.plus {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 8px;
padding: 4px;
border: 1px solid #EDEEEF;
border-radius: 4px;
background-color: #EDEEEF;
color: #5C626A;
cursor: pointer;
}
\ No newline at end of file
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import TableModal from '@/pages/member/components/TableModal';
import { useToggle } from '@umijs/hooks';
import { Table, Cascader } from 'antd';
import React from 'react';
import { schema } from './schema'
import styles from './index.less';
import { PlusOutlined } from '@ant-design/icons';
import { getProductGoodsGetDoesNotFreezeGoodsList, getProductMaterialProcessPageRelMaterial } from '@/services/ProductV2Api';
import { fetchBrand, fetchCategoryData, fetchStatus, fetchTreeData, useAsyncCascader } from '../../../common/useGetTableSearchData';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
/**
* 选择指定物料
*/
type ValueType = {
num: string,
name: string,
}
interface Iprops {
value: ValueType[],
mutators: {
change: (data: any) => void,
},
editable: boolean
}
const DEFAULT_RETURN_DATA = {
totalCount: 0,
data: []
}
const SelectMaterial: React.FC<Iprops> & { isFieldComponent: boolean} = (props: Iprops) => {
const { state: visible, toggle } = useToggle(false)
const { value, mutators, editable } = props
const columns = [
{
title: '物料编号',
dataIndex: 'code'
},
{
title: '物料名称',
dataIndex: 'name'
},
{
title: '物料组',
dataIndex: 'materialGroup'
},
{
title: '规格型号',
dataIndex: 'type',
render: (text, record) => {
const { materialAttributeList } = record;
const string = materialAttributeList?.reduce((prev, current) => {
const { customerAttributeValueList } = current;
const temp = customerAttributeValueList?.map((_item) => {
return _item.value
}).join('/');
return prev + "/" + temp
}, "").slice(1);
return (
<div>{string}</div>
)
}
},
{
title: '品类',
dataIndex: 'category',
render: (text, record) => {
return (
<div>{record.customerCategory?.name}</div>
)
}
},
{
title: '品牌',
dataIndex: 'brand',
render: (text, record) => {
return (
<div>{record.brand?.name}</div>
)
}
},
{
title: '单位',
dataIndex: 'unitName'
}
]
const handleDelete = (_row: { code: string }) => {
const temp = value.filter((_item) => !(_item.code === _row.code))
mutators.change(temp)
}
const withEdit = editable
? columns.concat([{
title: '操作',
render: (text, record) => {
return <a onClick={() => handleDelete(record)}>删除</a>
}
}] as any)
: columns
const handleOnOk = (selectRow, selectRowRecord) => {
console.log(selectRowRecord, selectRow);
mutators.change(selectRowRecord)
toggle(false);
}
const handleFetchData = async (params) => {
const { data, code } = await getProductGoodsGetDoesNotFreezeGoodsList(params);
if (code === 1000) {
return data
}
return DEFAULT_RETURN_DATA as any
}
return (
<div>
{
editable && (
<div
className={styles.plus}
onClick={() => toggle(true)}
>
<PlusOutlined />
添加
</div>
)
}
<Table
columns={withEdit}
dataSource={value}
/>
<TableModal
modalType='Drawer'
visible={visible}
onClose={() => toggle(false)}
title={"选择物料"}
columns={columns}
schema={schema}
onOk={handleOnOk}
fetchData={handleFetchData}
tableProps={{
rowKey: (record) => `${record.code}`,
}}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'code', FORM_FILTER_PATH);
useAsyncCascader('materialGroupId', fetchTreeData)
useAsyncCascader('categoryId', fetchCategoryData)
useAsyncSelect('brandId', fetchBrand, ["name", "id"])
useAsyncSelect('status', fetchStatus, ["name", "status"])
}}
components={{Cascader}}
mode={"checkbox"}
value={value}
/>
</div>
)
}
SelectMaterial.isFieldComponent = true
export default SelectMaterial
\ No newline at end of file
import { FORM_FILTER_PATH } from "@/formSchema/const";
import { ISchema } from "@formily/antd";
export const schema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
properties: {
code: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '物料编号',
align: 'flex-left',
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
rowStyle: {
justifyContent: 'flex-start',
},
colStyle: {
marginRight: 20,
},
},
properties: {
name: {
type: 'string',
"x-component-props": {
placeholder: '物料名称'
}
},
// type: {
// type: 'string',
// "x-component-props": {
// placeholder: '物料规格'
// }
// },
materialGroupId: {
type: 'string',
enum: [],
'x-component': 'Cascader',
"x-component-props": {
placeholder: '物料组',
allowClear: true,
style: {
width: 150
},
showSearch: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
}
},
categoryId: {
type: 'string',
'x-component': 'Cascader',
'x-component-props': {
placeholder: '品类',
allowClear: true,
style: { width: '150px' },
showSearch: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
}
},
brandId: {
type: 'string',
'x-component-props': {
placeholder: '品牌',
style: { width: '150px' },
}
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '提交'
},
},
},
},
},
},
},
};
import React, { useEffect, useRef } from 'react';
import StandardTable from '@/components/StandardTable';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import NiceForm from '@/components/NiceForm';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { querySchema } from './schemas/query';
import { Button, Card, Popconfirm, Space, Switch } from 'antd';
import { createFormActions } from '@formily/antd';
import {
getProductMaterialProcessDelete,
getProductMaterialProcessPage,
GetProductMaterialProcessPageResponseDetail,
postProductMaterialProcessUpdateStatus
} from '@/services/ProductV2Api';
import { Link } from 'umi';
/**
* 物料审核流程规则配置
*/
const formActions = createFormActions();
const CREATE_URL = '/memberCenter/material/materialAuditProcessConfig/add'
const MaterialAuditProcessConfig = () => {
const ref = useRef<any>({});
const handleEnableOrDisable = async (_row: GetProductMaterialProcessPageResponseDetail) => {
const { code } = await postProductMaterialProcessUpdateStatus({
processId: _row.processId,
status: _row.status ? 0 : 1
})
if (code === 1000) {
formActions.submit();
}
}
const handleDelete = async (_row: GetProductMaterialProcessPageResponseDetail) => {
const { code } = await getProductMaterialProcessDelete({
processId: `${_row.processId}`,
})
if (code === 1000) {
formActions.submit();
}
}
const columns = [
{
title: '流程规则ID',
dataIndex: 'processId',
render: (text, record) => {
return (
<Link to={`/memberCenter/material/materialAuditProcessConfig/detail?id=${record.processId}`}>
{record.processId}
</Link>
)
}
},
{
title: '流程规则名称',
dataIndex: 'name',
},
{
title: '状态',
dataIndex: 'status',
render: (text, record) => {
return (
<Switch checked={!!text} onChange={() => handleEnableOrDisable(record)} />
)
}
},
{
title: '操作时间',
dataIndex: 'createTime',
},
{
title: '操作',
dataIndex: 'actions',
render: (text, record) => {
if (record.status === 1) {
return null;
}
return (
<Space>
<Link to={`/memberCenter/material/materialAuditProcessConfig/edit?id=${record.processId}`}>
修改
</Link>
{
record.status !== 1 && (
<Popconfirm
title="确认删除?"
onConfirm={() => handleDelete(record)}
okText="是"
cancelText="否"
>
<a >删除</a>
</Popconfirm>
)
}
</Space>
)
}
}
]
const controllerBtns = () => {
return (
<Space>
<Link to={CREATE_URL}>
<Button
type="primary"
>
新增
</Button>
</Link>
</Space>
)
}
const handleSearch = (values: { name: string}) => {
ref.current.reload(values)
};
const fetchListData = async (params) => {
const { code, data } = await getProductMaterialProcessPage(params);
if (code === 1000) {
return data;
}
return {
totalCount: 0,
data: [],
}
}
return (
<PageHeaderWrapper
title={"物料审核流程规则配置"}
>
<Card>
<StandardTable
tableProps={{
rowKey: 'id',
}}
columns={columns}
currentRef={ref}
fetchTableData={fetchListData}
controlRender={
<NiceForm
components={{ controllerBtns }}
schema={querySchema}
actions={formActions}
onSubmit={handleSearch}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'name', FORM_FILTER_PATH);
}}
/>
}
/>
</Card>
</PageHeaderWrapper>
)
}
export default MaterialAuditProcessConfig
\ No newline at end of file
import { ISchema, Schema } from '@formily/antd'
/**
* 新增物料
*/
export const addSchema: ISchema = {
type: 'object',
properties: {
basic: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'basic',
title: '流程规则'
},
properties: {
layout: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'left',
labelCol: 4,
wrapperCol: 19,
grid: true,
autoRow: true,
columns: 2,
"responsive": {
"lg": 2,
"m": 1,
"s": 1
}
},
properties: {
name: {
title: '流程规则名称',
type: 'string',
'x-rules': [
{
required: true,
message: '请填写流程规则名称'
}
]
},
}
}
}
},
/** 根据品类动态获取schema */
processCard: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'type',
title: '物料流程'
},
properties: {
baseProcessId: {
type: 'string',
enum: [],
'x-component': 'ProcessRadio'
}
}
},
applyCard: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'apply',
title: '适用物料组/物料'
},
properties: {
suitableMaterialType: {
title: '',
type: 'string',
'x-component': 'ApplicableMaterial',
enum: [
{
title: '所有物料(默认)',
id: 1,
},
{
title: '选择部分物料组',
id: 2,
},
{
title: '选择部分物料',
id: 3,
},
] as any,
'x-linkages': [
{
type: 'value:visible',
target: 'materials',
condition: '{{ $self.value === 3 }}'
},
{
type: 'value:visible',
target: 'materialGroups',
condition: '{{ $self.value === 2 }}'
},
{
type: 'value:schema',
target: 'materials',
condition: `{{ $self.value === 3 }}`,
schema: {
"x-rules": [
{
required: true,
}
]
},
otherwise: {
"x-rules": [
{
required: false,
}
]
}
},
{
type: 'value:schema',
target: 'materialGroups',
condition: `{{ $self.value === 2 }}`,
schema: {
"x-rules": [
{
required: true,
}
]
},
otherwise: {
"x-rules": [
{
required: false,
}
]
}
}
]
},
materials: {
title: '',
type: 'string',
'x-component': 'SelectMaterial',
'x-component-props': {
}
},
materialGroups: {
title: '',
type: 'array',
'x-component': 'FormilyTransfer',
'x-component-props': {
}
}
}
}
},
}
import { getIntl } from 'umi';
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
const intl = getIntl();
export const querySchema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
properties: {
topLayout: {
type: 'object',
'x-component': 'Mega-Layout',
'x-component-props': {
grid: true,
},
properties: {
ctl: {
type: 'object',
"x-component": 'controllerBtns'
},
name: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '流程规则名称',
advanced: false
},
},
},
},
},
},
},
};
\ No newline at end of file
.page {
display: flex;
flex-direction: row;
.left {
width: 428px;
}
.content {
flex: 1;
margin-left: 24px;
}
}
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import MaterialTree, { onSelectParameters } from '../components/materialTree';
import styles from './index.less';
import { Button, Card, Empty, Popconfirm } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import NiceForm from '@/components/NiceForm';
import { schema } from './schema';
import { FormState } from '@/hooks/useTreeData';
import { createAsyncFormActions, FormButtonGroup, FormPath } from '@formily/antd';
import { getProductMaterialGroupDetail, getProductMaterialGroupTree, GetProductMaterialGroupTreeResponse, postProductMaterialGroupDelete, postProductMaterialGroupSaveOrUpdate } from '@/services/ProductV2Api';
import FormilyTreeSelect from '../components/formilyTreeSelect';
const formActions = createAsyncFormActions();
interface Iprops {}
type SubmitDataType = {
code: string,
name: string,
description?: string
}
type InfoType = {
status: 'edit' | 'create',
parentId: number | string,
id?: number | string,
}
/**
* 顶级
* @returns
*/
const MaterialGroup = () => {
const [treeData, setTreeData] = useState([]);
/** 记录当前是创建还是修改,记录创建的父级id */
const [info, setInfo] = useState<InfoType | null>(null);
const [formValue, setFormValue] = useState(null);
const transformTreeData = (data: GetProductMaterialGroupTreeResponse) => {
let result = [];
for (let i = 0; i < data.length; i++) {
const current = data[i];
const title = current.title
const children = current.children ? transformTreeData(current.children as GetProductMaterialGroupTreeResponse) : [];
const withChildren = children.length > 0 ? { children: children } : {}
result[i] = {
...current,
key: current.id,
title: title,
...withChildren,
}
}
return result;
}
const getTree = async () => {
const { data, code } = await getProductMaterialGroupTree({ rootNodeId: '0' });
const result = transformTreeData(data);
setTreeData(result);
}
useEffect(() => {
getTree()
}, [])
const refresh = () => {
setInfo(null);
getTree();
}
/**
* 选中状态下 获取接口, 渲染右边form
*/
const onSelect = async (selectedKeys: onSelectParameters[0], event: onSelectParameters[1]) => {
if (!event.selected) {
setFormValue(null)
setInfo(null)
return;
}
setInfo({
status: 'edit',
parentId: null,
id: event.node.key
})
formActions.setFieldState('parentId', (state) => {
FormPath.setIn(state, 'props.enum', [{
parentId: 0,
title: '顶级',
key: 0,
id: 0,
children: treeData
}])
FormPath.setIn(state, 'props.x-component-props', { currentId: event.node.key })
})
const { data, code } = await getProductMaterialGroupDetail({ id: `${event.node.key}` })
if (code === 1000) {
setFormValue(data)
}
}
const onSubmit = async (submitData: SubmitDataType) => {
console.log(submitData)
const postData = info?.status === 'create'
? {
parentId: +info.parentId,
...submitData,
}
: {
id: +info?.id,
...submitData
}
const { data, code } = await postProductMaterialGroupSaveOrUpdate(postData);
if (code === 1000) {
refresh()
}
}
const handleAdd = (params: { parentKey: string; depth: number }) => {
console.log(params);
setInfo({
status: 'create',
parentId: params.parentKey || 0,
})
setFormValue({
code: '',
description: '',
name: ''
})
formActions.setFieldState('parentId', (state) => {
FormPath.setIn(state, 'visible', false)
})
}
const handleDelete = async () => {
const { data, code } = await postProductMaterialGroupDelete({ id: +info.id! });
if (code === 1000) {
refresh();
}
}
const handleCreate = () => {
setInfo({
status: 'create',
parentId: 0,
})
formActions.setFieldState('parentId', (state) => {
FormPath.setIn(state, 'visible', false)
})
}
return (
<PageHeaderWrapper title="物料组">
<div className={styles.page}>
<div className={styles.left}>
<Card
title="列表"
extra={
treeData.length === 0 && (
<Button
onClick={handleCreate}
>
新建物料组
</Button>
) || null
}
>
<MaterialTree
treeData={treeData}
onSelect={onSelect}
onAdd={handleAdd}
/>
</Card>
</div>
<section className={styles.content}>
<Card title="编辑">
{
!info && (
<Empty />
) || (
<NiceForm
value={formValue}
components={{FormilyTreeSelect}}
schema={schema}
onSubmit={onSubmit}
actions={formActions}
>
<FormButtonGroup>
<Button htmlType='submit' type="primary" >
保存
</Button>
{
info.status === 'edit' && (
<Popconfirm
title="确定要删除吗?"
okText="是"
cancelText="否"
onConfirm={handleDelete}
>
<Button >
删除
</Button>
</Popconfirm>
) || null
}
</FormButtonGroup>
</NiceForm>
)
}
</Card>
</section>
</div>
</PageHeaderWrapper>
)
}
export default MaterialGroup
\ No newline at end of file
import { ISchema } from '@formily/antd';
export const schema: ISchema = {
type: 'object',
properties: {
megaLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 4,
labelAlign: "top",
full: true,
wrapperCol: 16,
},
properties: {
code: {
title: '物料组代码',
type: 'string',
'x-rules': [
{
required: true,
message: '请输入物料组代码'
},
{
limitByte: true,
maxByte: 24,
}
]
},
name: {
title: '物料组',
type: 'string',
'x-rules': [
{
required: true,
message: '请输入物料组代码'
},
{
limitByte: true,
maxByte: 24,
}
]
},
description: {
title: '物料组描述',
type: 'textarea',
},
parentId: {
title: '父级',
type: 'FormilyTreeSelect',
'x-rules': [
{
required: true,
message: '请选择父级'
}
]
}
}
}
}
}
\ No newline at end of file
.uploadContainer {
display: flex;
flex-direction: column;
width: 188px;
height: 188px;
background-color: #EDEEEF;
color: #91959B;
justify-content: center;
align-items: center;
cursor: pointer;
.icon {
border: 1px dashed #91959B;
padding: 4px 8px;
margin-bottom: 16px;
}
}
.fileItem {
width: 188px;
height: 188px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 16px;
position: relative;
border: 1px solid #EDEEEF;
border-radius: 8px;
margin-bottom: 8px;
.actions {
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 0.4);
position: absolute;
right: 0;
top: 0;
display: none;
.delete {
padding: 8px;
font-size: 16px;
color: #fff;
cursor: pointer;
}
}
&:hover {
.actions {
display: block;
}
}
img {
width: 128px;
height: 128px;
}
}
.addition {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border: 1px solid #EDEEEF;
border-radius: 4px;
padding: 4px;
background-color: #EDEEEF;
color: #5C626A;
margin-top: 12px;
cursor: pointer;
.text {
margin-left: 4px;
}
}
\ No newline at end of file
This diff is collapsed.
import React, { useRef, useState } from 'react';
import StandardTable from '@/components/StandardTable';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import NiceForm from '@/components/NiceForm';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useAsyncInitSelect } from '@/formSchema/effects/useAsyncInitSelect';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { getSchema } from '../common/searchTableSchema';
import { Button, Card, Cascader, Dropdown, Menu, message, Modal, Space } from 'antd';
import { getColumn } from '../common/columns';
import { createFormActions, Schema } from '@formily/antd';
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable';
import { getProductGoodsGetGoodsList, GetProductGoodsGetMaterialListRequest, getProductGoodsGetSubGoodsList, postProductGoodsDeleteBatchGoods, postProductGoodsSubmit } from '@/services/ProductV2Api';
import { SearchParams } from '../materialQuery';
import { EMPTY, fetchBrand, fetchCategoryData, fetchTreeData, useAsyncCascader } from '../common/useGetTableSearchData';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { Link, history } from 'umi';
import { DownOutlined } from '@ant-design/icons';
import TableModal from '@/pages/member/components/TableModal';
import { purchaseSchema } from './schema/purchase';
/**
* 物料查询
*/
const formActions = createFormActions();
const CREATE_URL = '/memberCenter/material/pendingAdd/add'
const MaterialQuery = () => {
const ref = useRef<any>({});
const schema = getSchema({ showStatus: false });
const [loading, setLoading] = useState(false)
const [visible, setVisible] = useState(false);
const [selectRow, selectRowFns] = useRowSelectionTable({
customKey: 'id',
});
const handleDelete = async (_row) => {
const { data, code } = await postProductGoodsDeleteBatchGoods({ idList: [_row.id] });
if (code === 1000) {
formActions.submit();
}
}
const handleSubmit = async (_row) => {
const { data, code } = await postProductGoodsSubmit({ id: _row.id });
if (code === 1000) {
formActions.submit();
} else if (code === 43149) {
Modal.warning({
title: '提交提醒',
content: `当前还未创建审核工作流, 请在物料审核流程规则配置设置`,
onOk() {
console.log('OK');
},
});
}
}
const columns = getColumn({
detailUrl: '/memberCenter/material/pendingAdd/detail',
extraColumn: [
{
title: '操作',
render: (text, record) => {
return (
<Space>
<Link to={`/memberCenter/material/pendingAdd/edit?id=${record.id}`}>修改</Link>
<a onClick={() => handleDelete(record)}>删除</a>
<a onClick={() => handleSubmit(record)}>提交</a>
</Space>
)
}
}
]
})
const supplierColumns = [
{
title: '供应商',
dataIndex: 'id',
},
{
title: '物料名称',
dataIndex: 'name',
},
{
title: '规格型号',
dataIndex: 'type',
},
{
title: '品类',
dataIndex: 'category',
},
{
title: '品牌',
dataIndex: 'brand',
}
]
const handleOpenModal = () => {
setVisible(true);
}
const controllerBtns = () => {
return (
<Space>
<Link to={CREATE_URL}>
<Button
type="primary"
>
新增
</Button>
</Link>
<Button
loading={loading}
onClick={handleBatchDelete}
>
批量删除
</Button>
<Button onClick={handleOpenModal}>
采购选品
</Button>
</Space>
)
}
const handleBatchDelete = async () => {
const selectedRowKeys = selectRowFns.selectedRowKeys;
if (selectedRowKeys.length === 0) {
message.info("请选择删除的物料")
return;
}
setLoading(true)
try {
const { data, code } = await postProductGoodsDeleteBatchGoods({ idList: selectedRowKeys })
if (code === 1000) {
ref.current.reload()
}
} catch(e) {}
finally {
setLoading(false);
}
}
const handleSearch = (values: SearchParams) => {
const formatMaterialGroupId = values.materialGroupId && values.materialGroupId.length > 0
? { materialGroupId: values.materialGroupId?.pop() }
: { }
const result = { ...values, ...formatMaterialGroupId }
ref.current.reload(result)
};
const fetchListData = async (params: GetProductGoodsGetMaterialListRequest) => {
try {
const { data, code } = await getProductGoodsGetGoodsList(params);
if (code === 1000) {
return data;
}
return EMPTY
} catch(e) {
return EMPTY
}
}
const handleFetchData = async (params: any) => {
try {
const {data, code} = await getProductGoodsGetSubGoodsList(params);
if (code === 1000) {
return data;
}
return EMPTY
} catch(e) {
return EMPTY
}
}
const handleOnOk = (selectRow, selectedRowRecord) => {
// const target = selectedRowRecord[0];
const target = {
memberId: 236,
memberRoleId: 3,
memberName: '我啊',
memberRoleName: '123',
code: 'yyy-123'
}
// localStorage.setItem('sourceData', JSON.stringify(target));
history.push({
pathname: '/memberCenter/material/pendingAdd/add',
query: {
type: 'sourceData',
},
state: {
dataSource: {
memberId: target.memberId,
memberName: target.memberName,
memberRoleId: target.memberRoleId,
memberRoleName: target.memberRoleName,
goodsNo: target.code
}
}
})
}
return (
<PageHeaderWrapper
title={"物料"}
>
<Card>
<StandardTable
tableProps={{
rowKey: 'id',
// rowSelection: selectRow
rowSelection: selectRow
}}
columns={columns}
currentRef={ref}
fetchTableData={fetchListData}
controlRender={
<NiceForm
components={{ controllerBtns, Cascader }}
schema={schema}
actions={formActions}
onSubmit={handleSearch}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'code', FORM_FILTER_PATH);
useAsyncCascader('materialGroupId', fetchTreeData)
useAsyncCascader('categoryId', fetchCategoryData)
useAsyncSelect('brandId', fetchBrand, ["name", "id"])
}}
/>
}
/>
</Card>
<TableModal
modalType='Drawer'
visible={visible}
onClose={() => setVisible(false)}
title={"选择供应商"}
columns={supplierColumns}
schema={purchaseSchema}
onOk={handleOnOk}
fetchData={handleFetchData}
tableProps={{
rowKey: (record) => `${record.memberId}_${record.roleId}`,
}}
components={{ Cascader }}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'userName', FORM_FILTER_PATH);
useAsyncCascader('categoryId', fetchCategoryData)
useAsyncSelect('brandId', fetchBrand, ["name", "id"])
}}
mode={"radio"}
// value={checkedValue}
/>
</PageHeaderWrapper>
)
}
export default MaterialQuery
import { ISchema, Schema } from '@formily/antd'
/**
* 新增物料
*/
export const getSchema = (schema: ISchema): ISchema => {
return {
type: 'object',
properties: {
basic: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'basic',
title: '基本信息'
},
properties: {
layout: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'left',
labelCol: 4,
wrapperCol: 19,
grid: true,
autoRow: true,
columns: 2,
"responsive": {
"lg": 2,
"m": 1,
"s": 1
}
},
properties: {
code: {
title: '编号',
type: 'string',
'x-rules': [
{
required: true,
message: '请填写物料编号'
},
{
limitByte: true,
maxByte: 20,
}
]
},
unitId: {
title: '单位',
type: "string",
enum: [],
'x-rules': [
{
required: true,
message: '请选择单位'
}
]
},
name: {
title: '物料名称',
type: 'string',
'x-rules': [
{
required: true,
message: '请填写物料名称'
},
{
limitByte: true,
maxByte: 40,
}
]
},
costPrice: {
title: '目录价',
type: 'string',
'x-component-props': {
placeholder: '请填写目录价',
addonBefore: '¥',
},
'x-rules': [
{
required: true,
message: '请填写目录价'
},
]
},
materialGroup: {
title: '所属物料组',
type: 'string',
"x-component": 'Cascader',
"x-component-props": {
options: [],
showSearch: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
},
},
brand: {
title: '品牌',
type: 'string',
enum: [],
'x-component-props': {
placeholder: '最长24个字符,12个汉字',
showSearch: true,
},
},
category: {
title: '品类',
"x-component": 'Cascader',
"x-component-props": {
options: [],
showSearch: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
},
'x-rules': [
{
required: true,
message: '请填写品类'
},
]
},
remark: {
title: '备注',
type: 'string',
'x-component-props': {
placeholder: '请输入'
}
}
}
}
}
},
/** 根据品类动态获取schema */
property: schema,
images: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'type',
title: '物料图片'
},
properties: {
goodsPic: {
type: 'array',
'x-component': 'FormilyUploadFiles',
'x-component-props': {
children: '{{uploadContainer}}',
customizeItemRender: '{{customizeFileItemRender}}',
containerStyle: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
flexWrap: 'wrap',
}
}
},
tips: {
type: 'object',
'x-component': 'UploadFileTip',
}
}
},
enclosureCard: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'enclosure',
title: '附件'
},
properties: {
urls: {
type: 'array',
'x-component': 'FormilyUploadEnclosure',
"x-component-props": {
// operations: false,
renderAddition: '{{renderAddition}}',
// renderRemove: '{{renderListTableRemove}}',
operations: {
title: `操作`
},
},
items: {
properties: {
file: {
title: '文件',
type: 'string',
"x-component": 'FileItem',
"x-component-props": {},
editable: false,
},
description: {
title: '备注',
type: 'string',
"x-rules": [
{
required: true,
message: '请填写备注',
}
]
}
}
}
}
}
},
changeCard: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'change',
title: '变更备注'
},
visible: false,
properties: {
layout: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'left',
labelCol: 4,
wrapperCol: 19,
grid: true,
autoRow: true,
columns: 2,
"responsive": {
"lg": 2,
"m": 1,
"s": 1
}
},
properties: {
changeRemark: {
title: '变更内容',
type: 'textarea'
}
}
},
}
},
sourceListCard: {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'enclosure',
title: '货源清单'
},
visible: false,
properties: {
layout: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'left',
labelCol: 4,
wrapperCol: 19,
grid: true,
autoRow: true,
columns: 2,
"responsive": {
"lg": 2,
"m": 1,
"s": 1
}
},
properties: {
memberId: {
title: '会员id',
type: 'string',
display: false,
},
memberName: {
title: '会员名称',
type: 'string',
editable: false
},
memberRoleId: {
title: '会员角色id',
type: 'string',
display: false,
},
memberRoleName: {
title: '会员角色名',
type: 'string',
display: false,
},
goodsNo: {
title: '供应商物料编号',
type: 'string'
},
userName: {
title: '联系人',
type: 'string',
},
phone: {
title: '联系电话',
type: 'string',
},
manufacturer: {
title: '生产厂家',
type: 'string',
},
origin: {
title: '产地',
type: 'string',
},
departure: {
title: '起运地',
type: 'string',
},
arrivalPeriod: {
title: '到货周期',
type: 'string',
},
deliveryMethod: {
title: '交付方式',
type: 'string'
}
}
}
}
}
},
}
}
export const propsCardSchema = (schema: ISchema): ISchema => {
return {
type: 'object',
"x-component": 'MellowCard',
"x-component-props": {
id: 'type',
title: '规格型号'
},
properties: {
layout: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'left',
labelCol: 4,
wrapperCol: 19,
grid: true,
autoRow: true,
columns: 2,
"responsive": {
"lg": 2,
"m": 1,
"s": 1
}
},
properties: schema as any
}
}
}
}
import { FORM_FILTER_PATH } from "@/formSchema/const";
import { ISchema } from "@formily/antd";
export const purchaseSchema: ISchema = {
type: 'object',
properties: {
layout: {
type: 'object',
"x-component": 'mega-layout',
properties: {
userName: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '供应商',
align: 'flex-left',
advanced: false
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
rowStyle: {
justifyContent: "flex-start",
},
colStyle: {
marginRight: 20,
},
},
properties: {
name: {
type: 'string',
'x-component-props': {
placeholder: '物料名称',
allowClear: true,
},
},
brandId: {
type: 'string',
enum: [],
'x-component-props': {
placeholder: '品牌',
allowClear: true,
showSearch: true,
style: { width: '150px' },
},
},
categoryId: {
type: 'string',
'x-component': 'Cascader',
'x-component-props': {
placeholder: '品类',
allowClear: true,
style: { width: '150px' },
showSearch: true,
fieldNames: { label: 'title', value: 'id', children: 'children' },
},
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '查询'
},
},
},
},
},
},
}
}
\ No newline at end of file
import { getProductGoodsGetGoods, GetProductGoodsGetGoodsResponse } from '@/services/ProductV2Api';
import React, { useEffect, useMemo, useState } from 'react';
type Options = {
id: string;
/** 货源清单 */
state?: {
dataSource?: {
code: string
memberId: number
memberName: string,
memberRoleId: number
}
},
query?: {
type: 'sourceData'
}
}
function useInitialValue(options: Options) {
const { id, state, query } = options
const [initialValue, setInitialValue] = useState<null | GetProductGoodsGetGoodsResponse>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!id) {
return;
}
async function getData() {
setLoading(true);
const { data, code } = await getProductGoodsGetGoods({ id: id });
if (code === 1000) {
setInitialValue(data)
}
setLoading(false);
}
getData();
}, []);
const formatInitialValue = useMemo(() => {
if (!initialValue) {
if (query.type === 'sourceData') {
return {
...state?.dataSource,
}
}
return null
}
const category = initialValue.customerCategory?.fullId?.split('.').map((_item) => _item.replace(/0+/, ""));
return {
...initialValue,
brand: initialValue?.brand?.id,
category: category,
materialGroup: `${initialValue?.materialGroup?.id}`,
urls: initialValue.urls?.map((_item) => {
return {
file: { name: _item.name, url: _item.url },
description: _item.description
}
}),
goodsPic: initialValue.goodsPic?.map((_item) => ({
name: _item,
url: _item,
}))
}
}, [initialValue])
return { loading, initialValue, formatInitialValue };
}
export default useInitialValue
\ No newline at end of file
import React, { useMemo, useState } from 'react';
import AnchorPage from '@/components/AnchorPage';
import AuditProcess from '@/components/AuditProcess';
import MellowCard from '@/components/MellowCard';
import useGetDetailCommon from '../common/useGetDetailCommon';
import { Button, Card, Space, Table } from 'antd';
import CustomizeColumn from '@/components/CustomizeColumn';
import ImageList from '../components/imageList'
import { usePageStatus } from '@/hooks/usePageStatus';
import useGetInitialValueDetail from '../common/useGetInitialValueDetail';
import { getProductGoodsGetGoodsProcessDetail, GetProductGoodsGetGoodsProcessDetailResponse, GetProductGoodsGetGoodsResponse, getProductGoodsGetMaterInnerLogList, GetProductGoodsGetMaterInnerLogListResponse, postProductGoodsGoodsExamineChange1 } from '@/services/ProductV2Api';
import ExamVerify, { SubmitDataTypes } from '@/components/ExamVerify';
/**
* 详情
*/
const Detail = () => {
const { id } = usePageStatus();
const [visible, setVisible] = useState<boolean>(false);
const { initialValue, record } = useGetInitialValueDetail<GetProductGoodsGetGoodsProcessDetailResponse, GetProductGoodsGetMaterInnerLogListResponse>({
id: id,
api: getProductGoodsGetGoodsProcessDetail,
logApi: getProductGoodsGetMaterInnerLogList
})
const {
anchorHeader,
auditProcess,
basicInfoList,
tableColumn,
recordColumn,
properties
} = useGetDetailCommon<GetProductGoodsGetGoodsProcessDetailResponse | null>({initialValue: initialValue})
console.log(auditProcess);
const urls = useMemo(() => {
if (initialValue) {
return initialValue.urls
}
return []
}, [initialValue])
const logs = useMemo(() => {
return record
}, [record])
const onExamVerifySubmit = async (value: SubmitDataTypes) => {
const { code, data } = await postProductGoodsGoodsExamineChange1({
id: +id,
state: value.status,
auditOpinion: value.reason
})
if (code === 1000) {
history.back();
}
}
return (
<AnchorPage
title={"物料详情"}
anchors={anchorHeader}
extra={
<Button onClick={() => setVisible(true)}>审核</Button>
}
>
<AuditProcess
{...auditProcess}
id="progress"
/>
<Space>
<CustomizeColumn
id="basic"
data={basicInfoList}
title={"基本信息"}
column={2}
/>
</Space>
<Space>
<CustomizeColumn
id="properties"
data={properties}
title={"物料型号"}
column={2}
/>
</Space>
<div style={{marginTop: '16px'}} id="images">
<Card title="物料图片" bodyStyle={{paddingTop: '0'}}>
<ImageList imageUrls={initialValue?.goodsPic} />
</Card>
</div>
<div style={{marginTop: '16px'}} id="files">
<Card title="附件">
<Table
columns={tableColumn}
dataSource={urls}
/>
</Card>
</div>
<div style={{marginTop: '16px'}} id="log">
<Card title="流转记录">
<Table
columns={recordColumn}
dataSource={logs}
/>
</Card>
</div>
<ExamVerify
visible={visible}
title={"一级审核"}
onSubmit={onExamVerifySubmit}
onCancel={() => setVisible(false)}
showLabel={false}
/>
</AnchorPage>
)
}
export default Detail;
\ No newline at end of file
import React, { useRef, useState } from 'react';
import StandardTable from '@/components/StandardTable';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import NiceForm from '@/components/NiceForm';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { getSchema } from '../common/searchTableSchema';
import { Button, Card, Cascader, message, Space } from 'antd';
import { getColumn } from '../common/columns';
import { createFormActions } from '@formily/antd';
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable';
import { EMPTY, fetchBrand, fetchCategoryData, fetchTreeData, useAsyncCascader } from '../common/useGetTableSearchData';
import { getProductGoodsGoodsExamineChangeList1, postProductGoodsGoodsExamineChangeBatch1 } from '@/services/ProductV2Api';
import { Link } from 'umi';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
/**
* 物料查询
*/
const formActions = createFormActions();
const querySchema = getSchema({ showStatus: false });
const MaterialQuery = () => {
const ref = useRef<any>({});
const [loading, setLoading] = useState(false)
const [selectRow, selectRowFns] = useRowSelectionTable({
customKey: 'id',
});
const columns = getColumn({
detailUrl: '/memberCenter/material/pendingExamI/detail',
extraColumn: [
{
title: '操作',
render: (text, record) => {
return (
<Space>
<Link to={`/memberCenter/material/pendingExamI/detail?id=${record.id}`}>修改</Link>
</Space>
)
}
}
]
})
const controllerBtns = () => {
return (
<Space>
<Button
type="primary"
onClick={handleBatchSuccess}
loading={loading}
>
批量审核通过
</Button>
</Space>
)
}
const handleBatchSuccess = async () => {
const selectedRowKeys = selectRowFns.selectedRowKeys;
if (selectedRowKeys.length === 0) {
message.info("请选择删除的物料")
return;
}
setLoading(true)
try {
const { data, code } = await postProductGoodsGoodsExamineChangeBatch1({ idList: selectedRowKeys })
if (code === 1000) {
ref.current.reload()
}
} catch(e) {}
finally {
setLoading(false);
}
}
const handleSearch = (values: any) => {
const formatMaterialGroupId = values.materialGroupId && values.materialGroupId.length > 0
? { materialGroupId: values.materialGroupId?.pop() }
: { }
const result = { ...values, ...formatMaterialGroupId }
ref.current.reload(result)
};
const fetchListData = async (params) => {
try {
const { data, code } = await getProductGoodsGoodsExamineChangeList1(params);
if (code === 1000) {
return data;
}
return EMPTY
} catch(e) {
return EMPTY
}
}
return (
<PageHeaderWrapper
title={"物料"}
>
<Card>
<StandardTable
tableProps={{
rowKey: 'id',
// rowSelection: selectRow
rowSelection: selectRow
}}
columns={columns}
currentRef={ref}
fetchTableData={fetchListData}
controlRender={
<NiceForm
components={{ controllerBtns, Cascader }}
schema={querySchema}
actions={formActions}
onSubmit={handleSearch}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'code', FORM_FILTER_PATH);
useAsyncCascader('materialGroupId', fetchTreeData)
useAsyncCascader('categoryId', fetchCategoryData)
useAsyncSelect('brandId', fetchBrand, ["name", "id"])
}}
/>
}
/>
</Card>
</PageHeaderWrapper>
)
}
export default MaterialQuery
\ No newline at end of file
import React, { useMemo, useState } from 'react';
import AnchorPage from '@/components/AnchorPage';
import AuditProcess from '@/components/AuditProcess';
import MellowCard from '@/components/MellowCard';
import useGetDetailCommon from '../common/useGetDetailCommon';
import { Button, Card, Space, Table } from 'antd';
import CustomizeColumn from '@/components/CustomizeColumn';
import ImageList from '../components/imageList'
import { usePageStatus } from '@/hooks/usePageStatus';
import useGetInitialValueDetail from '../common/useGetInitialValueDetail';
import { getProductGoodsGetGoodsProcessDetail, GetProductGoodsGetGoodsProcessDetailResponse, GetProductGoodsGetGoodsResponse, getProductGoodsGetMaterInnerLogList, GetProductGoodsGetMaterInnerLogListResponse, postProductGoodsGoodsExamineChange2 } from '@/services/ProductV2Api';
import ExamVerify, { SubmitDataTypes } from '@/components/ExamVerify';
/**
* 详情
*/
const Detail = () => {
const { id } = usePageStatus();
const [visible, setVisible] = useState<boolean>(false);
const { initialValue, record } = useGetInitialValueDetail<GetProductGoodsGetGoodsProcessDetailResponse, GetProductGoodsGetMaterInnerLogListResponse>({
id: id,
api: getProductGoodsGetGoodsProcessDetail,
logApi: getProductGoodsGetMaterInnerLogList
})
const {
anchorHeader,
auditProcess,
basicInfoList,
tableColumn,
recordColumn,
properties
} = useGetDetailCommon<GetProductGoodsGetGoodsProcessDetailResponse | null>({initialValue: initialValue})
console.log(auditProcess);
const urls = useMemo(() => {
if (initialValue) {
return initialValue.urls
}
return []
}, [initialValue])
const logs = useMemo(() => {
return record
}, [record])
const onExamVerifySubmit = async (value: SubmitDataTypes) => {
const { code, data } = await postProductGoodsGoodsExamineChange2({
id: +id,
state: value.status,
auditOpinion: value.reason
})
if (code === 1000) {
history.back();
}
}
return (
<AnchorPage
title={"物料详情"}
anchors={anchorHeader}
extra={
<Button onClick={() => setVisible(true)}>审核</Button>
}
>
<AuditProcess
{...auditProcess}
id="progress"
/>
<Space>
<CustomizeColumn
id="basic"
data={basicInfoList}
title={"基本信息"}
column={2}
/>
</Space>
<Space>
<CustomizeColumn
id="properties"
data={properties}
title={"物料型号"}
column={2}
/>
</Space>
<div style={{marginTop: '16px'}} id="images">
<Card title="物料图片" bodyStyle={{paddingTop: '0'}}>
<ImageList imageUrls={initialValue?.goodsPic} />
</Card>
</div>
<div style={{marginTop: '16px'}} id="files">
<Card title="附件">
<Table
columns={tableColumn}
dataSource={urls}
/>
</Card>
</div>
<div style={{marginTop: '16px'}} id="log">
<Card title="流转记录">
<Table
columns={recordColumn}
dataSource={logs}
/>
</Card>
</div>
<ExamVerify
visible={visible}
title={"一级审核"}
onSubmit={onExamVerifySubmit}
onCancel={() => setVisible(false)}
showLabel={false}
/>
</AnchorPage>
)
}
export default Detail;
\ No newline at end of file
import React, { useRef, useState } from 'react';
import StandardTable from '@/components/StandardTable';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import NiceForm from '@/components/NiceForm';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { getSchema } from '../common/searchTableSchema';
import { Button, Card, Cascader, message, Space } from 'antd';
import { getColumn } from '../common/columns';
import { createFormActions } from '@formily/antd';
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable';
import { EMPTY, fetchBrand, fetchCategoryData, fetchTreeData, useAsyncCascader } from '../common/useGetTableSearchData';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { getProductGoodsGoodsExamineChangeList2, postProductGoodsGoodsExamineChangeBatch2 } from '@/services/ProductV2Api';
import { Link } from 'umi';
/**
* 物料查询
*/
const formActions = createFormActions();
const querySchema = getSchema({ showStatus: false });
const MaterialQuery = () => {
const ref = useRef<any>({});
const [loading, setLoading] = useState(false)
const [selectRow, selectRowFns] = useRowSelectionTable({
customKey: 'id',
});
const columns = getColumn({
detailUrl: '/memberCenter/material/pendingExamII/detail',
extraColumn: [
{
title: '操作',
render: (text, record) => {
return (
<Space>
<Link to={`/memberCenter/material/pendingExamII/detail?id=${record.id}`}>修改</Link>
</Space>
)
}
}
]
})
const controllerBtns = () => {
return (
<Space>
<Button
type="primary"
onClick={handleBatchSuccess}
loading={loading}
>
批量审核通过
</Button>
</Space>
)
}
const handleBatchSuccess = async () => {
const selectedRowKeys = selectRowFns.selectedRowKeys;
if (selectedRowKeys.length === 0) {
message.info("请选择批量审核的物料")
return;
}
setLoading(true)
try {
const { data, code } = await postProductGoodsGoodsExamineChangeBatch2({ idList: selectedRowKeys })
if (code === 1000) {
ref.current.reload()
}
} catch(e) {}
finally {
setLoading(false);
}
}
const handleSearch = (values: any) => {
const formatMaterialGroupId = values.materialGroupId && values.materialGroupId.length > 0
? { materialGroupId: values.materialGroupId?.pop() }
: { }
const result = { ...values, ...formatMaterialGroupId }
ref.current.reload(result)
};
const fetchListData = async (params) => {
try {
const { data, code } = await getProductGoodsGoodsExamineChangeList2(params);
if (code === 1000) {
return data;
}
return EMPTY
} catch(e) {
return EMPTY
}
}
return (
<PageHeaderWrapper
title={"物料"}
>
<Card>
<StandardTable
tableProps={{
rowKey: 'id',
// rowSelection: selectRow
rowSelection: selectRow
}}
columns={columns}
currentRef={ref}
fetchTableData={fetchListData}
controlRender={
<NiceForm
components={{ controllerBtns, Cascader }}
schema={querySchema}
actions={formActions}
onSubmit={handleSearch}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'code', FORM_FILTER_PATH);
useAsyncCascader('materialGroupId', fetchTreeData)
useAsyncCascader('categoryId', fetchCategoryData)
useAsyncSelect('brandId', fetchBrand, ["name", "id"])
}}
/>
}
/>
</Card>
</PageHeaderWrapper>
)
}
export default MaterialQuery
\ No newline at end of file
import React, { useMemo, useState } from 'react';
import AnchorPage from '@/components/AnchorPage';
import AuditProcess from '@/components/AuditProcess';
import MellowCard from '@/components/MellowCard';
import useGetDetailCommon from '../common/useGetDetailCommon';
import { Button, Card, Space, Table } from 'antd';
import CustomizeColumn from '@/components/CustomizeColumn';
import ImageList from '../components/imageList'
import { usePageStatus } from '@/hooks/usePageStatus';
import useGetInitialValueDetail from '../common/useGetInitialValueDetail';
import { getProductGoodsGetGoodsProcessDetail, GetProductGoodsGetGoodsProcessDetailResponse, GetProductGoodsGetGoodsResponse, getProductGoodsGetMaterInnerLogList, GetProductGoodsGetMaterInnerLogListResponse, postProductGoodsGoodsExamine1 } from '@/services/ProductV2Api';
import ExamVerify, { SubmitDataTypes } from '@/components/ExamVerify';
/**
* 详情
*/
const Detail = () => {
const { id } = usePageStatus();
const [visible, setVisible] = useState<boolean>(false);
const { initialValue, record } = useGetInitialValueDetail<GetProductGoodsGetGoodsProcessDetailResponse, GetProductGoodsGetMaterInnerLogListResponse>({
id: id,
api: getProductGoodsGetGoodsProcessDetail,
logApi: getProductGoodsGetMaterInnerLogList
})
const {
anchorHeader,
auditProcess,
basicInfoList,
tableColumn,
recordColumn,
properties
} = useGetDetailCommon<GetProductGoodsGetGoodsProcessDetailResponse | null>({initialValue: initialValue})
console.log(auditProcess);
const urls = useMemo(() => {
if (initialValue) {
return initialValue.urls
}
return []
}, [initialValue])
const logs = useMemo(() => {
return record
}, [record])
const onExamVerifySubmit = async (value: SubmitDataTypes) => {
const { code, data } = await postProductGoodsGoodsExamine1({
id: +id,
state: value.status,
auditOpinion: value.reason
})
if (code === 1000) {
history.back();
}
}
return (
<AnchorPage
title={"物料详情"}
anchors={anchorHeader}
extra={
<Button onClick={() => setVisible(true)}>审核</Button>
}
>
<AuditProcess
{...auditProcess}
id="progress"
/>
<Space>
<CustomizeColumn
id="basic"
data={basicInfoList}
title={"基本信息"}
column={2}
/>
</Space>
<Space>
<CustomizeColumn
id="properties"
data={properties}
title={"物料型号"}
column={2}
/>
</Space>
<div style={{marginTop: '16px'}} id="images">
<Card title="物料图片" bodyStyle={{paddingTop: '0'}}>
<ImageList imageUrls={initialValue?.goodsPic} />
</Card>
</div>
<div style={{marginTop: '16px'}} id="files">
<Card title="附件">
<Table
columns={tableColumn}
dataSource={urls}
/>
</Card>
</div>
<div style={{marginTop: '16px'}} id="log">
<Card title="流转记录">
<Table
columns={recordColumn}
dataSource={logs}
/>
</Card>
</div>
<ExamVerify
visible={visible}
title={"一级审核"}
onSubmit={onExamVerifySubmit}
onCancel={() => setVisible(false)}
showLabel={false}
/>
</AnchorPage>
)
}
export default Detail;
\ No newline at end of file
import React, { useRef, useState } from 'react';
import StandardTable from '@/components/StandardTable';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import NiceForm from '@/components/NiceForm';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { getSchema } from '../common/searchTableSchema';
import { Button, Card, Cascader, message, Space } from 'antd';
import { getColumn } from '../common/columns';
import { createFormActions } from '@formily/antd';
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable';
import { Link } from 'umi';
import { getProductGoodsGoodsExamineList1, postProductGoodsGoodsExamineBatch1 } from '@/services/ProductV2Api';
import { EMPTY, fetchBrand, fetchCategoryData, fetchTreeData, useAsyncCascader } from '../common/useGetTableSearchData';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
/**
* 物料查询
*/
const formActions = createFormActions();
const querySchema = getSchema({ showStatus: false });
const MaterialQuery = () => {
const ref = useRef<any>({});
const [loading, setLoading] = useState(false);
const [selectRow, selectRowFns] = useRowSelectionTable({
customKey: 'id',
});
const columns = getColumn({
detailUrl: '/memberCenter/material/pendingExamI/detail',
extraColumn: [
{
title: '操作',
render: (text, record) => {
return (
<Space>
<Link to={`/memberCenter/material/pendingExamI/detail?id=${record.id}`}>修改</Link>
</Space>
)
}
}
]
})
const controllerBtns = () => {
return (
<Space>
<Button
type="primary"
onClick={handleBatchSuccess}
loading={loading}
>
批量审核通过
</Button>
</Space>
)
}
const handleBatchSuccess = async () => {
const selectedRowKeys = selectRowFns.selectedRowKeys;
if (selectedRowKeys.length === 0) {
message.info("请选择删除的物料")
return;
}
setLoading(true)
try {
const { data, code } = await postProductGoodsGoodsExamineBatch1({ idList: selectedRowKeys })
if (code === 1000) {
ref.current.reload()
}
} catch(e) {}
finally {
setLoading(false);
}
}
const handleSearch = (values: any) => {
const formatMaterialGroupId = values.materialGroupId && values.materialGroupId.length > 0
? { materialGroupId: values.materialGroupId?.pop() }
: { }
const result = { ...values, ...formatMaterialGroupId }
ref.current.reload(result)
};
const fetchListData = async (params) => {
try {
const { data, code } = await getProductGoodsGoodsExamineList1(params);
if (code === 1000) {
return data;
}
return EMPTY
} catch(e) {
return EMPTY
}
}
return (
<PageHeaderWrapper
title={"物料"}
>
<Card>
<StandardTable
tableProps={{
rowKey: 'id',
// rowSelection: selectRow
rowSelection: selectRow
}}
columns={columns}
currentRef={ref}
fetchTableData={fetchListData}
controlRender={
<NiceForm
components={{ controllerBtns, Cascader }}
schema={querySchema}
actions={formActions}
onSubmit={handleSearch}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, 'code', FORM_FILTER_PATH);
useAsyncCascader('materialGroupId', fetchTreeData)
useAsyncCascader('categoryId', fetchCategoryData)
useAsyncSelect('brandId', fetchBrand, ["name", "id"])
}}
/>
}
/>
</Card>
</PageHeaderWrapper>
)
}
export default MaterialQuery
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment