🐛 fix(模块): 修复了一些 bug

This commit is contained in:
cclu 2025-03-10 19:17:27 +08:00
parent 6a7a8985ce
commit 271a4ad751
25 changed files with 1118 additions and 996 deletions

View File

@ -1,127 +1,17 @@
// import { defineConfig } from "umi"; // import { defineConfig } from "umi";
import proxy from './proxy'; import proxy from './proxy';
import router from './router'
//不使用defineConfig则能在其他地方获取这些配置 //不使用defineConfig则能在其他地方获取这些配置
export default { export default {
plugins: [ plugins: [
'@umijs/plugins/dist/dva' '@umijs/plugins/dist/dva'
], ],
// proxy, proxy,
dva: {}, dva: {},
title: 'UMI4 Admin', title: 'UMI4 Admin',
favicons: [ favicons: [
'/favicon.svg' '/favicon.svg'
], ],
routes: [ routes: router
{
path: '/',
component: '@/layouts/index',
},
{
id: 2,
key: '2',
name: '关于',
path: '/about',
redirect: '',
routes: [
{
id: 21,
key: '2-1',
name: '关于你',
path: '/about/u',
redirect: '',
pid: 2,
routes: [
{
id: 211,
key: '2-1-1',
name: '关于你1',
path: '/about/u/index',
component: "@/pages/about/u/index",
redirect: '',
pid: 21
},
{
id: 212,
key: '2-1-2',
name: '关于你2',
path: '/about/u/2',
component: "@/pages/about/u/$id",
redirect: '',
pid: 21
}
]
},
{
id: 22,
key: '2-2',
path: '/about/m',
redirect: '',
name: '(页面元素权限)关于我',
component: "@/pages/about/m",
pid: 2
},
{
id: 23,
key: '2-3',
path: '/about/um',
redirect: '',
name: '关于你和我',
component: "@/pages/about/um",
pid: 2
}
]
},
{
id: 3,
key: '3',
name: '教师',
path: '/teacher',
redirect: '',
routes: [
{
id: 31,
key: '3-1',
path: '/teacher/u',
redirect: '',
name: '(403)关于你教师',
component: "@/pages/teacher/u",
pid: 3
},
{
id: 32,
key: '3-2',
path: '/teacher/m',
redirect: '',
name: '关于我教师',
component: "@/pages/teacher/m",
pid: 3
},
{
id: 33,
key: '3-3',
path: '/teacher/um',
redirect: '',
name: '关于你和我教师',
component: "@/pages/teacher/um",
pid: 3
}
]
},
{
id: 4,
key: '4',
name: '(404)学生',
path: '/student',
redirect: '',
},
{
id: 5,
key: '5',
path: '/user/login',
redirect: '',
name: '登录',
component: "@/pages/user/login",
},
]
}; };

View File

@ -1,8 +1,15 @@
const proxy = { const proxy = {
'/api': { '/auth': {
target: 'http://10.104.1.38:3000', target: 'http://home.robot-z.cn:7001',
changeOrigin: true, changeOrigin: true,
pathRewrite: { '^/api': '' }, secure: false,
pathRewrite: { '^/auth': '' },
onProxyRes: function(proxyRes) {
proxyRes.headers['Access-Control-Allow-Origin'] = '*';
proxyRes.headers['Access-Control-Allow-Methods'] = 'GET,PUT,POST,DELETE,OPTIONS';
proxyRes.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization';
proxyRes.headers['Access-Control-Allow-Credentials'] = 'true';
}
} }
} }

50
config/router.ts Normal file
View File

@ -0,0 +1,50 @@
// 路由配置文件
export default [
{
path: '/user',
routes: [
{
path: '/user/login',
redirect: '',
name: '登录',
component: "@/pages/user/login",
}
]
},
{
path: '/',
routes: [
{
path: '/standard/index',
redirect: '',
name: '生成标准页面',
component: "@/pages/standard/index",
},
{
path: '/examine',
redirect: '',
name: '走动式管理',
routes: [
{
path: '/examine/index',
name: '考评分类管理',
component: "@/pages/examine/index",
},
{
path: '/examine/question',
name: '考核问题管理',
component: "@/pages/examine/question",
},
{
path: '/examine/modal',
name: '考核模版管理',
component: "@/pages/examine/modal",
}
]
},
]
}
]

View File

@ -1,3 +1,11 @@
/*
* @Author: cclu 1106109051@qq.com
* @Date: 2025-02-25 11:39:58
* @LastEditors: cclu 1106109051@qq.com
* @LastEditTime: 2025-03-10 15:30:28
* @FilePath: \umi4-admin-main\src\components\PageAccess.tsx
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import type { FC, ReactElement } from 'react'; import type { FC, ReactElement } from 'react';
import { connect, history, useLocation } from 'umi'; import { connect, history, useLocation } from 'umi';
import { Button, Result } from 'antd'; import { Button, Result } from 'antd';
@ -17,6 +25,9 @@ const PageAccess: FC<Props> = (props): ReactElement | null => {
//当遇到输入的是菜单组件中可展开的节点的path的时候就没有权限了, 此时可能会报错, //当遇到输入的是菜单组件中可展开的节点的path的时候就没有权限了, 此时可能会报错,
//加可选操作符可避免这个报错导致的页面渲染问题 //加可选操作符可避免这个报错导致的页面渲染问题
const accessible = authority[pathname]?.some((item: string) => userAuthority.includes(item)); const accessible = authority[pathname]?.some((item: string) => userAuthority.includes(item));
console.log('authority', authority);
console.log('userAuthority', userAuthority);
console.log('children', children);
let res = children; let res = children;

View File

@ -41,7 +41,48 @@
.pageLayout {
:global {
.ant-pro-menu-item {
&.ant-menu-item-selected {
background-color: #1890ff !important;
.ant-pro-menu-item-title {
color: #fff !important;
}
&:hover {
background-color: #1890ff !important;
}
}
}
.ant-menu-item-selected {
background-color: #1890ff !important;
color: #fff !important;
&:hover {
background-color: #1890ff !important;
color: #fff !important;
}
a {
color: #fff !important;
}
}
.ant-menu-submenu-selected > .ant-menu-submenu-title {
color: #1890ff;
}
.ant-menu-item {
&:hover {
background-color: rgba(24, 144, 255, 0.1);
}
}
}
}
.ant-pro-layout-container{ .ant-pro-layout-container{
background-color: #f0f2f5; background-color: #f0f2f5;
main{ main{

View File

@ -1,3 +1,11 @@
/*
* @Author: cclu 1106109051@qq.com
* @Date: 2025-02-27 15:55:46
* @LastEditors: cclu 1106109051@qq.com
* @LastEditTime: 2025-03-10 17:46:55
* @FilePath: \umi4-admin-main\src\layouts\index.tsx
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import type { FC } from 'react'; import type { FC } from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Dropdown, Layout, Menu, Tabs, Tooltip } from 'antd'; import { Dropdown, Layout, Menu, Tabs, Tooltip } from 'antd';
@ -9,7 +17,6 @@ import LayoutWrapper from '@/components/LayoutWrapper';
import Nav from '@/components/Nav'; import Nav from '@/components/Nav';
import Page404 from '@/pages/404'; import Page404 from '@/pages/404';
import handleRecursiveNestedData from '@/utils/handleRecursiveNestedData'; import handleRecursiveNestedData from '@/utils/handleRecursiveNestedData';
import handleGetCurrentLocation from '@/utils/handleGetCurrentLocation';
import { MenuDataItem, ProLayout } from '@ant-design/pro-components'; import { MenuDataItem, ProLayout } from '@ant-design/pro-components';
import './index.less' import './index.less'
import logo from '../assets/logo.svg'; import logo from '../assets/logo.svg';
@ -41,10 +48,6 @@ const handleGetOpenKeys = (currentLocation: API.MenuItem[] | []): string[] => {
for (let i = 0; i < currentLocation.length - 1; i++) { for (let i = 0; i < currentLocation.length - 1; i++) {
res.push(`${currentLocation[i].key}`); res.push(`${currentLocation[i].key}`);
} }
console.log('res', res);
return res; return res;
}; };
@ -66,8 +69,8 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
console.log('props', props); console.log('props', props);
console.log('pathname', pathname); console.log('pathname', pathname);
console.log('indexValidMenuItemByPath', indexValidMenuItemByPath);
const validMenuItem = indexValidMenuItemByPath[pathname]; const validMenuItem = indexValidMenuItemByPath[pathname];
console.log('validMenuItem', validMenuItem);
// const selectedKeys = validMenuItem?.key; // const selectedKeys = validMenuItem?.key;
const [activeKey, setActiveKey] = useState<string>(validMenuItem?.path || '/') const [activeKey, setActiveKey] = useState<string>(validMenuItem?.path || '/')
@ -81,7 +84,6 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
//点击有子菜单的父菜单的回调 //点击有子菜单的父菜单的回调
// const onOpenChange: MenuProps['onOpenChange'] = (keys) => { // const onOpenChange: MenuProps['onOpenChange'] = (keys) => {
// setOpenKeys(keys) // setOpenKeys(keys)
// console.log('keys', keys);
// dispatch({ // dispatch({
// namespace: "global/setProfileData", // namespace: "global/setProfileData",
// payload: { // payload: {
@ -101,7 +103,8 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
? ( ? (
<Link <Link
to={item.path} to={item.path}
style={{ color: 'inherit' }} style={{ color: pathname === item.path ? '#1890ff' : 'inherit' }}
className={pathname === item.path ? 'ant-menu-item-selected' : ''}
>{item.name}</Link> >{item.name}</Link>
) )
: item.name, : item.name,
@ -167,68 +170,35 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
SYSTEMMODULE_DESC: "", SYSTEMMODULE_DESC: "",
guid: "1", guid: "1",
hideInMenu: false, hideInMenu: false,
name: "首页", name: "生成标准页面",
path: "/", path: "/standard/index",
}, },
{ {
SYSTEMMODULE_DESC: "", SYSTEMMODULE_DESC: "",
guid: "2", guid: "2",
hideInMenu: false, hideInMenu: false,
name: "关于你1", name: "考评分类管理",
path: "/about/u/index", path: "/examine/index",
}, },
{ {
SYSTEMMODULE_DESC: "", SYSTEMMODULE_DESC: "",
guid: "3", guid: "3",
hideInMenu: false, hideInMenu: false,
name: "关于你2", name: "考核问题管理",
path: "/about/u/2", path: "/examine/question",
}, },
{ {
SYSTEMMODULE_DESC: "", SYSTEMMODULE_DESC: "",
guid: "4", guid: "4",
hideInMenu: false, hideInMenu: false,
name: "(页面元素权限)关于我", name: "考核模版管理",
path: "/about/m", path: "/examine/modal",
},
{
SYSTEMMODULE_DESC: "",
guid: "5",
hideInMenu: false,
name: "关于你和我",
path: "/about/um",
},
{
SYSTEMMODULE_DESC: "",
guid: "5",
hideInMenu: false,
name: "(403)关于你教师",
path: "/teacher/u",
},
{
SYSTEMMODULE_DESC: "",
guid: "6",
hideInMenu: false,
name: "关于我教师",
path: "/teacher/m",
},
{
SYSTEMMODULE_DESC: "",
guid: "7",
hideInMenu: false,
name: "关于你和我教师",
path: "/teacher/um",
},
{
SYSTEMMODULE_DESC: "",
guid: "8",
hideInMenu: false,
name: "(404)学生",
path: "/student",
} }
] ]
console.log('tabsRoutes', tabsRoutes); console.log('tabsRoutes', tabsRoutes);
console.log('location', location);
console.log('consumableMenu', consumableMenu);
return ( return (
@ -247,10 +217,17 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
location={location} location={location}
style={{ minHeight: '100vh' }} style={{ minHeight: '100vh' }}
siderWidth={208} siderWidth={208}
menuDataRender={() => selectedKeys={[pathname]}
consumableMenu defaultSelectedKeys={[pathname]}
// menu menuDataRender={() => consumableMenu}
} menuItemRender={(menuItemProps, defaultDom) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom;
}
return (
<Link to={menuItemProps.path}>{defaultDom}</Link>
);
}}
subMenuItemRender={(menuItemProps) => { subMenuItemRender={(menuItemProps) => {
if (menuItemProps.icon && typeof menuItemProps.icon === 'string') { if (menuItemProps.icon && typeof menuItemProps.icon === 'string') {
const ele = React.createElement(Icon[menuItemProps.icon]) const ele = React.createElement(Icon[menuItemProps.icon])
@ -288,12 +265,11 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
}} }}
actionsRender={() => <nav />} actionsRender={() => <nav />}
onPageChange={(location) => { onPageChange={(location) => {
console.log('location------', location); console.log('location', location);
if (location?.pathname && location?.pathname !== '/') { if (location?.pathname && location?.pathname !== '/') {
console.log('oneFloorList', oneFloorList);
const nextModule: any = oneFloorList.filter(n => location?.pathname === n?.path) const nextModule: any = oneFloorList.filter(n => location?.pathname === n?.path)
console.log('nextModule', nextModule);
let title = '' let title = ''
if (nextModule && nextModule.length > 0) { if (nextModule && nextModule.length > 0) {
title = nextModule[0].name title = nextModule[0].name
@ -312,8 +288,8 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc
hideAdd hideAdd
type="editable-card" type="editable-card"
onChange={(value) => { onChange={(value) => {
// history.push(value) history.push(value)
// setActiveKey(value) setActiveKey(value)
}} }}
activeKey={activeKey} activeKey={activeKey}
onEdit={handleEdit} onEdit={handleEdit}

View File

@ -8,6 +8,7 @@ import handleRedirect from '@/utils/handleRedirect';
import handleGetRootSubmenuKeys from '@/utils/handleGetRootSubmenuKeys'; import handleGetRootSubmenuKeys from '@/utils/handleGetRootSubmenuKeys';
import handleGetEachDatumFromNestedDataByKey from '@/utils/handleGetEachDatumFromNestedDataByKey'; import handleGetEachDatumFromNestedDataByKey from '@/utils/handleGetEachDatumFromNestedDataByKey';
import handleGetIndexValidMenuItemByPath from '@/utils/handleGetIndexValidMenuItemByPath'; import handleGetIndexValidMenuItemByPath from '@/utils/handleGetIndexValidMenuItemByPath';
import { notification } from 'antd';
/** /**
* *
@ -69,40 +70,16 @@ const UserModel: UserModelType = {
loginBtnLoading: false, loginBtnLoading: false,
layoutWrapperLoading: true, layoutWrapperLoading: true,
menu: [ menu: [
{
id: 1,
key: '1',
path: '/',
label: '首页',
redirect: '',
},
], ],
indexAllMenuItemById: { indexAllMenuItemById: {
1: {
id: 1,
key: '1',
path: '/',
label: '首页',
redirect: '',
},
}, },
indexAllMenuItemByPath: { indexAllMenuItemByPath: {
'/': {
id: 1,
key: '1',
path: '/',
label: '首页',
redirect: '',
},
}, },
indexValidMenuItemByPath: { indexValidMenuItemByPath: {
'/': {
id: 1,
key: '1',
path: '/',
label: '首页',
redirect: '',
},
}, },
}, },
effects: { effects: {
@ -128,9 +105,10 @@ const UserModel: UserModelType = {
try { try {
const res: API.LoginResponse = yield call(userLogin, payload); const res: API.LoginResponse = yield call(userLogin, payload);
console.log('res', res);
if (!res.code) { if (res.code === 200) {
localStorage.setItem('Authorization', res.data.token); localStorage.setItem('Authorization', res.data.accessToken);
yield put({ yield put({
type: 'getUserInfoAuthorityMenu', type: 'getUserInfoAuthorityMenu',
@ -146,7 +124,7 @@ const UserModel: UserModelType = {
loginBtnLoading: false, loginBtnLoading: false,
}, },
}); });
return res; return { code: res.code, message: res.message };
} }
} catch (error) { } catch (error) {
yield put({ yield put({
@ -155,7 +133,7 @@ const UserModel: UserModelType = {
loginBtnLoading: false, loginBtnLoading: false,
}, },
}); });
return { code: 1, message: '登录失败,请稍后重试' }; return { code: 1, message: error?.message || '登录失败,请稍后重试' };
} }
}, },
//获取用户信息和权限以及菜单 //获取用户信息和权限以及菜单
@ -193,13 +171,15 @@ const UserModel: UserModelType = {
code: 0, code: 0,
message: '', message: '',
}; };
console.log('type', type);
//用户在登录页登录完成之后执行 //用户在登录页登录完成之后执行
if (type === 'concurrent') { if (type === 'concurrent') {
const res: API.UserInfoAuthMenuResponse = yield call(retrieveUserInfoAuthorityMenu); const res: API.UserInfoAuthMenuResponse = yield call(retrieveUserInfoAuthorityMenu);
console.log('res', res);
userInfoRes = res[0] as API.UserInfoResponse; userInfoRes = res[0] as API.UserInfoResponse;
userAuthorityRes = res[1] as API.UserAuthorityResponse; // userAuthorityRes = res[1] as API.UserAuthorityResponse;
menuRes = res[2] as API.MenuDataResponse; menuRes = res[1] as API.MenuDataResponse;
} else { } else {
//其他情形首先查询用户的登录状态, 未登录则不继续操作 //其他情形首先查询用户的登录状态, 未登录则不继续操作
try { try {
@ -224,15 +204,47 @@ const UserModel: UserModelType = {
} }
const res: API.UserAuthMenuResponse = yield call(retrieveUserAuthorityMenu); const res: API.UserAuthMenuResponse = yield call(retrieveUserAuthorityMenu);
userAuthorityRes = res[0] as API.UserAuthorityResponse; // userAuthorityRes = res[0] as API.UserAuthorityResponse;
menuRes = res[1] as API.MenuDataResponse; menuRes = res[0] as API.MenuDataResponse;
} }
const indexAllMenuItemByPath = handleGetEachDatumFromNestedDataByKey(menuRes.data, 'path'); console.log('userInfoRes', userInfoRes);
const indexValidMenuItemByPath = handleGetIndexValidMenuItemByPath(menuRes.data);
console.log('indexValidMenuItemByPath', indexValidMenuItemByPath);
menuRes.data = [{
path: '/standard/index',
redirect: '',
name: '生成标准页面',
component: "@/pages/standard/index",
}, {
path: '/examine',
redirect: '',
name: '走动式管理',
children: [
{
path: '/examine/index',
name: '考评分类管理',
component: "@/pages/examine/index",
},
{
path: '/examine/question',
name: '考核问题管理',
component: "@/pages/examine/question",
},
{
path: '/examine/modal',
name: '考核模版管理',
component: "@/pages/examine/modal",
}
]
},]
let indexAllMenuItemByPath: any = []
let indexValidMenuItemByPath: any = []
if (menuRes.data && menuRes.data.length > 0) {
indexAllMenuItemByPath = handleGetEachDatumFromNestedDataByKey(menuRes.data, 'path');
indexValidMenuItemByPath = handleGetIndexValidMenuItemByPath(menuRes.data);
}
//在登录完获取菜单数据之后做是否需要重定向的操作 //在登录完获取菜单数据之后做是否需要重定向的操作
yield call( yield call(
@ -241,7 +253,6 @@ const UserModel: UserModelType = {
indexAllMenuItemByPath, indexAllMenuItemByPath,
indexValidMenuItemByPath, indexValidMenuItemByPath,
); );
yield put({ yield put({
type: 'save', type: 'save',
payload: { payload: {
@ -252,27 +263,32 @@ const UserModel: UserModelType = {
indexAllMenuItemByPath, indexAllMenuItemByPath,
indexValidMenuItemByPath, indexValidMenuItemByPath,
layoutWrapperLoading: false, layoutWrapperLoading: false,
authority: userAuthorityRes.data.authority, // authority: userAuthorityRes.data.authority,
authority: [
'/standard/index',
'/examine/index',
'/examine/modal',
'/examine/question',
],
rootSubmenuKeys: handleGetRootSubmenuKeys(menuRes.data), rootSubmenuKeys: handleGetRootSubmenuKeys(menuRes.data),
indexAllMenuItemById: handleGetEachDatumFromNestedDataByKey(menuRes.data, 'id'), indexAllMenuItemById: handleGetEachDatumFromNestedDataByKey(menuRes.data, 'id'),
}, },
}); });
//为保证所有语句都return, 因此这里加一句这个 //为保证所有语句都return, 因此这里加一句这个
return true; return true;
}, },
//登出 //登出
*logout({ payload }, { call, put }) { * logout({ payload }, { call, put }) {
const res: API.LogoutResponse = yield call(userLogout, payload); const res: API.LogoutResponse = yield call(userLogout, payload);
console.log('res', res)
if (!res.code) { if (res.code === 200) {
yield put({ yield put({
type: 'resetLoginStatus', type: 'resetLoginStatus',
}); });
} }
}, },
//重置登录状态 //重置登录状态
*resetLoginStatus(_, { put }) { * resetLoginStatus(_, { put }) {
localStorage.removeItem('Authorization'); localStorage.removeItem('Authorization');
yield put({ yield put({

View File

@ -1,12 +0,0 @@
import type { FC } from 'react';
const Index: FC = () => {
return (
<div>
<div>/about</div>
</div>
);
};
export default Index;

View File

@ -1,5 +0,0 @@
const authority: Authority = {
aboutMUpdate: '/about/m/update',
};
export default authority;

View File

@ -1,24 +0,0 @@
import type { FC } from 'react';
import Access from '@/components/Access';
import authority from './authority';
const { aboutMUpdate } = authority;
const Index: FC = () => {
return (
<div>
<div>/about/m</div>
<Access
authority={aboutMUpdate}
fallback={
<div>/</div>
}
>
<div>/</div>
</Access>
</div>
);
};
export default Index;

View File

@ -1,14 +0,0 @@
import { useParams } from 'umi';
const Index = () => {
const params = useParams();
console.log(params);
return (
<div>{`动态路由: /about/u/${params.id}`}</div>
);
};
export default Index;

View File

@ -1,582 +0,0 @@
import { connect } from "umi";
import { MouseEvent, useRef, useState } from "react";
import type { FormInstance } from "antd";
import { Button, message, Spin } from "antd";
import type { ActionType } from "@ant-design/pro-table";
import ProTable from "@ant-design/pro-table";
import * as XLSX from 'xlsx'; // 读写数据的核心工具(无需写样式时可用)
import XLSXStyle from 'xlsx-style-fixed';
import moment from "moment";
import { handleFormatNumber } from "@/utils/publicMethods";
import LeftSelectTree from "@/components/leftSelectTree";
import { UserConnectedProps } from "@/models/user";
const saleRankReport = (props: any) => {
const { currentUser } = props
const downloadBtnRef = useRef<any>()
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>([{
SPREGIONTYPE_NAME: "1",
SERVERPART_NAME: "1",
MERCHANTS_NAME: "1",
SERVERPARTSHOP_NAME: "1",
IndexStr: "1",
COMPACT_STARTDATECOMPACT_ENDDATE: "1",
RENTFEE: "1",
GUARANTEERATIO: "1",
CASHPAY_AMOUNT: "1",
MOBILEPAY_AMOUNT: "1",
REVENUEDAILY_AMOUNTTotal: "1",
GUARANTEERATIOAMOUNT: "1",
BANKACCOUNT_AMOUNT: "1",
RECEIVABLEAMOUNT: "1",
SHOPEXPENSE_AMOUNT: "1",
PAID_AMOUNT: "1",
REFUND_SUPPLEMENT: "1",
STATISTICS_MONTH: "1",
settlementStateObjApprovalstate: "1",
REFUND_SUPPLEMENTdesc: "1",
}]); // 合计项数据源
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 [selectedId, setSelectedId] = useState<string>()
// 导出的加载效果
const [showLoading, setShowLoading] = useState<boolean>(false)
// 是否显示打印的表格
const [showExportTable, setShowExportTable] = useState<boolean>(false)
// 查询的条件
const [searchParams, setSearchParams] = useState<any>()
const columns: any = [
{
title: '统计日期',
dataIndex: 'StatisticsYear',
valueType: "date",
hideInTable: true,
hideInDescriptions: true,
initialValue: moment().subtract('1', 'month'),
fieldProps: {
picker: "year",
format: 'YYYY'
}
},
{
title: "结算状态",
dataIndex: 'SettlementState',
valueType: 'select',
hideInTable: true,
valueEnum: {
"9": "已结算",
"0": "待结算",
"-1": "提前退场",
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '管理中心',
dataIndex: 'SPREGIONTYPE_NAME',
width: 200,
hideInSearch: true,
ellipsis: true,
align: 'center'
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '服务区',
dataIndex: 'SERVERPART_NAME',
width: 200,
hideInSearch: true,
ellipsis: true,
align: 'center'
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '经营商户',
dataIndex: 'MERCHANTS_NAME',
width: 200,
hideInSearch: true,
ellipsis: true,
align: 'center'
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '门店',
dataIndex: 'SERVERPARTSHOP_NAME',
width: 150,
hideInSearch: true,
ellipsis: true,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'termInfo',
titleStr: '基础信息',
// fixed: 'left',
hideInSearch: true,
children: [
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '期数',
dataIndex: 'IndexStr',
width: 100,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '时限',
dataIndex: 'COMPACT_STARTDATECOMPACT_ENDDATE',
width: 180,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}>/</div>,
titleStr: '保底/固定租金',
exportType: "momeny",
align: 'right',
width: 140,
valueType: 'digit',
dataIndex: 'RENTFEE',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '提成比率',
exportType: "rate",
dataIndex: 'GUARANTEERATIO',
align: 'center',
width: 120,
}
]
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '营业额',
dataIndex: '',
hideInSearch: true,
children: [
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '现金',
exportType: "momeny",
align: 'right',
width: 120,
valueType: 'digit',
dataIndex: 'CASHPAY_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '移动支付',
exportType: "momeny",
align: 'right',
width: 120,
valueType: 'digit',
dataIndex: 'MOBILEPAY_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '小计',
exportType: "momeny",
align: 'right',
valueType: 'digit',
width: 120,
dataIndex: 'REVENUEDAILY_AMOUNTTotal',
},
]
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '提成金额',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'GUARANTEERATIOAMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '业主收款',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'BANKACCOUNT_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '应收租金',
exportType: "momeny",
align: 'right',
width: 120,
hideInSearch: true,
valueType: 'digit',
dataIndex: 'RECEIVABLEAMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '应收费用',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'SHOPEXPENSE_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '已缴费用',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'PAID_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}>退</div>,
titleStr: '退补款',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'REFUND_SUPPLEMENT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '结算月份',
align: 'center',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'STATISTICS_MONTH',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '是否结算',
align: 'center',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'settlementStateObjApprovalstate',
},
{
title: <div style={{ textAlign: 'center' }}>退</div>,
titleStr: '备注(收退款)',
align: 'center',
hideInSearch: true,
width: 120,
dataIndex: 'REFUND_SUPPLEMENTdesc',
},
]
const exportTable = (isHaveChildren: boolean, isHaveTitle?: boolean) => {
// isHaveChildren 判断表头是否有子集
// isHaveTitle 判断是否有title
let columnsNoChildren: any = []
// 先创建需要显示的列表头
let headerArray: any = isHaveChildren ? [[], []] : []
// 需要合并单元格的内容
let mergesList: any = []
// 因为子集也拆出来了 这样实际的行数会比columns多 所以得知道多了多少
let moreColumns: number = 0
// 显示的列表有多少个
let showItemNumber: number = 0
let showIndex: number = 0
if (columns && columns.length > 0) {
columns.forEach((item: any, index: number) => {
if (!item.hideInTable) {
showItemNumber += 1
if (item.children && item.children.length > 0) {
mergesList.push({
s: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex },
e: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex + item.children.length - 1 },
})
moreColumns += item.children.length - 1
showIndex += item.children.length
item.children.forEach((subItem: any) => {
if (!subItem.hideInTable) {
columnsNoChildren.push(subItem)
headerArray[0].push(item.titleStr)
headerArray[1].push(subItem.titleStr)
}
})
} else {
columnsNoChildren.push(item)
headerArray[0].push(item.titleStr)
if (isHaveChildren) {
headerArray[1].push(item.titleStr)
mergesList.push({
s: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex },
e: { r: 1 + (isHaveTitle ? 1 : 0), c: showIndex },
})
}
showIndex += 1
}
}
})
}
// **创建工作表**
let ws = XLSX.utils.aoa_to_sheet([]);
// 设置列宽:动态计算每列的最大内容长度
let colWidths: any = [];
// 设置样式:对齐方式
const cellStyle: any = {
alignment: {
horizontal: 'center',
vertical: 'center'
}
};
if (headerArray && headerArray.length > 0) {
headerArray.forEach((row: any, rowIndex: any) => {
row.forEach((cell: any, colIndex: any) => {
const cellRef = XLSX.utils.encode_cell({ r: (isHaveChildren ? rowIndex : rowIndex) + (isHaveTitle ? 1 : 0), c: colIndex });
ws[cellRef] = {
v: cell, t: 's', s: cellStyle
};
const cellLength = getCharWidth((cell && cell.toString()) || '');
if (!colWidths[colIndex]) {
colWidths[colIndex] = cellLength;
} else {
colWidths[colIndex] = Math.max(colWidths[colIndex], cellLength);
}
})
})
}
// 设置工作表的范围(必需)
const range = {
s: { r: 0, c: 0 }, // 开始行列
e: { r: reqDetailList.length + (isHaveChildren ? 2 : 1) + (isHaveTitle ? 1 : 0), c: columnsNoChildren.length } // 结束行列
};
ws["A1"] = {
v: `(${searchParams?.StatisticsYear})年度结算汇总表`, t: 's', s: {
font: {
bold: true, // 字体加粗
color: { rgb: "#000" }, // 字体颜色红色
sz: 20 // 字号为14
},
alignment: {
horizontal: 'center', // 水平居中
vertical: 'center' // 垂直居中
},
}
};
if (isHaveTitle) {
mergesList.push(
{
s: { r: 0, c: 0 },
e: { r: 0, c: columnsNoChildren.length },
}
)
}
ws['!merges'] = mergesList
ws['!ref'] = XLSX.utils.encode_range(range); // 设置单元格范围
if (reqDetailList && reqDetailList.length > 0) {
reqDetailList.forEach((item: any, index: number) => {
columnsNoChildren.forEach((subItem: any, subIndex: number) => {
for (let key in item) {
if (subItem.dataIndex === key) {
let res = item[key]
const cellRef = XLSX.utils.encode_cell({ r: (isHaveChildren ? index + 2 : index + 1) + (isHaveTitle ? 1 : 0), c: subIndex });
let newRes = res ? subItem.exportType === 'momeny' ? handleFormatNumber(res) : subItem.exportType === 'rate' ? `${res}%` : res : subItem.exportType === 'momeny' ? '0' : ""
ws[cellRef] = {
v: newRes, t: 's', s: {
alignment: {
horizontal: subItem.align, // 水平居中
vertical: 'center' // 垂直居中
}
}
};
const cellLength = getCharWidth((newRes && newRes.toString()) || '');
if (!colWidths[subIndex]) {
colWidths[subIndex] = cellLength;
} else {
colWidths[subIndex] = Math.max(colWidths[subIndex], cellLength);
}
}
}
})
})
}
ws['!cols'] = colWidths.map((width: any) => ({ wch: width + 2 })); // wch 表示字符宽度
// 创建工作簿
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSXStyle.write(wb, { bookType: 'xlsx', type: 'binary' });
const blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `(${searchParams?.StatisticsYear})年度结算汇总表.xlsx`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
URL.revokeObjectURL(url);
document.body.removeChild(a);
setShowLoading(false)
}, 100);
}
const s2ab = (s: string) => {
const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf);
for (let i = 0; i < s.length; i++) {
view[i] = s.charCodeAt(i) & 0xff;
}
return buf;
}
// 判断字符类型的辅助函数
const getCharWidth = (cell: any) => {
if (!cell) {
return ''
}
if (typeof cell === 'number') {
return cell.toString().length > 150 ? 150 : cell.toString().length; // 数字通常和英文字符宽度相同
} else if (/[^\x00-\xff]/.test(cell)) {
return cell.length * 1.8 > 300 ? 300 : cell.length * 1.8; // 中文字符宽度大约是英文字符的2倍
} else {
return cell.length > 150 ? 150 : cell.length; // 英文字符的宽度为1
}
}
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 && reqDetailList && reqDetailList.length > 0 ?
<ProTable
columns={columns}
dataSource={reqDetailList}
pagination={false}
expandable={{
defaultExpandAllRows: true
}}
/> : ''
}
</div>
<div id='hiddenBox' style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }} />
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} />
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
expandable={{
expandRowByClick: true
}}
scroll={{ x: '100%' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }}
request={async (params) => {
if (!selectedId) {
return
}
setSearchParams(params)
}}
toolbar={{
actions: [
<span style={{ visibility: 'hidden' }}>
</span>,
<Button
key="new"
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(true, true)
}, 100)
}, 100)
} else {
message.error('暂无数据可导出!')
}
}}
>
excel
</Button>
]
}}
/>
</div>
</div>
</div>
)
}
export default connect(
({ user }: { user: UserConnectedProps['user'] }) => ({
user
}),
)(saleRankReport);

View File

@ -1,14 +0,0 @@
import { PureComponent } from 'react';
class Index extends PureComponent {
render() {
return (
<div>/about/um</div>
);
}
}
export default Index;

View File

@ -1,33 +1,17 @@
const authority: PageAuthority = { const authority: PageAuthority = {
'/user/login': [], // 登录页面不需要权限验证 '/standard/index': [
'/': [ '/standard/index',
'/',
], ],
'/about/u/index': [ '/examine/index': [
'/about/u/index', '/examine/index'
], ],
'/about/u/2': [ '/examine/modal': [
'/about/u/2', '/examine/modal'
], ],
'/about/m': [ '/examine/question': [
'/about/m', '/examine/question',
],
'/about/um': [
'/about/um',
],
'/teacher/u': [
'/ttt',
],
'/teacher/m': [
'/teacher/m',
],
'/teacher/um': [
'/teacher/um',
],
'/student': [
'/student',
], ],
}; };
export default authority; export default authority;

190
src/pages/examine/index.tsx Normal file
View File

@ -0,0 +1,190 @@
import { connect } from "umi";
import { useRef, useState } from "react";
import { Button, message, Modal, Space, type FormInstance } from "antd";
import type { ActionType } from "@ant-design/pro-table";
import ProTable from "@ant-design/pro-table";
import moment from "moment";
import LeftSelectTree from "@/components/leftSelectTree";
import { handleGetAddExamineType, handleGetExamineTypeList } from "./service";
import { ProForm, ProFormSwitch, ProFormText, ProFormTextArea } from "@ant-design/pro-components";
const examineIndex: React.FC<{ currentUser: any }> = (props) => {
console.log('props', props);
const { currentUser } = props
console.log('currentUser', currentUser);
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
// 弹出框的表单实例
const modalRef = useRef<FormInstance>()
// 查询的条件
const [searchParams, setSearchParams] = useState<any>()
// 打开新增的悬浮框
const [openAddModal, setOpenAddModal] = useState<boolean>(false)
const columns: any = [
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "name",
align: 'left',
width: 200,
hideInSearch: true
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "description",
align: 'left',
width: 250,
hideInSearch: true
},
{
title: "有效状态",
dataIndex: "status",
align: 'center',
width: 150,
hideInSearch: true,
render: (_: any, record: { status: any; }) => {
return record?.status ? '有效' : '无效'
}
},
{
title: "创建时间",
dataIndex: "createdAt",
align: 'center',
width: 200,
hideInSearch: true,
render: (_: any, record: { status: any; }) => {
return record?.createdAt ? moment(record?.createdAt).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{
title: "更新时间",
dataIndex: "updatedAt",
align: 'center',
width: 200,
hideInSearch: true,
render: (_: any, record: { status: any; }) => {
return record?.updatedAt ? moment(record?.updatedAt).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{
title: '操作',
dataIndex: 'option',
align: 'center',
hideInSearch: true,
width: 150,
render: (_: any, record: any) => {
return <Space>
<a></a>
<a></a>
</Space>
}
}
]
return (
<div>
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<div style={{
width: '100%',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
expandable={{
expandRowByClick: true
}}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }}
request={async (params) => {
const data = await handleGetExamineTypeList()
console.log('table', data);
if (data && data.length > 0) {
return { data, success: true }
}
return { data: [], success: true }
}}
toolbar={{
actions: [
<Button type="primary" onClick={(e) => {
setOpenAddModal(true)
}}>
</Button>
]
}}
/>
</div>
</div>
<Modal
title="分类"
open={openAddModal}
onOk={() => {
modalRef.current?.validateFields().then(async (res) => {
console.log('res', res);
const req = {
name: res.name,
description: res.description,
sortOrder: "1",
status: res.status,
operator: currentUser?.operator
}
const data = await handleGetAddExamineType(req)
console.log('data', data);
if (data.code === 200) {
message.success(data.message)
setOpenAddModal(false)
actionRef.current?.reload()
} else {
message.error(data.message)
}
})
}}
onCancel={() => {
setOpenAddModal(false)
}}
>
<ProForm
formRef={modalRef}
submitter={false}
onFinish={async (values) => {
console.log('values', values);
}}
>
<ProFormText
label={"类别名称"}
name={"name"}
rules={[
{ required: true, message: '请输入类别名称!' }
]}
/>
<ProFormTextArea
label={"类别注释"}
name={"description"}
/>
<ProFormSwitch
label={"有效状态"}
name={"status"}
initialValue={true}
/>
</ProForm>
</Modal>
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.data
}))(examineIndex);

View File

@ -0,0 +1,21 @@
import { UserConnectedProps } from "@/models/user";
import { connect } from "umi";
type DetailProps = {
}
const modal = ({ }: DetailProps) => {
return (
<div>
modal
</div>
)
}
export default connect(
({ user }: { user: UserConnectedProps['user'] }) => ({
user
}),
)(modal);

View File

@ -0,0 +1,21 @@
import { UserConnectedProps } from "@/models/user";
import { connect } from "umi";
type DetailProps = {
}
const question = ({ }: DetailProps) => {
return (
<div>
question
</div>
)
}
export default connect(
({ user }: { user: UserConnectedProps['user'] }) => ({
user
}),
)(question);

View File

@ -0,0 +1,16 @@
import request from "@/utils/request"
// 拿到类别列表接口
export async function handleGetExamineTypeList(params?: any) {
const data = await request.get('/question-categories', params)
if (data.code === 200) {
return data.data
}
return []
}
// 新增类别列表接口
export async function handleGetAddExamineType(params: any) {
const data = await request.post('/question-categories', params)
return data
}

View File

@ -0,0 +1,582 @@
import { connect } from "umi";
import { MouseEvent, useRef, useState } from "react";
import type { FormInstance } from "antd";
import { Button, message, Spin } from "antd";
import type { ActionType } from "@ant-design/pro-table";
import ProTable from "@ant-design/pro-table";
import * as XLSX from 'xlsx'; // 读写数据的核心工具(无需写样式时可用)
import XLSXStyle from 'xlsx-style-fixed';
import moment from "moment";
import { handleFormatNumber } from "@/utils/publicMethods";
import LeftSelectTree from "@/components/leftSelectTree";
import { UserConnectedProps } from "@/models/user";
const saleRankReport = (props: any) => {
const { currentUser } = props
const downloadBtnRef = useRef<any>()
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>([{
SPREGIONTYPE_NAME: "1",
SERVERPART_NAME: "1",
MERCHANTS_NAME: "1",
SERVERPARTSHOP_NAME: "1",
IndexStr: "1",
COMPACT_STARTDATECOMPACT_ENDDATE: "1",
RENTFEE: "1",
GUARANTEERATIO: "1",
CASHPAY_AMOUNT: "1",
MOBILEPAY_AMOUNT: "1",
REVENUEDAILY_AMOUNTTotal: "1",
GUARANTEERATIOAMOUNT: "1",
BANKACCOUNT_AMOUNT: "1",
RECEIVABLEAMOUNT: "1",
SHOPEXPENSE_AMOUNT: "1",
PAID_AMOUNT: "1",
REFUND_SUPPLEMENT: "1",
STATISTICS_MONTH: "1",
settlementStateObjApprovalstate: "1",
REFUND_SUPPLEMENTdesc: "1",
}]); // 合计项数据源
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 [selectedId, setSelectedId] = useState<string>()
// 导出的加载效果
const [showLoading, setShowLoading] = useState<boolean>(false)
// 是否显示打印的表格
const [showExportTable, setShowExportTable] = useState<boolean>(false)
// 查询的条件
const [searchParams, setSearchParams] = useState<any>()
const columns: any = [
{
title: '统计日期',
dataIndex: 'StatisticsYear',
valueType: "date",
hideInTable: true,
hideInDescriptions: true,
initialValue: moment().subtract('1', 'month'),
fieldProps: {
picker: "year",
format: 'YYYY'
}
},
{
title: "结算状态",
dataIndex: 'SettlementState',
valueType: 'select',
hideInTable: true,
valueEnum: {
"9": "已结算",
"0": "待结算",
"-1": "提前退场",
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '管理中心',
dataIndex: 'SPREGIONTYPE_NAME',
width: 200,
hideInSearch: true,
ellipsis: true,
align: 'center'
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '服务区',
dataIndex: 'SERVERPART_NAME',
width: 200,
hideInSearch: true,
ellipsis: true,
align: 'center'
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '经营商户',
dataIndex: 'MERCHANTS_NAME',
width: 200,
hideInSearch: true,
ellipsis: true,
align: 'center'
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '门店',
dataIndex: 'SERVERPARTSHOP_NAME',
width: 150,
hideInSearch: true,
ellipsis: true,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'termInfo',
titleStr: '基础信息',
// fixed: 'left',
hideInSearch: true,
children: [
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '期数',
dataIndex: 'IndexStr',
width: 100,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '时限',
dataIndex: 'COMPACT_STARTDATECOMPACT_ENDDATE',
width: 180,
align: 'center',
},
{
title: <div style={{ textAlign: 'center' }}>/</div>,
titleStr: '保底/固定租金',
exportType: "momeny",
align: 'right',
width: 140,
valueType: 'digit',
dataIndex: 'RENTFEE',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '提成比率',
exportType: "rate",
dataIndex: 'GUARANTEERATIO',
align: 'center',
width: 120,
}
]
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '营业额',
dataIndex: '',
hideInSearch: true,
children: [
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '现金',
exportType: "momeny",
align: 'right',
width: 120,
valueType: 'digit',
dataIndex: 'CASHPAY_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '移动支付',
exportType: "momeny",
align: 'right',
width: 120,
valueType: 'digit',
dataIndex: 'MOBILEPAY_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '小计',
exportType: "momeny",
align: 'right',
valueType: 'digit',
width: 120,
dataIndex: 'REVENUEDAILY_AMOUNTTotal',
},
]
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '提成金额',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'GUARANTEERATIOAMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '业主收款',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'BANKACCOUNT_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '应收租金',
exportType: "momeny",
align: 'right',
width: 120,
hideInSearch: true,
valueType: 'digit',
dataIndex: 'RECEIVABLEAMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '应收费用',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'SHOPEXPENSE_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '已缴费用',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'PAID_AMOUNT',
},
{
title: <div style={{ textAlign: 'center' }}>退</div>,
titleStr: '退补款',
exportType: "momeny",
align: 'right',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'REFUND_SUPPLEMENT',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '结算月份',
align: 'center',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'STATISTICS_MONTH',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
titleStr: '是否结算',
align: 'center',
valueType: 'digit',
hideInSearch: true,
width: 120,
dataIndex: 'settlementStateObjApprovalstate',
},
{
title: <div style={{ textAlign: 'center' }}>退</div>,
titleStr: '备注(收退款)',
align: 'center',
hideInSearch: true,
width: 120,
dataIndex: 'REFUND_SUPPLEMENTdesc',
},
]
const exportTable = (isHaveChildren: boolean, isHaveTitle?: boolean) => {
// isHaveChildren 判断表头是否有子集
// isHaveTitle 判断是否有title
let columnsNoChildren: any = []
// 先创建需要显示的列表头
let headerArray: any = isHaveChildren ? [[], []] : []
// 需要合并单元格的内容
let mergesList: any = []
// 因为子集也拆出来了 这样实际的行数会比columns多 所以得知道多了多少
let moreColumns: number = 0
// 显示的列表有多少个
let showItemNumber: number = 0
let showIndex: number = 0
if (columns && columns.length > 0) {
columns.forEach((item: any, index: number) => {
if (!item.hideInTable) {
showItemNumber += 1
if (item.children && item.children.length > 0) {
mergesList.push({
s: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex },
e: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex + item.children.length - 1 },
})
moreColumns += item.children.length - 1
showIndex += item.children.length
item.children.forEach((subItem: any) => {
if (!subItem.hideInTable) {
columnsNoChildren.push(subItem)
headerArray[0].push(item.titleStr)
headerArray[1].push(subItem.titleStr)
}
})
} else {
columnsNoChildren.push(item)
headerArray[0].push(item.titleStr)
if (isHaveChildren) {
headerArray[1].push(item.titleStr)
mergesList.push({
s: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex },
e: { r: 1 + (isHaveTitle ? 1 : 0), c: showIndex },
})
}
showIndex += 1
}
}
})
}
// **创建工作表**
let ws = XLSX.utils.aoa_to_sheet([]);
// 设置列宽:动态计算每列的最大内容长度
let colWidths: any = [];
// 设置样式:对齐方式
const cellStyle: any = {
alignment: {
horizontal: 'center',
vertical: 'center'
}
};
if (headerArray && headerArray.length > 0) {
headerArray.forEach((row: any, rowIndex: any) => {
row.forEach((cell: any, colIndex: any) => {
const cellRef = XLSX.utils.encode_cell({ r: (isHaveChildren ? rowIndex : rowIndex) + (isHaveTitle ? 1 : 0), c: colIndex });
ws[cellRef] = {
v: cell, t: 's', s: cellStyle
};
const cellLength = getCharWidth((cell && cell.toString()) || '');
if (!colWidths[colIndex]) {
colWidths[colIndex] = cellLength;
} else {
colWidths[colIndex] = Math.max(colWidths[colIndex], cellLength);
}
})
})
}
// 设置工作表的范围(必需)
const range = {
s: { r: 0, c: 0 }, // 开始行列
e: { r: reqDetailList.length + (isHaveChildren ? 2 : 1) + (isHaveTitle ? 1 : 0), c: columnsNoChildren.length } // 结束行列
};
ws["A1"] = {
v: `(${searchParams?.StatisticsYear})年度结算汇总表`, t: 's', s: {
font: {
bold: true, // 字体加粗
color: { rgb: "#000" }, // 字体颜色红色
sz: 20 // 字号为14
},
alignment: {
horizontal: 'center', // 水平居中
vertical: 'center' // 垂直居中
},
}
};
if (isHaveTitle) {
mergesList.push(
{
s: { r: 0, c: 0 },
e: { r: 0, c: columnsNoChildren.length },
}
)
}
ws['!merges'] = mergesList
ws['!ref'] = XLSX.utils.encode_range(range); // 设置单元格范围
if (reqDetailList && reqDetailList.length > 0) {
reqDetailList.forEach((item: any, index: number) => {
columnsNoChildren.forEach((subItem: any, subIndex: number) => {
for (let key in item) {
if (subItem.dataIndex === key) {
let res = item[key]
const cellRef = XLSX.utils.encode_cell({ r: (isHaveChildren ? index + 2 : index + 1) + (isHaveTitle ? 1 : 0), c: subIndex });
let newRes = res ? subItem.exportType === 'momeny' ? handleFormatNumber(res) : subItem.exportType === 'rate' ? `${res}%` : res : subItem.exportType === 'momeny' ? '0' : ""
ws[cellRef] = {
v: newRes, t: 's', s: {
alignment: {
horizontal: subItem.align, // 水平居中
vertical: 'center' // 垂直居中
}
}
};
const cellLength = getCharWidth((newRes && newRes.toString()) || '');
if (!colWidths[subIndex]) {
colWidths[subIndex] = cellLength;
} else {
colWidths[subIndex] = Math.max(colWidths[subIndex], cellLength);
}
}
}
})
})
}
ws['!cols'] = colWidths.map((width: any) => ({ wch: width + 2 })); // wch 表示字符宽度
// 创建工作簿
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const wbout = XLSXStyle.write(wb, { bookType: 'xlsx', type: 'binary' });
const blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `(${searchParams?.StatisticsYear})年度结算汇总表.xlsx`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
URL.revokeObjectURL(url);
document.body.removeChild(a);
setShowLoading(false)
}, 100);
}
const s2ab = (s: string) => {
const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf);
for (let i = 0; i < s.length; i++) {
view[i] = s.charCodeAt(i) & 0xff;
}
return buf;
}
// 判断字符类型的辅助函数
const getCharWidth = (cell: any) => {
if (!cell) {
return ''
}
if (typeof cell === 'number') {
return cell.toString().length > 150 ? 150 : cell.toString().length; // 数字通常和英文字符宽度相同
} else if (/[^\x00-\xff]/.test(cell)) {
return cell.length * 1.8 > 300 ? 300 : cell.length * 1.8; // 中文字符宽度大约是英文字符的2倍
} else {
return cell.length > 150 ? 150 : cell.length; // 英文字符的宽度为1
}
}
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 && reqDetailList && reqDetailList.length > 0 ?
<ProTable
columns={columns}
dataSource={reqDetailList}
pagination={false}
expandable={{
defaultExpandAllRows: true
}}
/> : ''
}
</div>
<div id='hiddenBox' style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }} />
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<LeftSelectTree setSelectedId={setSelectedId} setCollapsible={setCollapsible} collapsible={collapsible} />
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
expandable={{
expandRowByClick: true
}}
scroll={{ x: '100%' }}
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}></span>}
search={{ span: 6 }}
request={async (params) => {
if (!selectedId) {
return
}
setSearchParams(params)
}}
toolbar={{
actions: [
<span style={{ visibility: 'hidden' }}>
</span>,
<Button
key="new"
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(true, true)
}, 100)
}, 100)
} else {
message.error('暂无数据可导出!')
}
}}
>
excel
</Button>
]
}}
/>
</div>
</div>
</div>
)
}
export default connect(
({ user }: { user: UserConnectedProps['user'] }) => ({
user
}),
)(saleRankReport);

View File

@ -1,10 +0,0 @@
import type { FC } from 'react';
const Index: FC = () => {
return (
<div>/teacher/m</div>
);
};
export default Index;

View File

@ -1,10 +0,0 @@
import type { FC } from 'react';
const Index: FC = () => {
return (
<div>/teacher/u</div>
);
};
export default Index;

View File

@ -1,10 +0,0 @@
import type { FC } from 'react';
const Index: FC = () => {
return (
<div>/teacher/um</div>
);
};
export default Index;

View File

@ -17,13 +17,16 @@ const Index: FC<UserConnectedProps> = (props) => {
const handleSubmit = async (values: API.LoginParams) => { const handleSubmit = async (values: API.LoginParams) => {
try { try {
const response = await dispatch?.({ const response = await dispatch?.({
type: 'user/login', type: 'user/login',
payload: values, payload: values,
}); });
if (response?.code) { if (response?.code === 200) {
message.error(response.message || '登录失败,请检查账号密码'); message.success('登录成功');
return true;
} else {
message.error(response?.message || '登录失败,请检查账号密码');
return false; return false;
} }
} catch (error) { } catch (error) {
@ -79,16 +82,16 @@ const Index: FC<UserConnectedProps> = (props) => {
}, },
}, },
}} }}
onFinish={(values) => { onFinish={async (values) => {
handleSubmit({ const success = await handleSubmit({
...values, ...values,
}) });
// LoginIP: baseInfo?.ip ? baseInfo?.ip : '', return success;
// LoginPlace: `${baseInfo?.prov ? baseInfo?.prov : ''}${baseInfo?.prov && baseInfo?.city ? '-' : ''}${baseInfo?.city ? baseInfo?.city : ''}`,
// BrowserVersion: browser || '',
// OperatingSystem: systemInfo || '',
return Promise.resolve();
}} }}
// LoginIP: baseInfo?.ip ? baseInfo?.ip : '',
// LoginPlace: `${baseInfo?.prov ? baseInfo?.prov : ''}${baseInfo?.prov && baseInfo?.city ? '-' : ''}${baseInfo?.city ? baseInfo?.city : ''}`,
// BrowserVersion: browser || '',
// OperatingSystem: systemInfo || '',
> >
<ProFormText <ProFormText
name="username" name="username"

View File

@ -7,36 +7,45 @@ import request from '@/utils/request';
// 登录 // 登录
export async function userLogin(params: any) { export async function userLogin(params: any) {
console.log('paramsuserLogin', params); const data = await request.post('/auth/admin/login', params)
const data = await request.post('/api/user/login', params)
// const data = await request.post('/auth/login', params)
console.log('data', data);
return data return data
} }
//获取用户信息 //获取用户信息
export const retrieveUserInfo = (): Promise<API.UserInfoResponse> => ( export async function retrieveUserInfo(params: any) {
request.get('/api/user/info') const data = await request.get('/auth/admin/profile', params)
); return data
}
// export const retrieveUserInfo = (): Promise<API.UserInfoResponse> => (
// request.get('/api/user/info')
// );
//获取用户权限 //获取用户权限
export const retrieveUserAuthority = (): Promise<API.UserAuthorityResponse> => ( export async function retrieveUserAuthority(params: any) {
request.get('/api/user/authority') const data = await request.get('/api/user/authority', params)
); return data
}
// export const retrieveUserAuthority = (): Promise<API.UserAuthorityResponse> => (
// request.get('/api/user/authority')
// );
//获取菜单数据 //获取菜单数据
export const retrieveMenuData = (): Promise<API.MenuDataResponse> => ( export async function retrieveMenuData(params: any) {
request.get('/api/user/menu') const data = await request.get('/menus', params)
); return data
}
// export const retrieveMenuData = (): Promise<API.MenuDataResponse> => (
// request.get('/api/user/menu')
// );
//获取用户信息和权限以及菜单 //获取用户信息和权限以及菜单
export const retrieveUserInfoAuthorityMenu = (): Promise<API.UserInfoAuthMenuResponse> => ( export const retrieveUserInfoAuthorityMenu = (): Promise<API.UserInfoAuthMenuResponse> => (
Promise.all([ Promise.all([
retrieveUserInfo(), retrieveUserInfo(),
retrieveUserAuthority(), // retrieveUserAuthority(),
retrieveMenuData(), retrieveMenuData(),
]) ])
); );
@ -44,14 +53,14 @@ export const retrieveUserInfoAuthorityMenu = (): Promise<API.UserInfoAuthMenuRes
//获取用户权限以及菜单 //获取用户权限以及菜单
export const retrieveUserAuthorityMenu = (): Promise<API.UserAuthMenuResponse> => ( export const retrieveUserAuthorityMenu = (): Promise<API.UserAuthMenuResponse> => (
Promise.all([ Promise.all([
retrieveUserAuthority(), // retrieveUserAuthority(),
retrieveMenuData(), retrieveMenuData(),
]) ])
); );
//登出 //登出
export const userLogout = (): Promise<API.LogoutResponse> => ( export const userLogout = (): Promise<API.LogoutResponse> => (
request.post('/api/user/logout') request.post('/auth/logout')
); );
//获取验证码 //获取验证码

View File

@ -6,9 +6,9 @@ import CryptoJS from "crypto-js";
const { UMI_APP_BASEURL } = process.env; const { UMI_APP_BASEURL } = process.env;
const instance = axios.create({ baseURL: UMI_APP_BASEURL }); // const instance = axios.create({ baseURL: UMI_APP_BASEURL });
// const instance = axios.create({ baseURL: 'https://api.eshangtech.com/EShangApiMain' }); // const instance = axios.create({ baseURL: 'https://api.eshangtech.com/EShangApiMain' });
// const instance = axios.create({ baseURL: '/api' }); const instance = axios.create({ baseURL: '/auth' });
@ -21,7 +21,8 @@ instance.interceptors.request.use(
config.headers = { config.headers = {
...config.headers, ...config.headers,
Authorization: localStorage.getItem('Authorization') || '', Authorization: `Bearer ${localStorage.getItem('Authorization') || ''}`,
"Content-Type": "application/json;charset=utf-8"
} as AxiosRequestHeaders; } as AxiosRequestHeaders;
return config; return config;
@ -33,9 +34,8 @@ instance.interceptors.response.use(
//状态码为2xx的时候执行 //状态码为2xx的时候执行
(response) => { (response) => {
const { data } = response; const { data } = response;
const { code } = data;
if (code !== 200) { if (data.code !== 200) {
notification.error({ notification.error({
message: data.message, message: data.message,
}); });
@ -43,46 +43,32 @@ instance.interceptors.response.use(
const timestamp = getFormattedDate() const timestamp = getFormattedDate()
// let res = JSON.parse(decryptAESECB(data.data, timestamp))
// return res;
// console.log('resdsa', data);
return data return data
}, },
//状态码不2xx的时候执行 //状态码不是2xx的时候执行
(error) => { (error) => {
const { response } = error; const { response } = error;
const { status } = response;
enum CodeMessage { if (response && response.status === 401) {
'发出的请求有错误,服务器没有进行新建或修改数据的操作。' = 400, // // 清除本地存储的token
'用户未登录。' = 401, // localStorage.removeItem('Authorization');
'用户得到授权,但是访问是被禁止的。' = 403, // // 重定向到登录页
'发出的请求针对的是不存在的记录,服务器没有进行操作。' = 404, // window.location.href = '/user/login';
'请求的格式不可得。' = 406, // notification.error({
'请求的资源被永久删除,且不会再得到的。' = 410, // message: response?.data?.message || '请求失败',
'当创建一个对象时,发生一个验证错误。' = 422, // description: error.message
'服务器发生错误,请检查服务器。' = 500, // });
'网关错误。' = 502, } else {
'服务不可用,服务器暂时过载或维护。' = 503, notification.error({
'网关超时。' = 504 message: response?.data?.message || '请求失败',
} description: error.message
notification.error({
message: response.data.message || CodeMessage[status],
});
if (status === 401) {
const {
_store: { dispatch },
} = getDvaApp();
dispatch({
type: 'user/resetLoginStatus',
}); });
} }
return Promise.reject(error); return Promise.reject({
code: response?.status || 500,
message: response?.data?.message || '请求失败'
});
}, },
); );