统计动态看板代码提交

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: { options: {
lessLoaderOptions: { lessLoaderOptions: {
lessOptions: { lessOptions: {
modifyVars: { '@primary-color': '#f45f04' }, modifyVars: { '@primary-color': '#B5DBFF' },
javascriptEnabled: true, javascriptEnabled: true,
}, },
}, },

@ -1,14 +1,19 @@
import React from 'react' import React from 'react'
import {Layout, Select} from 'antd'; import {Layout, Breadcrumb, Select} from 'antd';
import Header from 'views/header'; import Header from 'views/header';
import Slider from 'views/slider'; import Slider from 'views/slider';
import ContentMain from 'components/contentMain'; import ContentMain from 'components/contentMain';
import { withRouter } from 'react-router-dom'
import {findTitleByKey} from "./views/slider";
const { Content, Footer, Sider } = Layout; const { Content, Footer, Sider } = Layout;
class App extends React.Component { class App extends React.Component {
state = { state = {
collapsed: false, collapsed: false,
breadcrumb: []
}; };
onCollapse = (collapsed: boolean) => { onCollapse = (collapsed: boolean) => {
@ -20,10 +25,19 @@ class App extends React.Component {
} }
componentDidMount() { 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() { render() {
const { collapsed } = this.state; const { collapsed, breadcrumb } = this.state;
return ( return (
<Layout style={{ minHeight: '100vh' }}> <Layout style={{ minHeight: '100vh' }}>
<Sider theme={'light'} style={{ background: '#fff' }} trigger={null} collapsible collapsed={collapsed} onCollapse={this.onCollapse}> <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"> <Layout className="site-layout">
<Header changeCollapse={this.changeCollapse.bind(this)} /> <Header changeCollapse={this.changeCollapse.bind(this)} />
<Content className={collapsed === true ? 'noLeft' : 'left'} style={{ position: 'absolute', top: 70, right: 0, bottom: 0 }}> <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 /> <ContentMain />
</Content> </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>
</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', baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '/api',
timeout: 3000 timeout: 3000
}) })
export const serverUrl = 'http://localhost:8187/';
instance.interceptors.request.use( instance.interceptors.request.use(
config => { config => {

@ -1,9 +1,24 @@
import axios from '../axios'; import axios from '../axios';
export function getList(obj: any){ export function getList(obj: any){
console.log("1",obj)
return axios({ 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", method: "post",
data: obj data: obj
}) })

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

@ -71,19 +71,9 @@ class DemoList extends Component<any, States> {
name="sex"> name="sex">
<Select placeholder="全部" style={{ width: 120 }} allowClear> <Select placeholder="全部" style={{ width: 120 }} allowClear>
{ {
sexDict && sexDict.length > 0 ? sexDict && sexDict.map((item: { dictKey: any; dictValue: string }) => {
(() => { return <Option value={item.dictKey}>{item.dictValue}</Option>
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>
} }
</Select> </Select>
</Form.Item> </Form.Item>

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

@ -6,6 +6,25 @@ import { FolderOutlined, FileTextOutlined } from '@ant-design/icons';
const { SubMenu } = Menu; 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 { class Slider extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);

@ -23,85 +23,85 @@ const menuList = [
} }
] ]
}, },
{ // {
title: 'demo', // title: 'demo',
key: 'demo', // key: 'demo',
icon: 'icon-peizhi', // icon: 'icon-peizhi',
subs: [ // subs: [
{ // {
title: 'list', // title: 'list',
key: 'demoList', // key: 'demoList',
} // }
] // ]
}, // },
{ // {
title: '订单管理', // title: '订单管理',
key: 'order_mag', // key: 'order_mag',
icon: 'icon-gouwucheman', // icon: 'icon-gouwucheman',
subs: [ // subs: [
{ // {
title: '股东活动订单', // title: '股东活动订单',
key: 'promotion', // key: 'promotion',
}, // },
{ // {
title: '优惠券订单', // title: '优惠券订单',
key: 'couponOrder', // key: 'couponOrder',
} // }
] // ]
}, // },
{ // {
title: '配置管理', // title: '配置管理',
key: 'configuration_mag', // key: 'configuration_mag',
icon: 'icon-peizhi', // icon: 'icon-peizhi',
subs: [ // subs: [
{ // {
title: '金币配置', // title: '金币配置',
key: 'gold', // key: 'gold',
}, // },
{ // {
title: '公告管理', // title: '公告管理',
key: 'announcement', // key: 'announcement',
} // }
] // ]
}, // },
{ // {
title: '积分会员管理', // title: '积分会员管理',
key: 'integralmember_manage', // key: 'integralmember_manage',
icon: 'icon-jifen', // icon: 'icon-jifen',
subs: [ // subs: [
{ // {
title: '积分规则', // title: '积分规则',
key: 'integralRule', // key: 'integralRule',
} // }
] // ]
}, // },
{ // {
title: '小店管理', // title: '小店管理',
key: 'shop_manage', // key: 'shop_manage',
icon: 'icon-shop', // icon: 'icon-shop',
subs: [ // subs: [
{ // {
title: '店铺管理', // title: '店铺管理',
key: 'store', // key: 'store',
} // }
] // ]
}, // },
{ // {
title: '提现管理', // title: '提现管理',
key: 'withdrawal_manage', // key: 'withdrawal_manage',
icon: 'icon-withdrawal', // icon: 'icon-withdrawal',
subs: [ // subs: [
{ // {
title: '提现规则', // title: '提现规则',
key: 'store', // key: 'store',
} // }
] // ]
}, // },
{ // {
title: '首页', // title: '首页',
key: 'home', // key: 'home',
icon: 'icon-home' // icon: 'icon-home'
} // }
] ]
export default menuList; 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 { 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(); const location = useLocation();
// @ts-ignore
const { record } = location.state || {}; // Access the record passed from the previous page // 提取 record 数据,若没有则设为默认值
const { record } = location.state as { record: any } || {};
if (!record) {
return <div>No record found!</div>; console.log("record", record)
}
const items: TabsProps['items'] = [
{
return ( key: '1',
<div> label: '基本信息',
{/*<Breadcrumb style={{ margin: '16px 16px' }}>*/} children: <span style={{fontWeight: 600, fontSize: 16}}></span>,
{/* {breadcrumb.map((item, index) => (*/} },
{/* <Breadcrumb.Item key={index}>{item}</Breadcrumb.Item>*/} {
{/* ))}*/} key: '2',
{/*</Breadcrumb>*/} label: '保单',
<h1></h1> children: '保单',
<Descriptions title="客户信息"> },
<Descriptions.Item label="客户编号">{record.customerNo}</Descriptions.Item> {
<Descriptions.Item label="企业名称">{record.customerName}</Descriptions.Item> key: '3',
<Descriptions.Item label="行业分类">{record.typePname}</Descriptions.Item> label: '事故预防服务',
<Descriptions.Item label="企业联系人">{record.contacts}</Descriptions.Item> children: '事故预防服务',
<Descriptions.Item label="联系电话">{record.contactsPhone}</Descriptions.Item> },
</Descriptions> {
</div> key: '4',
); label: '风险辨识',
} children: '风险辨识',
},
export default Detail; {
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 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 { getList } from 'api/statistical'
import Detail from 'components/order_manage/promotion_detail'
import { dictionary } from "api/dict/index"; import { dictionary } from "api/dict/index";
const { Option } = Select const { Option } = Select
const { RangePicker } = DatePicker;
interface Props { interface Props {
currentId: number currentId: number
} }
@ -18,7 +15,7 @@ interface State {
keyword: string, keyword: string,
page: number, page: number,
num: number, num: number,
activityName?: string, affiliation?: string,
memberName?: string, memberName?: string,
mobile?: number | string, mobile?: number | string,
start?: number, 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) { handleDetail(record: any) {
// @ts-ignore // @ts-ignore
const { history } = this.props; const { history } = this.props;
console.log(record)
history.push({ history.push({
pathname: '/customerDetail', pathname: '/customerDetail',
state: { record }, state: { record },
@ -107,7 +97,6 @@ class Customer extends React.Component<Props, State>{
const _listQuery = { ...this.state.listQuery, customerNo, customerName, typePname, contacts, contactsPhone } const _listQuery = { ...this.state.listQuery, customerNo, customerName, typePname, contacts, contactsPhone }
this.setState({ listQuery: _listQuery }) this.setState({ listQuery: _listQuery })
this.getListApi(this.state.listQuery) this.getListApi(this.state.listQuery)
// console.log(this.state.listQuery)
} }
const onFinishFailed = (errorInfo: object) => { const onFinishFailed = (errorInfo: object) => {
console.log('failes', errorInfo) console.log('failes', errorInfo)
@ -124,7 +113,6 @@ class Customer extends React.Component<Props, State>{
align: 'center', align: 'center',
fixed: 'right', fixed: 'right',
width: 100, 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>, 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); this.getListApi(query);
} }
// 详情model
const handleCancel = () => {
this.setState({ detailVisible: false })
};
const onReset = () => { const onReset = () => {
const form = this.formRef.current; const form = this.formRef.current;
// 获取字段的当前值 // const keyword = form.getFieldValue('keyword');
const keyword = form.getFieldValue('keyword'); // const affiliation = form.getFieldValue('affiliation');
const affiliation = form.getFieldValue('affiliation'); // const manageType = form.getFieldValue('manageType');
const manageType = form.getFieldValue('manageType');
console.log('Current values before reset:', keyword, affiliation, manageType);
// 清空字段值 // 清空字段值
form.resetFields(); form.resetFields();
}; };
@ -214,7 +192,7 @@ class Customer extends React.Component<Props, State>{
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>
<Table dataSource={list} columns={columns} rowKey="orderId" <Table dataSource={list} columns={columns} rowKey="customerId"
loading={loading} scroll={{ y: '400px' }} loading={loading} scroll={{ y: '400px' }}
pagination={{ pagination={{
total: this.state.total, total: this.state.total,
@ -225,20 +203,6 @@ class Customer extends React.Component<Props, State>{
onShowSizeChange: selectchange, onShowSizeChange: selectchange,
onChange: changePage 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> </div>
) )
} }

Loading…
Cancel
Save