第一次
This commit is contained in:
18
src/pages/404.tsx
Normal file
18
src/pages/404.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { history, useIntl } from '@umijs/max';
|
||||
import { Button, Result } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const NoFoundPage: React.FC = () => (
|
||||
<Result
|
||||
status="404"
|
||||
title="404"
|
||||
subTitle={useIntl().formatMessage({ id: 'pages.404.subTitle' })}
|
||||
extra={
|
||||
<Button type="primary" onClick={() => history.push('/')}>
|
||||
{useIntl().formatMessage({ id: 'pages.404.buttonText' })}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export default NoFoundPage;
|
||||
45
src/pages/Admin.tsx
Normal file
45
src/pages/Admin.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Alert, Card, Typography } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const Admin: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<PageContainer
|
||||
content={intl.formatMessage({
|
||||
id: 'pages.admin.subPage.title',
|
||||
defaultMessage: 'This page can only be viewed by admin',
|
||||
})}
|
||||
>
|
||||
<Card>
|
||||
<Alert
|
||||
message={intl.formatMessage({
|
||||
id: 'pages.welcome.alertMessage',
|
||||
defaultMessage: 'Faster and stronger heavy-duty components have been released.',
|
||||
})}
|
||||
type="success"
|
||||
showIcon
|
||||
banner
|
||||
style={{
|
||||
margin: -12,
|
||||
marginBottom: 48,
|
||||
}}
|
||||
/>
|
||||
<Typography.Title level={2} style={{ textAlign: 'center' }}>
|
||||
<SmileTwoTone /> Ant Design Pro <HeartTwoTone twoToneColor="#eb2f96" /> You
|
||||
</Typography.Title>
|
||||
</Card>
|
||||
<p style={{ textAlign: 'center', marginTop: 24 }}>
|
||||
Want to add more pages? Please refer to{' '}
|
||||
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
|
||||
use block
|
||||
</a>
|
||||
。
|
||||
</p>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Admin;
|
||||
164
src/pages/Welcome.tsx
Normal file
164
src/pages/Welcome.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { useModel } from '@umijs/max';
|
||||
import { Card, theme } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* 每个单独的卡片,为了复用样式抽成了组件
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
const InfoCard: React.FC<{
|
||||
title: string;
|
||||
index: number;
|
||||
desc: string;
|
||||
href: string;
|
||||
}> = ({ title, href, index, desc }) => {
|
||||
const { useToken } = theme;
|
||||
|
||||
const { token } = useToken();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: token.colorBgContainer,
|
||||
boxShadow: token.boxShadow,
|
||||
borderRadius: '8px',
|
||||
fontSize: '14px',
|
||||
color: token.colorTextSecondary,
|
||||
lineHeight: '22px',
|
||||
padding: '16px 19px',
|
||||
minWidth: '220px',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '4px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
lineHeight: '22px',
|
||||
backgroundSize: '100%',
|
||||
textAlign: 'center',
|
||||
padding: '8px 16px 16px 12px',
|
||||
color: '#FFF',
|
||||
fontWeight: 'bold',
|
||||
backgroundImage:
|
||||
"url('https://gw.alipayobjects.com/zos/bmw-prod/daaf8d50-8e6d-4251-905d-676a24ddfa12.svg')",
|
||||
}}
|
||||
>
|
||||
{index}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
color: token.colorText,
|
||||
paddingBottom: 8,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
color: token.colorTextSecondary,
|
||||
textAlign: 'justify',
|
||||
lineHeight: '22px',
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
{desc}
|
||||
</div>
|
||||
<a href={href} target="_blank" rel="noreferrer">
|
||||
了解更多 {'>'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Welcome: React.FC = () => {
|
||||
const { token } = theme.useToken();
|
||||
const { initialState } = useModel('@@initialState');
|
||||
return (
|
||||
<PageContainer>
|
||||
<Card
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
}}
|
||||
bodyStyle={{
|
||||
backgroundImage:
|
||||
initialState?.settings?.navTheme === 'realDark'
|
||||
? 'background-image: linear-gradient(75deg, #1A1B1F 0%, #191C1F 100%)'
|
||||
: 'background-image: linear-gradient(75deg, #FBFDFF 0%, #F5F7FF 100%)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
backgroundPosition: '100% -30%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: '274px auto',
|
||||
backgroundImage:
|
||||
"url('https://gw.alipayobjects.com/mdn/rms_a9745b/afts/img/A*BuFmQqsB2iAAAAAAAAAAAAAAARQnAQ')",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
color: token.colorTextHeading,
|
||||
}}
|
||||
>
|
||||
欢迎使用 Ant Design Pro
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
color: token.colorTextSecondary,
|
||||
lineHeight: '22px',
|
||||
marginTop: 16,
|
||||
marginBottom: 32,
|
||||
width: '65%',
|
||||
}}
|
||||
>
|
||||
Ant Design Pro 是一个整合了 umi,Ant Design 和 ProComponents
|
||||
的脚手架方案。致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: 16,
|
||||
}}
|
||||
>
|
||||
<InfoCard
|
||||
index={1}
|
||||
href="https://umijs.org/docs/introduce/introduce"
|
||||
title="了解 umi"
|
||||
desc="umi 是一个可扩展的企业级前端应用框架,umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。"
|
||||
/>
|
||||
<InfoCard
|
||||
index={2}
|
||||
title="了解 ant design"
|
||||
href="https://ant.design"
|
||||
desc="antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。"
|
||||
/>
|
||||
<InfoCard
|
||||
index={3}
|
||||
title="了解 Pro Components"
|
||||
href="https://procomponents.ant.design"
|
||||
desc="ProComponents 是一个基于 Ant Design 做了更高抽象的模板组件,以 一个组件就是一个页面为开发理念,为中后台开发带来更好的体验。"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Welcome;
|
||||
134
src/pages/config/agreement/edit.tsx
Normal file
134
src/pages/config/agreement/edit.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormText
|
||||
} from '@ant-design/pro-components';
|
||||
import { Form, Modal } from 'antd';
|
||||
import { useIntl } from '@umijs/max';
|
||||
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
import { ContentUtils } from 'braft-utils'
|
||||
// 引入编辑器组件
|
||||
import BraftEditor from 'braft-editor'
|
||||
// 引入编辑器样式
|
||||
import 'braft-editor/dist/index.css'
|
||||
|
||||
const agreementForm: React.FC = (props: any) => {
|
||||
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(''))
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { values } = props;
|
||||
|
||||
useEffect(() => {
|
||||
setEditorState(BraftEditor.createEditorState(values.protocolContent ? values.protocolContent : ''))
|
||||
form.resetFields();
|
||||
form.setFieldsValue(values);
|
||||
}, [form, props]);
|
||||
|
||||
const handleOk = () => {
|
||||
form.submit();
|
||||
};
|
||||
const handleCancel = () => {
|
||||
props.onCancel();
|
||||
};
|
||||
const handleFinish = async (values: any) => {
|
||||
let data = JSON.parse(JSON.stringify(values))
|
||||
data.protocolContent = editorState.toHTML()
|
||||
props.onSubmit(data);
|
||||
};
|
||||
|
||||
|
||||
const controls = [
|
||||
'undo', 'redo', 'separator',
|
||||
'font-size', 'line-height', 'letter-spacing', 'separator',
|
||||
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
|
||||
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
|
||||
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
|
||||
'link', 'separator', 'hr', 'separator',
|
||||
'clear'
|
||||
]
|
||||
|
||||
const onChange = async (e) => {
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.3g2'];
|
||||
// 将URL转换为小写,以便进行不区分大小写的比较
|
||||
const lowerCaseUrl = e.toLowerCase();
|
||||
// 检查链接是否以任何一个视频扩展名结尾
|
||||
if (videoExtensions.some(e => lowerCaseUrl.endsWith(e))) {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{
|
||||
type: 'VIDEO',
|
||||
url: e
|
||||
}]));
|
||||
} else {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{ type: 'IMAGE', url: e, },]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const extendControls = [
|
||||
'separator',
|
||||
{
|
||||
key: 'FilesManagerImage', // 控件唯一标识,必传
|
||||
title: '上传图片/视频', // 指定鼠标悬停提示文案
|
||||
html: null, // 指定在按钮中渲染的html字符串
|
||||
text: <FilesManager
|
||||
fileType="image"
|
||||
mode=""
|
||||
imagesShow={false}
|
||||
onChange={onChange}
|
||||
count={1}
|
||||
/>, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
onClick: () => {
|
||||
console.log('Hello World!');
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
return (
|
||||
<Modal
|
||||
width={1200}
|
||||
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}
|
||||
/>
|
||||
<ProFormText
|
||||
width={300}
|
||||
name="protocolName"
|
||||
label="协议名称"
|
||||
placeholder="协议名称"
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
name="protocolContent"
|
||||
>
|
||||
<div className="border-solid border-2 border-indigo-600">
|
||||
<BraftEditor value={editorState} controls={controls} extendControls={extendControls} onChange={(val) => {
|
||||
setEditorState(val)
|
||||
}} />
|
||||
</div>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default agreementForm;
|
||||
213
src/pages/config/agreement/index.tsx
Normal file
213
src/pages/config/agreement/index.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import { protocolUpdate, protocolPage, protocolAdd, protocolDelete, protocolInfo } from '@/services/config/agreement';
|
||||
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal, Image } from 'antd';
|
||||
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
await protocolAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await protocolUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await protocolDelete(params);
|
||||
hide();
|
||||
message.success('删除成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const MenuTableList: React.FC = () => {
|
||||
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [currentRow, setCurrentRow] = useState();
|
||||
const access = useAccess();
|
||||
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '协议名称',
|
||||
dataIndex: 'protocolName',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'updateTime',
|
||||
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={async () => {
|
||||
let { data } = await protocolInfo({ id: record.id })
|
||||
setCurrentRow(data);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
key="api/sys/menu/deleteBatchByIds"
|
||||
onClick={async () => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const success = await handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.System.Menu>
|
||||
headerTitle={'菜单管理'}
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="menuList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await protocolPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...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 MenuTableList;
|
||||
107
src/pages/config/banner/edit.tsx
Normal file
107
src/pages/config/banner/edit.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormText,
|
||||
ProFormSwitch
|
||||
} 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>
|
||||
<ProFormText
|
||||
name="title"
|
||||
label={'标题'}
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="排序"
|
||||
name={'sortOrder'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
|
||||
|
||||
<ProFormText
|
||||
name="jumpUrl"
|
||||
label={'跳转地址'}
|
||||
placeholder="请输入跳转地址"
|
||||
/>
|
||||
|
||||
<ProFormSwitch
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
checkedChildren="启用"
|
||||
unCheckedChildren="禁用"
|
||||
name="status"
|
||||
label="是否启用"
|
||||
fieldProps={
|
||||
{
|
||||
defaultChecked: true
|
||||
}
|
||||
}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item label="图片" name="imageUrl">
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={values?.imageUrl}
|
||||
count={1}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleForm;
|
||||
239
src/pages/config/banner/index.tsx
Normal file
239
src/pages/config/banner/index.tsx
Normal file
@@ -0,0 +1,239 @@
|
||||
import { bannerUpdate, bannerPage, bannerAdd, bannerDelete } from '@/services/config/banner';
|
||||
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal, Image } from 'antd';
|
||||
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
fields.status = fields.status ? 0 : 1
|
||||
await bannerAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await bannerUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await bannerDelete(params);
|
||||
hide();
|
||||
message.success('删除成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const MenuTableList: React.FC = () => {
|
||||
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
|
||||
const intRow = {
|
||||
"id": 1,
|
||||
"imageUrl": "",
|
||||
"sortOrder": 0,
|
||||
"status": 0,
|
||||
"title": "",
|
||||
"jumpUrl": ""
|
||||
}
|
||||
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [currentRow, setCurrentRow] = useState(intRow);
|
||||
const access = useAccess();
|
||||
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'title',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '图片',
|
||||
dataIndex: 'imageUrl',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
return <Image src={record.imageUrl} width={60}></Image>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'orderNum',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '跳转路径',
|
||||
dataIndex: 'jumpUrl',
|
||||
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"
|
||||
hidden={!access.hasPerms('admin/banner/update')}
|
||||
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 handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.System.Menu>
|
||||
headerTitle={'菜单管理'}
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="menuList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await bannerPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...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 MenuTableList;
|
||||
421
src/pages/config/config/index.tsx
Normal file
421
src/pages/config/config/index.tsx
Normal file
@@ -0,0 +1,421 @@
|
||||
import {
|
||||
PageContainer,
|
||||
ProForm,
|
||||
ProFormText,
|
||||
ProFormDigit
|
||||
} from '@ant-design/pro-components';
|
||||
import { Card, message, Tabs, InputNumber, TimePicker } from 'antd';
|
||||
import type { FC } from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { config, configUpdate } from '@/services/config/index'
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
|
||||
|
||||
const BasicForm: FC<Record<string, any>> = () => {
|
||||
const [dataValue, setDateValue] = useState<any>([])
|
||||
const [activeKey, setActiveKey] = useState('DEPOSIT');
|
||||
// const [startTime, setStartTime] = useState('');
|
||||
useEffect(() => {
|
||||
config().then(({ data }) => {
|
||||
configName(data)
|
||||
})
|
||||
}, []);
|
||||
|
||||
const onChange = (newActiveKey: string) => {
|
||||
setActiveKey(newActiveKey);
|
||||
};
|
||||
|
||||
|
||||
const onFinish = async (values: Record<string, any>) => {
|
||||
if (values.beginTime) {
|
||||
values.beginTime = dayjs(values.beginTime).format('HH:mm')
|
||||
}
|
||||
try {
|
||||
await configUpdate(values);
|
||||
message.success('配置成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
message.error('配置失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const configName = (data) => {
|
||||
setDateValue(data.map((i) => {
|
||||
let data = null
|
||||
let configValue = JSON.parse(i.configValue)
|
||||
let value = {
|
||||
id: i.id,
|
||||
configName: i.configName,
|
||||
configKey: i.configKey,
|
||||
...configValue
|
||||
}
|
||||
|
||||
if (i.configKey == 'DEPOSIT') {
|
||||
data = <ProForm
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxWidth: 600,
|
||||
}}
|
||||
layout="vertical"
|
||||
initialValues={value}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="最低充值"
|
||||
name={'minMoney'}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
}
|
||||
|
||||
|
||||
if (i.configKey == 'WITHDRAWAL') {
|
||||
data = <ProForm
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxWidth: 600,
|
||||
}}
|
||||
layout="vertical"
|
||||
initialValues={value}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="最低提现"
|
||||
name={'minMoney'}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="提现手续费"
|
||||
name={'free'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
addonAfter="%"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="日限额"
|
||||
name={'dayQuota'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="月限额"
|
||||
name={'monthQuota'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="年龄限制(男)"
|
||||
name={'ageMan'}
|
||||
>
|
||||
<InputNumber
|
||||
max={65}
|
||||
addonAfter="岁"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="年龄限制(女)"
|
||||
name={'ageGirl'}
|
||||
>
|
||||
<InputNumber
|
||||
max={65}
|
||||
addonAfter="岁"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
}
|
||||
|
||||
|
||||
if (i.configKey == 'SENSITIVITY') {
|
||||
data = <ProForm
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxWidth: 600,
|
||||
}}
|
||||
layout="vertical"
|
||||
initialValues={value}
|
||||
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="手机号(接收验证码)"
|
||||
name={'mobile'}
|
||||
>
|
||||
<ProFormText />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
}
|
||||
|
||||
if (i.configKey == 'PILE') {
|
||||
data = <ProForm
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxWidth: 600,
|
||||
}}
|
||||
layout="vertical"
|
||||
initialValues={value}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="收益"
|
||||
name={'earnings'}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="上下浮动"
|
||||
name={'earingsFloat'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
addonAfter="%"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="交付时间"
|
||||
name={'deliveryDay'}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
addonAfter="天"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="基本收益"
|
||||
name={'earningsBase'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm >
|
||||
}
|
||||
|
||||
if (i.configKey == 'MAINTAIN') {
|
||||
if (value.beginTime) {
|
||||
const time = dayjs(value.beginTime, 'HH:mm');
|
||||
value.beginTime = time
|
||||
}
|
||||
|
||||
|
||||
data = <ProForm
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxWidth: 600,
|
||||
}}
|
||||
layout="vertical"
|
||||
initialValues={value}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="开始时间"
|
||||
name={'beginTime'}
|
||||
>
|
||||
<TimePicker format={'HH:mm'} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="持续时间"
|
||||
name={'continueTime'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
addonAfter="分钟"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm >
|
||||
}
|
||||
|
||||
|
||||
if (i.configKey == 'DISTRIBUTION') {
|
||||
data = <ProForm
|
||||
style={{
|
||||
marginTop: 8,
|
||||
maxWidth: 600,
|
||||
}}
|
||||
layout="vertical"
|
||||
initialValues={value}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="直推"
|
||||
name={'directPush'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
addonAfter="%"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="间推"
|
||||
name={'inDirectPush'}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
addonAfter="%"
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm >
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
key: i.configKey,
|
||||
label: i.configName,
|
||||
children: data,
|
||||
// closable: true
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
// const add = () => {
|
||||
// let data = JSON.parse(JSON.stringify(dataValue))
|
||||
// data.push(
|
||||
// {
|
||||
// key: 'IENVJJD',
|
||||
// label: '测试',
|
||||
// children: `Content of Tab Pane`,
|
||||
// closable: true
|
||||
// }
|
||||
// )
|
||||
// setDateValue(data)
|
||||
// setActiveKey('IENVJJD');
|
||||
// };
|
||||
|
||||
|
||||
// const remove = (targetKey) => {
|
||||
// console.log(targetKey,'targetKey');
|
||||
|
||||
// let data = JSON.parse(JSON.stringify(dataValue))
|
||||
// let index = data.findIndex(val => val.key === targetKey);
|
||||
// if (index !== -1) {
|
||||
// data.splice(index, 1);
|
||||
// } else {
|
||||
// return
|
||||
// }
|
||||
// console.log(data);
|
||||
|
||||
// setDateValue(data)
|
||||
// console.log(data[data.length - 1].key);
|
||||
// setActiveKey(data[data.length - 1].key);
|
||||
// };
|
||||
|
||||
|
||||
// const onEdit = (
|
||||
// targetKey: React.MouseEvent | React.KeyboardEvent | string,
|
||||
// action: 'add' | 'remove',
|
||||
// ) => {
|
||||
// if (action === 'add') {
|
||||
// add();
|
||||
// } else {
|
||||
// remove(targetKey);
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<Card bordered={false}>
|
||||
<Tabs
|
||||
type="card"
|
||||
onChange={onChange}
|
||||
items={dataValue}
|
||||
// onEdit={onEdit}
|
||||
activeKey={activeKey}
|
||||
/>
|
||||
</Card>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
export default BasicForm;
|
||||
73
src/pages/facility/serviceCharge/index.tsx
Normal file
73
src/pages/facility/serviceCharge/index.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { deviceAmountPage } from '@/services/facility/index';
|
||||
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '服务费金额',
|
||||
dataIndex: 'amout',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '服务费产生日期',
|
||||
dataIndex: 'incomeDay',
|
||||
valueType: 'dateRange',
|
||||
search: {
|
||||
transform: (value) => {
|
||||
return {
|
||||
incomeDayBegin: value[0],
|
||||
incomeDayEnd: value[1],
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await deviceAmountPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
162
src/pages/finance/earnings/index.tsx
Normal file
162
src/pages/finance/earnings/index.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import { variationPage } from '@/services/finance/index';
|
||||
|
||||
|
||||
import React, { useRef, useEffect,useState } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Select } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
import moneyType from '@/assets/json/moneyType.json'
|
||||
import { usersPage } from '@/services/user/user';
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const [shopOption, setShopOption] = useState([]);
|
||||
|
||||
const searchShop = async (value) => {
|
||||
const { success, data } = await usersPage({
|
||||
userName: value
|
||||
});
|
||||
data.records.map((i) => {
|
||||
i.userName = i.userName + ' ' + i.phone
|
||||
})
|
||||
setShopOption(data.records)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
dataIndex: 'userId',
|
||||
valueType: 'text',
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="请输入用户名"
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
onSearch={
|
||||
(e) => {
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
searchShop(e);
|
||||
}
|
||||
}
|
||||
fieldNames={{
|
||||
label: "userName",
|
||||
value: "id"
|
||||
}}
|
||||
options={shopOption}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '记录名称',
|
||||
dataIndex: 'recordName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '当前积分余额',
|
||||
dataIndex: 'curPoints',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '积分变动',
|
||||
dataIndex: 'points',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
// {
|
||||
// title: '来源',
|
||||
// dataIndex: 'sourceType',
|
||||
// valueType: 'text',
|
||||
// search: false,
|
||||
// },
|
||||
{
|
||||
title: '记录类型',
|
||||
dataIndex: 'type',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let list = moneyType
|
||||
return list.find((val) => val.id == record.sourceType)?.name
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
fieldNames={{
|
||||
label: "name",
|
||||
value: "id"
|
||||
}}
|
||||
options={moneyType}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'dateRange',
|
||||
search: {
|
||||
transform: (value) => {
|
||||
return {
|
||||
createTimeBegin: value[0],
|
||||
createTimeEnd: value[1],
|
||||
};
|
||||
},
|
||||
},
|
||||
render: (_, record) => {
|
||||
return record?.createTime
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
defaultCollapsed: false,
|
||||
}}
|
||||
request={async (params, sorter, filter) => {
|
||||
params.pointType = 1
|
||||
|
||||
let { data } = await variationPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
72
src/pages/finance/integral/index.tsx
Normal file
72
src/pages/finance/integral/index.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { integralPage } from '@/services/finance/index';
|
||||
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
dataIndex: 'userId',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '钱包',
|
||||
dataIndex: 'points2',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'updateTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await integralPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
162
src/pages/finance/variation/index.tsx
Normal file
162
src/pages/finance/variation/index.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import { variationPage } from '@/services/finance/index';
|
||||
|
||||
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Select } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable, ProFormSelect } from '@ant-design/pro-components';
|
||||
import moneyType from '@/assets/json/moneyType.json'
|
||||
import { usersPage } from '@/services/user/user';
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [shopOption, setShopOption] = useState([]);
|
||||
|
||||
const searchShop = async (value) => {
|
||||
const { success, data } = await usersPage({
|
||||
userName: value
|
||||
});
|
||||
data.records.map((i) => {
|
||||
i.userName = i.userName + ' ' + i.phone
|
||||
})
|
||||
setShopOption(data.records)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
dataIndex: 'userId',
|
||||
valueType: 'text',
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="请输入用户名"
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
onSearch={
|
||||
(e) => {
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
searchShop(e);
|
||||
}
|
||||
}
|
||||
fieldNames={{
|
||||
label: "userName",
|
||||
value: "id"
|
||||
}}
|
||||
options={shopOption}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '记录名称',
|
||||
dataIndex: 'recordName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '当前积分余额',
|
||||
dataIndex: 'curPoints',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '积分变动',
|
||||
dataIndex: 'points',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
// {
|
||||
// title: '来源',
|
||||
// dataIndex: 'sourceType',
|
||||
// valueType: 'text',
|
||||
// search: false,
|
||||
// },
|
||||
{
|
||||
title: '记录类型',
|
||||
dataIndex: 'type',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let list = moneyType
|
||||
return list.find((val) => val.id == record.sourceType)?.name
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
fieldNames={{
|
||||
label: "name",
|
||||
value: "id"
|
||||
}}
|
||||
options={moneyType}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'dateRange',
|
||||
search: {
|
||||
transform: (value) => {
|
||||
return {
|
||||
createTimeBegin: value[0],
|
||||
createTimeEnd: value[1],
|
||||
};
|
||||
},
|
||||
},
|
||||
render: (_, record) => {
|
||||
return record?.createTime
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
defaultCollapsed: false,
|
||||
}}
|
||||
request={async (params, sorter, filter) => {
|
||||
params.pointType = 2
|
||||
let { data } = await variationPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
48
src/pages/finance/withdraw/components/TempFormModal.tsx
Normal file
48
src/pages/finance/withdraw/components/TempFormModal.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* @Note:
|
||||
* @Author: 2058827620@qq.com
|
||||
* @Date: 2022-04-03 17:02:15
|
||||
*/
|
||||
|
||||
import { Divider, Modal, Upload, Button, UploadProps, UploadFile } from 'antd';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { DownloadOutlined,UploadOutlined } from '@ant-design/icons';
|
||||
|
||||
export default ({ modalOpenState, onModalOpenState, onSubmit }) => {
|
||||
|
||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const props: UploadProps = {
|
||||
onRemove: (file) => {
|
||||
const index = fileList.indexOf(file);
|
||||
const newFileList = fileList.slice();
|
||||
newFileList.splice(index, 1);
|
||||
setFileList(newFileList);
|
||||
},
|
||||
beforeUpload: (file) => {
|
||||
setFileList([...fileList, file]);
|
||||
|
||||
return false;
|
||||
},
|
||||
fileList,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal title="导入提现结果" open={modalOpenState} onOk={onSubmit} footer={(_, { OkBtn, CancelBtn }) => (
|
||||
<>
|
||||
<OkBtn />
|
||||
</>
|
||||
)}>
|
||||
<div className='flex'>
|
||||
<Upload className='mr-6' {...props}>
|
||||
<Button icon={<UploadOutlined />}>选择文件</Button>
|
||||
</Upload>
|
||||
<Button icon={<DownloadOutlined />}>下载模板</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
249
src/pages/finance/withdraw/index.tsx
Normal file
249
src/pages/finance/withdraw/index.tsx
Normal file
@@ -0,0 +1,249 @@
|
||||
import { withdrawPage } from '@/services/finance/index';
|
||||
import { usersPage } from '@/services/user/user';
|
||||
|
||||
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Select, Button, Upload } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable, ProFormSelect } from '@ant-design/pro-components';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import TempFormModal from './components/TempFormModal';
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [tempFormModal, setTempFormModal] = useState(false);
|
||||
|
||||
const [shopOption, setShopOption] = useState([]);
|
||||
|
||||
const searchShop = async (value) => {
|
||||
const { success, data } = await usersPage({
|
||||
userName: value
|
||||
});
|
||||
data.records.map((i) => {
|
||||
i.userName = i.userName + ' ' + i.phone
|
||||
})
|
||||
|
||||
setShopOption(data.records)
|
||||
}
|
||||
|
||||
|
||||
const handleTemp = async (fields) => {
|
||||
return
|
||||
const { success } = await userTransferHanging(fields);
|
||||
if (success) {
|
||||
message.success('导入成功');
|
||||
actionRef.current?.reload();
|
||||
setTempFormModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户信息',
|
||||
dataIndex: 'userId',
|
||||
valueType: 'text',
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="请输入用户名"
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
onSearch={
|
||||
(e) => {
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
searchShop(e);
|
||||
}
|
||||
}
|
||||
fieldNames={{
|
||||
label: "userName",
|
||||
value: "id"
|
||||
}}
|
||||
options={shopOption}
|
||||
/>
|
||||
// <div className='w-52'>
|
||||
// <ProFormSelect
|
||||
// name="formUserId"
|
||||
// fieldProps={{
|
||||
// fieldNames: {
|
||||
// label: "userName",
|
||||
// value: "id"
|
||||
// },
|
||||
// placeholder: "请输入用户名",
|
||||
// showSearch: true
|
||||
// }}
|
||||
// request={async (param) => {
|
||||
// const { success, data } = await usersPage({
|
||||
// userName: param.keyWords
|
||||
// });
|
||||
// data.records.map((i) => {
|
||||
// i.userName = i.userName + ' ' + i.phone
|
||||
// })
|
||||
// return data.records;
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'orderNo',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '提取积分',
|
||||
dataIndex: 'points',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '收益',
|
||||
dataIndex: 'points1',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
valueType: 'text',
|
||||
render: (_: any, record: any) => {
|
||||
let color = record.status == 0 ? 'orange' : record.status == 1 ? 'green' : record.status == -1 ? 'red' : ''
|
||||
return <Tag color={color}>{record.status == 0 ? '申请中' : record.status == 1 ? '提现成功' : record.status == -1 ? '失败' : ''}</Tag>
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
const statusMap = [
|
||||
{ label: '申请中', value: 0 },
|
||||
{ label: '提现成功', value: 1 },
|
||||
{ label: '失败', value: -1 },
|
||||
]
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
fieldNames={{
|
||||
label: "label",
|
||||
value: "value"
|
||||
}}
|
||||
options={statusMap}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '提现信息',
|
||||
dataIndex: 'type',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_: any, record: any) => {
|
||||
let color = record.type == 1 ? 'cyan' : record.type == 2 ? 'red' : ''
|
||||
return <div>
|
||||
<div>
|
||||
<Tag color={color}>{record.type == 1 ? '个人' : record.type == 2 ? '企业' : ''}</Tag>
|
||||
{record.name}
|
||||
</div>
|
||||
<div hidden={record.type == 2}>
|
||||
身份证号:{record.idCard}
|
||||
</div>
|
||||
<div>
|
||||
银行卡号:{record.bankCard}
|
||||
</div>
|
||||
<div>
|
||||
开户行:{record.idCard}
|
||||
</div>
|
||||
<div>
|
||||
开户支行:{record.idCard}
|
||||
</div>
|
||||
<div hidden={record.type == 1}>
|
||||
纳税人识别号:{record.taxNo}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '完成时间',
|
||||
dataIndex: 'completeTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '处理结果',
|
||||
dataIndex: 'handleMessage',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'dateRange',
|
||||
search: {
|
||||
transform: (value) => {
|
||||
return {
|
||||
createTimeBegin: value[0],
|
||||
createTimeEnd: value[1],
|
||||
};
|
||||
},
|
||||
},
|
||||
render: (_, record) => {
|
||||
return record?.createTime
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<TempFormModal
|
||||
modalOpenState={tempFormModal}
|
||||
onModalOpenState={setTempFormModal}
|
||||
onSubmit={handleTemp}
|
||||
/>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
defaultCollapsed: false,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button type="primary" onClick={() => {
|
||||
window.location.href = exportLink(searchParams, '/admin/withdraw/export1');
|
||||
}}>导出提现申请</Button>,
|
||||
<Button icon={<UploadOutlined />} onClick={() => {
|
||||
setTempFormModal(true)
|
||||
}}>导入提现结果</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await withdrawPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
251
src/pages/goods/$id/create.tsx
Normal file
251
src/pages/goods/$id/create.tsx
Normal file
@@ -0,0 +1,251 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { InputNumber, Space, message, Popconfirm } from 'antd';
|
||||
import {
|
||||
PageContainer,
|
||||
ProForm,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
ProFormSwitch,
|
||||
ProFormSelect,
|
||||
ProFormRadio
|
||||
} from '@ant-design/pro-components';
|
||||
|
||||
import { useParams } from '@umijs/max';
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
import { ContentUtils } from 'braft-utils'
|
||||
|
||||
|
||||
import { goodsAdd } from '@/services/goods/index';
|
||||
|
||||
|
||||
// 引入编辑器组件
|
||||
import BraftEditor from 'braft-editor'
|
||||
// 引入编辑器样式
|
||||
import 'braft-editor/dist/index.css'
|
||||
|
||||
|
||||
|
||||
|
||||
// import services from '@/services/admin';
|
||||
//
|
||||
// const { articleCreate, articleUpdate, articleItem } = services.ArticleController;
|
||||
// const { articleCategoryItems } = services.ArticleCategoryController;
|
||||
|
||||
export default () => {
|
||||
const params = useParams();
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(''))
|
||||
|
||||
const actionRef = useRef();
|
||||
|
||||
const initRow = {
|
||||
"goodsName": "",
|
||||
"itemType": "",
|
||||
"salePrice": '',
|
||||
"profit": '',
|
||||
"picture": "",
|
||||
"details": "",
|
||||
"stock": '',
|
||||
"sales": '',
|
||||
"isUp": 1
|
||||
};
|
||||
|
||||
const [row, setRow] = useState(initRow);
|
||||
|
||||
useEffect(() => {
|
||||
// articleItem(params).then(({ data }) => {
|
||||
// if (data) {
|
||||
// setRow(data);
|
||||
// }
|
||||
// });
|
||||
}, []);
|
||||
|
||||
|
||||
const handleCreate = async (fields) => {
|
||||
setRow(fields);
|
||||
let data = JSON.parse(JSON.stringify(fields))
|
||||
// data.details = editorState.toHTML()
|
||||
data.picture = JSON.stringify(data.picture)
|
||||
data.details = JSON.stringify(data.details)
|
||||
const { success } = await goodsAdd({ ...data });
|
||||
if (success) {
|
||||
message.success('添加成功')
|
||||
setRow(initRow);
|
||||
//history.push('article/0/create');
|
||||
history.back();
|
||||
}
|
||||
};
|
||||
|
||||
const controls = [
|
||||
'undo', 'redo', 'separator',
|
||||
'font-size', 'line-height', 'letter-spacing', 'separator',
|
||||
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
|
||||
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
|
||||
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
|
||||
'link', 'separator', 'hr', 'separator',
|
||||
'clear'
|
||||
]
|
||||
|
||||
|
||||
const onChange = async (e) => {
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.3g2'];
|
||||
// 将URL转换为小写,以便进行不区分大小写的比较
|
||||
const lowerCaseUrl = e.toLowerCase();
|
||||
// 检查链接是否以任何一个视频扩展名结尾
|
||||
if (videoExtensions.some(e => lowerCaseUrl.endsWith(e))) {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{
|
||||
type: 'VIDEO',
|
||||
url: e
|
||||
}]));
|
||||
} else {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{ type: 'IMAGE', url: e, },]))
|
||||
}
|
||||
}
|
||||
|
||||
const extendControls = [
|
||||
'separator',
|
||||
{
|
||||
key: 'FilesManagerImage', // 控件唯一标识,必传
|
||||
title: '上传图片/视频', // 指定鼠标悬停提示文案
|
||||
html: null, // 指定在按钮中渲染的html字符串
|
||||
text: <FilesManager
|
||||
fileType="image"
|
||||
mode=""
|
||||
imagesShow={false}
|
||||
onChange={onChange}
|
||||
count={1}
|
||||
/>, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
onClick: () => {
|
||||
console.log('Hello World!');
|
||||
},
|
||||
},
|
||||
// 'separator',
|
||||
// {
|
||||
// key: 'FilesManagerVideo', // 控件唯一标识,必传
|
||||
// title: '上传视频', // 指定鼠标悬停提示文案
|
||||
// html: null, // 指定在按钮中渲染的html字符串
|
||||
// text: <FilesManager
|
||||
// fileType="video"
|
||||
// mode=""
|
||||
// imagesShow={false}
|
||||
// onChange={onChange}
|
||||
// count={1}
|
||||
// />, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
// onClick: () => {
|
||||
// console.log('Hello World!');
|
||||
// },
|
||||
// }
|
||||
]
|
||||
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '商品添加',
|
||||
}}
|
||||
ghost
|
||||
>
|
||||
<>
|
||||
<ProForm
|
||||
initialValues={row}
|
||||
onFinish={(values) => {
|
||||
handleCreate(values)
|
||||
}}
|
||||
>
|
||||
<ProFormTextArea
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="goodsName"
|
||||
label="商品名称"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProFormText
|
||||
width={300}
|
||||
name="itemType"
|
||||
label="功率"
|
||||
placeholder="功率"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="销售价格"
|
||||
name={'salePrice'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="商品利润"
|
||||
name={'profit'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="库存"
|
||||
name={'stock'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="销量"
|
||||
name={'sales'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
|
||||
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="主图"
|
||||
name="picture"
|
||||
tooltip="商品的封面图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.picture}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProFormSwitch
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
checkedChildren="上架"
|
||||
unCheckedChildren="下架"
|
||||
name="isUp"
|
||||
label="是否上架"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="详情图"
|
||||
name="details"
|
||||
tooltip="商品的详情图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.details}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
{/* <ProForm.Item
|
||||
label="富文本"
|
||||
name="details"
|
||||
tooltip="自定义详情内容,可插入图片、文本样式"
|
||||
>
|
||||
<div className="border-solid border-2 border-indigo-600">
|
||||
<BraftEditor value={editorState} controls={controls} extendControls={extendControls} onChange={(val) => {
|
||||
setEditorState(val)
|
||||
}} />
|
||||
</div>
|
||||
</ProForm.Item> */}
|
||||
</ProForm>
|
||||
</>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
265
src/pages/goods/$id/update.tsx
Normal file
265
src/pages/goods/$id/update.tsx
Normal file
@@ -0,0 +1,265 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { InputNumber, Space, message, Popconfirm } from 'antd';
|
||||
import {
|
||||
PageContainer,
|
||||
ProForm,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
ProFormSwitch,
|
||||
ProFormSelect,
|
||||
ProFormRadio,
|
||||
ProFormDigit
|
||||
} from '@ant-design/pro-components';
|
||||
|
||||
import { useParams } from '@umijs/max';
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
import { ContentUtils } from 'braft-utils'
|
||||
|
||||
|
||||
import { goodsAdd, goodsInfo, goodsUpdate } from '@/services/goods/index';
|
||||
|
||||
|
||||
// 引入编辑器组件
|
||||
import BraftEditor from 'braft-editor'
|
||||
// 引入编辑器样式
|
||||
import 'braft-editor/dist/index.css'
|
||||
|
||||
|
||||
|
||||
|
||||
// import services from '@/services/admin';
|
||||
//
|
||||
// const { articleCreate, articleUpdate, articleItem } = services.ArticleController;
|
||||
// const { articleCategoryItems } = services.ArticleCategoryController;
|
||||
|
||||
export default () => {
|
||||
const params = useParams();
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(''))
|
||||
|
||||
const actionRef = useRef();
|
||||
|
||||
const initRow = {
|
||||
"goodsName": "",
|
||||
"itemType": "",
|
||||
"salePrice": '',
|
||||
"profit": '',
|
||||
"picture": "",
|
||||
"details": "",
|
||||
"stock": '',
|
||||
"sales": '',
|
||||
"isUp": 1
|
||||
};
|
||||
|
||||
const [row, setRow] = useState(initRow);
|
||||
|
||||
useEffect(() => {
|
||||
goodsInfo(params).then(({ data }) => {
|
||||
if (data) {
|
||||
data.picture = JSON.parse(data.picture)
|
||||
data.details = JSON.parse(data.details)
|
||||
console.log(data, data);
|
||||
setRow(data);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
const handleCreate = async (fields) => {
|
||||
setRow(fields);
|
||||
let data = JSON.parse(JSON.stringify(fields))
|
||||
// data.details = editorState.toHTML()
|
||||
data.picture = JSON.stringify(data.picture)
|
||||
data.details = JSON.stringify(data.details)
|
||||
const { success } = await goodsUpdate({ ...data });
|
||||
if (success) {
|
||||
message.success('修改成功')
|
||||
setRow(initRow);
|
||||
history.back();
|
||||
}
|
||||
};
|
||||
|
||||
const controls = [
|
||||
'undo', 'redo', 'separator',
|
||||
'font-size', 'line-height', 'letter-spacing', 'separator',
|
||||
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
|
||||
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
|
||||
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
|
||||
'link', 'separator', 'hr', 'separator',
|
||||
'clear'
|
||||
]
|
||||
|
||||
|
||||
const onChange = async (e) => {
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.3g2'];
|
||||
// 将URL转换为小写,以便进行不区分大小写的比较
|
||||
const lowerCaseUrl = e.toLowerCase();
|
||||
// 检查链接是否以任何一个视频扩展名结尾
|
||||
if (videoExtensions.some(e => lowerCaseUrl.endsWith(e))) {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{
|
||||
type: 'VIDEO',
|
||||
url: e
|
||||
}]));
|
||||
} else {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{ type: 'IMAGE', url: e, },]))
|
||||
}
|
||||
}
|
||||
|
||||
const extendControls = [
|
||||
'separator',
|
||||
{
|
||||
key: 'FilesManagerImage', // 控件唯一标识,必传
|
||||
title: '上传图片/视频', // 指定鼠标悬停提示文案
|
||||
html: null, // 指定在按钮中渲染的html字符串
|
||||
text: <FilesManager
|
||||
fileType="image"
|
||||
mode=""
|
||||
imagesShow={false}
|
||||
onChange={onChange}
|
||||
count={1}
|
||||
/>, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
onClick: () => {
|
||||
console.log('Hello World!');
|
||||
},
|
||||
},
|
||||
// 'separator',
|
||||
// {
|
||||
// key: 'FilesManagerVideo', // 控件唯一标识,必传
|
||||
// title: '上传视频', // 指定鼠标悬停提示文案
|
||||
// html: null, // 指定在按钮中渲染的html字符串
|
||||
// text: <FilesManager
|
||||
// fileType="video"
|
||||
// mode=""
|
||||
// imagesShow={false}
|
||||
// onChange={onChange}
|
||||
// count={1}
|
||||
// />, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
// onClick: () => {
|
||||
// console.log('Hello World!');
|
||||
// },
|
||||
// }
|
||||
]
|
||||
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '商品添加',
|
||||
}}
|
||||
ghost
|
||||
>
|
||||
<>
|
||||
<ProForm
|
||||
// initialValues={row}
|
||||
onFinish={(values) => {
|
||||
handleCreate(values)
|
||||
}}
|
||||
request={async () => {
|
||||
const { data } = await goodsInfo(params)
|
||||
data.picture = JSON.parse(data.picture)
|
||||
data.details = JSON.parse(data.details)
|
||||
return data
|
||||
}}
|
||||
>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProFormTextArea
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="goodsName"
|
||||
label="商品名称"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProFormText
|
||||
width={300}
|
||||
name="itemType"
|
||||
label="功率"
|
||||
placeholder="功率"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="销售价格"
|
||||
name={'salePrice'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="商品利润"
|
||||
name={'profit'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="库存"
|
||||
name={'stock'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="销量"
|
||||
name={'sales'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
|
||||
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="主图"
|
||||
name="picture"
|
||||
tooltip="商品的封面图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.picture}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProFormSwitch
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
checkedChildren="上架"
|
||||
unCheckedChildren="下架"
|
||||
name="isUp"
|
||||
label="是否上架"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="详情图"
|
||||
name="details"
|
||||
tooltip="商品的详情图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.details}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
{/* <ProForm.Item
|
||||
label="富文本"
|
||||
name="details"
|
||||
tooltip="自定义详情内容,可插入图片、文本样式"
|
||||
>
|
||||
<div className="border-solid border-2 border-indigo-600">
|
||||
<BraftEditor value={editorState} controls={controls} extendControls={extendControls} onChange={(val) => {
|
||||
setEditorState(val)
|
||||
}} />
|
||||
</div>
|
||||
</ProForm.Item> */}
|
||||
</ProForm>
|
||||
</>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
162
src/pages/goods/index.tsx
Normal file
162
src/pages/goods/index.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import { goodsPage, goodsUpdate } from '@/services/goods/index';
|
||||
import { history } from '@umijs/max';
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Button, Modal, Image, Switch } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
|
||||
|
||||
const GoodsTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
dataIndex: 'goodsName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '功率',
|
||||
dataIndex: 'itemType',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '销售价格',
|
||||
dataIndex: 'salePrice',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '利润',
|
||||
dataIndex: 'profit',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '库存',
|
||||
dataIndex: 'stock',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '销量',
|
||||
dataIndex: 'sales',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '商品主图',
|
||||
dataIndex: 'picture',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
let _Img = record.picture ? JSON.parse(record.picture)[0] : ''
|
||||
return _Img ? <Image src={_Img} width={50}></Image> : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否上架',
|
||||
dataIndex: 'isUp',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
let onChange = async (e) => {
|
||||
let data = JSON.parse(JSON.stringify(record))
|
||||
data.isUp = e ? 1 : 0
|
||||
let _res = await goodsUpdate(data)
|
||||
message.success(e ? '上架成功' : '下架成功');
|
||||
}
|
||||
return <Switch checkedChildren="上架" value={record.isUp == 1 ? true : false} unCheckedChildren="下架" defaultChecked onChange={onChange} />
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
width: '220px',
|
||||
valueType: 'option',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
history.push(`goods/${record.id}/update`);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
key="api/sys/menu/deleteBatchByIds"
|
||||
onClick={async () => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const success = await handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="goodsList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
history.push('goods/0/create');
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await goodsPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoodsTableList;
|
||||
378
src/pages/goodsSetMeal/$id/create.tsx
Normal file
378
src/pages/goodsSetMeal/$id/create.tsx
Normal file
@@ -0,0 +1,378 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { InputNumber, Space, message, Popconfirm, Transfer, Image, Table } from 'antd';
|
||||
import {
|
||||
PageContainer,
|
||||
ProForm,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
ProFormSwitch,
|
||||
ProFormSelect,
|
||||
ProFormRadio,
|
||||
ProTable
|
||||
} from '@ant-design/pro-components';
|
||||
|
||||
import { useParams } from '@umijs/max';
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
import { ContentUtils } from 'braft-utils'
|
||||
import TableTransfer from '@/components/TableTransfer';
|
||||
|
||||
|
||||
|
||||
import { goodspackagesAdd, goodspackagesPage } from '@/services/goods/setMeal';
|
||||
import { goodsPage } from '@/services/goods/index';
|
||||
|
||||
|
||||
// 引入编辑器组件
|
||||
import BraftEditor from 'braft-editor'
|
||||
// 引入编辑器样式
|
||||
import 'braft-editor/dist/index.css'
|
||||
|
||||
|
||||
|
||||
|
||||
// import services from '@/services/admin';
|
||||
//
|
||||
// const { articleCreate, articleUpdate, articleItem } = services.ArticleController;
|
||||
// const { articleCategoryItems } = services.ArticleCategoryController;
|
||||
|
||||
export default () => {
|
||||
const params = useParams();
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(''))
|
||||
const [goodsList, setGoodsList] = useState([])
|
||||
const [goodsNum, setGoodsNum] = useState([])
|
||||
|
||||
const actionRef = useRef();
|
||||
|
||||
const initRow = {
|
||||
"packagesName": "",
|
||||
"salePrice": '',
|
||||
"profit": '',
|
||||
"picture": "",
|
||||
"details": "",
|
||||
"goodsDataList": [],
|
||||
"isUp": 1
|
||||
};
|
||||
|
||||
|
||||
// const TableTransfer = (props) => {
|
||||
// const { leftColumns, rightColumns, ...restProps } = props;
|
||||
// return (
|
||||
// <Transfer
|
||||
// style={{
|
||||
// width: '100%',
|
||||
// }}
|
||||
// {...restProps}
|
||||
// >
|
||||
// {({
|
||||
// direction,
|
||||
// filteredItems,
|
||||
// onItemSelect,
|
||||
// onItemSelectAll,
|
||||
// selectedKeys: listSelectedKeys,
|
||||
// disabled: listDisabled,
|
||||
// }) => {
|
||||
// const columns = direction === 'left' ? leftColumns : rightColumns;
|
||||
// const rowSelection = {
|
||||
// getCheckboxProps: () => ({
|
||||
// disabled: listDisabled,
|
||||
// }),
|
||||
// onChange(selectedRowKeys, selectedRows) {
|
||||
// onItemSelectAll(selectedRowKeys, 'replace');
|
||||
|
||||
// },
|
||||
// selectedRowKeys: listSelectedKeys,
|
||||
// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, Table.SELECTION_NONE],
|
||||
// };
|
||||
// return (
|
||||
// <Table
|
||||
// rowSelection={rowSelection}
|
||||
// columns={columns}
|
||||
// dataSource={filteredItems}
|
||||
// size="small"
|
||||
// style={{
|
||||
// pointerEvents: listDisabled ? 'none' : undefined,
|
||||
// }}
|
||||
// rowKey={'id'}
|
||||
// onRow={({ key, disabled: itemDisabled }) => ({
|
||||
// onClick: () => {
|
||||
// if (itemDisabled || listDisabled) {
|
||||
// return;
|
||||
// }
|
||||
// onItemSelect(key, !listSelectedKeys.includes(key));
|
||||
// },
|
||||
// })}
|
||||
// />
|
||||
// );
|
||||
// }}
|
||||
// </Transfer>
|
||||
// );
|
||||
// };
|
||||
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '商品名称',
|
||||
dataIndex: 'goodsName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '销售价格',
|
||||
dataIndex: 'salePrice',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '利润',
|
||||
dataIndex: 'profit',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '商品主图',
|
||||
dataIndex: 'picture',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
let _Img = record.picture ? JSON.parse(record.picture)[0] : ''
|
||||
return _Img ? <Image src={_Img} width={50}></Image> : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'goodsNum',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record, index) => {
|
||||
record.goodsNum = 123
|
||||
let onNum = (e) => {
|
||||
let list = JSON.parse(JSON.stringify(goodsList))
|
||||
let find = list.find((val) => val.id == record.id)
|
||||
find.goodsNum = e
|
||||
setGoodsList(list)
|
||||
}
|
||||
return <InputNumber value={goodsList.find((val) => val.id == record.id).goodsNum} style={{ width: 200 }} onChange={onNum} />
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
const [row, setRow] = useState(initRow);
|
||||
|
||||
useEffect(() => {
|
||||
mockData()
|
||||
}, []);
|
||||
|
||||
|
||||
const handleCreate = async (fields) => {
|
||||
setRow(fields);
|
||||
let data = JSON.parse(JSON.stringify(fields))
|
||||
data.goodsDataList = data.goodsDataList.map((item) => {
|
||||
let find = goodsList.find((val) => val.id == item)
|
||||
return {
|
||||
goodsId: item,
|
||||
goodsNum: find.goodsNum
|
||||
}
|
||||
})
|
||||
data.isUp = data.isUp ? 1 : 0
|
||||
// data.details = editorState.toHTML()
|
||||
data.picture = JSON.stringify(data.picture)
|
||||
data.details = JSON.stringify(data.details)
|
||||
const { success } = await goodspackagesAdd({ ...data });
|
||||
if (success) {
|
||||
message.success('添加成功')
|
||||
setRow(initRow);
|
||||
//history.push('article/0/create');
|
||||
history.back();
|
||||
}
|
||||
};
|
||||
|
||||
const controls = [
|
||||
'undo', 'redo', 'separator',
|
||||
'font-size', 'line-height', 'letter-spacing', 'separator',
|
||||
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
|
||||
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
|
||||
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
|
||||
'link', 'separator', 'hr', 'separator',
|
||||
'clear'
|
||||
]
|
||||
|
||||
|
||||
const onChange = async (e) => {
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.3g2'];
|
||||
// 将URL转换为小写,以便进行不区分大小写的比较
|
||||
const lowerCaseUrl = e.toLowerCase();
|
||||
// 检查链接是否以任何一个视频扩展名结尾
|
||||
if (videoExtensions.some(e => lowerCaseUrl.endsWith(e))) {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{
|
||||
type: 'VIDEO',
|
||||
url: e
|
||||
}]));
|
||||
} else {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{ type: 'IMAGE', url: e, },]))
|
||||
}
|
||||
}
|
||||
|
||||
const extendControls = [
|
||||
'separator',
|
||||
{
|
||||
key: 'FilesManagerImage', // 控件唯一标识,必传
|
||||
title: '上传图片/视频', // 指定鼠标悬停提示文案
|
||||
html: null, // 指定在按钮中渲染的html字符串
|
||||
text: <FilesManager
|
||||
fileType="image"
|
||||
mode=""
|
||||
imagesShow={false}
|
||||
onChange={onChange}
|
||||
count={1}
|
||||
/>, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
onClick: () => {
|
||||
console.log('Hello World!');
|
||||
},
|
||||
},
|
||||
// 'separator',
|
||||
// {
|
||||
// key: 'FilesManagerVideo', // 控件唯一标识,必传
|
||||
// title: '上传视频', // 指定鼠标悬停提示文案
|
||||
// html: null, // 指定在按钮中渲染的html字符串
|
||||
// text: <FilesManager
|
||||
// fileType="video"
|
||||
// mode=""
|
||||
// imagesShow={false}
|
||||
// onChange={onChange}
|
||||
// count={1}
|
||||
// />, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
// onClick: () => {
|
||||
// console.log('Hello World!');
|
||||
// },
|
||||
// }
|
||||
]
|
||||
|
||||
const filterOption = (input, item) => item.id == input.id;
|
||||
|
||||
const [targetKeys, setTargetKeys] = useState([]);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const onChangeCs = (nextTargetKeys) => {
|
||||
console.log(nextTargetKeys, 'nextTargetKeys');
|
||||
console.log(goodsList.find((val) => val.id == nextTargetKeys[0]));
|
||||
|
||||
setTargetKeys(nextTargetKeys);
|
||||
};
|
||||
const toggleDisabled = (checked) => {
|
||||
setDisabled(checked);
|
||||
};
|
||||
|
||||
const mockData = async () => {
|
||||
let { data } = await goodsPage({})
|
||||
setGoodsList(data?.records)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '商品添加',
|
||||
}}
|
||||
ghost
|
||||
>
|
||||
<>
|
||||
<ProForm
|
||||
initialValues={row}
|
||||
onFinish={(values) => {
|
||||
handleCreate(values)
|
||||
}}
|
||||
>
|
||||
<ProFormTextArea
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="packagesName"
|
||||
label="套餐名称"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="销售价格"
|
||||
name={'salePrice'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="商品利润"
|
||||
name={'profit'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="主图"
|
||||
name="picture"
|
||||
tooltip="商品的封面图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.picture}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProFormSwitch
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
checkedChildren="上架"
|
||||
unCheckedChildren="下架"
|
||||
name="isUp"
|
||||
label="是否上架"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="详情图"
|
||||
name="details"
|
||||
tooltip="商品的详情图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.details}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="选择商品"
|
||||
name="goodsDataList"
|
||||
tooltip="商品的封面图"
|
||||
>
|
||||
<TableTransfer
|
||||
style={{ width: 1200 }}
|
||||
dataSource={goodsList}
|
||||
targetKeys={targetKeys}
|
||||
disabled={disabled}
|
||||
showSelectAll={false}
|
||||
onChange={onChangeCs}
|
||||
leftColumns={columns}
|
||||
rightColumns={columns}
|
||||
rowKey={(record) => record.id}
|
||||
></TableTransfer>
|
||||
|
||||
{/* <TableTransfer
|
||||
dataSource={goodsList}
|
||||
targetKeys={targetKeys}
|
||||
disabled={disabled}
|
||||
showSelectAll={false}
|
||||
onChange={onChangeCs}
|
||||
filterOption={filterOption}
|
||||
leftColumns={columns}
|
||||
rightColumns={columns}
|
||||
/> */}
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
301
src/pages/goodsSetMeal/$id/update.tsx
Normal file
301
src/pages/goodsSetMeal/$id/update.tsx
Normal file
@@ -0,0 +1,301 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { InputNumber, Space, message, Popconfirm, Transfer, Image, Table } from 'antd';
|
||||
import {
|
||||
PageContainer,
|
||||
ProForm,
|
||||
ProFormTextArea,
|
||||
ProFormSwitch,
|
||||
ProFormDigit
|
||||
} from '@ant-design/pro-components';
|
||||
|
||||
import { useParams } from '@umijs/max';
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
import { ContentUtils } from 'braft-utils'
|
||||
import TableTransfer from '@/components/TableTransfer';
|
||||
|
||||
|
||||
|
||||
import { goodspackagesUpdate, goodspackagesInfo } from '@/services/goods/setMeal';
|
||||
import { goodsPage } from '@/services/goods/index';
|
||||
|
||||
|
||||
// 引入编辑器组件
|
||||
import BraftEditor from 'braft-editor'
|
||||
// 引入编辑器样式
|
||||
import 'braft-editor/dist/index.css'
|
||||
|
||||
|
||||
|
||||
|
||||
// import services from '@/services/admin';
|
||||
//
|
||||
// const { articleCreate, articleUpdate, articleItem } = services.ArticleController;
|
||||
// const { articleCategoryItems } = services.ArticleCategoryController;
|
||||
|
||||
export default () => {
|
||||
const params = useParams();
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(''))
|
||||
const [goodsList, setGoodsList] = useState([])
|
||||
const [goodsNum, setGoodsNum] = useState([])
|
||||
|
||||
const actionRef = useRef();
|
||||
|
||||
var goodsList_new = []
|
||||
|
||||
|
||||
const initRow = {
|
||||
"packagesName": "",
|
||||
"salePrice": '',
|
||||
"profit": '',
|
||||
"picture": "",
|
||||
"details": "",
|
||||
"isUp": 1,
|
||||
"goodsDataList": []
|
||||
};
|
||||
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '商品名称',
|
||||
dataIndex: 'goodsName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '销售价格',
|
||||
dataIndex: 'salePrice',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '利润',
|
||||
dataIndex: 'profit',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '商品主图',
|
||||
dataIndex: 'picture',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
let _Img = record.picture ? JSON.parse(record.picture)[0] : ''
|
||||
return _Img ? <Image src={_Img} width={50}></Image> : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'goodsNum',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record, index) => {
|
||||
record.goodsNum = 123
|
||||
let onNum = (e) => {
|
||||
let list = JSON.parse(JSON.stringify(goodsList))
|
||||
let find = list.find((val) => val.id == record.id)
|
||||
find.goodsNum = e
|
||||
setGoodsList(list)
|
||||
}
|
||||
return <InputNumber value={goodsList.find((val) => val.id == record.id).goodsNum} style={{ width: 200 }} onChange={onNum} />
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
const [row, setRow] = useState(initRow);
|
||||
|
||||
useEffect(() => {
|
||||
// mockData()
|
||||
}, []);
|
||||
|
||||
|
||||
const handleCreate = async (fields) => {
|
||||
setRow(fields);
|
||||
let data = JSON.parse(JSON.stringify(fields))
|
||||
data.goodsDataList = data.goodsDataList.map((item) => {
|
||||
let find = goodsList.find((val) => val.id == item)
|
||||
return {
|
||||
goodsId: item,
|
||||
goodsNum: find.goodsNum
|
||||
}
|
||||
})
|
||||
data.isUp = data.isUp ? 1 : 0
|
||||
// data.details = editorState.toHTML()
|
||||
data.picture = JSON.stringify(data.picture)
|
||||
data.details = JSON.stringify(data.details)
|
||||
const { success } = await goodspackagesUpdate({ ...data });
|
||||
if (success) {
|
||||
message.success('修改成功')
|
||||
setRow(initRow);
|
||||
//history.push('article/0/create');
|
||||
history.back();
|
||||
}
|
||||
};
|
||||
|
||||
const controls = [
|
||||
'undo', 'redo', 'separator',
|
||||
'font-size', 'line-height', 'letter-spacing', 'separator',
|
||||
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
|
||||
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
|
||||
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
|
||||
'link', 'separator', 'hr', 'separator',
|
||||
'clear'
|
||||
]
|
||||
|
||||
|
||||
const onChange = async (e) => {
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.3g2'];
|
||||
// 将URL转换为小写,以便进行不区分大小写的比较
|
||||
const lowerCaseUrl = e.toLowerCase();
|
||||
// 检查链接是否以任何一个视频扩展名结尾
|
||||
if (videoExtensions.some(e => lowerCaseUrl.endsWith(e))) {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{
|
||||
type: 'VIDEO',
|
||||
url: e
|
||||
}]));
|
||||
} else {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{ type: 'IMAGE', url: e, },]))
|
||||
}
|
||||
}
|
||||
|
||||
const [targetKeys, setTargetKeys] = useState([]);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const onChangeCs = (nextTargetKeys) => {
|
||||
setTargetKeys(nextTargetKeys);
|
||||
};
|
||||
const toggleDisabled = (checked) => {
|
||||
setDisabled(checked);
|
||||
};
|
||||
|
||||
const mockData = async () => {
|
||||
let { data } = await goodsPage({})
|
||||
setGoodsList(data?.records)
|
||||
goodsList_new = data?.records
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '商品添加',
|
||||
}}
|
||||
ghost
|
||||
>
|
||||
<>
|
||||
<ProForm
|
||||
onFinish={(values) => {
|
||||
handleCreate(values)
|
||||
}}
|
||||
request={async () => {
|
||||
await mockData()
|
||||
const { data } = await goodspackagesInfo(params)
|
||||
data.picture = JSON.parse(data.picture)
|
||||
data.details = JSON.parse(data.details)
|
||||
data.goodsDataList = data.goodsPackageItemsDetailVoList.map((val) => {
|
||||
let find = goodsList_new.find((item) => item.id == val.goodsId)
|
||||
find.goodsNum = val.goodsNum
|
||||
setGoodsList(goodsList_new)
|
||||
return val.goodsId
|
||||
})
|
||||
setTargetKeys(data.goodsDataList)
|
||||
return data
|
||||
}}
|
||||
>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
<ProFormTextArea
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="packagesName"
|
||||
label="套餐名称"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="销售价格"
|
||||
name={'salePrice'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
rules={[{ required: true, message: '请输入!' }]}
|
||||
label="商品利润"
|
||||
name={'profit'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="主图"
|
||||
name="picture"
|
||||
tooltip="商品的封面图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.picture}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProFormSwitch
|
||||
checkedChildren="上架"
|
||||
unCheckedChildren="下架"
|
||||
name="isUp"
|
||||
label="是否上架"
|
||||
/>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="详情图"
|
||||
name="details"
|
||||
tooltip="商品的详情图"
|
||||
>
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={row?.details}
|
||||
count={9}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="选择商品"
|
||||
name="goodsDataList"
|
||||
tooltip="商品的封面图"
|
||||
>
|
||||
<TableTransfer
|
||||
style={{ width: 1200 }}
|
||||
dataSource={goodsList}
|
||||
targetKeys={targetKeys}
|
||||
disabled={disabled}
|
||||
showSelectAll={false}
|
||||
onChange={onChangeCs}
|
||||
leftColumns={columns}
|
||||
rightColumns={columns}
|
||||
rowKey={(record) => record.id}
|
||||
></TableTransfer>
|
||||
|
||||
{/* <TableTransfer
|
||||
dataSource={goodsList}
|
||||
targetKeys={targetKeys}
|
||||
disabled={disabled}
|
||||
showSelectAll={false}
|
||||
onChange={onChangeCs}
|
||||
filterOption={filterOption}
|
||||
leftColumns={columns}
|
||||
rightColumns={columns}
|
||||
/> */}
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
169
src/pages/goodsSetMeal/index.tsx
Normal file
169
src/pages/goodsSetMeal/index.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import { goodspackagesPage, goodspackagesUpdate } from '@/services/goods/setMeal';
|
||||
import { history } from '@umijs/max';
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Button, Modal, Image, Switch } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
|
||||
|
||||
const GoodsSetMealTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
dataIndex: 'goodsName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '功率',
|
||||
dataIndex: 'itemType',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '销售价格',
|
||||
dataIndex: 'salePrice',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '利润',
|
||||
dataIndex: 'profit',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '库存',
|
||||
dataIndex: 'stock',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '销量',
|
||||
dataIndex: 'sales',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '商品主图',
|
||||
dataIndex: 'picture',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
let _Img = record.picture ? JSON.parse(record.picture)[0] : ''
|
||||
return _Img ? <Image src={_Img} width={50}></Image> : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否上架',
|
||||
dataIndex: 'isUp',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
let onChange = async (e) => {
|
||||
let data = JSON.parse(JSON.stringify(record))
|
||||
data.isUp = e ? 1 : 0
|
||||
data.goodsDataList = data.goodsPackagesItemsVo.map(({ goodsId, goodsNum }) => {
|
||||
return {
|
||||
goodsId,
|
||||
goodsNum
|
||||
}
|
||||
})
|
||||
let _res = await goodspackagesUpdate(data)
|
||||
message.success(e ? '上架成功' : '下架成功');
|
||||
}
|
||||
return <Switch checkedChildren="上架" unCheckedChildren="下架" defaultChecked onChange={onChange} />
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
width: '220px',
|
||||
valueType: 'option',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="api/sys/menu/update"
|
||||
onClick={() => {
|
||||
history.push(`goodsSetMeal/${record.id}/update`);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
key="api/sys/menu/deleteBatchByIds"
|
||||
onClick={async () => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const success = await handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="goodsSetMealList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
history.push('goodsSetMeal/0/create');
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await goodspackagesPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoodsSetMealTableList;
|
||||
77
src/pages/note/cate/edit.tsx
Normal file
77
src/pages/note/cate/edit.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormText,
|
||||
ProFormSwitch
|
||||
} 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>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label={'名称'}
|
||||
placeholder="请输入名称"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
label="排序"
|
||||
name={'sort'}
|
||||
>
|
||||
<InputNumber style={{ width: 200 }} />
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleForm;
|
||||
223
src/pages/note/cate/index.tsx
Normal file
223
src/pages/note/cate/index.tsx
Normal file
@@ -0,0 +1,223 @@
|
||||
import { contentcategoryUpdate, contentcategoryPage, contentcategoryAdd, contentcategoryDelete } from '@/services/note/cate';
|
||||
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal, Image } from 'antd';
|
||||
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
fields.status = fields.status ? 0 : 1
|
||||
await contentcategoryAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await contentcategoryUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await contentcategoryDelete(params);
|
||||
hide();
|
||||
message.success('删除成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const MenuTableList: React.FC = () => {
|
||||
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
|
||||
const intRow = {
|
||||
"id": 1,
|
||||
"imageUrl": "",
|
||||
"sortOrder": 0,
|
||||
"status": 0,
|
||||
"title": "",
|
||||
"jumpUrl": ""
|
||||
}
|
||||
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [currentRow, setCurrentRow] = useState(intRow);
|
||||
const access = useAccess();
|
||||
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
width: '220px',
|
||||
valueType: 'option',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
hidden={!access.hasPerms('admin/banner/update')}
|
||||
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 handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.System.Menu>
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="menuList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await contentcategoryPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...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 MenuTableList;
|
||||
172
src/pages/note/note/edit.tsx
Normal file
172
src/pages/note/note/edit.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { contentcategoryPage } from '@/services/note/cate';
|
||||
|
||||
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';
|
||||
|
||||
import { ContentUtils } from 'braft-utils'
|
||||
// 引入编辑器组件
|
||||
import BraftEditor from 'braft-editor'
|
||||
// 引入编辑器样式
|
||||
import 'braft-editor/dist/index.css'
|
||||
|
||||
const RoleForm: React.FC = (props: any) => {
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(''))
|
||||
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) => {
|
||||
values.detail = editorState.toHTML()
|
||||
props.onSubmit(values);
|
||||
};
|
||||
|
||||
|
||||
const controls = [
|
||||
'undo', 'redo', 'separator',
|
||||
'font-size', 'line-height', 'letter-spacing', 'separator',
|
||||
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
|
||||
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
|
||||
'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator',
|
||||
'link', 'separator', 'hr', 'separator',
|
||||
'clear'
|
||||
]
|
||||
|
||||
const onChange = async (e) => {
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.3g2'];
|
||||
// 将URL转换为小写,以便进行不区分大小写的比较
|
||||
const lowerCaseUrl = e.toLowerCase();
|
||||
// 检查链接是否以任何一个视频扩展名结尾
|
||||
if (videoExtensions.some(e => lowerCaseUrl.endsWith(e))) {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{
|
||||
type: 'VIDEO',
|
||||
url: e
|
||||
}]));
|
||||
} else {
|
||||
setEditorState(ContentUtils.insertMedias(editorState, [{ type: 'IMAGE', url: e, },]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const extendControls = [
|
||||
'separator',
|
||||
{
|
||||
key: 'FilesManagerImage', // 控件唯一标识,必传
|
||||
title: '上传图片/视频', // 指定鼠标悬停提示文案
|
||||
html: null, // 指定在按钮中渲染的html字符串
|
||||
text: <FilesManager
|
||||
fileType="images"
|
||||
mode=""
|
||||
imagesShow={false}
|
||||
onChange={onChange}
|
||||
count={1}
|
||||
/>, // 指定按钮文字,此处可传入jsx,若已指定html,则text不会显示
|
||||
onClick: () => {
|
||||
console.log('Hello World!');
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
return (
|
||||
<Modal
|
||||
width={800}
|
||||
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}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="cid"
|
||||
label="分类"
|
||||
fieldProps={{
|
||||
fieldNames: {
|
||||
label: "name",
|
||||
value: "id"
|
||||
},
|
||||
showSearch: true
|
||||
}}
|
||||
request={async (param) => {
|
||||
console.log(param);
|
||||
const { success, data } = await contentcategoryPage(param);
|
||||
return data.records;
|
||||
}}
|
||||
/>
|
||||
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
name="title"
|
||||
label={'标题'}
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item label="封面图" name="cover">
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={values?.cover}
|
||||
count={1}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item label="视频" name="video">
|
||||
<FilesManager
|
||||
fileType="video"
|
||||
defaultValue={values?.video}
|
||||
count={1}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item
|
||||
name="protocolContent"
|
||||
>
|
||||
<div className="border-solid border-2 border-indigo-600">
|
||||
<BraftEditor value={editorState} controls={controls} extendControls={extendControls} onChange={(val) => {
|
||||
setEditorState(val)
|
||||
}} />
|
||||
</div>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleForm;
|
||||
229
src/pages/note/note/index.tsx
Normal file
229
src/pages/note/note/index.tsx
Normal file
@@ -0,0 +1,229 @@
|
||||
import { contentUpdate, contentPage, contentAdd, contentDelete } from '@/services/note/note';
|
||||
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal, Image } from 'antd';
|
||||
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
fields.status = fields.status ? 0 : 1
|
||||
await contentAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await contentUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await contentDelete(params);
|
||||
hide();
|
||||
message.success('删除成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const MenuTableList: React.FC = () => {
|
||||
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
|
||||
const intRow = {
|
||||
"id": 1,
|
||||
"imageUrl": "",
|
||||
"sortOrder": 0,
|
||||
"status": 0,
|
||||
"title": "",
|
||||
"jumpUrl": ""
|
||||
}
|
||||
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [currentRow, setCurrentRow] = useState(intRow);
|
||||
const access = useAccess();
|
||||
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'title',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '封面图',
|
||||
dataIndex: 'cover',
|
||||
valueType: 'image',
|
||||
search: false,
|
||||
},
|
||||
// {
|
||||
// title: '视频地址',
|
||||
// dataIndex: 'video',
|
||||
// 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"
|
||||
hidden={!access.hasPerms('admin/banner/update')}
|
||||
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 handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.System.Menu>
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="menuList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await contentPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...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 MenuTableList;
|
||||
203
src/pages/order/index.tsx
Normal file
203
src/pages/order/index.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
import { orderPage, orderExport } from '@/services/order';
|
||||
import { exportData } from '@/utils/func';
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Image, Button, Select } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [searchParams, setSearchParams] = useState(null);
|
||||
const columns = [
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'orderNo',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
return <div className={'flex items-center'}>
|
||||
{record.orderNo}
|
||||
<Button type="link" onClick={() => {
|
||||
|
||||
}}>查看</Button>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '用户信息',
|
||||
dataIndex: 'ordersGoodsList',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_: any, record: any) => {
|
||||
return <div className={'flex'}>
|
||||
<div>
|
||||
<div>
|
||||
用户ID:{record?.usersDto?.id}
|
||||
</div>
|
||||
<div>
|
||||
用户昵称:{record?.usersDto?.nickName || '-'}
|
||||
</div>
|
||||
<div>
|
||||
用户账号:{record?.usersDto?.userName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '商品信息',
|
||||
dataIndex: 'ordersGoodsList',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_: any, record: any) => {
|
||||
return record.ordersGoodsList ? record.ordersGoodsList.map((i) => {
|
||||
let _Img = i?.goodsSnapshot?.picture ? JSON.parse(i?.goodsSnapshot?.picture)[0] : ''
|
||||
return <div className={'flex'} key={i.id}>
|
||||
<Image width={60} src={_Img}></Image>
|
||||
<div>
|
||||
<div>
|
||||
商品名称:{i.goodsSnapshot?.goodsName}
|
||||
</div>
|
||||
<div>
|
||||
商品价格:{i.salePrice}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}) : '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '订单金额',
|
||||
dataIndex: 'amount',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '订单利润',
|
||||
dataIndex: 'profit',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '支付类型',
|
||||
dataIndex: 'businessPayType',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let color = record.businessPayType == 1 ? 'volcano' : record.businessPayType == 2 ? 'orange' : 'red'
|
||||
return <Tag color={color}>{record.businessPayType == 1 ? '微信' : record.businessPayType == 2 ? '支付宝' : '钱包'}</Tag>
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
const statusMap = [
|
||||
{ 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: 'status',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let colors = ['gold', 'green', 'volcano', 'red', 'magenta']
|
||||
return <Tag color={colors[record.status]}>{record.status == 0 ? '待支付' : record.status == 1 ? '支付成功' : record.status == -1 ? '取消' : record.status == -2 ? '退款中' : record.status == -3 ? '退款完成' : ''}</Tag>
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
const statusMap = [
|
||||
{ label: '待支付', value: 0 },
|
||||
{ label: '支付成功', value: 1 },
|
||||
{ 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: 'remarks',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
danger
|
||||
onClick={() => {
|
||||
// setRefundFormModal(true);
|
||||
}}
|
||||
>
|
||||
退单
|
||||
</Button>,
|
||||
<Button type="primary" onClick={async () => {
|
||||
let _res: any = await orderExport(searchParams)
|
||||
exportData(_res, '订单信息')
|
||||
}}>导出表格</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
setSearchParams({ ...params });
|
||||
let { data } = await orderPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
187
src/pages/payment/index.tsx
Normal file
187
src/pages/payment/index.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { paymentPage } from '@/services/payment';
|
||||
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Image, Button, Select } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'orderNo',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
return <div className={'flex items-center'}>
|
||||
{record.orderNo}
|
||||
<Button type="link" onClick={() => {
|
||||
|
||||
}}>查看</Button>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '用户信息',
|
||||
dataIndex: 'ordersGoodsList',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_: any, record: any) => {
|
||||
// return record.ordersGoodsList.map((i) => {
|
||||
// let _Img = i.picture ? JSON.parse(i.picture)[0] : ''
|
||||
// return <div className={'flex'} key={i.id}>
|
||||
// <Image src={_Img}></Image>
|
||||
// <div>
|
||||
// <div>
|
||||
// 用户ID:{i.goodsName}
|
||||
// </div>
|
||||
// <div>
|
||||
// 用户账号:{i.salePrice}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// })
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '支付类型',
|
||||
dataIndex: 'payType',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let color = record.payType == 1 ? 'volcano' : record.payType == 2 ? 'orange' : 'red'
|
||||
return <Tag color={color}>{record.payType == 1 ? '支付宝' : '微信'}</Tag>
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
const statusMap = [
|
||||
{ label: '支付宝', value: 1 },
|
||||
{ label: '微信', value: 2 },
|
||||
]
|
||||
return <Select
|
||||
{...fieldProps}
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
filterOption={false}
|
||||
fieldNames={{
|
||||
label: "label",
|
||||
value: "value"
|
||||
}}
|
||||
options={statusMap}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '支付通道',
|
||||
dataIndex: 'platform',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let color = record.platform == 1 ? 'volcano' : record.platform == 2 ? 'orange' : 'red'
|
||||
return <Tag color={color}>{record.platform == 1 ? '上海汇付' : record.platform == 2 ? '支付宝' : '微信'}</Tag>
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
const statusMap = [
|
||||
{ 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: 'status',
|
||||
valueType: 'text',
|
||||
search: true,
|
||||
render: (_: any, record: any) => {
|
||||
let colors = ['gold', 'green', 'volcano', 'red', 'magenta']
|
||||
return <Tag color={colors[record.status]}>{record.status == 0 ? '待支付' : record.status == 1 ? '支付成功' : record.status == -1 ? '取消' : record.status == -2 ? '退款中' : record.status == -3 ? '退款完成' : ''}</Tag>
|
||||
},
|
||||
renderFormItem: (
|
||||
_,
|
||||
{ type, defaultRender, formItemProps, fieldProps, ...rest },
|
||||
form,
|
||||
) => {
|
||||
const statusMap = [
|
||||
{ label: '待支付', value: 0 },
|
||||
{ label: '支付成功', value: 1 },
|
||||
{ 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: 'remarks',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await paymentPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
143
src/pages/system/log/index.tsx
Normal file
143
src/pages/system/log/index.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import { logPage } from '@/services/system/log';
|
||||
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'title',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'businessType',
|
||||
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>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '方法',
|
||||
dataIndex: 'method',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '请求方式',
|
||||
dataIndex: 'requestMethod',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '操作类别',
|
||||
dataIndex: 'operatorType',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '操作人员',
|
||||
dataIndex: 'operName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '请求URL',
|
||||
dataIndex: 'operUrl',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
|
||||
{
|
||||
title: '主机地址',
|
||||
dataIndex: 'operIp',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '操作地点',
|
||||
dataIndex: 'operLocation',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '请求参数',
|
||||
dataIndex: 'operParam',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '返回参数',
|
||||
dataIndex: 'jsonResult',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '错误消息',
|
||||
dataIndex: 'errorMsg',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'operTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '消耗时间',
|
||||
dataIndex: 'costTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await logPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogTableList;
|
||||
298
src/pages/system/menu/edit.tsx
Normal file
298
src/pages/system/menu/edit.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormText,
|
||||
ProFormRadio,
|
||||
ProFormTreeSelect,
|
||||
ProFormSelect,
|
||||
} from '@ant-design/pro-components';
|
||||
import { Form, Modal } from 'antd';
|
||||
import { useIntl, FormattedMessage } from '@umijs/max';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
import { createIcon } from '@/utils/IconUtil';
|
||||
import { DictValueEnumObj } from '@/components/DictTag';
|
||||
import IconSelector from '@/components/IconSelector';
|
||||
|
||||
export type MenuFormData = Record<string, unknown> & Partial<API.System.Menu>;
|
||||
|
||||
export type MenuFormProps = {
|
||||
onCancel: (flag?: boolean, formVals?: MenuFormData) => void;
|
||||
onSubmit: (values: MenuFormData) => Promise<void>;
|
||||
open: boolean;
|
||||
values: Partial<API.System.Menu>;
|
||||
visibleOptions: DictValueEnumObj;
|
||||
statusOptions: DictValueEnumObj;
|
||||
menuTree: DataNode[];
|
||||
};
|
||||
|
||||
const MenuForm: React.FC<MenuFormProps> = (props) => {
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [menuType, setmenuType] = useState<any>('M');
|
||||
const [menuIconName, setMenuIconName] = useState<any>();
|
||||
const [iconSelectorOpen, setIconSelectorOpen] = useState<boolean>(false);
|
||||
|
||||
const { menuTree, visibleOptions, statusOptions } = props;
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
setMenuIconName(props.values.icon);
|
||||
setmenuType(props.values.menuType)
|
||||
form.setFieldsValue({
|
||||
id: props.values.id,
|
||||
name: props.values.name,
|
||||
parentId: props.values.parentId,
|
||||
orderNum: props.values.orderNum,
|
||||
path: props.values.path,
|
||||
component: props.values.component,
|
||||
query: props.values.query,
|
||||
isFrame: props.values.isFrame,
|
||||
isCache: props.values.isCache,
|
||||
menuType: props.values.menuType,
|
||||
visible: props.values.visible,
|
||||
status: props.values.status,
|
||||
perms: props.values.perms,
|
||||
icon: props.values.icon,
|
||||
createBy: props.values.createBy,
|
||||
createTime: props.values.createTime,
|
||||
updateBy: props.values.updateBy,
|
||||
updateTime: props.values.updateTime,
|
||||
remark: props.values.remark,
|
||||
});
|
||||
}, [form, props]);
|
||||
|
||||
|
||||
const intl = useIntl();
|
||||
const handleOk = () => {
|
||||
form.submit();
|
||||
};
|
||||
const handleCancel = () => {
|
||||
props.onCancel();
|
||||
};
|
||||
const handleFinish = async (values: Record<string, any>) => {
|
||||
props.onSubmit(values as MenuFormData);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
width={640}
|
||||
title={'编辑菜单权限'}
|
||||
open={props.open}
|
||||
forceRender
|
||||
destroyOnClose
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<ProForm
|
||||
form={form}
|
||||
grid={true}
|
||||
submitter={false}
|
||||
layout="horizontal"
|
||||
onFinish={handleFinish}>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
label={'菜单编号'}
|
||||
placeholder="请输入菜单编号"
|
||||
disabled
|
||||
hidden={true}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入菜单编号!" defaultMessage="请输入菜单编号!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormTreeSelect
|
||||
name="parentId"
|
||||
label={'上级菜单'}
|
||||
params={{ menuTree }}
|
||||
request={async () => {
|
||||
return [{
|
||||
name: "全部",
|
||||
id: 0,
|
||||
children: menuTree
|
||||
}];
|
||||
}}
|
||||
placeholder="请输入父菜单编号"
|
||||
fieldProps={{
|
||||
defaultValue: 0,
|
||||
fieldNames: {
|
||||
label: "name",
|
||||
value: "id"
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
name="menuType"
|
||||
valueEnum={{
|
||||
M: '目录',
|
||||
C: '菜单',
|
||||
F: '按钮',
|
||||
}}
|
||||
label={'菜单类型'}
|
||||
placeholder="请输入菜单类型"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入菜单类型!" defaultMessage="请输入菜单类型!" />,
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
onChange: (e) => {
|
||||
setmenuType(e.target.value);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="icon"
|
||||
label={'菜单图标'}
|
||||
valueEnum={{}}
|
||||
hidden={menuType === 'F'}
|
||||
addonBefore={createIcon(menuIconName)}
|
||||
fieldProps={{
|
||||
onClick: () => {
|
||||
setIconSelectorOpen(true);
|
||||
},
|
||||
}}
|
||||
placeholder="请输入菜单图标"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入菜单图标!" defaultMessage="请输入菜单图标!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label={'菜单名称'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入菜单名称"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入菜单名称!" defaultMessage="请输入菜单名称!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormDigit
|
||||
name="orderNum"
|
||||
label={'显示顺序'}
|
||||
width="lg"
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入显示顺序"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入显示顺序!" defaultMessage="请输入显示顺序!" />,
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
defaultValue: 1
|
||||
}}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
name="isFrame"
|
||||
valueEnum={{
|
||||
0: '是',
|
||||
1: '否',
|
||||
}}
|
||||
initialValue="1"
|
||||
label={'是否为外链'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入是否为外链"
|
||||
hidden={menuType === 'F'}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入是否为外链!" defaultMessage="请输入是否为外链!" />,
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
defaultValue: '1'
|
||||
}}
|
||||
/>
|
||||
<ProFormText
|
||||
name="path"
|
||||
label={'路由地址'}
|
||||
width="lg"
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入路由地址"
|
||||
hidden={menuType === 'F'}
|
||||
rules={[
|
||||
{
|
||||
required: menuType !== 'F',
|
||||
message: <FormattedMessage id="请输入路由地址!" defaultMessage="请输入路由地址!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="perms"
|
||||
label={'权限标识'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入权限标识"
|
||||
hidden={menuType === 'M'}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入权限标识!" defaultMessage="请输入权限标识!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/* <ProFormRadio.Group
|
||||
name="visible"
|
||||
valueEnum={visibleOptions}
|
||||
label={'显示状态'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入显示状态"
|
||||
hidden={menuType === 'F'}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: <FormattedMessage id="请输入显示状态!" defaultMessage="请输入显示状态!" />,
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
defaultValue: '0'
|
||||
}}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
valueEnum={statusOptions}
|
||||
name="status"
|
||||
label={'菜单状态'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入菜单状态"
|
||||
hidden={menuType === 'F'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入菜单状态!" defaultMessage="请输入菜单状态!" />,
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
defaultValue: '0'
|
||||
}}
|
||||
/> */}
|
||||
</ProForm>
|
||||
<Modal
|
||||
width={800}
|
||||
open={iconSelectorOpen}
|
||||
onCancel={() => {
|
||||
setIconSelectorOpen(false);
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<IconSelector
|
||||
onSelect={(name: string) => {
|
||||
form.setFieldsValue({ icon: name });
|
||||
setMenuIconName(name);
|
||||
setIconSelectorOpen(false);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuForm;
|
||||
274
src/pages/system/menu/index.tsx
Normal file
274
src/pages/system/menu/index.tsx
Normal file
@@ -0,0 +1,274 @@
|
||||
import { roleMenuTreeSelect, menuDele, menuUpdate, menuAdd } from '@/services/user/menu';
|
||||
import { getMenuList } from '@/services/system/menu';
|
||||
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal } from 'antd';
|
||||
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
if (!fields.parentId) {
|
||||
fields.parentId = 0
|
||||
}
|
||||
await menuAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在配置');
|
||||
try {
|
||||
await menuUpdate(fields);
|
||||
hide();
|
||||
message.success('配置成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('配置失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await menuDele(params);
|
||||
hide();
|
||||
message.success('删除成功,即将刷新');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const MenuTableList: React.FC = () => {
|
||||
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [currentRow, setCurrentRow] = useState();
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
|
||||
const [menuTree, setMenuTree] = useState<DataNode[]>([]);
|
||||
const [visibleOptions, setVisibleOptions] = useState<any>([]);
|
||||
const [statusOptions, setStatusOptions] = useState<any>([]);
|
||||
|
||||
const access = useAccess();
|
||||
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
getMenuList().then((res) => {
|
||||
const { data } = res
|
||||
setMenuTree(data)
|
||||
})
|
||||
|
||||
// getDictValueEnum('sys_show_hide').then((data) => {
|
||||
// setVisibleOptions(data);
|
||||
// });
|
||||
// getDictValueEnum('sys_normal_disable').then((data) => {
|
||||
// setStatusOptions(data);
|
||||
// });
|
||||
}, []);
|
||||
|
||||
const columns: ProColumns<API.System.Menu>[] = [
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'name',
|
||||
valueType: 'text',
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
valueType: 'text',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'orderNum',
|
||||
valueType: 'text',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '组件路径',
|
||||
dataIndex: 'path',
|
||||
valueType: 'text',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
dataIndex: 'perms',
|
||||
valueType: 'text',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '菜单状态',
|
||||
dataIndex: 'status',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
width: '220px',
|
||||
valueType: 'option',
|
||||
render: (_, record:any) => [
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="api/sys/menu/xaiji"
|
||||
onClick={() => {
|
||||
setModalVisible(true);
|
||||
setCurrentRow({
|
||||
parentId: record.id
|
||||
});
|
||||
}}
|
||||
>
|
||||
添加下级
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="api/sys/menu/update"
|
||||
onClick={() => {
|
||||
setModalVisible(true);
|
||||
setCurrentRow(record);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
key="api/sys/menu/deleteBatchByIds"
|
||||
onClick={async () => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const success = await handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.System.Menu>
|
||||
headerTitle={'菜单管理'}
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="menuList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={async () => {
|
||||
let { data } = await getMenuList()
|
||||
function childrenRemove(item: []) {
|
||||
let data = item.map((i: any) => {
|
||||
if (i?.children.length === 0) {
|
||||
delete i.children
|
||||
} else {
|
||||
childrenRemove(i.children)
|
||||
}
|
||||
return i
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
return {
|
||||
data: childrenRemove(data)
|
||||
}
|
||||
}}
|
||||
columns={columns}
|
||||
rowSelection={{
|
||||
onChange: (_, selectedRows) => {
|
||||
setSelectedRows(selectedRows);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values } as API.System.Menu);
|
||||
} else {
|
||||
success = await handleAdd({ ...values } as API.System.Menu);
|
||||
}
|
||||
if (success) {
|
||||
setModalVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCancel={() => {
|
||||
setModalVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
}}
|
||||
open={modalVisible}
|
||||
values={currentRow || {}}
|
||||
visibleOptions={visibleOptions}
|
||||
statusOptions={statusOptions}
|
||||
menuTree={menuTree}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuTableList;
|
||||
107
src/pages/system/role/edit.tsx
Normal file
107
src/pages/system/role/edit.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormText,
|
||||
ProFormRadio,
|
||||
} from '@ant-design/pro-components';
|
||||
import { Form, Modal, Tree } from 'antd';
|
||||
import { useIntl, FormattedMessage } from '@umijs/max';
|
||||
|
||||
const RoleForm: React.FC = (props: any) => {
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [treeDate, setTree] = useState([]);
|
||||
|
||||
|
||||
const { values, menuTree } = props;
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
form.setFieldsValue(values);
|
||||
setTree(!values?.sysMenuDTOS ? [] : values?.sysMenuDTOS.map((item: any) => item.id))
|
||||
}, [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}
|
||||
grid={true}
|
||||
submitter={false}
|
||||
layout="horizontal"
|
||||
onFinish={handleFinish}>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
label={'ID'}
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
name="roleName"
|
||||
label={'角色名称'}
|
||||
colProps={{ md: 24, xl: 24 }}
|
||||
placeholder="请输入角色名称"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入角色名称!" defaultMessage="请输入角色名称!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="roleKey"
|
||||
label={'权限字符'}
|
||||
colProps={{ md: 24, xl: 24 }}
|
||||
placeholder="请输入权限字符"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入权限字符!" defaultMessage="请输入权限字符!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item label="菜单权限" name="menuIds">
|
||||
<Tree
|
||||
checkable
|
||||
fieldNames={{
|
||||
title: 'label',
|
||||
key: 'id',
|
||||
}}
|
||||
defaultCheckedKeys={treeDate}
|
||||
treeData={menuTree}
|
||||
onCheck={(e) => {
|
||||
setTree(e)
|
||||
form.setFieldsValue({ menuIds: e });
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleForm;
|
||||
219
src/pages/system/role/index.tsx
Normal file
219
src/pages/system/role/index.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
import { findAllSysRole, roleAdd, roleDelete, roleUpdate, roleInfo } from '@/services/system/role';
|
||||
import { roleMenuTreeSelect } from '@/services/user/menu';
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal } from 'antd';
|
||||
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
await menuAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await roleUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await roleDelete(params);
|
||||
hide();
|
||||
message.success('删除成功,即将刷新');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const RoleTableList: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
const [currentRow, setCurrentRow] = useState();
|
||||
const [menuTree, setMenuTree] = useState([]);
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
roleMenuTreeSelect().then((res) => {
|
||||
const { data } = res
|
||||
setMenuTree(data)
|
||||
})
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '角色编号',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'roleName',
|
||||
valueType: 'text',
|
||||
},
|
||||
{
|
||||
title: '权限字符',
|
||||
dataIndex: 'roleKey',
|
||||
valueType: 'text',
|
||||
},
|
||||
{
|
||||
title: '显示顺序',
|
||||
dataIndex: 'roleSort',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '角色状态',
|
||||
dataIndex: 'status',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'status',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
|
||||
dataIndex: 'option',
|
||||
width: '220px',
|
||||
valueType: 'option',
|
||||
render: (_, record: any) => [
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="edit"
|
||||
onClick={async () => {
|
||||
setModalVisible(true);
|
||||
const { data } = await roleInfo({
|
||||
id: record.id
|
||||
})
|
||||
setCurrentRow(data);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
key="batchRemove"
|
||||
onClick={async () => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const success = await handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="roleList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>
|
||||
]}
|
||||
request={findAllSysRole}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...values });
|
||||
}
|
||||
if (success) {
|
||||
setModalVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCancel={() => {
|
||||
setModalVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
}}
|
||||
open={modalVisible}
|
||||
values={currentRow || {}}
|
||||
menuTree={menuTree}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleTableList;
|
||||
190
src/pages/system/user/edit.tsx
Normal file
190
src/pages/system/user/edit.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormText,
|
||||
ProFormRadio,
|
||||
ProFormSelect
|
||||
} from '@ant-design/pro-components';
|
||||
import { Form, Modal } from 'antd';
|
||||
import { useIntl, FormattedMessage } from '@umijs/max';
|
||||
import { findAllSysRole } from '@/services/system/role';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const UserForm: React.FC = (props: any) => {
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { values } = props;
|
||||
|
||||
useEffect(() => {
|
||||
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}
|
||||
grid={true}
|
||||
submitter={false}
|
||||
layout="horizontal"
|
||||
onFinish={handleFinish}>
|
||||
<ProFormDigit
|
||||
name="id"
|
||||
label={'用户ID'}
|
||||
disabled
|
||||
hidden={true}
|
||||
/>
|
||||
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
name="username"
|
||||
label={'用户名'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入用户名"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入用户名!" defaultMessage="请输入用户名!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="nickName"
|
||||
label={'用户昵称'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入用户昵称"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入用户昵称!" defaultMessage="请输入用户昵称!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="password"
|
||||
label={'用户密码'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入用户密码"
|
||||
rules={[
|
||||
{
|
||||
required: values?.id ? false : true,
|
||||
message: <FormattedMessage id="请输入用户密码!" defaultMessage="请输入用户密码!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="phoneNumber"
|
||||
label={'手机号'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入手机号"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请输入手机号!" defaultMessage="请输入手机号!" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
name="email"
|
||||
label={'邮箱'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="请输入邮箱"
|
||||
/>
|
||||
{/* <ProFormText
|
||||
name="loginIp"
|
||||
label={'登录IP'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
placeholder="登录IP"
|
||||
/> */}
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProFormSelect
|
||||
name="roleIds"
|
||||
label={'选择角色'}
|
||||
colProps={{ md: 24, xl: 24 }}
|
||||
placeholder="请选择角色"
|
||||
fieldProps={
|
||||
{
|
||||
mode: "multiple",
|
||||
fieldNames: {
|
||||
label: 'roleName',
|
||||
value: 'id'
|
||||
},
|
||||
defaultValue: () => {
|
||||
return values?.sysRoleDTOList?.map((item: any) => item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: <FormattedMessage id="请选择角色!" defaultMessage="请选择角色!" />,
|
||||
},
|
||||
]}
|
||||
request={async () => {
|
||||
let { data } = await findAllSysRole()
|
||||
return data
|
||||
}}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
|
||||
|
||||
|
||||
<ProFormRadio.Group
|
||||
name="userType"
|
||||
valueEnum={{
|
||||
0: '系统用户',
|
||||
}}
|
||||
initialValue="1"
|
||||
label={'用户类型'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
fieldProps={{
|
||||
defaultValue: 0
|
||||
}}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
name="sex"
|
||||
valueEnum={{
|
||||
0: '男',
|
||||
1: '女',
|
||||
2: '未知',
|
||||
}}
|
||||
initialValue="1"
|
||||
label={'性别'}
|
||||
colProps={{ md: 12, xl: 12 }}
|
||||
fieldProps={{
|
||||
defaultValue: 0
|
||||
}}
|
||||
/>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserForm;
|
||||
265
src/pages/system/user/index.tsx
Normal file
265
src/pages/system/user/index.tsx
Normal file
@@ -0,0 +1,265 @@
|
||||
import { userPage, userUpPwd, userInfo, userUpdate, userAdd, userDelete } from '@/services/system/user';
|
||||
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
|
||||
import { Button, message, Modal, Tag } from 'antd';
|
||||
import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import UpdateForm from './edit';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
await userAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await userUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow: API.System.Menu) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = [selectedRow.id];
|
||||
await userDelete(params);
|
||||
hide();
|
||||
message.success('删除成功,即将刷新');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const UserTableList: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
const [currentRow, setCurrentRow] = useState();
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
/** 国际化配置 */
|
||||
const intl = useIntl();
|
||||
const access = useAccess();
|
||||
useEffect(() => {
|
||||
|
||||
// getDictValueEnum('sys_show_hide').then((data) => {
|
||||
// setVisibleOptions(data);
|
||||
// });
|
||||
// getDictValueEnum('sys_normal_disable').then((data) => {
|
||||
// setStatusOptions(data);
|
||||
// });
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
dataIndex: 'avatar',
|
||||
valueType: 'image',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'username',
|
||||
valueType: 'text',
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickName',
|
||||
valueType: 'text',
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
dataIndex: 'sysRoleDTOList',
|
||||
valueType: 'text',
|
||||
render: (_: any, record: any) => {
|
||||
return record?.sysRoleDTOList?.map((item: any) => {
|
||||
return <Tag key={item.id}>{item.roleName}</Tag>
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
dataIndex: 'email',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phoneNumber',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '最后登录IP',
|
||||
dataIndex: 'loginIp',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '最后登录时间',
|
||||
dataIndex: 'loginDate',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
|
||||
dataIndex: 'option',
|
||||
width: '220px',
|
||||
valueType: 'option',
|
||||
render: (_, record: any) => [
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="edit"
|
||||
onClick={() => {
|
||||
setModalVisible(true);
|
||||
setCurrentRow(record);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
key="batchRemove"
|
||||
onClick={async () => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const success = await handleRemoveOne(record);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
],
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.System.Menu>
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="userList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
hidden={!access.hasPerms('system:user:add')}
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新增
|
||||
</Button>
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
let { data } = await userPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...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 UserTableList;
|
||||
160
src/pages/user/grade/index.tsx
Normal file
160
src/pages/user/grade/index.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { history } from '@umijs/max';
|
||||
|
||||
import { Button, Space, Image, Popconfirm } from 'antd';
|
||||
import {
|
||||
PageContainer,
|
||||
EditableProTable,
|
||||
ActionType
|
||||
} from '@ant-design/pro-components';
|
||||
|
||||
import { userlevelPage, userlevelDelete, userlevelUpdate, userlevelAdd } from "@/services/user/grade"
|
||||
|
||||
|
||||
|
||||
export default () => {
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const initRow = {
|
||||
id: '',
|
||||
name: '',
|
||||
level: '',
|
||||
num: '',
|
||||
note: '',
|
||||
fee: '',
|
||||
team_prize: ''
|
||||
};
|
||||
|
||||
const [row, setRow] = useState(initRow);
|
||||
const [editableKeys, setEditableRowKeys] = useState([]);
|
||||
|
||||
const handleDelete = async (fields) => {
|
||||
|
||||
const { success } = await userlevelDelete(fields);
|
||||
if (success) {
|
||||
actionRef.current.clearSelected();
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleCreate = async (fields) => {
|
||||
setRow(fields);
|
||||
|
||||
const { success } = await userlevelAdd({ ...fields });
|
||||
|
||||
if (success) {
|
||||
setRow(initRow);
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = async (fields) => {
|
||||
|
||||
const { success } = await userlevelUpdate({ ...fields });
|
||||
|
||||
if (success) {
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '等级',
|
||||
dataIndex: 'level',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '分成比例',
|
||||
dataIndex: 'ratio',
|
||||
search: false,
|
||||
},
|
||||
// {
|
||||
// title: '服务费',
|
||||
// dataIndex: 'fee',
|
||||
// search: false,
|
||||
// },
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
valueType: 'option',
|
||||
render: (text, record, _, action) => [
|
||||
<Button
|
||||
key="editable"
|
||||
type="link"
|
||||
onClick={() => {
|
||||
setRow(record);
|
||||
action?.startEditable?.(record.id);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Popconfirm
|
||||
title="确认删除?"
|
||||
onConfirm={() => {
|
||||
handleDelete([record.id]);
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
key="delete"
|
||||
type="link"
|
||||
danger
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
ghost
|
||||
>
|
||||
<>
|
||||
<EditableProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
search={false}
|
||||
request={async (params, sorter, filter) => {
|
||||
const { data, success } = await userlevelPage({
|
||||
...params
|
||||
});
|
||||
return {
|
||||
data: data.records || [],
|
||||
total: 0,
|
||||
success,
|
||||
};
|
||||
}}
|
||||
recordCreatorProps={
|
||||
{
|
||||
position: 'bottom',
|
||||
record: () => ({ id: 0 }),
|
||||
}
|
||||
}
|
||||
editable={{
|
||||
type: 'multiple',
|
||||
editableKeys,
|
||||
onSave: async (rowKey, data, row) => {
|
||||
if (rowKey) {
|
||||
handleUpdate(data)
|
||||
} else {
|
||||
handleCreate(data)
|
||||
}
|
||||
|
||||
},
|
||||
onChange: setEditableRowKeys,
|
||||
}}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
/>
|
||||
</>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
7
src/pages/user/login/index.css
Normal file
7
src/pages/user/login/index.css
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
.ant-pro-layout-content {
|
||||
padding: 0;
|
||||
padding-block: 0 !important;
|
||||
padding-inline: 0 !important;
|
||||
height: 100vh !important;
|
||||
}
|
||||
212
src/pages/user/login/index.tsx
Normal file
212
src/pages/user/login/index.tsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import { Footer } from '@/components';
|
||||
import { login } 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 { createStyles } from 'antd-style';
|
||||
import React, { useState } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import Settings from '../../../../config/defaultSettings';
|
||||
import "./index.css";
|
||||
|
||||
const useStyles = createStyles(({ token }) => {
|
||||
return {
|
||||
action: {
|
||||
marginLeft: '8px',
|
||||
color: 'rgba(0, 0, 0, 0.2)',
|
||||
fontSize: '24px',
|
||||
verticalAlign: 'middle',
|
||||
cursor: 'pointer',
|
||||
transition: 'color 0.3s',
|
||||
'&:hover': {
|
||||
color: token.colorPrimaryActive,
|
||||
},
|
||||
},
|
||||
lang: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
lineHeight: '42px',
|
||||
position: 'fixed',
|
||||
right: 16,
|
||||
borderRadius: token.borderRadius,
|
||||
':hover': {
|
||||
backgroundColor: token.colorBgTextHover,
|
||||
},
|
||||
},
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
backgroundImage:
|
||||
"url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
|
||||
backgroundSize: '100% 100%',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
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 fetchUserInfo = async () => {
|
||||
const userInfo = await initialState?.fetchUserInfo?.();
|
||||
console.log(userInfo, 'userInfo');
|
||||
|
||||
if (userInfo) {
|
||||
flushSync(() => {
|
||||
setInitialState((s) => ({
|
||||
...s,
|
||||
currentUser: userInfo,
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: API.LoginParams) => {
|
||||
try {
|
||||
// 登录
|
||||
const msg = await login({ ...values });
|
||||
console.log(msg);
|
||||
|
||||
if (msg.success || msg.status == 'ok') {
|
||||
localStorage.setItem('token', msg.data?.token)
|
||||
const defaultLoginSuccessMessage = intl.formatMessage({
|
||||
id: 'pages.login.success',
|
||||
defaultMessage: '登录成功!',
|
||||
});
|
||||
message.success(defaultLoginSuccessMessage);
|
||||
await fetchUserInfo();
|
||||
const urlParams = new URL(window.location.href).searchParams;
|
||||
window.location.href = urlParams.get('redirect') || '/';
|
||||
return;
|
||||
}
|
||||
setUserLoginState(msg);
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Helmet>
|
||||
<title>
|
||||
{intl.formatMessage({
|
||||
id: 'menu.login',
|
||||
defaultMessage: '登录页',
|
||||
})}
|
||||
- {Settings.title}
|
||||
</title>
|
||||
</Helmet>
|
||||
{/* <Lang /> */}
|
||||
<div
|
||||
style={{
|
||||
flex: '1',
|
||||
padding: '32px 0',
|
||||
}}
|
||||
>
|
||||
<LoginForm
|
||||
contentStyle={{
|
||||
minWidth: 280,
|
||||
maxWidth: '75vw',
|
||||
}}
|
||||
logo={<img alt="logo" src="/logo.svg" />}
|
||||
title="云充电 管理后台"
|
||||
subTitle={'云充电 管理后台'}
|
||||
initialValues={{
|
||||
autoLogin: true,
|
||||
}}
|
||||
onFinish={async (values) => {
|
||||
await handleSubmit(values as API.LoginParams);
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<ProFormText
|
||||
name="username"
|
||||
fieldProps={{
|
||||
size: 'large',
|
||||
prefix: <UserOutlined />,
|
||||
}}
|
||||
placeholder={'请输入用户名'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入用户名!',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText.Password
|
||||
name="password"
|
||||
fieldProps={{
|
||||
size: 'large',
|
||||
prefix: <LockOutlined />,
|
||||
}}
|
||||
placeholder={'请输入密码'}
|
||||
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密码!',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
</LoginForm>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
96
src/pages/user/login/login.test.tsx
Normal file
96
src/pages/user/login/login.test.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { TestBrowser } from '@@/testBrowser';
|
||||
import { act, fireEvent, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
// @ts-ignore
|
||||
import { startMock } from '@@/requestRecordMock';
|
||||
|
||||
const waitTime = (time: number = 100) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, time);
|
||||
});
|
||||
};
|
||||
|
||||
let server: {
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
describe('Login Page', () => {
|
||||
beforeAll(async () => {
|
||||
server = await startMock({
|
||||
port: 8000,
|
||||
scene: 'login',
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server?.close();
|
||||
});
|
||||
|
||||
it('should show login form', async () => {
|
||||
const historyRef = React.createRef<any>();
|
||||
const rootContainer = render(
|
||||
<TestBrowser
|
||||
historyRef={historyRef}
|
||||
location={{
|
||||
pathname: '/user/login',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await rootContainer.findAllByText('Ant Design');
|
||||
|
||||
act(() => {
|
||||
historyRef.current?.push('/user/login');
|
||||
});
|
||||
|
||||
expect(rootContainer.baseElement?.querySelector('.ant-pro-form-login-desc')?.textContent).toBe(
|
||||
'Ant Design is the most influential web design specification in Xihu district',
|
||||
);
|
||||
|
||||
expect(rootContainer.asFragment()).toMatchSnapshot();
|
||||
|
||||
rootContainer.unmount();
|
||||
});
|
||||
|
||||
it('should login success', async () => {
|
||||
const historyRef = React.createRef<any>();
|
||||
const rootContainer = render(
|
||||
<TestBrowser
|
||||
historyRef={historyRef}
|
||||
location={{
|
||||
pathname: '/user/login',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await rootContainer.findAllByText('Ant Design');
|
||||
|
||||
const userNameInput = await rootContainer.findByPlaceholderText('Username: admin or user');
|
||||
|
||||
act(() => {
|
||||
fireEvent.change(userNameInput, { target: { value: 'admin' } });
|
||||
});
|
||||
|
||||
const passwordInput = await rootContainer.findByPlaceholderText('Password: ant.design');
|
||||
|
||||
act(() => {
|
||||
fireEvent.change(passwordInput, { target: { value: 'ant.design' } });
|
||||
});
|
||||
|
||||
await (await rootContainer.findByText('Login')).click();
|
||||
|
||||
// 等待接口返回结果
|
||||
await waitTime(5000);
|
||||
|
||||
await rootContainer.findAllByText('Ant Design Pro');
|
||||
|
||||
expect(rootContainer.asFragment()).toMatchSnapshot();
|
||||
|
||||
await waitTime(2000);
|
||||
|
||||
rootContainer.unmount();
|
||||
});
|
||||
});
|
||||
38
src/pages/user/register-result/index.tsx
Normal file
38
src/pages/user/register-result/index.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Link, useSearchParams } from '@umijs/max';
|
||||
import { Button, Result } from 'antd';
|
||||
import React from 'react';
|
||||
import useStyles from './style.style';
|
||||
|
||||
const RegisterResult: React.FC<Record<string, unknown>> = () => {
|
||||
const { styles } = useStyles();
|
||||
const [params] = useSearchParams();
|
||||
|
||||
const actions = (
|
||||
<div className={styles.actions}>
|
||||
<a href="">
|
||||
<Button size="large" type="primary">
|
||||
<span>查看邮箱</span>
|
||||
</Button>
|
||||
</a>
|
||||
<Link to="/">
|
||||
<Button size="large">返回首页</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
const email = params?.get('account') || 'AntDesign@example.com';
|
||||
return (
|
||||
<Result
|
||||
className={styles.registerResult}
|
||||
status="success"
|
||||
title={
|
||||
<div className={styles.title}>
|
||||
<span>你的账户:{email} 注册成功</span>
|
||||
</div>
|
||||
}
|
||||
subTitle="激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。"
|
||||
extra={actions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default RegisterResult;
|
||||
26
src/pages/user/register-result/style.style.ts
Normal file
26
src/pages/user/register-result/style.style.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const useStyles = createStyles(() => {
|
||||
return {
|
||||
registerResult: {
|
||||
width: '800px',
|
||||
minHeight: '400px',
|
||||
margin: 'auto',
|
||||
padding: '80px',
|
||||
background: 'none',
|
||||
},
|
||||
anticon: {
|
||||
fontSize: '64px',
|
||||
},
|
||||
title: {
|
||||
marginTop: '32px',
|
||||
fontSize: '20px',
|
||||
lineHeight: '28px',
|
||||
},
|
||||
actions: {
|
||||
marginTop: '40px',
|
||||
'a + a': { marginLeft: '8px' },
|
||||
},
|
||||
};
|
||||
});
|
||||
export default useStyles;
|
||||
9
src/pages/user/register/_mock.ts
Normal file
9
src/pages/user/register/_mock.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { Request, Response } from 'express';
|
||||
|
||||
export default {
|
||||
'POST /api/register': (_: Request, res: Response) => {
|
||||
res.send({
|
||||
data: { status: 'ok', currentAuthority: 'user' },
|
||||
});
|
||||
},
|
||||
};
|
||||
292
src/pages/user/register/index.tsx
Normal file
292
src/pages/user/register/index.tsx
Normal file
@@ -0,0 +1,292 @@
|
||||
import { history, Link, useRequest } from '@umijs/max';
|
||||
import { Button, Col, Form, Input, message, Popover, Progress, Row, Select, Space } from 'antd';
|
||||
import type { Store } from 'antd/es/form/interface';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { StateType } from './service';
|
||||
import { fakeRegister } from './service';
|
||||
import useStyles from './style.style';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const { Option } = Select;
|
||||
|
||||
const passwordProgressMap: {
|
||||
ok: 'success';
|
||||
pass: 'normal';
|
||||
poor: 'exception';
|
||||
} = {
|
||||
ok: 'success',
|
||||
pass: 'normal',
|
||||
poor: 'exception',
|
||||
};
|
||||
const Register: FC = () => {
|
||||
const { styles } = useStyles();
|
||||
const [count, setCount]: [number, any] = useState(0);
|
||||
const [open, setVisible]: [boolean, any] = useState(false);
|
||||
const [prefix, setPrefix]: [string, any] = useState('86');
|
||||
const [popover, setPopover]: [boolean, any] = useState(false);
|
||||
const confirmDirty = false;
|
||||
let interval: number | undefined;
|
||||
|
||||
const passwordStatusMap = {
|
||||
ok: (
|
||||
<div className={styles.success}>
|
||||
<span>强度:强</span>
|
||||
</div>
|
||||
),
|
||||
pass: (
|
||||
<div className={styles.warning}>
|
||||
<span>强度:中</span>
|
||||
</div>
|
||||
),
|
||||
poor: (
|
||||
<div className={styles.error}>
|
||||
<span>强度:太短</span>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
const [form] = Form.useForm();
|
||||
useEffect(
|
||||
() => () => {
|
||||
clearInterval(interval);
|
||||
},
|
||||
[interval],
|
||||
);
|
||||
const onGetCaptcha = () => {
|
||||
let counts = 59;
|
||||
setCount(counts);
|
||||
interval = window.setInterval(() => {
|
||||
counts -= 1;
|
||||
setCount(counts);
|
||||
if (counts === 0) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
const getPasswordStatus = () => {
|
||||
const value = form.getFieldValue('password');
|
||||
if (value && value.length > 9) {
|
||||
return 'ok';
|
||||
}
|
||||
if (value && value.length > 5) {
|
||||
return 'pass';
|
||||
}
|
||||
return 'poor';
|
||||
};
|
||||
const { loading: submitting, run: register } = useRequest<{
|
||||
data: StateType;
|
||||
}>(fakeRegister, {
|
||||
manual: true,
|
||||
onSuccess: (data, params) => {
|
||||
if (data.status === 'ok') {
|
||||
message.success('注册成功!');
|
||||
history.push({
|
||||
pathname: `/user/register-result?account=${params[0].email}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
const onFinish = (values: Store) => {
|
||||
register(values);
|
||||
};
|
||||
const checkConfirm = (_: any, value: string) => {
|
||||
const promise = Promise;
|
||||
if (value && value !== form.getFieldValue('password')) {
|
||||
return promise.reject('两次输入的密码不匹配!');
|
||||
}
|
||||
return promise.resolve();
|
||||
};
|
||||
const checkPassword = (_: any, value: string) => {
|
||||
const promise = Promise;
|
||||
// 没有值的情况
|
||||
if (!value) {
|
||||
setVisible(!!value);
|
||||
return promise.reject('请输入密码!');
|
||||
}
|
||||
// 有值的情况
|
||||
if (!open) {
|
||||
setVisible(!!value);
|
||||
}
|
||||
setPopover(!popover);
|
||||
if (value.length < 6) {
|
||||
return promise.reject('');
|
||||
}
|
||||
if (value && confirmDirty) {
|
||||
form.validateFields(['confirm']);
|
||||
}
|
||||
return promise.resolve();
|
||||
};
|
||||
const changePrefix = (value: string) => {
|
||||
setPrefix(value);
|
||||
};
|
||||
const renderPasswordProgress = () => {
|
||||
const value = form.getFieldValue('password');
|
||||
const passwordStatus = getPasswordStatus();
|
||||
return value && value.length ? (
|
||||
<div className={styles[`progress-${passwordStatus}`]}>
|
||||
<Progress
|
||||
status={passwordProgressMap[passwordStatus]}
|
||||
strokeWidth={6}
|
||||
percent={value.length * 10 > 100 ? 100 : value.length * 10}
|
||||
showInfo={false}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<h3>注册</h3>
|
||||
<Form form={form} name="UserRegister" onFinish={onFinish}>
|
||||
<FormItem
|
||||
name="email"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入邮箱地址!',
|
||||
},
|
||||
{
|
||||
type: 'email',
|
||||
message: '邮箱地址格式错误!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input size="large" placeholder="邮箱" />
|
||||
</FormItem>
|
||||
<Popover
|
||||
getPopupContainer={(node) => {
|
||||
if (node && node.parentNode) {
|
||||
return node.parentNode as HTMLElement;
|
||||
}
|
||||
return node;
|
||||
}}
|
||||
content={
|
||||
open && (
|
||||
<div
|
||||
style={{
|
||||
padding: '4px 0',
|
||||
}}
|
||||
>
|
||||
{passwordStatusMap[getPasswordStatus()]}
|
||||
{renderPasswordProgress()}
|
||||
<div
|
||||
style={{
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<span>请至少输入 6 个字符。请不要使用容易被猜到的密码。</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
overlayStyle={{
|
||||
width: 240,
|
||||
}}
|
||||
placement="right"
|
||||
open={open}
|
||||
>
|
||||
<FormItem
|
||||
name="password"
|
||||
className={
|
||||
form.getFieldValue('password') &&
|
||||
form.getFieldValue('password').length > 0 &&
|
||||
styles.password
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
validator: checkPassword,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input size="large" type="password" placeholder="至少6位密码,区分大小写" />
|
||||
</FormItem>
|
||||
</Popover>
|
||||
<FormItem
|
||||
name="confirm"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '确认密码',
|
||||
},
|
||||
{
|
||||
validator: checkConfirm,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input size="large" type="password" placeholder="确认密码" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="mobile"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入手机号!',
|
||||
},
|
||||
{
|
||||
pattern: /^\d{11}$/,
|
||||
message: '手机号格式错误!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Select
|
||||
size="large"
|
||||
value={prefix}
|
||||
onChange={changePrefix}
|
||||
style={{
|
||||
width: '30%',
|
||||
}}
|
||||
>
|
||||
<Option value="86">+86</Option>
|
||||
<Option value="87">+87</Option>
|
||||
</Select>
|
||||
|
||||
<Input size="large" placeholder="手机号" />
|
||||
</Space.Compact>
|
||||
</FormItem>
|
||||
{/* <Row gutter={8}>
|
||||
<Col span={16}>
|
||||
<FormItem
|
||||
name="captcha"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入验证码!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input size="large" placeholder="验证码" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Button
|
||||
size="large"
|
||||
disabled={!!count}
|
||||
className={styles.getCaptcha}
|
||||
onClick={onGetCaptcha}
|
||||
>
|
||||
{count ? `${count} s` : '获取验证码'}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row> */}
|
||||
<FormItem>
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
size="large"
|
||||
loading={submitting}
|
||||
className={styles.submit}
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
>
|
||||
<span>注册</span>
|
||||
</Button>
|
||||
<Link to="/user/login">
|
||||
<span>使用已有账户登录</span>
|
||||
</Link>
|
||||
</div>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default Register;
|
||||
22
src/pages/user/register/service.ts
Normal file
22
src/pages/user/register/service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { request } from '@umijs/max';
|
||||
|
||||
export interface StateType {
|
||||
status?: 'ok' | 'error';
|
||||
currentAuthority?: 'user' | 'guest' | 'admin';
|
||||
}
|
||||
|
||||
export interface UserRegisterParams {
|
||||
mail: string;
|
||||
password: string;
|
||||
confirm: string;
|
||||
mobile: string;
|
||||
captcha: string;
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
export async function fakeRegister(params: UserRegisterParams) {
|
||||
return request('/api/register', {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
46
src/pages/user/register/style.style.ts
Normal file
46
src/pages/user/register/style.style.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const useStyles = createStyles(({ token }) => {
|
||||
return {
|
||||
main: {
|
||||
width: '368px',
|
||||
margin: '0 auto',
|
||||
h3: { marginBottom: '20px', fontSize: '16px' },
|
||||
},
|
||||
password: {
|
||||
marginBottom: '24px',
|
||||
'.ant-form-item-explain': { display: 'none' },
|
||||
},
|
||||
getCaptcha: {
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
footer: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
submit: {
|
||||
width: '50%',
|
||||
},
|
||||
success: {
|
||||
transition: 'color 0.3s',
|
||||
color: token.colorSuccess,
|
||||
},
|
||||
warning: {
|
||||
transition: 'color 0.3s',
|
||||
color: token.colorWarning,
|
||||
},
|
||||
error: {
|
||||
transition: 'color 0.3s',
|
||||
color: token.colorError,
|
||||
},
|
||||
'progress-pass > .progress': {
|
||||
'.ant-progress-bg': { backgroundColor: token.colorWarning },
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default useStyles;
|
||||
86
src/pages/user/user/components/LevelFormModal.tsx
Normal file
86
src/pages/user/user/components/LevelFormModal.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* @Note:
|
||||
* @Author: 2058827620@qq.com
|
||||
* @Date: 2022-04-03 17:02:15
|
||||
*/
|
||||
|
||||
import { ProForm, ModalForm, ProFormSelect, ProFormText, ProFormTextArea, ProFormInstance } from '@ant-design/pro-components';
|
||||
import { Tree } from 'antd';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
import { userlevelPage } from '@/services/user/grade'
|
||||
|
||||
|
||||
export default ({ modalOpenState, onModalOpenState, onSubmit }) => {
|
||||
const restFormRef = useRef<ProFormInstance>();
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalForm
|
||||
title="投资人-等级"
|
||||
formRef={restFormRef}
|
||||
submitter={{
|
||||
searchConfig: {
|
||||
resetText: '重置',
|
||||
},
|
||||
resetButtonProps: {
|
||||
onClick: () => {
|
||||
restFormRef.current?.resetFields();
|
||||
},
|
||||
},
|
||||
}}
|
||||
onFinish={async (e) => {
|
||||
await onSubmit(e)
|
||||
restFormRef.current?.resetFields();
|
||||
}}
|
||||
open={modalOpenState}
|
||||
onOpenChange={onModalOpenState}
|
||||
>
|
||||
<ProFormText hidden={true} width="md" name="id" />
|
||||
<ProForm.Group>
|
||||
<ProFormSelect
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
name="level"
|
||||
label="等级"
|
||||
fieldProps={{
|
||||
fieldNames: {
|
||||
label: "level",
|
||||
value: "level"
|
||||
},
|
||||
}}
|
||||
request={async (param) => {
|
||||
const { success, data } = await userlevelPage({});
|
||||
return data.records;
|
||||
}}
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="userName"
|
||||
label="用户名"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="phone"
|
||||
label="手机号"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProFormTextArea
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="remark"
|
||||
label="备注"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</ModalForm>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
88
src/pages/user/user/components/TempFormModal.tsx
Normal file
88
src/pages/user/user/components/TempFormModal.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* @Note:
|
||||
* @Author: 2058827620@qq.com
|
||||
* @Date: 2022-04-03 17:02:15
|
||||
*/
|
||||
|
||||
import { ProForm, ModalForm, ProFormSelect, ProFormText, ProFormTextArea, ProFormInstance } from '@ant-design/pro-components';
|
||||
import { Divider } from 'antd';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import FilesManager from '@/components/FilesManage/index';
|
||||
|
||||
export default ({ values, modalOpenState, onModalOpenState, onSubmit }) => {
|
||||
const restFormRef = useRef<ProFormInstance>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalForm
|
||||
title="投资人-转挂"
|
||||
formRef={restFormRef}
|
||||
submitter={{
|
||||
searchConfig: {
|
||||
resetText: '重置',
|
||||
},
|
||||
resetButtonProps: {
|
||||
onClick: () => {
|
||||
restFormRef.current?.resetFields();
|
||||
},
|
||||
},
|
||||
}}
|
||||
initialValues={values}
|
||||
onFinish={async (e) => {
|
||||
await onSubmit(e)
|
||||
restFormRef.current?.resetFields();
|
||||
}}
|
||||
open={modalOpenState}
|
||||
onOpenChange={onModalOpenState}
|
||||
>
|
||||
<div className='text-red-600 '>
|
||||
<div>A:需要转挂的投资人</div>
|
||||
<div>B:转挂该投资人名下</div>
|
||||
<div>该操作把A转挂到B名下</div>
|
||||
</div>
|
||||
<Divider>A:需要转挂的投资人</Divider>
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="userNameSource"
|
||||
label="用户名(A)"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="phoneSource"
|
||||
label="手机号(A)"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<Divider>B:把A转挂该投资人名下</Divider>
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="userNameTarget"
|
||||
label="用户名(B)"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="phoneTarget"
|
||||
label="手机号(B)"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProFormTextArea
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
width="md"
|
||||
name="remark"
|
||||
label="备注"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</ModalForm>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
144
src/pages/user/user/edit.tsx
Normal file
144
src/pages/user/user/edit.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
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';
|
||||
import { usersPage } from '@/services/user/user';
|
||||
|
||||
|
||||
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>
|
||||
<ProFormSelect
|
||||
width="md"
|
||||
colProps={{ xl: 8, md: 12 }}
|
||||
name="formUserId"
|
||||
label="推荐人"
|
||||
fieldProps={{
|
||||
fieldNames: {
|
||||
label: "userName",
|
||||
value: "id"
|
||||
},
|
||||
placeholder: "请输入用户名",
|
||||
showSearch: true
|
||||
}}
|
||||
request={async (param) => {
|
||||
const { success, data } = await usersPage({
|
||||
userName: param.keyWords
|
||||
});
|
||||
data.records.map((i) => {
|
||||
i.userName = i.userName + ' ' + i.phone
|
||||
})
|
||||
return data.records;
|
||||
}}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
|
||||
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
name="userName"
|
||||
label={'用户名'}
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
name="nickName"
|
||||
label={'昵称'}
|
||||
placeholder="请输入昵称"
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProFormText
|
||||
rules={[{ required: true, message: '请输入' }]}
|
||||
name="phone"
|
||||
label={'手机号'}
|
||||
placeholder="请输入手机号"
|
||||
/>
|
||||
|
||||
|
||||
<ProFormText
|
||||
rules={!values.id ? [{ required: true, message: '请输入' }] : []}
|
||||
name="passwd"
|
||||
label={'密码'}
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
|
||||
<ProFormSwitch
|
||||
checkedChildren="正常"
|
||||
unCheckedChildren="禁用"
|
||||
name="status"
|
||||
label="是否禁用"
|
||||
fieldProps={
|
||||
{
|
||||
defaultChecked: true
|
||||
}
|
||||
}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
<ProForm.Group>
|
||||
<ProForm.Item label="头像" name="avatar">
|
||||
<FilesManager
|
||||
fileType="image"
|
||||
defaultValue={values?.avatar}
|
||||
count={1}
|
||||
mode=""
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm.Group>
|
||||
</ProForm>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleForm;
|
||||
366
src/pages/user/user/index.tsx
Normal file
366
src/pages/user/user/index.tsx
Normal file
@@ -0,0 +1,366 @@
|
||||
import { usersPage, usersAdd, usersDelete, usersUpdate, updateUserLevel, userTransferHanging, exportUser } from '@/services/user/user';
|
||||
|
||||
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { useIntl, useAccess } from '@umijs/max';
|
||||
import { message, Tag, Image, Button, Select, Modal } from 'antd';
|
||||
import { ActionType, PageContainer, ProTable } from '@ant-design/pro-components';
|
||||
import UpdateForm from './edit';
|
||||
import { PlusOutlined, LineChartOutlined } from '@ant-design/icons';
|
||||
import { exportData } from '@/utils/func';
|
||||
import LevelFormModal from './components/LevelFormModal';
|
||||
import TempFormModal from './components/TempFormModal';
|
||||
/**
|
||||
* 添加节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleAdd = async (fields) => {
|
||||
const hide = message.loading('正在添加');
|
||||
try {
|
||||
fields.status = fields.status ? 0 : 1
|
||||
await usersAdd({ ...fields });
|
||||
hide();
|
||||
message.success('添加成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('添加失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*
|
||||
* @param fields
|
||||
*/
|
||||
const handleUpdate = async (fields: API.System.Menu) => {
|
||||
const hide = message.loading('正在修改');
|
||||
try {
|
||||
await usersUpdate(fields);
|
||||
hide();
|
||||
message.success('修改成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('修改失败请重试!');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveOne = async (selectedRow) => {
|
||||
const hide = message.loading('正在删除');
|
||||
if (!selectedRow) return true;
|
||||
try {
|
||||
const params = selectedRow;
|
||||
await usersDelete(params);
|
||||
hide();
|
||||
message.success('删除成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error('删除失败,请重试');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const LogTableList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
const [currentRow, setCurrentRow] = useState();
|
||||
const [selectedRowsState, setSelectedRows] = useState([]);
|
||||
const [levelFormModal, setLevelFormModal] = useState(false);
|
||||
const [tempFormModal, setTempFormModal] = useState(false);
|
||||
const [searchParams, setSearchParams] = useState(null);
|
||||
|
||||
|
||||
// 更改等级
|
||||
const handleLevel = async (fields) => {
|
||||
const { success } = await updateUserLevel({ ...fields });
|
||||
if (success) {
|
||||
message.success('等级变更成功');
|
||||
actionRef.current.reload();
|
||||
setLevelFormModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTemp = async (fields) => {
|
||||
const { success } = await userTransferHanging(fields);
|
||||
if (success) {
|
||||
message.success('转挂成功');
|
||||
actionRef.current?.reload();
|
||||
setTempFormModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
dataIndex: 'avatar',
|
||||
valueType: 'image',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'userName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '用户昵称',
|
||||
dataIndex: 'nickName',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '钱包',
|
||||
dataIndex: 'points2',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '收益',
|
||||
dataIndex: 'points2',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '等级',
|
||||
dataIndex: 'level',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '邀请码',
|
||||
dataIndex: 'shareCode',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
return <Tag color={record.status == 1 ? 'green' : 'red'}>{record.status == 1 ? '正常' : '禁用'}</Tag>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '推广码',
|
||||
dataIndex: 'qrCode',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
render: (_, record) => {
|
||||
return record.qrCode ? <Image src={record.qrCode} width={60}></Image> : '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
valueType: 'text',
|
||||
search: false,
|
||||
},
|
||||
// {
|
||||
// title: '操作',
|
||||
// dataIndex: 'option',
|
||||
// width: '220px',
|
||||
// valueType: 'option',
|
||||
// render: (_, record) => [
|
||||
// // <Button
|
||||
// // type="link"
|
||||
// // size="small"
|
||||
// // // hidden={!access.hasPerms('admin/banner/update')}
|
||||
// // 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 handleRemoveOne(record);
|
||||
// // if (success) {
|
||||
// // if (actionRef.current) {
|
||||
// // actionRef.current.reload();
|
||||
// // }
|
||||
// // }
|
||||
// // },
|
||||
// // });
|
||||
// // }}
|
||||
// // >
|
||||
// // 删除
|
||||
// // </Button>,
|
||||
// ],
|
||||
// },
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<LevelFormModal
|
||||
modalOpenState={levelFormModal}
|
||||
onModalOpenState={setLevelFormModal}
|
||||
onSubmit={handleLevel}
|
||||
/>
|
||||
<TempFormModal
|
||||
modalOpenState={tempFormModal}
|
||||
onModalOpenState={setTempFormModal}
|
||||
onSubmit={handleTemp}
|
||||
/>
|
||||
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
key="logList"
|
||||
search={{
|
||||
labelWidth: 120,
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
danger
|
||||
onClick={() => {
|
||||
setTradeFormModal(true)
|
||||
}}
|
||||
>
|
||||
余额变更
|
||||
</Button>,
|
||||
<Button
|
||||
danger
|
||||
onClick={() => {
|
||||
setTradeFormModal(true)
|
||||
}}
|
||||
>
|
||||
账号变更
|
||||
</Button>,
|
||||
<Button
|
||||
danger
|
||||
onClick={() => {
|
||||
setTempFormModal(true)
|
||||
}}
|
||||
>
|
||||
转挂
|
||||
</Button>,
|
||||
<Button
|
||||
danger
|
||||
onClick={() => {
|
||||
setLevelFormModal(true)
|
||||
}}
|
||||
>
|
||||
等级变更
|
||||
</Button>,
|
||||
<Button
|
||||
danger
|
||||
onClick={() => {
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定删除该项吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
let data = selectedRowsState.map((item, index) => {
|
||||
return item.id
|
||||
})
|
||||
const success = await handleRemoveOne(data);
|
||||
if (success) {
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
let _res: any = await exportUser(searchParams)
|
||||
exportData(_res, '用户信息')
|
||||
}}
|
||||
>
|
||||
<LineChartOutlined /> 导出
|
||||
</Button>,
|
||||
<Button
|
||||
type="primary"
|
||||
key="add"
|
||||
onClick={async () => {
|
||||
setCurrentRow(undefined);
|
||||
setModalVisible(true);
|
||||
}}
|
||||
>
|
||||
<PlusOutlined /> 新建
|
||||
</Button>,
|
||||
]}
|
||||
request={async (params, sorter, filter) => {
|
||||
setSearchParams({ token: '', ...params });
|
||||
let { data } = await usersPage(params)
|
||||
return {
|
||||
data: data?.records || [],
|
||||
total: data?.total,
|
||||
};
|
||||
}}
|
||||
columns={columns}
|
||||
rowSelection={{
|
||||
onChange: (_, selectedRows) => {
|
||||
setSelectedRows(selectedRows)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<UpdateForm
|
||||
onSubmit={async (values) => {
|
||||
let success = false;
|
||||
if (values.id) {
|
||||
success = await handleUpdate({ ...values });
|
||||
} else {
|
||||
success = await handleAdd({ ...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;
|
||||
Reference in New Issue
Block a user