Commit 53d77bcf authored by 前端-许佳敏's avatar 前端-许佳敏
parents f2cfd246 a2ae6acf
/*
* @Author: LeeJiancong
* @Date: 2020-08-04 16:47:35
* @LastEditors: LeeJiancong
* @LastEditTime: 2020-08-05 11:00:35
* @LastEditors: XieZhiXiong
* @LastEditTime: 2020-08-25 10:47:21
*/
const memberAbility =
{
......@@ -89,10 +89,9 @@ const memberAbility =
component: '@/pages/member/memberLevel/index',
},
{
path: '/memberAbility/manage/addEquity',
path: '/memberAbility/manage/level/addEquity',
name: 'addEquity',
hideInMenu: true,
hidePageHeader: true,
component: '@/pages/member/memberLevel/addEquity',
},
],
......
......@@ -2,6 +2,7 @@ import React from 'react';
import SchemaForm, {
IAntdSchemaFormProps, createVirtualBox, registerVirtualBox, Schema, SchemaField, FormButtonGroup, Reset, createControllerBox, FormItem,
} from '@formily/antd';
import { Input } from '@formily/antd-components';
import { Button, Space, Row, Col } from 'antd';
import styled from 'styled-components'
import CustomUpload from './components/CustomUpload';
......@@ -114,7 +115,8 @@ const NiceForm: React.FC<NiceFormProps> = props => {
CircleBox,
SchemaFormButtonGroup,
FlexBox,
TableTagList
TableTagList,
Input,
};
const defineComponents = Object.assign(customComponents, components);
......
/*
* @Author: XieZhiXiong
* @Date: 2020-08-20 16:15:59
* @LastEditors: XieZhiXiong
* @LastEditTime: 2020-08-21 17:08:26
* @Description: 基于 NormalTable 简单的可编辑列 Table
*/
import React, { useContext, useState, useEffect, useRef } from 'react';
import {
Card,
Input,
Button,
Form,
message,
} from 'antd';
import { StandardTableProps, EditableCellProps, EditableColumns } from './interface';
import NormalTable from './NormalTable';
const EditableContext = React.createContext<any>({});
interface EditableRowProps {
index: number;
}
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
index,
record,
rules = [],
addonAfter = null,
onSave,
onValidateError,
...restProps
}) => {
const [editing, setEditing] = useState(true);
const inputRef = useRef();
const form = useContext(EditableContext);
const inputId = `${dataIndex}-${index}`; // 拼接 name-index,不然全部展示输入框会警告 id 不唯一的问题
useEffect(() => {
if (editing) {
// inputRef.current.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async e => {
try {
const values = await form.validateFields();
// toggleEdit();
if (onSave) {
onSave({
...record,
[dataIndex]: values[inputId],
});
}
} catch (errInfo) {
if (onValidateError) {
onValidateError(errInfo);
}
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={inputId}
rules={rules}
initialValue={record[dataIndex]}
>
<Input
ref={inputRef}
onPressEnter={save}
onBlur={save}
addonAfter={addonAfter}
/>
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }} onClick={toggleEdit}
>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
const EditableCellTable: React.FC<StandardTableProps> = props => {
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
return (
<NormalTable
components={components}
rowClassName={() => 'editable-row'}
{...props}
/>
);
};
export default React.memo(EditableCellTable);
\ No newline at end of file
/*
* @Author: XieZhiXiong
* @Date: 2020-08-20 16:15:59
* @LastEditors: XieZhiXiong
* @LastEditTime: 2020-08-21 17:07:35
* @Description: 简单封装了分页事件的 Table
*/
import React from 'react';
import { Table } from 'antd';
import classNames from 'classnames';
import { StandardTableProps } from './interface';
import styles from './index.less';
export default class NormalTable extends React.PureComponent<StandardTableProps> {
state = {
page: 1,
size: 10,
};
handlePaginationChange = (page: number, size: number) => {
const { pagination = {}, onPaginationChange } = this.props;
const { current, pageSize } = pagination;
// 内部自己维护 page、size, 单独控制当前页 或 当前页数
if (!('current' in pagination)) {
this.setState({ page: current });
}
if (!('pageSize' in pagination)) {
this.setState({ size: pageSize });
}
if (onPaginationChange) {
onPaginationChange(page, size);
}
};
render() {
const { page, size } = this.state;
const {
columns,
dataSource,
rowKey = 'id',
pagination = {},
loading,
className,
onPaginationChange,
...restProps
} = this.props;
const newPagination: any = pagination ? {
current: page,
pageSize: size,
showSizeChanger: true,
showQuickJumper: true,
onChange: this.handlePaginationChange,
onShowSizeChange: this.handlePaginationChange,
size: 'small',
showTotal: () => `共 ${pagination.total || 0} 条`,
...pagination,
} : false;
return (
<div className={classNames(className)}>
<Table
rowKey={rowKey}
columns={columns}
dataSource={dataSource}
loading={loading}
pagination={newPagination}
{...restProps}
/>
</div>
)
}
}
\ No newline at end of file
import NormalTable from './NormalTable';
import EditableCellTable from './EditableCellTable';
export {
NormalTable as default,
EditableCellTable,
}
\ No newline at end of file
import { TableProps, TablePaginationConfig, ColumnType } from 'antd/lib/table';
export interface StandardTableProps extends TableProps<any> {
pagination?: TablePaginationConfig;
onPaginationChange?: (page: number, size: number) => void;
}
export interface EditableCellProps {
onSave?: (row: {[key: string]: any}) => void,
onValidateError?: (error: {[key: string]: any}) => void,
record?: any;
index?: number;
dataIndex?: string | number;
title?: React.ReactNode;
editable?: boolean;
children?: React.ReactNode;
rules?: Array<any>;
addonAfter?: React.ReactNode;
}
interface EditableColumns extends ColumnType<any> {
dataIndex?: string | number;
editable?: boolean;
rules?: Array<any>;
}
\ No newline at end of file
......@@ -18,3 +18,8 @@ export const STATUS_ENUM = [
value: 0
}
]
// 会员规则类型
export const VIP_RULE_TRANSACTION = 1; // 交易
export const VIP_RULE_LOGIN = 2; // 登录
export const VIP_RULE_COMMENT = 3; // 评论
\ No newline at end of file
......@@ -8,7 +8,7 @@ export enum PageStatus {
}
export const usePageStatus = () => {
const { preview, id = '' } = history.location.query
const { preview, id = '', ...rest } = history.location.query
// 默认预览状态
let pageStatus = PageStatus.PREVIEW
if (preview === '1') {
......@@ -24,6 +24,7 @@ export const usePageStatus = () => {
return {
pageStatus,
id,
preview
preview,
...rest,
}
}
\ No newline at end of file
.headerTop {
display: flex;
align-items: center;
font-size: 20px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
&-icon {
width: 48px;
height: 48px;
line-height: 48px;
border-radius: 4px;
border: 1px solid #DFE1E6;
color: #fff;
text-align: center;
background-color: #8777D9;
}
&-level {
color: #172B4D;
margin: 0 8px 0 12px;
}
&-identity {
width: 72px;
height: 24px;
line-height: 24px;
background-color: #FFEBE6;
border-radius: 4px;
color: #E63F3B;
font-size: 14px;
font-weight: 400;
text-align: center;
}
}
.headerMain {
display: flex;
&-left {
flex: 6;
display: flex;
flex-wrap: wrap;
padding-left: 90px;
&-option {
display: flex;
width: calc(100% / 3);
margin-bottom: 17px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #6B778C;
padding-right: 20px;
&:nth-of-type(n + 4) {
margin: 0;
}
div {
flex: 1;
&:nth-last-of-type(1) {
flex: 2;
}
}
}
}
&-right {
flex: 1;
text-align: right;
}
}
.extra {
&-main {
display: flex;
&-content {
position: relative;
flex: 1;
&:nth-last-of-type(1) {
flex: 2.5;
}
}
.left {
display: flex;
align-items: center;
padding: 35px 0 28px 8%;
background: rgba(250, 251, 252, 1);
border-radius: 4px;
.icon {
position: absolute;
left: 0;
top: 0;
width: 72px;
height: 24px;
color: #fff;
background-color: #42526E;
border-radius: 4px 0px 4px 0px;
text-align: center;
}
.input {
display: flex;
justify-content: center;
&-main {
position: relative;
width: 128px;
height: 38px;
padding-bottom: 8px;
&::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-color: #DFE1E6;
}
// :global {
// .ant-input {
// border: none;
// box-shadow: none;
// background-color: transparent;
// }
// }
&-com {
width: 100%;
height: 100%;
border: 0;
outline: none;
background-color: rgba(0, 0, 0, 0);
font-size: 32px;
font-weight: 500;
color: #172B4D;
&:focus {
border: none;
box-shadow: none;
background-color: transparent;
}
}
}
}
}
.right {
padding: 27px 40px;
}
}
}
This diff is collapsed.
......@@ -7,51 +7,6 @@
margin-left: 16px;
}
.headerMain {
display: flex;
&-left {
flex : 6;
display : flex;
flex-wrap : wrap;
padding-left: 90px;
&-option {
display : flex;
width : calc(100% / 3);
margin-bottom: 17px;
font-size : 14px;
font-family : PingFangSC-Regular, PingFang SC;
font-weight : 400;
color : #6B778C;
padding-right: 20px;
&:nth-of-type(n + 4) {
margin: 0;
}
div {
flex: 1;
&:nth-last-of-type(1) {
flex: 2;
}
}
}
}
&-right {
flex : 1;
text-align: right;
.saveBtn {
color : #fff;
background: @main-color
}
}
}
.nameCellTitle {
color : @main-color;
cursor: pointer;
......@@ -85,116 +40,7 @@
background-size: 24px 20px;
}
.headerTop {
display : flex;
align-items: center;
font-size : 20px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
&-icon {
width : 48px;
height : 48px;
line-height : 48px;
border-radius : 4px;
border : 1px solid #DFE1E6;
color : #fff;
text-align : center;
background-color: #8777D9;
}
&-level {
color : #172B4D;
margin: 0 8px 0 12px;
}
&-identity {
width : 72px;
height : 24px;
line-height : 24px;
background-color: #FFEBE6;
border-radius : 4px;
color : #E63F3B;
font-size : 14px;
font-weight : 400;
text-align : center;
}
}
h3 {
margin-bottom: 20px;
}
\ No newline at end of file
.extra {
margin-bottom: 48px;
&-main {
display: flex;
&-content {
position: relative;
flex : 1;
height : 104px;
&:nth-last-of-type(1) {
flex: 2.5;
}
}
.left {
display : flex;
justify-content: center;
align-items : center;
.icon {
position : absolute;
left : 0;
top : 0;
width : 72px;
height : 24px;
color : #fff;
background-color: #42526E;
border-radius : 4px 0px 4px 0px;
text-align : center;
}
.input {
display : flex;
justify-content: center;
&-main {
position : relative;
width : 128px;
height : 38px;
padding-bottom: 8px;
&::after {
content : '';
position : absolute;
left : 0;
bottom : 0;
width : 100%;
height : 1px;
background-color: #DFE1E6;
}
&-com {
width : 100%;
height : 100%;
border : 0;
outline : none;
background-color: rgba(0, 0, 0, 0);
font-size : 32px;
font-family : PingFangSC-Medium, PingFang SC;
font-weight : 500;
color : #172B4D;
}
}
}
}
.right {
padding: 27px 40px;
}
}
}
\ No newline at end of file
import React, { useRef } from 'react';
import { history } from 'umi';
import { Button, Card } from 'antd';
import { Button, Card, message } from 'antd';
import { StandardTable } from 'god';
import { ColumnType } from 'antd/lib/table/interface';
import { createFormActions } from '@formily/antd';
......@@ -17,7 +17,11 @@ const formActions = createFormActions();
const fetchData = async (params: any) => {
const res = await PublicApi.getMemberManageLevelPage(params);
if (res.code === 1000) {
return res.data;
}
return {};
};
const MemberLevel: React.FC<[]> = () => {
......@@ -28,13 +32,11 @@ const MemberLevel: React.FC<[]> = () => {
title: 'ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '会员等级',
dataIndex: 'level',
align: 'center',
key: 'level',
render: (text: any, record: any) => (
<div className={styles[`levelIcon${record.level}`]}></div>
),
......@@ -46,7 +48,7 @@ const MemberLevel: React.FC<[]> = () => {
key: 'levelTag',
render: (text: any, record: any) => (
<EyePreview
url={`/memberAbility/manage/addEquity?id=${record.id}&preview=1`}
url={`/memberAbility/manage/level/addEquity?id=${record.id}&preview=1`}
>
{text}
</EyePreview>
......@@ -54,45 +56,38 @@ const MemberLevel: React.FC<[]> = () => {
},
{
title: '会员等级类型',
dataIndex: 'memberLevelType',
dataIndex: 'memberLevelTypeName',
align: 'center',
key: 'memberLevelType',
},
{
title: '升级分值标签',
dataIndex: 'scoreTag',
align: 'center',
key: 'scoreTag',
},
{
title: '会员角色名称',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '角色类型',
dataIndex: 'roleTypeName',
align: 'center',
key: 'roleTypeName',
},
{
title: '会员类型',
dataIndex: 'memberTypeName',
align: 'center',
key: 'memberTypeName',
},
{
title: '升级阈值',
dataIndex: 'levelUpPoint',
align: 'center',
key: 'levelUpPoint',
},
{
title: '状态',
dataIndex: 'status',
align: 'center',
key: 'status',
render: (text: any, record: any) => (
<StatusSwitch
handleConfirm={() => handleModify(record)}
......@@ -109,7 +104,7 @@ const MemberLevel: React.FC<[]> = () => {
<Button
type="link"
onClick={() =>
history.push(`/memberAbility/manage/addEquity?id=${record.id}`)
history.push(`/memberAbility/manage/level/addEquity?id=${record.id}`)
}
>
设置
......@@ -118,12 +113,21 @@ const MemberLevel: React.FC<[]> = () => {
},
];
// status: 0-无效 1-生效
const handleModify = record => {
PublicApi.postMemberManageLevelStatus({
const disabled = record.status === 0;
PublicApi.postMemberManageLevelUpdatestatus({
id: record.id,
status: record.status === 1 ? 0 : 1,
status: disabled ? 1 : 0,
}, {
ctlType: 'none',
}).then(res => {
if (res.code === 1000) return ref.current.reload();
if (res.code === 1000) {
const msg = disabled ? '启用成功' : '禁用成功'
message.success(msg);
ref.current.reload();
}
});
};
......@@ -139,13 +143,13 @@ const MemberLevel: React.FC<[]> = () => {
actions={formActions}
onSubmit={values => ref.current.reload(values)}
effects={($, actions) => {
$('onFormInputChange').subscribe(state => {
ref.current.reload(state.values);
});
// $('onFieldInputChange', 'levelTag').subscribe(state => {
// ref.current.reload(state.values);
// });
useStateFilterSearchLinkageEffect(
$,
actions,
'cond',
'levelTag',
FORM_FILTER_PATH,
);
}}
......
......@@ -15,7 +15,7 @@ export const levelSchema: ISchema = {
grid: true,
},
properties: {
name: {
levelTag: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
......@@ -24,6 +24,34 @@ export const levelSchema: ISchema = {
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
rowStyle: {
flexWrap: 'nowrap',
},
colStyle: {
marginLeft: 20,
},
},
properties: {
roleName: {
type: 'string',
'x-component': 'Input',
'x-component-props': {
placeholder: '角色名称',
allowClear: true,
},
},
submit: {
'x-component': 'Submit',
'x-component-props': {
children: '查询',
},
},
},
},
},
},
},
......
import React, { useState, useRef, ReactNode } from 'react';
import { history } from 'umi';
import React, { useState, useEffect } from 'react';
import {
Card,
Button,
message,
} from 'antd';
import { PublicApi } from '@/services/api';
import { VIP_RULE_TRANSACTION, VIP_RULE_LOGIN, VIP_RULE_COMMENT } from '@/constants';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { Card, Input, Button } from 'antd';
import { ContainerOutlined } from '@ant-design/icons';
import { StandardTable } from 'god';
import { ColumnType } from 'antd/lib/table/interface';
import { PublicApi } from '@/services/api';
import { EditableCellTable } from '@/components/PolymericTable';
import { EditableCellProps, EditableColumns } from '@/components/PolymericTable/interface';
import { GetMemberManageLevelRulePageResponseDetail } from '@/services';
const MemberUpgradeRule: React.FC<[]> = () => {
const PAGE_SIZE = 10;
const [page, setPage] = useState(1);
const [size, setSize] = useState(PAGE_SIZE);
const [total, setTotal] = useState(0);
const [dataSource, setDataSource] = useState<GetMemberManageLevelRulePageResponseDetail[]>([]);
const [listLoading, setListLoading] = useState(true);
const [submitLoading, setSubmitLoading] = useState(false);
const [submitDisabled, setSubmitDisabled] = useState(false);
const fetchData = async (params: any) => {
const getRuleList = async (params) => {
setListLoading(true);
const res = await PublicApi.getMemberManageLevelRulePage(params);
return res.data;
};
const MemberUpgradeRule: React.FC<[]> = () => {
const ref = useRef({});
if (res.code === 1000) {
const { data = [], totalCount } = res.data;
setDataSource(data);
setTotal(totalCount);
}
setListLoading(false);
};
useEffect(() => {
getRuleList({
current: page,
pageSize: size,
});
}, [page, size]);
const columns: ColumnType<any>[] = [
const columns: EditableColumns[] = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '项目',
dataIndex: 'ruleName',
align: 'center',
key: 'ruleName',
render: (text: any, record: any) => <span>{text}</span>,
},
{
title: '项目说明',
dataIndex: 'remark',
align: 'center',
key: 'remark',
},
{
title: '可获取的分值',
dataIndex: 'point',
dataIndex: 'score',
align: 'center',
key: 'point',
render: (text: any, record: any) => {
let component: ReactNode = null;
component =
record.id === 1 ? (
<Input
addonAfter="%"
value={record.point}
onChange={e => {
e.preventDefault();
record.point = e.target.value;
}}
/>
) : (
<Input value={record.point} />
);
return component;
},
width: '30%',
editable: true,
},
];
const handleSubmit = () => {};
const handlePaginationChange = (page: number, size: number) => {
setPage(page);
setSize(size);
};
// 重新保存 dataSource
const handleSave = row => {
const newData = [...dataSource];
const index = newData.findIndex(item => item.id === row.id);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
score: +row.score,
});
setDataSource(newData);
};
// 表单元素校验失败事件
const handleValidateError = errInfo => {
// do something
};
const handleSubmit = async () => {
if (!dataSource.length) {
return;
}
const payload = dataSource.map(item => ({
id: item.id,
score: item.score,
}));
setSubmitLoading(true);
const res = await PublicApi.postMemberManageLevelRuleUpdatescore({
items: payload,
}, {
ctlType: 'none',
});
if (res.code === 1000) {
message.success('保存成功');
getRuleList({
current: page,
pageSize: size,
});
}
setSubmitLoading(false);
};
const rulesMap = {
[VIP_RULE_TRANSACTION]: [
{
pattern: /^([0]|[1-9]+[0-9]*)(\.[0-9]+)?$/,
message: '请输入整数或小数',
},
],
[VIP_RULE_LOGIN]: [
{
pattern: /^[0]$|^[1-9]+[0-9]*$/,
message: '请输入整数数值',
},
],
[VIP_RULE_COMMENT]: [
{
pattern: /^[0]$|^[1-9]+[0-9]*$/,
message: '请输入整数数值',
},
],
};
const newColumns: any = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record, index): EditableCellProps => ({
onSave: handleSave,
onValidateError: handleValidateError,
record,
index,
dataIndex: col.dataIndex,
title: col.title,
editable: col.editable || false,
rules: [
{
required: true,
message: '请输入相应值',
},
...(rulesMap[record.ruleTypeEnum] || []),
],
addonAfter: record.ruleTypeEnum === VIP_RULE_TRANSACTION ? '%' : null,
}),
};
});
return (
<PageHeaderWrapper
......@@ -68,6 +170,8 @@ const MemberUpgradeRule: React.FC<[]> = () => {
<Button
type="primary"
icon={<ContainerOutlined />}
loading={submitLoading}
disabled={submitDisabled}
onClick={handleSubmit}
>
保存
......@@ -75,11 +179,15 @@ const MemberUpgradeRule: React.FC<[]> = () => {
}
>
<Card>
<StandardTable
tableProps={{ rowKey: 'id' }}
columns={columns}
currentRef={ref}
fetchTableData={(params: any) => fetchData(params)}
<EditableCellTable
dataSource={dataSource}
columns={newColumns}
loading={listLoading}
pagination={{
pageSize: size,
total,
}}
onPaginationChange={handlePaginationChange}
/>
</Card>
</PageHeaderWrapper>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment