ylj20011123 f3703b1f81 update
2025-10-20 18:44:48 +08:00

612 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 经营品牌管理
import { connect } from "umi";
import type { CurrentUser } from "umi";
import type { ConnectState } from "@/models/connect";
import React, { useEffect, useRef, useState } from "react";
import ProCard from "@ant-design/pro-card";
import { MenuFoldOutlined } from "@ant-design/icons";
import type { FormInstance } from "antd";
import { Avatar, Button, Col, Form, message, Popconfirm, Row, Space, Spin, Tree, TreeSelect } from "antd";
import useRequest from "@ahooksjs/use-request";
import { getServerpartTree } from "@/services/options";
import type { ActionType } from "@ant-design/pro-table";
import ProTable from "@ant-design/pro-table";
import ReactHTMLTableToExcel from "react-html-table-to-excel";
import LeftSelectTree from "@/pages/reports/settlementAccount/component/leftSelectTree";
import PageTitleBox from "@/components/PageTitleBox";
import { getBusniessBrandTree } from "../service";
import { initial } from "lodash";
import { ModalForm, ProFormDigit, ProFormSelect, ProFormText, ProFormTextArea } from "@ant-design/pro-form";
import { getList } from "../BusinessTrade/service";
import { handleAddUpdate, handleDelete, handleGetBrandList } from "./service";
import { getList as handleGetTradeList } from '../BusinessTrade/service'
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
import { formatTreeData } from "@/utils/format";
import moment from 'moment'
const operatingBrand: React.FC<{ currentUser: CurrentUser }> = (props) => {
const { currentUser } = props
const downloadBtnRef = useRef<any>()
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>(); // 合计项数据源
const [printOut, setPrintOut] = useState<any>(); // 打印数据的内容
const [collapsible, setCollapsible] = useState<boolean>(false)
const [treeView, setTreeView] = useState<any>()
const [printIndex, setPrintIndex] = useState<number>(new Date().getTime())
const [currentRow, setCurrentRow] = useState<any>();// 当前选中行数据
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string>()
// 导出的加载效果
const [showLoading, setShowLoading] = useState<boolean>(false)
// 是否显示打印的表格
const [showExportTable, setShowExportTable] = useState<boolean>(false)
// 查询的条件
const [searchParams, setSearchParams] = useState<any>()
// 导出数据
const [exportData, setExportData] = useState<any>()
const [createModalVisible, handleModalVisible] = useState<boolean>(false); // 新建窗口的弹窗
const [treeSelectOption, setTreeSelectOption] = useState<any[]>(); // 可选择项
// 业态品牌
const [businessBrand, setBusinessBrand] = useState<any[]>(); // 业态品牌
// 所有业态最里层的数据
const [extractLeafIdsList, setExtractLeafIdsList] = useState<any[]>([]);
const columns: any = [
{
title: "品牌图标",
dataIndex: 'BRAND_INTRO',
width: 120,
align: 'center',
hideInSearch: true,
hideInDescriptions: true,
render: (_, record) => {
return record?.BRAND_INTRO ? <img style={{ width: "90px", height: "90px", borderRadius: '50%' }} src={record?.BRAND_INTRO} /> :
<Avatar src={_} size={16} shape="square">{record.BRAND_NAME ? record.BRAND_NAME.substring(0, 1) : ''}</Avatar>
}
},
{
title: "品牌索引",
dataIndex: "BRAND_INDEX",
width: 120,
align: 'center',
hideInSearch: true,
ellipisis: true,
},
{
title: "品牌名称",
dataIndex: "BRAND_NAME",
width: 120,
align: 'center',
hideInSearch: true,
ellipisis: true,
},
{
title: "经营业态名称",
dataIndex: "BUSINESSTRADE_NAME",
width: 120,
align: 'center',
hideInSearch: true,
ellipisis: true,
},
{
title: "有效状态",
width: 120,
align: 'center',
dataIndex: "BRAND_STATE",
valueType: "select",
fieldProps: {
options: [{ label: "有效", value: 1 }, { label: "无效", value: 0 }]
},
initialValue: 1,
},
{
title: '操作',
width: 120,
dataIndex: 'option',
valueType: 'option',
align: 'center',
hideInDescriptions: true,
render: (_, record) => record?.BRAND_ID ? [
<a
key="edit"
onClick={() => {
setCurrentRow(record);
handleModalVisible(true);
}}
>
</a>,
<Popconfirm title="确认删除该品牌?" onConfirm={async () => {
const sucesse = await handleDelete({ BrandId: record.BRAND_ID })
if (sucesse && actionRef.current) {
actionRef.current.reload()
}
}}>
<a></a>
</Popconfirm>
] : ""
}
]
const exportColumns: any = [
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'Business_Format',
align: 'center',
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'BusinessTrade_Name',
align: 'center',
hideInSearch: true,
},
]
const exportTable = (e) => {
e.stopPropagation(); // 防止Collapse组件收起
const main = document.getElementsByClassName(`saleReportHideBox${printIndex}`)[0]
const thead = main.querySelector('thead').cloneNode(true); // 深克隆DOM节点
const tbody = main.querySelector('tbody').cloneNode(true); // 深克隆DOM节点
const container = document.querySelector('#hiddenBox');
const tempTable = document.createElement('table');
tempTable.appendChild(thead);
tempTable.appendChild(tbody);
tempTable.setAttribute('id', 'table-to-xls-operatingBrand'); // 给table添加id值与按钮上的table字段对应
container.appendChild(tempTable); // 把创建的节点添加到页面容器中
setShowLoading(false)
downloadBtnRef.current.handleDownload();
setShowExportTable(false)
tempTable.remove() // 防止重复打印一个内容
}
useEffect(async () => {
// 拿到经营品牌数据
await handleGetBusinessTrade();
}, [])
const normalizeTreeChildren = (list: any[]): any[] => {
return list.map((node: any) => {
// 复制节点,防止修改原始数据
const newNode = { ...node };
// 可能存在两种字段BrandTreeList / children
const subList = newNode.children || newNode.BrandTreeList;
if (Array.isArray(subList) && subList.length > 0) {
// 递归处理子节点
newNode.children = normalizeTreeChildren(subList);
} else {
// ✅ 空数组改为 null
newNode.children = null;
}
// 删除旧字段
delete newNode.BrandTreeList;
return newNode;
});
};
// 拿到经营业态的数据
const handleGetBusinessTrade = async () => {
const data: any = await handleGetTradeList({ BusinessTradeModelPID: -1, BusinessTradeState: 1 })
console.log('handleGetBusinessTradehandleGetBusinessTrade', data);
setBusinessBrand(data.data)
setExtractLeafIdsList(extractLeafIds(data.data))
setTreeSelectOption(markNonLeafDisabled([{ AUTOSTATISTICS_NAME: '默认', AUTOSTATISTICS_ID: -1, children: [...data.data] }]))
}
// 拿到全部最里层的 业态值
const extractLeafIds = (data: any) => {
const result: any = [];
const traverse = (list: any) => {
list.forEach((item: any) => {
if (!item.children || item.children.length === 0) {
// 没有 children就是最里层节点
result.push(item.AUTOSTATISTICS_ID);
} else {
// 有 children继续递归
traverse(item.children);
}
});
}
traverse(data);
return result;
}
// 不是没有children 都禁用
const markNonLeafDisabled = (list: any) => {
return list.map((node: any) => {
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
// 复制原节点,添加 disabled 字段(只有非叶子节点 disabled
const newNode = {
...node,
// 如果你更喜欢使用 selectable 而不是 disabled可用: selectable: !hasChildren
disabled: hasChildren ? true : undefined,
};
if (hasChildren) {
newNode.children = markNonLeafDisabled(node.children);
}
return newNode;
});
}
return (
<div ref={(el) => {
// 打印报表
if (!reqDetailList || reqDetailList.length === 0) return;
setPrintOut(el);
}} >
{
showLoading ?
<div
style={{
width: '100%',
height: '100%',
background: 'rgba(0,0,0,0.1)',
position: 'fixed',
zIndex: 5,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '15px 20px 10px',
background: '#fff',
borderRadius: '8px',
width: '200px'
}}>
<Spin />
<span style={{ marginLeft: '5px' }}>...</span>
</div>
</div> : ''
}
<div className={`saleReportHideBox${printIndex}`} style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }}>
{
showExportTable && exportData && exportData.length > 0 ?
<ProTable
columns={exportColumns}
dataSource={exportData}
pagination={false}
expandable={{
defaultExpandAllRows: true
}}
/> : ''
}
</div>
<div id='hiddenBox' style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }} />
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<ProCard
style={{ width: !collapsible ? "300px" : "60px" }}
className="pageTable-leftnav"
bodyStyle={{ padding: 0, paddingTop: 20, paddingLeft: 20, width: !collapsible ? "300px" : "60px" }}
extra={<MenuFoldOutlined onClick={() => {
setCollapsible(!collapsible);
}} />}
colSpan={!collapsible ? "300px" : "60px"}
title={!collapsible ? "请选择业态" : ""}
headerBordered
collapsed={collapsible}
>
{businessBrand && businessBrand.length > 0 ? <Tree
checkable
treeData={[{
AUTOSTATISTICS_NAME: '全部',
value: 0,
AUTOSTATISTICS_ID: '0-0',
children: businessBrand
}]}
blockNode
defaultExpandAll={true}
defaultExpandedKeys={['0-0']}
onCheck={(checkedKeys: React.Key[] | any, info) => {
let res: any = []
if (checkedKeys && checkedKeys.length > 0) {
if (extractLeafIdsList && extractLeafIdsList.length > 0) {
checkedKeys.forEach((item: any) => {
if (extractLeafIdsList.indexOf(item) > -1) {
res.push(item);
}
})
}
}
setSelectedId(res)
}}
fieldNames={{
title: "AUTOSTATISTICS_NAME",
key: "AUTOSTATISTICS_ID"
}}
/> : ''}
</ProCard>
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
rowKey={(record) => {
return `${record?.BusinessTrade_Id}`
}}
bordered
expandable={{
expandRowByClick: true
}}
scroll={{ x: "100%", y: "calc(100vh - 410px)" }}
headerTitle={<PageTitleBox props={props} />} // 列表表头
search={{ span: 6 }}
request={async (params) => {
if (!selectedId) {
return { data: [], success: true }
}
const req: any = {
SearchParameter: {
BRAND_CATEGORY: 1000,
BRAND_INDUSTRYS: selectedId && selectedId.length > 0 ? selectedId.toString() : '',
BRAND_STATE: params.BRAND_STATE || "",
},
PageIndex: 1,
PageSize: 999999
}
const data = await handleGetBrandList(req)
console.log('handleGetBrandListhandleGetBrandList', data);
if (data && data.length > 0) {
let fieldData: any = []
let enumList: any = ["BRAND_STATE",]
let newPrintData: any = formatTreeData(JSON.parse(JSON.stringify(data)), fieldData, enumList, [[{ label: "有效", value: 1 }, { label: "无效", value: 0 }]], [])
setReqDetailList(newPrintData)
return { data: data, success: true }
}
// const req: any = {
// ProvinceCode: currentUser?.ProvinceCode,
// BrandState: 1
// }
// setSearchParams(params)
// const data = await getBusniessBrandTree(req)
// if (data && data.length > 0) {
// let exportData: any = []
// data.forEach((item: any) => {
// exportData.push({
// Business_Format: item.BusinessTrade_Name,
// BusinessTrade_Name: ""
// })
// if (item.children && item.children.length > 0) {
// item.children.forEach((subItem: any) => {
// exportData.push({
// Business_Format: item.BusinessTrade_Name,
// BusinessTrade_Name: subItem.BusinessTrade_Name,
// })
// })
// }
// })
// setExportData(exportData)
// console.log('datadatadata', data);
// let res: any = normalizeTreeChildren(data)
// console.log(res, 'res');
// return { data: res, success: true }
// }
setReqDetailList([])
return { data: [], success: true }
}}
toolbar={{
actions: [
// <span style={{ visibility: 'hidden' }}>
// <ReactHTMLTableToExcel
// buttonText={'导出excel'}
// ref={downloadBtnRef}
// table="table-to-xls-operatingBrand"
// filename={`经营品牌列表`}
// sheet="sheet1"
// />
// </span>,
<Button
key="new"
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns.slice(0, columns.length - 1),
reqDetailList,
`经营品牌列表`,
{
footerMaker: currentUser?.Name,
footerMakerTime: moment().format('YYYY-MM-DD HH:mm:ss'),
footerStatsTime: ""
}
)
} else {
message.error('暂无数据可导出!')
}
// if (exportData && exportData.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel
</Button>,
<Button
key="new"
type="primary"
onClick={(e) => {
handleModalVisible(true)
}}
>
</Button>
]
}}
/>
</div>
</div>
{ /* 更新 信息弹出框 */}
<ModalForm
layout={'horizontal'}
wrapperCol={{ span: 16 }}
labelCol={{ span: 6 }}
width={600}
title={currentRow ? '更新经营业态' : '新建经营业态'}
visible={createModalVisible}
formRef={formRef}
onVisibleChange={(value) => {
handleModalVisible(value)
if (!value) {
formRef.current?.resetFields();
setCurrentRow(undefined);
} else {
formRef.current?.setFieldsValue(currentRow || { AUTOSTATISTICS_TYPE: 2000 })
}
}}
onFinish={async (values) => {
let newValue: any = { ...values }
if (currentRow) { // 编辑数据
newValue = { ...currentRow, ...newValue }
}
newValue.AUTOSTATISTICS_STATE = values.AUTOSTATISTICS_STATE ? 1 : 0
newValue.AUTOSTATISTICS_TYPE = currentRow?.AUTOSTATISTICS_TYPE || 2000
const success = await handleAddUpdate(newValue);
if (success) {
if (actionRef.current) {
actionRef.current.reload();
}
return true
}
return false
}}
>
<Row>
<Col span={24}>
<Form.Item
name="BRAND_INDUSTRY"
label="上级业态"
rules={[{ required: true, message: '请选择上级业态' }]}
>
<TreeSelect
placeholder="请选择上级业态"
dropdownStyle={{ maxHeight: 300, overflow: 'auto' }}
treeDefaultExpandAll
showArrow={false}
showSearch
treeData={treeSelectOption}
fieldNames={{
label: 'AUTOSTATISTICS_NAME',
value: 'AUTOSTATISTICS_ID',
}}
filterTreeNode={(input, node) =>
String(node.AUTOSTATISTICS_NAME)
.toLowerCase()
.includes(String(input).toLowerCase())
}
/>
</Form.Item>
<ProFormText
name="BRAND_NAME"
label="品牌名称"
placeholder="请输入品牌名称"
rules={[
{
required: true,
message: '请输入品牌名称',
},
]} />
<ProFormDigit
name="BRAND_INDEX"
label="品牌索引"
placeholder="请输入品牌索引"
min={0}
max={9999}
rules={[
{
required: true,
message: '请输入品牌索引',
},
]}
fieldProps={{ precision: 0 }}
/>
<ProFormSelect
name="BRAND_CATEGORY"
label="品牌类型"
rules={[
{
required: true,
message: '请选择品牌类型',
},
]}
options={[{ label: '经营品牌', value: 1000 }]}
initialValue={1000}
// , { label: '商城品牌', value: 2000 }
/>
<ProFormSelect
name="BRAND_STATE"
label="是否有效"
options={[{ label: '有效', value: 1 }, { label: '无效', value: 0 }]}
initialValue={1}
/>
<ProFormTextArea
name="BRAND_DESC"
label="品牌介绍"
placeholder="请输入介绍" />
</Col>
</Row>
</ModalForm>
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.currentUser
}))(operatingBrand);