统计动态看板代码提交

main
liuyiliang 4 months ago
parent bf15dbb29d
commit 9d69f174c6
  1. 2
      packages/examination/craco.config.js
  2. 28
      packages/examination/src/App.tsx
  3. 1
      packages/examination/src/api/axios.js
  4. 19
      packages/examination/src/api/statistical/index.tsx
  5. 4
      packages/examination/src/style/common.css
  6. 16
      packages/examination/src/views/demo/demoList.tsx
  7. 244
      packages/examination/src/views/home/index.tsx
  8. 19
      packages/examination/src/views/slider/index.js
  9. 158
      packages/examination/src/views/slider/menu.tsx
  10. 318
      packages/examination/src/views/statistical/customerRetention.tsx
  11. 95
      packages/examination/src/views/statistical/detail.tsx
  12. 156
      packages/examination/src/views/statistical/enterpriseFile.tsx
  13. 52
      packages/examination/src/views/statistical/list.tsx

@ -7,7 +7,7 @@ module.exports = {
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#f45f04' },
modifyVars: { '@primary-color': '#B5DBFF' },
javascriptEnabled: true,
},
},

@ -1,14 +1,19 @@
import React from 'react'
import {Layout, Select} from 'antd';
import {Layout, Breadcrumb, Select} from 'antd';
import Header from 'views/header';
import Slider from 'views/slider';
import ContentMain from 'components/contentMain';
import { withRouter } from 'react-router-dom'
import {findTitleByKey} from "./views/slider";
const { Content, Footer, Sider } = Layout;
class App extends React.Component {
state = {
collapsed: false,
breadcrumb: []
};
onCollapse = (collapsed: boolean) => {
@ -20,10 +25,19 @@ class App extends React.Component {
}
componentDidMount() {
// @ts-ignore
this.props.history.listen(path=>{
console.log("全局路由监听" + path.pathname)
var tag = findTitleByKey(path.pathname.slice(1));
console.log(tag)
if (tag && tag.length > 0) {
this.setState({ breadcrumb: tag });
}
})
}
render() {
const { collapsed } = this.state;
const { collapsed, breadcrumb } = this.state;
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider theme={'light'} style={{ background: '#fff' }} trigger={null} collapsible collapsed={collapsed} onCollapse={this.onCollapse}>
@ -32,13 +46,19 @@ class App extends React.Component {
<Layout className="site-layout">
<Header changeCollapse={this.changeCollapse.bind(this)} />
<Content className={collapsed === true ? 'noLeft' : 'left'} style={{ position: 'absolute', top: 70, right: 0, bottom: 0 }}>
<Breadcrumb style={{ margin: '0 16px' }}>
{breadcrumb.map((item, index) => (
<Breadcrumb.Item key={index}>{item}</Breadcrumb.Item>
))}
</Breadcrumb>
<ContentMain />
</Content>
<Footer className={collapsed === true ? 'noLeft footer' : 'left footer'} style={{ position: 'absolute', right: 0, bottom: 0 }}>React and Ant Design ©2021 Created by Machao </Footer>
<Footer className={collapsed === true ? 'noLeft footer' : 'left footer'} style={{ position: 'absolute', right: 0, bottom: 0 }}></Footer>
</Layout>
</Layout>
)
}
}
export default App;
// @ts-ignore
export default withRouter(App);

@ -15,6 +15,7 @@ const instance = axios.create({
baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '/api',
timeout: 3000
})
export const serverUrl = 'http://localhost:8187/';
instance.interceptors.request.use(
config => {

@ -1,9 +1,24 @@
import axios from '../axios';
export function getList(obj: any){
console.log("1",obj)
return axios({
url: "/ex/statistics/page",
url: "/ex/statistics/page?page=" + obj.page + "&num=" + obj.num,
method: "post",
data: obj
})
}
export function getCustomerRetPage(obj: any, page: number, num: number){
return axios({
url: "/ex/statistics/customerRetPage?page=" + page + "&num=" + num,
method: "post",
data: obj
})
}
export function saveCustomerRet(obj: any){
return axios({
url: "/ex/statistics/submitCustomerRet",
method: "post",
data: obj
})

@ -78,7 +78,7 @@ body {
border-left: 1px solid #ccc;
}
.iconColor{
color: #f45f04;
color: #B5DBFF;
}
.container{
background: #fff;
@ -139,7 +139,7 @@ body {
}
.link{
color:#f45f04;
color: rgb(58 167 255);
cursor: pointer;
}

@ -71,19 +71,9 @@ class DemoList extends Component<any, States> {
name="sex">
<Select placeholder="全部" style={{ width: 120 }} allowClear>
{
sexDict && sexDict.length > 0 ?
(() => {
let rows = [];
for (let i = 0; i < sexDict.length; i++) {
const item = sexDict[i];
rows.push(
<Option value={item.dictKey}>{item.dictValue}</Option>
);
}
return rows;
})()
:
<Option disabled></Option>
sexDict && sexDict.map((item: { dictKey: any; dictValue: string }) => {
return <Option value={item.dictKey}>{item.dictValue}</Option>
})
}
</Select>
</Form.Item>

@ -211,131 +211,131 @@ class Home extends React.Component <{}, State>{
const { sellerInfo, report, todayRevenue, todayPay, memberdData } = this.state;
return (
<div>
<div className="content-head mb20">
<Card hoverable={true}>
<Row gutter={20}>
<Col span={8}>
<div className="seller">
<div className="icon" style={{ backgroundImage: `url(${sellerInfo.picture}` }}></div>
<div className="seller-content">
<div className="title">{getSellerName()}</div>
<div className="address">
<PhoneOutlined className="iconColor mr5" />
<span className="mr5">{sellerInfo.contactPhone}</span>
<ScheduleOutlined className="iconColor mr5" />
<span></span>
</div>
</div>
</div>
</Col>
<Col className="br tc" span={4}>
<p className="price">{report.orderPayMoneyToday}</p>
<p className="label"> ()</p>
</Col>
<Col className="br tc" span={4}>
<p className="price">{report.salesAmountMonth}</p>
<p className="label"> ()</p>
</Col>
<Col className="br tc" span={4}>
<p className="price">{report.balance}</p>
<p className="label"> ()</p>
</Col>
<Col className="br tc" span={4}>
<p>
<Button type="primary" size="small"></Button>
</p>
<p className="label">
<Button type="text" size="small"></Button>
</p>
</Col>
</Row>
</Card>
</div>
{/*<div className="content-head mb20">*/}
{/* <Card hoverable={true}>*/}
{/* <Row gutter={20}>*/}
{/* <Col span={8}>*/}
{/* <div className="seller">*/}
{/* <div className="icon" style={{ backgroundImage: `url(${sellerInfo.picture}` }}></div>*/}
{/* <div className="seller-content">*/}
{/* <div className="title">{getSellerName()}</div>*/}
{/* <div className="address">*/}
{/* <PhoneOutlined className="iconColor mr5" />*/}
{/* <span className="mr5">{sellerInfo.contactPhone}</span>*/}
{/* <ScheduleOutlined className="iconColor mr5" />*/}
{/* <span>已认证</span>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* </Col>*/}
{/* <Col className="br tc" span={4}>*/}
{/* <p className="price">{report.orderPayMoneyToday}</p>*/}
{/* <p className="label">今日交易金额 (元)</p>*/}
{/* </Col>*/}
{/* <Col className="br tc" span={4}>*/}
{/* <p className="price">{report.salesAmountMonth}</p>*/}
{/* <p className="label">本月销售额 (元)</p>*/}
{/* </Col>*/}
{/* <Col className="br tc" span={4}>*/}
{/* <p className="price">{report.balance}</p>*/}
{/* <p className="label">账户余额 (元)</p>*/}
{/* </Col>*/}
{/* <Col className="br tc" span={4}>*/}
{/* <p>*/}
{/* <Button type="primary" size="small">提现</Button>*/}
{/* </p>*/}
{/* <p className="label">*/}
{/* <Button type="text" size="small">账户中心</Button>*/}
{/* </p>*/}
{/* </Col>*/}
{/* </Row>*/}
{/* </Card>*/}
{/*</div>*/}
<div className="mb20">
<Row gutter={20}>
<Col span={8} className="homeCard">
<Card hoverable={true}>
<div className="title">
()<p color="#FF0000">¥ {todayRevenue.orderAmount} / {todayRevenue.orderCount} </p>
</div>
<div className="text">
<p>¥ {todayPay.userPayMoney} </p>
<p>¥ {todayPay.payFee}</p>
</div>
</Card>
</Col>
<Col span={8} className="homeCard">
<Card hoverable={true}>
<div className="title">
()<p color="#FF0000">¥ {todayRevenue.couponAmount} / {todayRevenue.couponCount} </p>
</div>
<div className="text">
<p>¥ {todayPay.closerAmount} </p>
<p>¥ {todayPay.getFee} </p>
</div>
</Card>
</Col>
<Col span={8} className="homeCard">
<Card hoverable={true}>
<div className="title">
()<p color="#FF0000">¥ {todayRevenue.otherAmount} / {todayRevenue.otherCount} </p>
</div>
<div className="text other">
<p>¥ {todayPay.feeAmount} </p>
<p>¥ {todayPay.couponRecommedAmount} </p>
</div>
<div className="text">
<p>广¥ {todayPay.recommedAmount} </p>
<p>¥ {todayPay.couponFeeAmount} </p>
</div>
</Card>
</Col>
</Row>
</div>
{/*<div className="mb20">*/}
{/* <Row gutter={20}>*/}
{/* <Col span={8} className="homeCard">*/}
{/* <Card hoverable={true}>*/}
{/* <div className="title">*/}
{/* 今日订单收入(元):<p color="#FF0000">¥ {todayRevenue.orderAmount} / {todayRevenue.orderCount} 笔</p>*/}
{/* </div>*/}
{/* <div className="text">*/}
{/* <p>用户支付金额:¥ {todayPay.userPayMoney} </p>*/}
{/* <p>手续费:¥ {todayPay.payFee}</p>*/}
{/* </div>*/}
{/* </Card>*/}
{/* </Col>*/}
{/* <Col span={8} className="homeCard">*/}
{/* <Card hoverable={true}>*/}
{/* <div className="title">*/}
{/* 今日优惠券收入(元):<p color="#FF0000">¥ {todayRevenue.couponAmount} / {todayRevenue.couponCount} 笔</p>*/}
{/* </div>*/}
{/* <div className="text">*/}
{/* <p>核销收入:¥ {todayPay.closerAmount} </p>*/}
{/* <p>补贴收入:¥ {todayPay.getFee} </p>*/}
{/* </div>*/}
{/* </Card>*/}
{/* </Col>*/}
{/* <Col span={8} className="homeCard">*/}
{/* <Card hoverable={true}>*/}
{/* <div className="title">*/}
{/* 今日联盟收入(元):<p color="#FF0000">¥ {todayRevenue.otherAmount} / {todayRevenue.otherCount} 笔</p>*/}
{/* </div>*/}
{/* <div className="text other">*/}
{/* <p>佣金收入:¥ {todayPay.feeAmount} </p>*/}
{/* <p>券分享收入:¥ {todayPay.couponRecommedAmount} </p>*/}
{/* </div>*/}
{/* <div className="text">*/}
{/* <p>推广收入:¥ {todayPay.recommedAmount} </p>*/}
{/* <p>关联订单收入:¥ {todayPay.couponFeeAmount} </p>*/}
{/* </div>*/}
{/* </Card>*/}
{/* </Col>*/}
{/* </Row>*/}
{/*</div>*/}
<div className="member mb20">
<Row gutter={20}>
<Col span={8} className="homeCard">
<Card hoverable={true} style={{ borderTop: '2px solid #F45D3C' }}>
<div className="title tc">
</div>
<div className="text tc">
<IconFont className="fontStyle" type="icon-wo" style={{ color: '#F45D3C' }} />
<p className="fontStyle ml15" style={{ color: '#F45D3C' }}>{memberdData.todayMemberCount}</p>
</div>
</Card>
</Col>
<Col span={8} className="homeCard">
<Card hoverable={true} style={{ borderTop: '2px solid #2489F3' }}>
<div className="title tc">
</div>
<div className="text tc">
<IconFont className="fontStyle" type="icon-zengjiarenyuan" style={{ color: '#2489F3' }} />
<p className="fontStyle ml15" style={{ color: '#2489F3' }}>{memberdData.allMemberCount}</p>
</div>
</Card>
</Col>
<Col span={8} className="homeCard">
<Card hoverable={true} style={{ borderTop: '2px solid #5FD76D' }}>
<div className="title tc">
</div>
<div className="text tc">
<IconFont className="fontStyle" type="icon-qian" style={{ color: '#5FD76D' }} />
<p className="fontStyle ml15" style={{ color: '#5FD76D' }}>{memberdData.allOtherAmount}</p>
</div>
</Card>
</Col>
</Row>
</div>
{/*<div className="member mb20">*/}
{/* <Row gutter={20}>*/}
{/* <Col span={8} className="homeCard">*/}
{/* <Card hoverable={true} style={{ borderTop: '2px solid #F45D3C' }}>*/}
{/* <div className="title tc">*/}
{/* 今日新增会员数(人)*/}
{/* </div>*/}
{/* <div className="text tc">*/}
{/* <IconFont className="fontStyle" type="icon-wo" style={{ color: '#F45D3C' }} />*/}
{/* <p className="fontStyle ml15" style={{ color: '#F45D3C' }}>{memberdData.todayMemberCount}</p>*/}
{/* </div>*/}
{/* </Card>*/}
{/* </Col>*/}
{/* <Col span={8} className="homeCard">*/}
{/* <Card hoverable={true} style={{ borderTop: '2px solid #2489F3' }}>*/}
{/* <div className="title tc">*/}
{/* 累计会员数(人)*/}
{/* </div>*/}
{/* <div className="text tc">*/}
{/* <IconFont className="fontStyle" type="icon-zengjiarenyuan" style={{ color: '#2489F3' }} />*/}
{/* <p className="fontStyle ml15" style={{ color: '#2489F3' }}>{memberdData.allMemberCount}</p>*/}
{/* </div>*/}
{/* </Card>*/}
{/* </Col>*/}
{/* <Col span={8} className="homeCard">*/}
{/* <Card hoverable={true} style={{ borderTop: '2px solid #5FD76D' }}>*/}
{/* <div className="title tc">*/}
{/* 累计联盟收入(元)*/}
{/* </div>*/}
{/* <div className="text tc">*/}
{/* <IconFont className="fontStyle" type="icon-qian" style={{ color: '#5FD76D' }} />*/}
{/* <p className="fontStyle ml15" style={{ color: '#5FD76D' }}>{memberdData.allOtherAmount}</p>*/}
{/* </div>*/}
{/* </Card>*/}
{/* </Col>*/}
{/* </Row>*/}
{/*</div>*/}
{/* 数据统计 */}
<Card title="数据统计">
<ReactEcharts option={this.state.option} />
</Card>
{/*/!* 数据统计 *!/*/}
{/*<Card title="数据统计">*/}
{/* <ReactEcharts option={this.state.option} />*/}
{/*</Card>*/}
</div>
)
}

@ -6,6 +6,25 @@ import { FolderOutlined, FileTextOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
// 查找key对应的title
export const findTitleByKey = (key) => {
var result = [];
const findMenu = (menus, targetKey, path = []) => {
for (const menu of menus) {
const currentPath = [...path, menu.title];
if (menu.key === targetKey) {
result = currentPath;
return;
}
if (menu.subs && menu.subs.length > 0) {
findMenu(menu.subs, targetKey, currentPath);
}
}
};
findMenu(menuList, key);
return result;
};
class Slider extends React.Component {
constructor(props) {
super(props);

@ -23,85 +23,85 @@ const menuList = [
}
]
},
{
title: 'demo',
key: 'demo',
icon: 'icon-peizhi',
subs: [
{
title: 'list',
key: 'demoList',
}
]
},
{
title: '订单管理',
key: 'order_mag',
icon: 'icon-gouwucheman',
subs: [
{
title: '股东活动订单',
key: 'promotion',
},
{
title: '优惠券订单',
key: 'couponOrder',
}
]
},
{
title: '配置管理',
key: 'configuration_mag',
icon: 'icon-peizhi',
subs: [
{
title: '金币配置',
key: 'gold',
},
{
title: '公告管理',
key: 'announcement',
}
]
},
{
title: '积分会员管理',
key: 'integralmember_manage',
icon: 'icon-jifen',
subs: [
{
title: '积分规则',
key: 'integralRule',
}
]
},
{
title: '小店管理',
key: 'shop_manage',
icon: 'icon-shop',
subs: [
{
title: '店铺管理',
key: 'store',
}
]
},
{
title: '提现管理',
key: 'withdrawal_manage',
icon: 'icon-withdrawal',
subs: [
{
title: '提现规则',
key: 'store',
}
]
},
{
title: '首页',
key: 'home',
icon: 'icon-home'
}
// {
// title: 'demo',
// key: 'demo',
// icon: 'icon-peizhi',
// subs: [
// {
// title: 'list',
// key: 'demoList',
// }
// ]
// },
// {
// title: '订单管理',
// key: 'order_mag',
// icon: 'icon-gouwucheman',
// subs: [
// {
// title: '股东活动订单',
// key: 'promotion',
// },
// {
// title: '优惠券订单',
// key: 'couponOrder',
// }
// ]
// },
// {
// title: '配置管理',
// key: 'configuration_mag',
// icon: 'icon-peizhi',
// subs: [
// {
// title: '金币配置',
// key: 'gold',
// },
// {
// title: '公告管理',
// key: 'announcement',
// }
// ]
// },
// {
// title: '积分会员管理',
// key: 'integralmember_manage',
// icon: 'icon-jifen',
// subs: [
// {
// title: '积分规则',
// key: 'integralRule',
// }
// ]
// },
// {
// title: '小店管理',
// key: 'shop_manage',
// icon: 'icon-shop',
// subs: [
// {
// title: '店铺管理',
// key: 'store',
// }
// ]
// },
// {
// title: '提现管理',
// key: 'withdrawal_manage',
// icon: 'icon-withdrawal',
// subs: [
// {
// title: '提现规则',
// key: 'store',
// }
// ]
// },
// {
// title: '首页',
// key: 'home',
// icon: 'icon-home'
// }
]
export default menuList;

@ -0,0 +1,318 @@
import React, { useState, useEffect } from 'react';
import {Table, Button, Spin, Space, Modal, Form, Upload, Image, notification} from 'antd';
import TextArea from "antd/es/input/TextArea";
import {getCustomerRetPage, saveCustomerRet} from "../../api/statistical";
import {serverUrl} from "../../api/axios";
type NotificationPlacement = 'top' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
type NotificationType = 'success' | 'info' | 'warning' | 'error';
interface CustomerRetentionProps {
customer: any;
}
interface Enterprise {
key: string;
uploadTime: string;
description: string;
fileUid: string;
}
interface FormData {
num: number;
page: number;
}
interface FileRes {
fileId: string;
fileName: string;
}
const CustomerRetention: React.FC<CustomerRetentionProps> = ({ customer }) => {
// if (!customer) {
// // @ts-ignore
// const { history } = this.props;
// history.push({
// pathname: '/customer'
// });
// return (<div/>);
// }
if (!customer) {
customer = {
customerId: 0,
customerName: 'asda'
}
}
const [data, setData] = useState<Enterprise[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [query, setQuery] = useState<FormData | null>(null);
const [total, setTotal] = useState<number | null>(0);
const [fileList, setFileList] = useState<any[]>([]);
const [fileUrl, setFileUrl] = useState<string | null>(null);
const [fileRes, setFileRes] = useState<FileRes | null>(null);
const [form] = Form.useForm();
// Simulate data fetching on component mount
useEffect(() => {
const query = {page: 1, num: 20};
setQuery(query);
getListApi({}, query.page, query.num)
}, []);
const columns = [
{
title: '序号',
dataIndex: 'key',
render: (text: string, record: any, index: number) => index + 1
},
{
title: '说明描述',
dataIndex: 'description',
key: 'description',
},
{
title: '上传时间',
dataIndex: 'uploadTime',
key: 'uploadTime',
},
{
title: '附件',
dataIndex: 'file',
key: 'file',
sorter: true,
render: (text: any, record: { fileUid: string; }) => (
<Space size="middle">
<a onClick={() => showModal2(record.fileUid)}></a>
</Space>
),
},
];
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleOk = () => {
setLoading(true);
if (fileList.length == 0) {
openNotification('top', '请上传文件', 'info');
return;
}
var formData = {
customerId: customer.customerId,
fileId: fileRes?.fileId,
fileName: fileRes?.fileName,
description: form.getFieldValue('description')
}
saveCustomerRet(formData).then(res => {
openNotification('top', '保存成功', 'success');
const query = {page: 1, num: 20};
setQuery(query);
getListApi({}, query.page, query.num)
setFileList([]); // 清空文件列表
form.resetFields(['description']); // 重置description字段
setIsModalOpen(false);
}).catch(() => { })
};
const handleCancel = () => {
setFileList([]); // 清空文件列表
form.resetFields(['description']); // 重置description字段
setIsModalOpen(false);
};
const [isModalOpen2, setIsModalOpen2] = useState(false);
const showModal2 = (fileUid: string) => {
console.log(fileUid)
setFileUrl('./ex/file/download?fileName=' + fileUid);
setIsModalOpen2(true);
};
const handleOk2 = () => {
debugger
const url = serverUrl + fileUrl;
const link = document.createElement('a');
link.href = url;
// link.download = 'file.png';
link.click();
setFileUrl(null);
setIsModalOpen2(false);
};
const handleCancel2 = () => {
setFileUrl(null);
setIsModalOpen2(false);
};
// 多少每页
const selectChange = (page: number, num: number) => {
const query = { page, num }
setQuery(query);
getListApi({}, query.page, query.num)
}
// 条数切换
const changePage = (current: number, pageSize?: number) => {
debugger
const query = { page: current, num: pageSize || 20 }
setTimeout(() => {
setQuery(query);
getListApi({}, query.page, query.num)
}, 0)
}
// 列表接口
const getListApi = (formData: object, page: number, num: number) => {
setLoading(true);
var formObj = JSON.parse(JSON.stringify(formData));
formObj.customerId = customer.customerId;
getCustomerRetPage(formObj, page, num).then(res => {
setData(res.data.records);
setTotal(res.data.total);
setLoading(false);
}).catch(() => { })
}
const clickUpload = () => {
const uploadButton = document.querySelector('.ant-upload-drag-container') as HTMLDivElement;
if (uploadButton) {
uploadButton.click();
}
}
const [api, contextHolder] = notification.useNotification();
const openNotification = (placement: NotificationPlacement, msg: string, type: NotificationType) => {
api[type]({
message: '提示',
description: msg,
placement,
});
return false;
};
const beforeUpload = (file: File) => {
};
const handleChange = (info: any) => {
var file = info.file;
// 限制文件类型
const isImage = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isImage) {
openNotification('top', '只支持上传JPG或PNG格式的文件','info');
return;
}
setFileList(info.fileList);
};
// 自定义字段,包含 fileType
const customRequest = async (options: any) => {
const { onSuccess, onError, file } = options;
const formData = new FormData();
formData.append('file', file);
formData.append('fileType', 'CUSTOMER_RETENTION');
try {
const response = await fetch('/ex/file/upload', {
method: 'POST',
body: formData,
});
if (response.ok) {
const res = await response.json();
setFileRes({
fileId: res.data.id,
fileName: res.data.name
})
onSuccess?.({}, file);
} else {
throw new Error('上传失败');
}
} catch (error) {
onError?.(error);
}
};
return (
<div className="container">
{contextHolder}
<div>
<div>
<span style={{fontWeight: 600, fontSize: 16}}>{customer.customerName}</span>
<Button type="primary" style={{ marginBottom: 20, float: 'right' }} onClick={showModal}></Button>
</div>
{loading ? (
<Spin tip="Loading..." />
) : (
<Table
dataSource={data}
columns={columns}
rowKey="key"
loading={loading}
pagination={{
total: total || 0,
current: query?.page || 1,
pageSize: query?.num || 10,
showQuickJumper: true,
showSizeChanger: true,
showTotal: total => `${total}`,
onShowSizeChange: selectChange,
onChange: changePage
}}
/>
)}
</div>
<Modal title="信息留存" open={isModalOpen} onOk={handleOk} onCancel={handleCancel} centered okText="保存">
<Form
form={form}
name="wrap"
labelCol={{ flex: '110px' }}
labelAlign="left"
labelWrap
wrapperCol={{ flex: 1 }}
colon={false}
style={{ maxWidth: 600 }}
>
<Form.Item label="信息上传" name="fileUpload" rules={[{ required: false }]}>
<Button type="primary" onClick={clickUpload}>
</Button>
<span style={{color: 'rgba(0, 0, 0, 0.45)'}}><span style={{color: 'red', marginLeft: 10}}>*</span>jpgpng文件</span>
</Form.Item>
<div style={{marginBottom: 40, height: 120}}>
<Upload.Dragger
name="files"
beforeUpload={beforeUpload}
customRequest={customRequest}
onChange={handleChange}
fileList={fileList}
maxCount={1}
accept=".jpg,.jpeg,.png">
<p className="ant-upload-hint">"选择文件"</p>
</Upload.Dragger>
</div>
<Form.Item label="说明描述" name="description" rules={[{ required: false }]}>
<TextArea rows={4} />
</Form.Item>
</Form>
</Modal>
<Modal title="查看留存" open={isModalOpen2} onOk={handleOk2} onCancel={handleCancel2} centered okText="下载" width={350}>
<Image
width={300} height={320}
src={fileUrl ?? ''}
preview={{
src: fileUrl ?? '',
}}
/>
</Modal>
</div>
);
};
export default CustomerRetention;

@ -1,34 +1,67 @@
import React from 'react'
import React from 'react';
import { Tabs } from 'antd';
import type { TabsProps } from 'antd';
import EnterpriseFile from 'views/statistical/enterpriseFile';
import CustomerRetention from 'views/statistical/customerRetention';
import { useLocation } from 'react-router-dom';
import {Breadcrumb, Descriptions} from 'antd';
const Detail = () => {
const onChange = (key: string) => {
console.log(key);
};
const App: React.FC = () => {
const location = useLocation();
// @ts-ignore
const { record } = location.state || {}; // Access the record passed from the previous page
if (!record) {
return <div>No record found!</div>;
}
return (
<div>
{/*<Breadcrumb style={{ margin: '16px 16px' }}>*/}
{/* {breadcrumb.map((item, index) => (*/}
{/* <Breadcrumb.Item key={index}>{item}</Breadcrumb.Item>*/}
{/* ))}*/}
{/*</Breadcrumb>*/}
<h1></h1>
<Descriptions title="客户信息">
<Descriptions.Item label="客户编号">{record.customerNo}</Descriptions.Item>
<Descriptions.Item label="企业名称">{record.customerName}</Descriptions.Item>
<Descriptions.Item label="行业分类">{record.typePname}</Descriptions.Item>
<Descriptions.Item label="企业联系人">{record.contacts}</Descriptions.Item>
<Descriptions.Item label="联系电话">{record.contactsPhone}</Descriptions.Item>
</Descriptions>
</div>
);
}
export default Detail;
// 提取 record 数据,若没有则设为默认值
const { record } = location.state as { record: any } || {};
console.log("record", record)
const items: TabsProps['items'] = [
{
key: '1',
label: '基本信息',
children: <span style={{fontWeight: 600, fontSize: 16}}></span>,
},
{
key: '2',
label: '保单',
children: '保单',
},
{
key: '3',
label: '事故预防服务',
children: '事故预防服务',
},
{
key: '4',
label: '风险辨识',
children: '风险辨识',
},
{
key: '5',
label: '隐患排查',
children: '隐患排查',
},
{
key: '6',
label: '理赔案件',
children: '理赔案件',
},
{
key: '7',
label: '一企一档',
children: <EnterpriseFile />,
},
{
key: '8',
label: '客户留存',
children: <CustomerRetention customer={record} />,
},
];
return <Tabs defaultActiveKey="1" items={items} onChange={onChange} />;
};
export default App;

@ -0,0 +1,156 @@
import React, {useState, useEffect} from 'react';
import {Table, Button, Spin, Descriptions} from 'antd';
import html2canvas from 'html2canvas';
import { jsPDF } from 'jspdf';
interface Enterprise {
key: string;
customerNo: string;
customerName: string;
industry: string;
contacts: string;
phone: string;
}
const EnterpriseFile: React.FC = () => {
// State to store data and loading state
const [data, setData] = useState<Enterprise[]>([]);
const [loading, setLoading] = useState<boolean>(false);
// Simulate data fetching on component mount
useEffect(() => {
setLoading(true);
setTimeout(() => {
// Simulated response data
const fetchedData: Enterprise[] = [
{ key: '1', customerNo: '001', customerName: 'Company A', industry: 'Technology', contacts: 'John Doe', phone: '123-456-789' },
{ key: '2', customerNo: '002', customerName: 'Company B', industry: 'Finance', contacts: 'Jane Smith', phone: '987-654-321' },
// Add more data as needed
];
setData(fetchedData);
setLoading(false);
}, 1000); // Simulate a delay of 2 seconds
}, []);
const columns = [
{
title: '客户编号',
dataIndex: 'customerNo',
key: 'customerNo',
},
{
title: '企业名称',
dataIndex: 'customerName',
key: 'customerName',
},
{
title: '行业分类',
dataIndex: 'industry',
key: 'industry',
},
{
title: '企业联系人',
dataIndex: 'contacts',
key: 'contacts',
},
{
title: '联系电话',
dataIndex: 'phone',
key: 'phone',
},
];
const items = [
{
key: '1',
label: 'UserName',
children: 'Zhou Maomao',
},
{
key: '2',
label: 'Telephone',
children: '1810000000',
},
{
key: '3',
label: 'Live',
children: 'Hangzhou, Zhejiang',
},
{
key: '4',
label: 'Address',
span: 2,
children: 'No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China',
},
{
key: '5',
label: 'Remark',
children: 'empty',
},
];
const handleCapture = () => {
const element = document.querySelector('.main-customer-htm');
if (element) {
html2canvas(element as HTMLElement, {
useCORS: true, // Allow CORS for cross-origin resources
}).then((canvas) => {
// Convert the canvas to image data
const imgData = canvas.toDataURL('image/png');
// Create a new jsPDF instance
const pdf = new jsPDF();
// Add the image to the PDF (at position (10, 10) with size (pdf width - 20, auto))
pdf.addImage(imgData, 'PNG', 10, 10, pdf.internal.pageSize.width - 20, canvas.height * (pdf.internal.pageSize.width - 20) / canvas.width);
// Save the PDF
pdf.save('企业档案.pdf');
});
}
}
return (
<div className="container">
<div>
<Button type="primary" style={{ marginBottom: 20, float: 'right' }} onClick={handleCapture}>PDF下载</Button>
</div>
<div className="main-customer-htm">
<Descriptions title="企业基本信息" layout="vertical" bordered>
{items.map(item => (
<Descriptions.Item label={item.label} key={item.key}>
{item.children}
</Descriptions.Item>
))}
</Descriptions>
<br />
{loading ? (
<Spin tip="Loading..." />
) : (
<Table
dataSource={data}
columns={columns}
rowKey="key"
pagination={false}
/>
)}
<br />
<Table
dataSource={data}
columns={columns}
rowKey="key"
pagination={false}
/>
<br />
<Table
dataSource={data}
columns={columns}
rowKey="key"
pagination={false}
/>
</div>
</div>
);
};
export default EnterpriseFile;

@ -1,14 +1,11 @@
import React from 'react'
import { Form, Input, Button, DatePicker, Table, Modal, Select, Descriptions} from 'antd'
import { Form, Input, Button, DatePicker, Table, Select} from 'antd'
import { getList } from 'api/statistical'
import Detail from 'components/order_manage/promotion_detail'
import { dictionary } from "api/dict/index";
const { Option } = Select
const { RangePicker } = DatePicker;
interface Props {
currentId: number
}
@ -18,7 +15,7 @@ interface State {
keyword: string,
page: number,
num: number,
activityName?: string,
affiliation?: string,
memberName?: string,
mobile?: number | string,
start?: number,
@ -83,18 +80,11 @@ class Customer extends React.Component<Props, State>{
}
})
}
// handleDetail(text: any, record: any) {
// // console.log(text)
// // console.log(record)
// this.setState({
// detailVisible: true,
// currentId: text.orderId,
// showDetail: true
// })
// }
handleDetail(record: any) {
// @ts-ignore
const { history } = this.props;
console.log(record)
history.push({
pathname: '/customerDetail',
state: { record },
@ -107,7 +97,6 @@ class Customer extends React.Component<Props, State>{
const _listQuery = { ...this.state.listQuery, customerNo, customerName, typePname, contacts, contactsPhone }
this.setState({ listQuery: _listQuery })
this.getListApi(this.state.listQuery)
// console.log(this.state.listQuery)
}
const onFinishFailed = (errorInfo: object) => {
console.log('failes', errorInfo)
@ -124,7 +113,6 @@ class Customer extends React.Component<Props, State>{
align: 'center',
fixed: 'right',
width: 100,
// render: (text: any, record: any) => <p className='link' onClick={() => { this.handleDetail(text, record) }}>查看</p>,
render: (text: any, record: any) => <p className='link' onClick={() => { this.handleDetail(record) }}></p>,
}
];
@ -145,21 +133,11 @@ class Customer extends React.Component<Props, State>{
this.getListApi(query);
}
// 详情model
const handleCancel = () => {
this.setState({ detailVisible: false })
};
const onReset = () => {
const form = this.formRef.current;
// 获取字段的当前值
const keyword = form.getFieldValue('keyword');
const affiliation = form.getFieldValue('affiliation');
const manageType = form.getFieldValue('manageType');
console.log('Current values before reset:', keyword, affiliation, manageType);
// const keyword = form.getFieldValue('keyword');
// const affiliation = form.getFieldValue('affiliation');
// const manageType = form.getFieldValue('manageType');
// 清空字段值
form.resetFields();
};
@ -214,7 +192,7 @@ class Customer extends React.Component<Props, State>{
</Form.Item>
</Form>
</div>
<Table dataSource={list} columns={columns} rowKey="orderId"
<Table dataSource={list} columns={columns} rowKey="customerId"
loading={loading} scroll={{ y: '400px' }}
pagination={{
total: this.state.total,
@ -225,20 +203,6 @@ class Customer extends React.Component<Props, State>{
onShowSizeChange: selectchange,
onChange: changePage
}} />
{/* 查看详情 */}
<Modal title="查看详情"
width={'80%'}
bodyStyle={{ lineHeight: '2.8' }}
visible={this.state.detailVisible}
maskClosable={false}
onCancel={handleCancel}
footer={[
<Button type="primary" key="back" onClick={handleCancel}>
</Button>
]}>
<Detail currentId={this.state.currentId}></Detail>
</Modal>
</div>
)
}

Loading…
Cancel
Save