es6 Map set应用

16 篇文章 0 订阅
//  我想要评论!!!
//  我想要赞!!!
//  看过的帅哥美女们,点一下,谢谢!!!!

import React, { Component } from 'react';
import { Button, message, Spin } from 'antd';
import { connect } from 'react-redux';
import { CrModal, CrTable } from 'creams-ui';
import cx from 'classnames';
import shortid from 'shortid';
import { getBuildingsId, deleteBuildingsPortfoliosId } from 'redux/modules/swaggerGen/Buildings';

import {
    postReceipt,
    postReceiptPrint,
    putReceiptStatus,
    getReceiptTemplateIdCurrentNum,
    getReceiptSendRecycleNumberForReceipt,
    getReceiptGetNumbersIncludeManualForReceipt,
} from 'redux/modules/swaggerGen/Receipt';
import { getOssUploadStsToken, getOssDownloadStsToken } from 'redux/modules/swaggerGen/OSS';
import { changeLoadingStatus } from 'redux/modules/ui/spinLoading';
import { refreshBox } from 'redux/modules/ui/global';
import { getOaSelection } from 'redux/modules/swaggerGen/OA';

import split from '../util';
import { OaStartModal } from '../../../creams-web-AuditSetting';
import InvoiceSplitModal from '../../BodyAccount/AccountModal/InvoiceSplitModal/Modal';
import EditModal from '../EditModal';
import generateColumns from './config';
import styles from './index.less';

const editActiveId = Symbol('editActiveId');
const form = Symbol('form');


let receiptNumRanger = [];

const promiseAdd = (request, action) => new Promise((res, rej) => {
    action(
        {
            ...request,
            success: data => res(data),
            error: e => rej(e),
        },
    );
});
const formatBill = arr => arr.reduce((target, { id, value }) => {
    target.push({
        billId: id,
        amount: value,
    });
    return target;
}, []);
const unique = (array) => {
    const hash = {};
    return array.filter((element) => {
        const newKey = typeof element + JSON.stringify(element);
        if (!hash.hasOwnProperty(newKey)) {
            hash[newKey] = true;
            return true;
        }
        return false;
    });
};
/**
 * 拆分 合并 要操作 relateBills receiptNum
 */
@connect(
    state => ({
        customer: state.user.customerInfo,
    }),
    {
        postReceipt,
        refreshBox,
        postReceiptPrint,
        getOssUploadStsToken,
        getOssDownloadStsToken,
        changeLoadingStatus,
        putReceiptStatus,
        getReceiptTemplateIdCurrentNum,
        getReceiptSendRecycleNumberForReceipt,
        getReceiptGetNumbersIncludeManualForReceipt,
        getBuildingsId,
        getOaSelection,
    },
)
class CreateModal extends Component {
    static defaultProps = {
        data: [{}],
    };
    constructor(props) {
        super();
        // const dataItem = { ...props.data[0] };
        this.state = {

            splitModalVisible: false,
            editModalVisible: false,
            previewModalVisible: false,
            listData: new Map(),
            disabledAll: false,
            receiptNumRanger: [],
            startOa: { show: false },

            loading: false,
            saveLoading: false,
            clickEditRow: {}, // table被点击的行
            selectedRowKeys: [], // 被select选中的行集合 keys
            selectedRows: [], //    被select选中的行集合
            splitedRows: {}, //  被拆分的行数据
        };
        this[editActiveId] = 0;
        this[form] = {
            amount: 0,
            buildingId: props.buildingId,
            buildingName: props.buildingName,
            customerId: props.customer && props.customer.id || '',
            memo: '',
            monetaryUnit: '',
            paymentUnit: '',
            billTypeAlias: '',
            relateBills: [
                {
                    amount: 0,
                    billId: 0,
                    billType: props.billType,
                },
            ],
            receiver: {
                address: '',
                companyName: '',
                contacts: '',
                tel: '',
            },
            payer: {
                address: '',
                companyName: '',
                contacts: '',
                tel: '',
            },
            receiptDetails: [{
                amount: 0,
                count: 0,
                paymentName: '',
                unitPrice: 0,
                roomNumber: '',
            }],
            remittanceMethod: '',
        };
    }
    componentDidMount() {
        this.initDataSource();
    }
    onSelectChange = (selectedRowKeys, selectedRows) => {
        this.setState({ selectedRowKeys, selectedRows });
    };
    onAllmount = data => data.reduce((total, { payedAmount, receiptAmount }) => {
        let _total = total;
        _total += payedAmount - receiptAmount;
        return _total;
    }, 0);
    // 新建收据 && 修改收据状态 如果需要审核则发送审核
    asyncPostCreateReceipt = async (body) => {
        const requests = body.map((item) => {
            if (item.receiver) {
                const { id, buildingId, ...rest } = item.receiver;
                item.receiver = { ...rest };
            }
            item.serialNumberRule = item.prefix;
            delete item.prefix;
            delete item.receiptDetails;
            delete item.billType;
            delete item.disabled;
            delete item.generateId;
            delete item.billId;
            delete item.orderNo;
            return item;
        });
        const buildingIds = body[0].buildingId;
        this.setState({ saveLoading: true });
        try {
            const resPostReceiptData = await promiseAdd({ body: { requests } }, this.props.postReceipt);
            const ids = resPostReceiptData.receiptPrintRequests.map(item => item.receiptId);
            const query = { ids, status: 'NEW_CREATE' };
            await promiseAdd({ query }, this.props.putReceiptStatus);
            message.success('收据新建成功');
            this.props.handleVisible(false);
            const objectIds = resPostReceiptData.receiptPrintRequests.map(item => item.receiptId);
            const dataOaSelection = await promiseAdd({ query: { buildingIds } }, this.props.getOaSelection);
            const enableApproval = dataOaSelection.items[0].auditElements.includes('RECEIPT__NEW_APPROVAL');
            const startOa = {
                show: true,
                description: '',
                objectIds,
                buildingIds: [{ buildingId: buildingIds }],
                subDomainType: 'NEW_APPROVAL',
                domainType: 'RECEIPT',
                title: '开具收据审核流程',
                onClose: () => { this.setState({ startOa: { show: false } }); },
                success: () => { this.props.handleReceiptModalVisible(false); },
                enableApproval,
            };
            this.setState({ startOa, saveLoading: false });
        } catch (e) {
            this.setState({ saveLoading: false });
            this.props.handleReceiptModalVisible(false);
            message.error(e);
        }
    };
    // 收据编辑
    handleEditItem = obj => () => {
        this[editActiveId] = obj.splitId;
        this.toggleModals('edit', true)();
    };
    handleSubmit = () => {
        const { selectedRows } = this.state;
        if (!selectedRows.length) {
            return message.error('请选择要保存的收据');
        }
        this.asyncPostCreateReceipt(selectedRows);
    };
    //  更改 modal状态
    toggleModals = (type, bool) => () => {
        this.setState({
            [`${type}ModalVisible`]: bool,
        });
    };
    //  初始化数据
    //  收据开具的编号 需求有很大逻辑问题
    initDataSource = async () => {
        this.setState({ loading: true });
        try {
            let numbers,
                prefix;
            const { data } = this.props;
            const buildingId = this[form].buildingId;
            const requestReceipt = {
                buildingId,
                reqSize: data.length,
            };
            const resBuilding = await promiseAdd({ id: buildingId }, this.props.getBuildingsId);
            //  如果设置 收据规则 则获取收据编号 填充数据
            if (resBuilding.serialNumberRule) {
                const resRecieptNumber = await promiseAdd({ query: requestReceipt }, this.props.getReceiptGetNumbersIncludeManualForReceipt);
                numbers = resRecieptNumber.numbers;
                prefix = resRecieptNumber.prefix;
            }
            const listData = data.reduce((map, currVal, currIndex) => {
                const generateId = shortid.generate();
                const billId = typeof this.props.billId === 'number' ? this.props.billId : this.props.billId[currIndex];
                const { payedAmount, receiptAmount, other, monetaryUnit, billType, orderNo, roomNumber, payee, collectingCompany} = currVal;
                const amount = payedAmount - receiptAmount;
                const { payer, receiver, ...restProps } = this[form];
                //  给每个数据增加一个 generateId   prefix 收据编号前缀  number 收据编号  disabled 是否禁用
                const receiptNum = resBuilding.serialNumberRule ? numbers[currIndex].split(prefix)[1] : '';
                const result = {
                    ...restProps,
                    amount,
                    monetaryUnit,
                    paymentUnit: other,
                    billType,
                    // billId,
                    orderNo,
                    receiptDetails: [{ amount, paymentName: currVal.billType, roomNumber }],
                    payer: { ...payer, companyName: other },
                    relateBills: [{ amount, billId, billType }],
                    receiver: resBuilding.receiptReceiverSettingBuildingModel,
                    generateId,
                    prefix,
                    serialNumberRule: resBuilding.serialNumberRule,
                    receiptNum,
                    disabled: false, // 是否选中
                };
                map.set(generateId, result);
                return map;
            }, new Map());
            this.setState({
                listData,
                disabledAll: false,
                loading: false,
            });
        } catch (e) {
            message.error(e);
        }
    }
    //  CrTableHandle EditModal's data
    handleCrTableClick = (clickEditRow) => {
        this.setState({
            clickEditRow,
        }, () => {
            this.toggleModals('edit', true)();
        });
    }
    // splitButton CrTable's data
    handleSplitButtonClick = () => {
        //  点击拆分 设置拆分的数据
        const { selectedRowKeys, listData } = this.state;
        if (selectedRowKeys.length !== 1) {
            return false;
        }
        this.setState({
            splitedRows: listData.get(selectedRowKeys[0]),
        });
        this.toggleModals('split', true)();
    }
    // mergeButton  CrTable's data
    handleMergeButtonClick = () => {
        const { selecedLength, listData, selectedRowKeys, selectedRows } = this.state;
        const paymentUnits = selectedRows.map(item => item.paymentUnit);
        const payers = selectedRows.map(item => item.payer.companyName);
        const receivers = selectedRows.map(item => item.payer.companyName);
        const data = new Map(listData);
        const generateId = selectedRowKeys[0];
        const hasReceiptNumbers = selectedRows.filter(item => item.serialNumberRule);
        const mergeData = selectedRows[0];
        // 关联账单
        const bills = this.state.selectedRows.reduce((mapData, { relateBills }) => {
            relateBills.map(({ billId, amount }) => {
                if (mapData.has(billId)) {
                    const currObj = mapData.get(billId);
                    currObj.amount += amount;
                    mapData.set(billId, currObj);
                } else {
                    mapData.set(billId, {
                        amount,
                        billId,
                    });
                }
                return false;
            });
            return mapData;
        }, new Map());
        const relateBills = [...bills.values()];
        const reducer = (accumulator, currentValue) => accumulator + currentValue.amount;
        const amount = selectedRows.reduce(reducer, 0);
        let receiptNum = '';
        if (selecedLength <= 1) {
            return false;
        }
        //   租客名称 收款方 付款方 一致才可以合并
        if ([...new Set(paymentUnits)].length > 1) { return message.error('不同租客无法进行收据合并'); }
        if ([...new Set(payers)].length > 1) { return message.error('不同付款方无法进行收据合并'); }
        if ([...new Set(receivers)].length > 1) { return message.error('不同收款方无法进行收据合并'); }
        //  合并 要取得最小编号
        if (hasReceiptNumbers.length) {
            const numbers = hasReceiptNumbers.map(num => num.receiptNum);
            receiptNum = Math.min(...numbers);
        }
        selectedRowKeys.forEach((row, index) => { if (index !== 0) data.delete(row); });
        data.set(generateId, { ...mergeData, amount, receiptNum, generateId, relateBills });
        this.setState({
            listData: data,
            selectedRowKeys: [],
            selectedRows: [],
        });
    }
    // EditModalHandle's submit
    handleEditModalSubmit = (value) => {
        const { clickEditRow: { generateId }, listData } = this.state;
        const data = new Map(listData);
        data.set(generateId, { ...data.get(generateId), ...value });
        this.setState({
            listData: data,
        });
    }
    //  InvoiceSplitModal's submit
    handleSplitSubmit = async ({ matchPrice, number, resPrice }) => {
        //  如果有编号 则把编号放入数据 没有 编号则为空
        //  要拆分的编号是 receiptNum = 191  拆分number= 3 则请求 reqSize= 2 拆分后的编号应为 191 192 193 保留原来的191
        const { splitedRows, listData } = this.state;
        const { buildingId, serialNumberRule, prefix } = splitedRows;
        const oldGenerateId = splitedRows.generateId;
        const requestReceipt = { buildingId, reqSize: resPrice > 0 ? number : number - 1 };
        const data = new Map();
        const valuesData = [...listData.values()];
        const splitIndex = [...listData.keys()].findIndex(item => item === splitedRows.generateId);
        let newRecieptNumber;
        const relateBills = splitedRows.relateBills.slice();
        const newRelateBills = relateBills.reduce((arr, { billId, amount }) => {
            arr.push({
                id: billId,
                value: amount,
            });
            return arr;
        }, []);

        const { splitValue, restValue } = split(
            newRelateBills,
            number,
            matchPrice,
        );
        const splitBills = formatBill(splitValue);
        const restBills = formatBill(restValue);
        try {
            if (serialNumberRule) {
                const resRecieptNumber = await promiseAdd({ query: requestReceipt }, this.props.getReceiptGetNumbersIncludeManualForReceipt);
                newRecieptNumber = resRecieptNumber.numbers;
            }
            const splitData = new Array(resPrice > 0 ? number + 1 : number).fill(0).map((num, index) => {
                let newRowData;
                const generateId = shortid.generate();
                const recieptNum = serialNumberRule && index > 0 ? newRecieptNumber[index - 1].split(prefix)[1] : '';
                newRowData = { ...splitedRows, generateId, recieptNum, amount: matchPrice, relateBills: unique(splitBills) };
                if (index === 0) {
                    newRowData = { ...newRowData, generateId: oldGenerateId, recieptNum: splitedRows.recieptNum };
                }
                if (resPrice > 0 && index === number) {
                    newRowData = { ...splitedRows, generateId, recieptNum, amount: resPrice, relateBills: unique(restBills) };
                }
                return newRowData;
            });
            valuesData.splice(splitIndex, 1, ...splitData);
            valuesData.map(value => data.set(value.generateId, value));
            this.setState({
                listData: data,
                selectedRowKeys: [],
                selectedRows: [],
            });
        } catch (e) {
            message.error(e);
        }
    }
    renderFooter = () => [
        <Button
            type={'primary'}
            onClick={this.handleSubmit}
            disabled={!this.state.selectedRowKeys.length}
            key={'saveBtn'}
            loading={this.state.saveLoading}
        >
            保存
        </Button>,
    ];
    render() {
        const { visible, handleReceiptModalVisible, buildingId, data } = this.props;

        const {
            selectedRowKeys,
            splitModalVisible,
            listData,
            editModalVisible,
            disabledAll,
            startOa,
            clickEditRow,
            loading,
            splitedRows,
        } = this.state;
        const rowSelection = {
            selectedRowKeys,
            onChange: this.onSelectChange,
            getCheckboxProps: () => ({
                disabled: disabledAll,
            }),
        };
        const selecedLength = selectedRowKeys.length;
        return (
            <div>
                <CrModal
                    title="添加收据"
                    width={1000}
                    show={visible}
                    handleCancel={() => handleReceiptModalVisible(false)}
                    wrapClassName={styles.modal}
                    footer={this.renderFooter()}
                >
                    {splitModalVisible && (
                        <InvoiceSplitModal
                            visible={splitModalVisible}
                            handleCloseModal={this.toggleModals('split', false)}
                            splitedRows={splitedRows}
                            title="收据"
                            handleSplitSubmit={this.handleSplitSubmit}
                        />
                    )}
                    {editModalVisible && (
                        <EditModal
                            close={this.toggleModals('edit', false)}
                            visible={editModalVisible}
                            buildingId={buildingId}
                            data={clickEditRow}
                            callback={this.handleEditModalSubmit}
                            receiptNumRanger={{
                                min: [].concat(receiptNumRanger).shift(),
                                middle: [].concat(receiptNumRanger).pop(),
                                max: [].concat(this.state.receiptNumRanger).pop(),
                            }}
                        />
                    )}
                    <div className={styles.header}>
                        <div className={styles.text}>
                            <span>开据数:{listData.size}</span>
                            {'  '}
                            <span>开据金额: {this.onAllmount(data)}</span>
                        </div>
                        <div className={styles.end}>
                            <span
                                className={cx(`${styles.btn}`, {
                                    [`${styles.active}`]: selecedLength === 1,
                                })}
                                onClick={this.handleSplitButtonClick}
                            >
                                拆分
                            </span>
                            <span
                                className={cx(`${styles.btn}`, {
                                    [`${styles.active}`]: selecedLength > 1,
                                })}
                                onClick={this.handleMergeButtonClick}
                            >
                                合并
                            </span>
                        </div>
                    </div>
                    <div className={styles.table}>
                        <Spin spinning={loading}>
                            <CrTable
                                rowKey={'generateId'}
                                columns={generateColumns({
                                    onClick: this.handleCrTableClick,
                                })}
                                rowSelection={rowSelection}
                                dataSource={[...listData.values()]}
                                pagination={false}
                            />
                        </Spin>
                    </div>
                </CrModal>
                <OaStartModal
                    {...startOa}
                />
            </div>
        );
    }
}

export default CreateModal;






// utils.js
const split = (items, size = 1, value = 13) => {
    const total = items.reduce((t, item) => t + item.value, 0);
    if (total >= size * value) {
        return getTargets(items, value, size);
    }
}


function getTargets(items, value, totalSize) {
    const arr = [];
    items.forEach(({ id, value: itemValue }) => {
        if (Math.floor(itemValue / value) > 0) {
            arr.push({
                id,
                size: Math.floor(itemValue / value),
                value: itemValue,
            })
        } else {
            arr.push({
                id,
                size: 0,
                value: itemValue,
            });
        }
    })

    const currentSize = arr.reduce((s, arrItem) => {
        if (s + arrItem.size <= totalSize) {
            arrItem.use = arrItem.size;
            return s + arrItem.size;
        }
        arrItem.use = totalSize - s;
        return totalSize;
    }, 0)

    let size = 0;
    let newArr = arr.map(item => ({ id: item.id, value: item.value - item.use * value }));
    let moreTargets = [];

    if (currentSize < totalSize) {
        while (size < totalSize - currentSize) {
            const target = [];
            newArr.forEach((item, index) => {
                const currentValue = getTargetValue(target);
                if (currentValue < value) {
                    target.push(item);
                    if (currentValue + item.value >= value) {
                        const originValue = target[target.length - 1].value;
                        target[target.length - 1].value = originValue - getTargetValue(target) + value;
                        size += 1;
                        newArr = [{ ...target[target.length - 1], value: originValue - target[target.length - 1].value, use: 0 }, ...newArr.slice(index + 1)];
                    }
                    if (index + 1 === newArr.length) size += 1;
                }
            });
            moreTargets.push(target);
        }
    }

    return {
        splitValue: arr.reduce((targets, item) => {
            const newTargets = [];
            for (let i = 0; i < item.use; i++) {
                newTargets.push({
                    id: item.id,
                    value,
                });
            }
            return [...targets, ...newTargets];
        }, moreTargets),
        restValue: newArr,
    };
}

function getTargetValue(target) {
    return target.reduce((total, item) => total + item.value, 0);
}

export default split;



/**
 *  总结 set map 使用业务场景 
 * set用于没有重复项的数组类型数据
 * map用于value是数组 需要通过给定keys 来区分数组value 对数组value进行操作 的业务场景 很方便 很实用
 * */

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值