diff --git a/packages/examination/craco.config.js b/packages/examination/craco.config.js index 21280dc..c39d847 100644 --- a/packages/examination/craco.config.js +++ b/packages/examination/craco.config.js @@ -7,7 +7,7 @@ module.exports = { options: { lessLoaderOptions: { lessOptions: { - modifyVars: { '@primary-color': '#f45f04' }, + modifyVars: { '@primary-color': '#B5DBFF' }, javascriptEnabled: true, }, }, diff --git a/packages/examination/src/App.tsx b/packages/examination/src/App.tsx index 5face0d..39b0574 100644 --- a/packages/examination/src/App.tsx +++ b/packages/examination/src/App.tsx @@ -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 ( @@ -32,13 +46,19 @@ class App extends React.Component {
+ + {breadcrumb.map((item, index) => ( + {item} + ))} + -
React and Ant Design ©2021 Created by Machao
+
) } } -export default App; \ No newline at end of file +// @ts-ignore +export default withRouter(App); \ No newline at end of file diff --git a/packages/examination/src/api/axios.js b/packages/examination/src/api/axios.js index 3b11fa7..ee4accf 100644 --- a/packages/examination/src/api/axios.js +++ b/packages/examination/src/api/axios.js @@ -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 => { diff --git a/packages/examination/src/api/statistical/index.tsx b/packages/examination/src/api/statistical/index.tsx index ec94e96..ce64f0b 100644 --- a/packages/examination/src/api/statistical/index.tsx +++ b/packages/examination/src/api/statistical/index.tsx @@ -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 }) diff --git a/packages/examination/src/style/common.css b/packages/examination/src/style/common.css index f8da2c1..49e7d11 100644 --- a/packages/examination/src/style/common.css +++ b/packages/examination/src/style/common.css @@ -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; } diff --git a/packages/examination/src/views/demo/demoList.tsx b/packages/examination/src/views/demo/demoList.tsx index 8158ba5..252a853 100644 --- a/packages/examination/src/views/demo/demoList.tsx +++ b/packages/examination/src/views/demo/demoList.tsx @@ -71,19 +71,9 @@ class DemoList extends Component { name="sex"> diff --git a/packages/examination/src/views/home/index.tsx b/packages/examination/src/views/home/index.tsx index 713d2ab..31160b9 100644 --- a/packages/examination/src/views/home/index.tsx +++ b/packages/examination/src/views/home/index.tsx @@ -211,131 +211,131 @@ class Home extends React.Component <{}, State>{ const { sellerInfo, report, todayRevenue, todayPay, memberdData } = this.state; return (
-
- - - -
-
-
-
{getSellerName()}
-
- - {sellerInfo.contactPhone} - - 已认证 -
-
-
- - -

{report.orderPayMoneyToday}

-

今日交易金额 (元)

- - -

{report.salesAmountMonth}

-

本月销售额 (元)

- - -

{report.balance}

-

账户余额 (元)

- - -

- -

-

- -

- -
-
-
+ {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/*
*/} + {/*
*/} + {/*
{getSellerName()}
*/} + {/*
*/} + {/* */} + {/* {sellerInfo.contactPhone}*/} + {/* */} + {/* 已认证*/} + {/*
*/} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/*

{report.orderPayMoneyToday}

*/} + {/*

今日交易金额 (元)

*/} + {/* */} + {/* */} + {/*

{report.salesAmountMonth}

*/} + {/*

本月销售额 (元)

*/} + {/* */} + {/* */} + {/*

{report.balance}

*/} + {/*

账户余额 (元)

*/} + {/* */} + {/* */} + {/*

*/} + {/* */} + {/*

*/} + {/*

*/} + {/* */} + {/*

*/} + {/* */} + {/*
*/} + {/*
*/} + {/*
*/} -
- - - -
- 今日订单收入(元):

¥ {todayRevenue.orderAmount} / {todayRevenue.orderCount} 笔

-
-
-

用户支付金额:¥ {todayPay.userPayMoney}

-

手续费:¥ {todayPay.payFee}

-
-
- - - -
- 今日优惠券收入(元):

¥ {todayRevenue.couponAmount} / {todayRevenue.couponCount} 笔

-
-
-

核销收入:¥ {todayPay.closerAmount}

-

补贴收入:¥ {todayPay.getFee}

-
-
- - - -
- 今日联盟收入(元):

¥ {todayRevenue.otherAmount} / {todayRevenue.otherCount} 笔

-
-
-

佣金收入:¥ {todayPay.feeAmount}

-

券分享收入:¥ {todayPay.couponRecommedAmount}

-
-
-

推广收入:¥ {todayPay.recommedAmount}

-

关联订单收入:¥ {todayPay.couponFeeAmount}

-
-
- -
-
+ {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* 今日订单收入(元):

¥ {todayRevenue.orderAmount} / {todayRevenue.orderCount} 笔

*/} + {/*
*/} + {/*
*/} + {/*

用户支付金额:¥ {todayPay.userPayMoney}

*/} + {/*

手续费:¥ {todayPay.payFee}

*/} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* 今日优惠券收入(元):

¥ {todayRevenue.couponAmount} / {todayRevenue.couponCount} 笔

*/} + {/*
*/} + {/*
*/} + {/*

核销收入:¥ {todayPay.closerAmount}

*/} + {/*

补贴收入:¥ {todayPay.getFee}

*/} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* 今日联盟收入(元):

¥ {todayRevenue.otherAmount} / {todayRevenue.otherCount} 笔

*/} + {/*
*/} + {/*
*/} + {/*

佣金收入:¥ {todayPay.feeAmount}

*/} + {/*

券分享收入:¥ {todayPay.couponRecommedAmount}

*/} + {/*
*/} + {/*
*/} + {/*

推广收入:¥ {todayPay.recommedAmount}

*/} + {/*

关联订单收入:¥ {todayPay.couponFeeAmount}

*/} + {/*
*/} + {/*
*/} + {/* */} + {/*
*/} + {/*
*/} -
- - - -
- 今日新增会员数(人) -
-
- -

{memberdData.todayMemberCount}

-
-
- - - -
- 累计会员数(人) -
-
- -

{memberdData.allMemberCount}

-
-
- - - -
- 累计联盟收入(元) -
-
- -

{memberdData.allOtherAmount}

-
-
- -
-
+ {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* 今日新增会员数(人)*/} + {/*
*/} + {/*
*/} + {/* */} + {/*

{memberdData.todayMemberCount}

*/} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* 累计会员数(人)*/} + {/*
*/} + {/*
*/} + {/* */} + {/*

{memberdData.allMemberCount}

*/} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* 累计联盟收入(元)*/} + {/*
*/} + {/*
*/} + {/* */} + {/*

{memberdData.allOtherAmount}

*/} + {/*
*/} + {/*
*/} + {/* */} + {/*
*/} + {/*
*/} - {/* 数据统计 */} - - - + {/*/!* 数据统计 *!/*/} + {/**/} + {/* */} + {/**/}
) } diff --git a/packages/examination/src/views/slider/index.js b/packages/examination/src/views/slider/index.js index 4f49868..f1767ff 100644 --- a/packages/examination/src/views/slider/index.js +++ b/packages/examination/src/views/slider/index.js @@ -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); diff --git a/packages/examination/src/views/slider/menu.tsx b/packages/examination/src/views/slider/menu.tsx index 73b3292..fd6a7f7 100644 --- a/packages/examination/src/views/slider/menu.tsx +++ b/packages/examination/src/views/slider/menu.tsx @@ -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; \ No newline at end of file diff --git a/packages/examination/src/views/statistical/customerRetention.tsx b/packages/examination/src/views/statistical/customerRetention.tsx new file mode 100644 index 0000000..cae2abb --- /dev/null +++ b/packages/examination/src/views/statistical/customerRetention.tsx @@ -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 = ({ customer }) => { + + // if (!customer) { + // // @ts-ignore + // const { history } = this.props; + // history.push({ + // pathname: '/customer' + // }); + // return (
); + // } + + if (!customer) { + customer = { + customerId: 0, + customerName: 'asda' + } + } + + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [query, setQuery] = useState(null); + const [total, setTotal] = useState(0); + const [fileList, setFileList] = useState([]); + const [fileUrl, setFileUrl] = useState(null); + + const [fileRes, setFileRes] = useState(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; }) => ( + + showModal2(record.fileUid)}>服务声明扫描图片文件链接 + + ), + }, + ]; + + 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 ( +
+ {contextHolder} +
+
+ {customer.customerName} + +
+ {loading ? ( + + ) : ( + `共 ${total} 条`, + onShowSizeChange: selectChange, + onChange: changePage + }} + /> + )} + + +
+ + + *上传格式为jpg、png文件 + +
+ +

点击上方"选择文件"或将文件拖拽到此区域

+
+
+ +