This commit is contained in:
PC-202306242200\Administrator
2024-09-19 18:28:02 +08:00
parent 9ea99f1dda
commit 2081393a33
39 changed files with 821 additions and 194 deletions

File diff suppressed because one or more lines are too long

View File

@@ -86,7 +86,7 @@ export default (props: any) => {
"fixedHeader": false,
"fixSiderbar": true,
"colorWeak": false,
"title": "云充电管理后台",
"title": "广东星动管理后台",
"pwa": true,
"logo": false,
"iconfontUrl": "",

View File

@@ -214,6 +214,10 @@
display: block
}
.inline-block {
display: inline-block
}
.flex {
display: -webkit-box;
display: -ms-flexbox;

File diff suppressed because one or more lines are too long

View File

@@ -86,7 +86,7 @@ export default (props: any) => {
"fixedHeader": false,
"fixSiderbar": true,
"colorWeak": false,
"title": "云充电管理后台",
"title": "广东星动管理后台",
"pwa": true,
"logo": false,
"iconfontUrl": "",

View File

@@ -8,12 +8,12 @@ const Footer: React.FC = () => {
style={{
background: 'none',
}}
copyright="Powered by Ant Desgin"
copyright="中帅新能源"
links={[
{
key: 'Ant Design Pro',
title: '云充电管理后台',
href: 'https://pro.ant.design',
title: '中帅新能源提供技术支持',
href: '',
blankTarget: true,
},
// {

View File

View File

@@ -0,0 +1,97 @@
import React, { useEffect, useState } from 'react';
import {
ProForm,
ProFormDigit,
ProFormText,
ProFormSwitch,
ProFormSelect
} from '@ant-design/pro-components';
import { Form, Modal, InputNumber } from 'antd';
import { useIntl } from '@umijs/max';
import FilesManager from '@/components/FilesManage/index';
const RoleForm: React.FC = (props: any) => {
const [form] = Form.useForm();
const { values } = props;
useEffect(() => {
console.log(values, 'values');
form.resetFields();
form.setFieldsValue(values);
}, [form, props]);
const intl = useIntl();
const handleOk = () => {
form.submit();
};
const handleCancel = () => {
props.onCancel();
};
const handleFinish = async (values: any) => {
props.onSubmit(values);
};
return (
<Modal
width={640}
title={'设备补贴'}
open={props.open}
forceRender
destroyOnClose
onOk={handleOk}
onCancel={handleCancel}
>
<ProForm
form={form}
submitter={false}
layout="horizontal"
onFinish={handleFinish}>
<ProFormDigit
name="id"
label={'ID'}
disabled
hidden={true}
/>
<ProForm.Group>
<ProForm.Item
label="设备类型"
name={'deviceType'}
>
<InputNumber addonAfter="W" style={{ width: 150 }} />
</ProForm.Item>
<ProForm.Item
label="补贴金额"
name={'amount'}
>
<InputNumber addonBefore="¥" style={{ width: 150 }} />
</ProForm.Item>
</ProForm.Group>
<ProForm.Group>
<ProForm.Item
label="上下浮动比例"
name={'earingsFloat'}
>
<InputNumber addonAfter="%" style={{ width: 150 }} />
</ProForm.Item>
<ProForm.Item
label="基础收入服务费"
name={'baseAmount'}
>
<InputNumber style={{ width: 150 }} />
</ProForm.Item>
<ProForm.Item
label="交付时间"
name={'deliverDate'}
>
<InputNumber addonAfter="天" style={{ width: 150 }} />
</ProForm.Item>
</ProForm.Group>
</ProForm>
</Modal>
);
};
export default RoleForm;

View File

@@ -0,0 +1,187 @@
import { findList, configUpdate, configAdd, configDeleteBatchByIds } from '@/services/facility/index';
import React, { useRef, useEffect, useState } from 'react';
import { useIntl, useAccess } from '@umijs/max';
import { message, Tag, Select, Button, Modal } from 'antd';
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
import UpdateForm from './edit';
const LogTableList: React.FC = () => {
const actionRef = useRef<ActionType>();
const [modalVisible, setModalVisible] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState();
const columns = [
{
title: 'ID',
dataIndex: 'id',
valueType: 'text',
search: false,
},
{
title: '设备类型',
dataIndex: 'deviceType',
valueType: 'text',
render: (_, record) => {
return record?.deviceType + 'W'
},
// renderFormItem: (
// _,
// { type, defaultRender, formItemProps, fieldProps, ...rest },
// form,
// ) => {
// const statusMap = [
// { label: '7W', value: 7 },
// { label: '20W', value: 20 },
// { label: '30W', value: 30 },
// ]
// return <Select
// {...fieldProps}
// allowClear
// style={{ width: "100%" }}
// filterOption={false}
// fieldNames={{
// label: "label",
// value: "value"
// }}
// options={statusMap}
// />
// },
},
{
title: '补贴金额',
dataIndex: 'amount',
valueType: 'text',
search: false,
},
{
title: '上下浮动比例',
dataIndex: 'earingsFloat',
valueType: 'text',
search: true,
},
{
title: '基础收入服务费',
dataIndex: 'baseAmount',
valueType: 'text',
search: false,
},
{
title: '交付时间',
dataIndex: 'deliverDate',
valueType: 'text',
search: false,
},
{
title: '创建时间',
dataIndex: 'createTime',
valueType: 'text',
search: false,
},
{
title: '操作',
dataIndex: 'option',
width: '220px',
valueType: 'option',
render: (_, record) => [
<Button
type="link"
size="small"
onClick={() => {
setModalVisible(true);
setCurrentRow(record);
}}
>
</Button>,
<Button
type="link"
size="small"
danger
// hidden={!access.hasPerms('admin:banner:update')}
onClick={async () => {
Modal.confirm({
title: '删除',
content: '确定删除该项吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
const success = await configDeleteBatchByIds([record.id]);
if (success) {
if (actionRef.current) {
actionRef.current.reload();
}
}
},
});
}}
>
</Button>,
],
},
];
return (
<PageContainer>
<div style={{ width: '100%', float: 'right' }}>
<ProTable
actionRef={actionRef}
rowKey="id"
key="logList"
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button
type="primary"
key="add"
onClick={async () => {
setCurrentRow(undefined);
setModalVisible(true);
}}
>
</Button>
]}
request={async (params, sorter, filter) => {
let { data } = await findList(params)
return {
data: data || [],
total: data.length,
};
}}
columns={columns}
/>
</div>
<UpdateForm
onSubmit={async (values) => {
let success = false;
if (values.id) {
success = await configUpdate({ ...values });
} else {
success = await configAdd({ ...values });
}
if (success) {
setModalVisible(false);
setCurrentRow(undefined);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
onCancel={() => {
setModalVisible(false);
setCurrentRow(undefined);
}}
open={modalVisible}
values={currentRow || {}}
/>
</PageContainer>
);
};
export default LogTableList;

123
src/pages/home/index.tsx Normal file
View File

@@ -0,0 +1,123 @@
import React, { useState, useEffect } from 'react';
import { PageContainer, ProTable } from '@ant-design/pro-components';
import { Card, Col, Row, Statistic, Divider, Tag, Image } from 'antd';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
export default () => {
const [deviceData, setDeviceData] = useState([]);
const [profit, setProfit] = useState([]);
const [shop, setShop] = useState([]);
const deviceConfig = {
data: deviceData,
xField: 'name',
yField: 'value',
seriesField: 'category',
xAxis: {
type: 'time',
},
yAxis: {
label: {
// 数值格式化为千分位
formatter: (v) => `${v}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`),
},
},
};
useEffect(() => {
}, []);
return (
<>
<Divider></Divider>
<Row gutter={10}>
<Col span={6}>
<Card>
<Statistic
title="今日充电"
value={profit?.device_count?.today}
precision={2}
prefix="¥"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="昨日充电"
value={profit?.device_count?.yesterday}
precision={2}
prefix="¥"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="上周充电"
value={profit?.device_count?.week}
precision={2}
prefix="¥"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="上月充电"
value={profit?.device_count?.month}
precision={2}
prefix="¥"
/>
</Card>
</Col>
</Row>
<Divider></Divider>
<Row gutter={10}>
<Col span={6}>
<Card>
<Statistic
title="今天成交"
value={profit?.shop_count?.today}
precision={2}
prefix="¥"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="昨天成交"
value={profit?.shop_count?.yesterday}
precision={2}
prefix="¥"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="上周成交"
value={profit?.shop_count?.week}
precision={2}
prefix="¥"
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="上月成交"
value={profit?.shop_count?.month}
precision={2}
prefix="¥"
/>
</Card>
</Col>
</Row>
{/* <Divider>商城(商品数量/金额)-折线图</Divider> */}
</>
);
};

View File

@@ -35,7 +35,7 @@ const RoleForm: React.FC = (props: any) => {
return (
<Modal
width={640}
title={'轮播图'}
title={'公告分类'}
open={props.open}
forceRender
destroyOnClose

View File

@@ -3,7 +3,7 @@ import { logPage } from '@/services/system/log';
import React, { useRef, useEffect } from 'react';
import { useIntl, useAccess } from '@umijs/max';
import { message, Tag } from 'antd';
import { message, Tag, Select, Tooltip } from 'antd';
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
@@ -23,7 +23,7 @@ const LogTableList: React.FC = () => {
title: '标题',
dataIndex: 'title',
valueType: 'text',
search: false,
search: true,
},
{
title: '类型',
@@ -31,27 +31,78 @@ const LogTableList: React.FC = () => {
valueType: 'text',
search: false,
render: (_: any, record: any) => {
let color = record.businessType == 1 ? 'volcano' : record.businessType == 2 ? 'orange' : 'red'
return <Tag color={color}>{record.businessType}</Tag>
}
let color = record.businessType == 1 ? 'purple' : record.businessType == 2 ? 'blue' : record.businessType == 3 ? 'magenta' : 'red'
let statusMap = [
{ label: '其它', value: 0 },
{ label: '新增', value: 1 },
{ label: '修改', value: 2 },
{ label: '删除', value: 3 },
]
return <Tag color={color}>{statusMap.find((val) => val.value == record.businessType)?.label}</Tag>
},
// 0其它 1新增 2修改 3删除
// renderFormItem: (
// _,
// { type, defaultRender, formItemProps, fieldProps, ...rest },
// form,
// ) => {
// const statusMap = [
// { label: '其它', value: 0 },
// { label: '新增', value: 1 },
// { label: '修改', value: 2 },
// { label: '删除', value: 3 },
// ]
// return <Select
// {...fieldProps}
// allowClear
// style={{ width: "100%" }}
// filterOption={false}
// fieldNames={{
// label: "label",
// value: "value"
// }}
// options={statusMap}
// />
// },
},
{
title: '方法',
dataIndex: 'method',
valueType: 'text',
search: false,
search: true,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
title: '请求方式',
dataIndex: 'requestMethod',
valueType: 'text',
search: false,
search: true,
},
{
title: '操作类别',
dataIndex: 'operatorType',
valueType: 'text',
search: false,
render: (_: any, record: any) => {
let statusMap = [
{ label: '其它', value: 0 },
{ label: '后台用户', value: 1 },
{ label: '手机端用户', value: 2 },
]
return statusMap.find((val) => val.value == record.operatorType)?.label
},
},
{
title: '操作人员',
@@ -64,6 +115,19 @@ const LogTableList: React.FC = () => {
dataIndex: 'operUrl',
valueType: 'text',
search: false,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
@@ -71,36 +135,105 @@ const LogTableList: React.FC = () => {
dataIndex: 'operIp',
valueType: 'text',
search: false,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
title: '操作地点',
dataIndex: 'operLocation',
valueType: 'text',
search: false,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
title: '请求参数',
dataIndex: 'operParam',
valueType: 'text',
search: false,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
title: '返回参数',
dataIndex: 'jsonResult',
valueType: 'text',
search: false,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
title: '状态',
dataIndex: 'status',
valueType: 'text',
search: false,
render: (_: any, record: any) => {
let color = record.status == 0 ? 'green' : 'red'
return <Tag color={color}>{record.status == 0 ? '正常' : '异常'}</Tag>
},
},
{
title: '错误消息',
dataIndex: 'errorMsg',
valueType: 'text',
search: false,
render: (data) => {
return (
<div className="ellipsis" style={{
float: 'left',
maxWidth: '100px',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}} title={data}>
<Tooltip placement="topLeft" title={data}>{data}</Tooltip>
</div>
)
},
},
{
title: '操作时间',

View File

@@ -1,23 +1,17 @@
import { Footer } from '@/components';
import { login } from '@/services/user/index';
import { login, validateCode } from '@/services/user/index';
import {
AlipayCircleOutlined,
LockOutlined,
MobileOutlined,
TaobaoCircleOutlined,
UserOutlined,
WeiboCircleOutlined,
} from '@ant-design/icons';
import {
LoginForm,
ProFormCaptcha,
ProFormCheckbox,
ProFormText,
} from '@ant-design/pro-components';
import { FormattedMessage, Helmet, SelectLang, useIntl, useModel } from '@umijs/max';
import { Alert, message, Tabs } from 'antd';
import { FormattedMessage, Helmet, useIntl, useModel } from '@umijs/max';
import { message, Row, Col, Image } from 'antd';
import { createStyles } from 'antd-style';
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { flushSync } from 'react-dom';
import Settings from '../../../../config/defaultSettings';
import "./index.css";
@@ -58,48 +52,24 @@ const useStyles = createStyles(({ token }) => {
};
});
const ActionIcons = () => {
const { styles } = useStyles();
return (
<>
<AlipayCircleOutlined key="AlipayCircleOutlined" className={styles.action} />
<TaobaoCircleOutlined key="TaobaoCircleOutlined" className={styles.action} />
<WeiboCircleOutlined key="WeiboCircleOutlined" className={styles.action} />
</>
);
};
const Lang = () => {
const { styles } = useStyles();
return (
<div className={styles.lang} data-lang>
{SelectLang && <SelectLang />}
</div>
);
};
const LoginMessage: React.FC<{
content: string;
}> = ({ content }) => {
return (
<Alert
style={{
marginBottom: 24,
}}
message={content}
type="error"
showIcon
/>
);
};
const Login: React.FC = () => {
const [userLoginState, setUserLoginState] = useState<API.LoginResult>({});
const { initialState, setInitialState } = useModel('@@initialState');
const { styles } = useStyles();
const intl = useIntl();
const [captchaCode, setCaptchaCode] = useState<string>('');
const [uuid, setUuid] = useState<string>('');
const getCaptchaCode = async () => {
const { data } = await validateCode();
const imgdata = `data:image/png;base64,${data.img}`;
setCaptchaCode(imgdata);
setUuid(data.uuid);
};
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.();
@@ -118,26 +88,26 @@ const Login: React.FC = () => {
const handleSubmit = async (values: API.LoginParams) => {
try {
// 登录
const msg = await login({ ...values });
console.log(msg);
const msg = await login({ ...values, uuid });
if (msg.success || msg.status == 'ok') {
localStorage.setItem('token', msg.data?.token)
const defaultLoginSuccessMessage = intl.formatMessage({
id: 'pages.login.success',
defaultMessage: '登录成功!',
});
const defaultLoginSuccessMessage = '登录成功!';
message.success(defaultLoginSuccessMessage);
await fetchUserInfo();
const urlParams = new URL(window.location.href).searchParams;
window.location.href = urlParams.get('redirect') || '/';
return;
}
setUserLoginState(msg);
} catch (error) {
getCaptchaCode()
}
};
useEffect(() => {
getCaptchaCode();
}, []);
return (
<div className={styles.container}>
<Helmet>
@@ -161,9 +131,9 @@ const Login: React.FC = () => {
minWidth: 280,
maxWidth: '75vw',
}}
logo={<img alt="logo" src="/logo.svg" />}
title="云充电 管理后台"
subTitle={'云充电 管理后台'}
// logo={<img alt="logo" src="/logo.svg" />}
title="广东星动 管理后台"
subTitle={'广东星动 管理后台'}
initialValues={{
autoLogin: true,
}}
@@ -201,6 +171,38 @@ const Login: React.FC = () => {
},
]}
/>
<Row>
<Col flex={3}>
<ProFormText
style={{
float: 'right',
}}
name="code"
placeholder={'请输入验证码'}
rules={[
{
required: true,
message: '请输入验证码',
},
]}
/>
</Col>
<Col flex={2}>
<Image
src={captchaCode}
alt="验证码"
style={{
display: 'inline-block',
verticalAlign: 'top',
cursor: 'pointer',
paddingLeft: '10px',
width: '100px',
}}
preview={false}
onClick={() => getCaptchaCode()}
/>
</Col>
</Row>
</>
</LoginForm>
</div>

View File

@@ -8,4 +8,47 @@ export async function deviceAmountPage(body) {
},
data: body || {},
});
}
}
export async function findList(body) {
return request('/device/subsidy/config/findList', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: body || {},
});
}
export async function configUpdate(body) {
return request('/device/subsidy/config/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: body || {},
});
}
export async function configDeleteBatchByIds(body) {
return request('/device/subsidy/config/deleteBatchByIds', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: body || {},
});
}
export async function configAdd(body) {
return request('/device/subsidy/config/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: body || {},
});
}

View File

@@ -2,7 +2,7 @@ import { request } from '@umijs/max';
// 查询菜单权限列表
export async function logPage(body) {
return request('/api/sys/oper/log/page', {
return request('/sys/oper/log/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',

View File

@@ -26,4 +26,12 @@ export async function currentUser(options?: {}) {
method: 'GET',
...(options || {}),
});
}
}
export async function validateCode(options?: {}) {
return request('/sys/validate/code', {
method: 'GET',
...(options || {}),
});
}

View File

@@ -9,6 +9,7 @@ interface Item {
menuType?: string;
disabledTooltip?: boolean;
access?: any;
orderNum?: number;
}
@@ -37,6 +38,5 @@ export const childrenRemove = (data: Item[] | undefined): Item[] => {
list.push(i);
}
});
return list; // 返回处理后的列表
return list.sort((a, b) => (a.orderNum === 0 ? -1 : b.orderNum === 0 ? 1 : a.orderNum - b.orderNum));
};