Commit 593349ab authored by 前端-许佳敏's avatar 前端-许佳敏

fix: 补充组件问题

parent 94c91d13
/*
* @Author: XieZhiXiong
* @Date: 2021-06-10 14:30:16
* @LastEditors: XieZhiXiong
* @LastEditTime: 2021-06-10 15:29:04
* @Description: 区域选择单个Select
*/
import React from 'react';
import { Select } from 'antd';
import { SelectProps } from 'antd/lib/select';
interface IProps extends SelectProps<string> {
/**
* 选项
*/
options: {
/**
* 描述
*/
label: string,
/**
* 值
*/
value: any,
}[],
}
const AreaSelectItem = (props: IProps) => {
const {
disabled,
value,
options,
} = props;
const current = options.find((item) => item.value === value);
if (!disabled) {
return (
<Select
style={{
width: '100%',
}}
{...props}
>
{options.map((item) => (
<Select.Option
key={item.value}
value={item.value}
>
{item.label}
</Select.Option>
))}
</Select>
);
}
return (
<div>{current?.label}</div>
);
};
export default AreaSelectItem;
/*
* @Author: XieZhiXiong
* @Date: 2021-06-10 14:27:37
* @LastEditors: XieZhiXiong
* @LastEditTime: 2021-06-10 16:00:07
* @Description: 区域选择
*/
import React, { useState, useEffect } from 'react';
import { Row, Col } from 'antd';
import SelectItem from './SelectItem';
import { getMemberAreaCity, getMemberAreaDistrict, getMemberAreaProvince } from '@/services/MemberV2Api';
function isObj(value) {
return typeof value === 'object';
};
type AreaCodeType = {
/**
* 区域编码
*/
value: string
/**
* 区域名称
*/
label: string
}
const AreaSelect = (props) => {
const {
mutators,
editable,
} = props;
const value = isObj(props.value) ? props.value : {};
const XComponentProps = props.props['x-component-props'] || {};
const SelectProps = {
...XComponentProps,
disabled: !editable || XComponentProps.disabled,
};
const [provinceList, setProvinceList] = useState<AreaCodeType[]>([]);
const [cityList, setCityList] = useState<AreaCodeType[]>([]);
const [districtList, setDistrictList] = useState<AreaCodeType[]>([]);
const [provinceLoading, setProvinceLoading] = useState(false);
const [cityLoading, setCityLoading] = useState(false);
const [districtLoading, setDistrictLoading] = useState(false);
const [provinceValue, setProvinceValue] = useState<string | undefined>('');
const [cityValue, setCityValue] = useState<string | undefined>('');
const [districtValue, setDistrictValue] = useState<string | undefined>('');
const getProvinceList = async () => {
setProvinceLoading(true);
const res = await getMemberAreaProvince();
if (res.code === 1000) {
setProvinceList(res.data.map((item) => ({ label: item.name, value: item.code })));
}
setProvinceLoading(false);
};
const getCityList = async (code?: string) => {
if (!code) {
return;
}
setCityLoading(true);
const res = await getMemberAreaCity({ code });
if (res.code === 1000) {
setCityList(res.data.map((item) => ({ label: item.name, value: item.code })));
}
setCityLoading(false);
};
const getDistrictList = async (code?: string) => {
if (!code) {
return;
}
setDistrictLoading(true);
const res = await getMemberAreaDistrict({ code });
if (res.code === 1000) {
setDistrictList(res.data.map((item) => ({ label: item.name, value: item.code })));
}
setDistrictLoading(false);
};
useEffect(() => {
const {
provinceCode,
cityCode,
districtCode,
} = value;
setProvinceValue(provinceCode);
setCityValue(cityCode);
setDistrictValue(districtCode);
}, [value]);
useEffect(() => {
getProvinceList();
}, []);
useEffect(() => {
getCityList(provinceValue);
}, [provinceValue]);
useEffect(() => {
getDistrictList(cityValue);
}, [cityValue]);
const handleProvinceChange = (next: string) => {
setProvinceValue(next);
setCityValue(undefined);
setCityList([]);
setDistrictList([]);
setDistrictValue(undefined);
mutators.change({ ...value, provinceCode: next, cityCode: undefined, districtCode: undefined });
};
const handleCityChange = (next: string) => {
setCityValue(next);
setDistrictList([]);
setDistrictValue(undefined);
mutators.change({ ...value, cityCode: next, districtCode: undefined });
};
const handleDistrictChange = (next: string) => {
setDistrictValue(next);
mutators.change({ ...value, districtCode: next });
};
return (
<div
style={{
width: '100%',
}}
>
<Row gutter={16}>
<Col span={8}>
<SelectItem
placeholder="- 省 -"
allowClear
{...SelectProps}
value={provinceValue}
options={provinceList}
loading={provinceLoading}
onChange={handleProvinceChange}
/>
</Col>
<Col span={8}>
<SelectItem
placeholder="- 市 -"
allowClear
{...SelectProps}
value={cityValue}
options={cityList}
loading={cityLoading}
onChange={handleCityChange}
/>
</Col>
<Col span={8}>
<SelectItem
placeholder="- 县 / 区 -"
allowClear
{...SelectProps}
value={districtValue}
options={districtList}
loading={districtLoading}
onChange={handleDistrictChange}
/>
</Col>
</Row>
</div>
);
};
AreaSelect.isFieldComponent = true;
export default AreaSelect
import React from 'react';
import {
Steps,
Tabs,
} from 'antd';
import MellowCard from '@/components/MellowCard';
import styles from './index.less';
interface AuditProcessProp {
outerVerifyCurrent?: number;
innerVerifyCurrent?: number;
outerVerifySteps?: { step: number, stepName: string, roleName: string }[];
innerVerifySteps?: { step: number, stepName: string, roleName: string }[];
};
const AuditProcess: React.FC<AuditProcessProp> = ({
outerVerifyCurrent = 0,
innerVerifyCurrent = 0,
outerVerifySteps = [],
innerVerifySteps = [],
}) => (
<MellowCard>
<Tabs onChange={() => {}}>
<Tabs.TabPane tab="外部审核流程" key="1">
<Steps style={{ marginTop: 30 }} progressDot current={outerVerifyCurrent}>
{outerVerifySteps.map(item => (
<Steps.Step
key={item.step}
title={item.stepName}
description={item.roleName}
/>
))}
</Steps>
</Tabs.TabPane>
<Tabs.TabPane tab="内部审核流程" key="2">
<Steps style={{ marginTop: 30 }} progressDot current={innerVerifyCurrent}>
{innerVerifySteps.map(item => (
<Steps.Step
key={item.step}
title={item.roleName}
description={item.stepName}
/>
))}
</Steps>
</Tabs.TabPane>
</Tabs>
</MellowCard>
);
export default AuditProcess;
\ No newline at end of file
.basicInfo {
.descriptions {
:global {
.ant-descriptions-item-label {
flex: 0 0 128px;
color: rgba(107, 119, 140, 1);
}
}
}
}
\ No newline at end of file
import React, { useState } from 'react';
import {
Row,
Col,
Descriptions,
} from 'antd';
import MellowCard from '@/components/MellowCard';
import {
MEMBER_TYPE_CHANNEL_CORPORATE,
MEMBER_TYPE_CHANNEL_INDIVIDUAL,
} from '@/constants/const/member';
import { renderFieldTypeContent } from '../../utils';
import PicWrap from '../PicWrap';
import FlowRecords, { InnerHistoryItem, OuterHistoryItem } from '../FlowRecords';
import styles from './index.less';
interface BasicInfoProps {
basic?: {
account?: string,
phone?: string,
email?: string,
created?: string,
};
channel?: {
memberType?: number,
level?: string,
type?: string,
areas?: string[],
desc?: string,
};
extra?: {
groupName: string,
elements: {
/**
* 注册资料id
*/
id?: number,
/**
* 字段名称
*/
fieldName?: string,
/**
* 中文名称
*/
fieldLocalName?: string,
/**
* 字段类型
*/
fieldType?: string,
/**
* 字段类型附加属性(该参数为map)
*/
attr?: { [key: string]: any },
/**
* 字段长度
*/
fieldLength?: number,
/**
* 是否可为空0-不能为空1-可以为空
*/
fieldEmpty?: number,
/**
* 字段顺序
*/
fieldOrder?: number,
/**
* 帮助信息
*/
fieldRemark?: string,
/**
* 枚举标签列表
*/
fieldEnum?: {
value?: number,
label?: string,
}[],
/**
* 字段校验规则枚举:0-无校验规则,1-邮箱规则,2-手机号码规则,3-身份证规则,4-电话号码规则
*/
ruleEnum?: number,
/**
* 校验规则的正则表达式
*/
pattern?: string,
/**
* 校验错误的提示语
*/
msg?: string,
/**
* 值
*/
fieldValue?: any,
/**
* 是否禁用
*/
disabled?: boolean,
}[],
}[];
outerHistory?: OuterHistoryItem[];
innerHistory?: InnerHistoryItem[];
channelRender?: React.ReactNode; // 自定义渲染渠道信息
}
const BasicInfo: React.FC<BasicInfoProps> = ({
basic = {},
channel = {},
extra = [],
outerHistory = [],
innerHistory = [],
channelRender,
}) => {
return (
<div className={styles.basicInfo}>
<Row gutter={[0, 24]}>
<Col span={24}>
<MellowCard
title="基本信息"
>
<Descriptions column={2} className={styles.descriptions}>
<Descriptions.Item label="登录账户">{basic.account}</Descriptions.Item>
<Descriptions.Item label="注册手机号">{basic.phone}</Descriptions.Item>
<Descriptions.Item label="注册邮箱">{basic.email}</Descriptions.Item>
<Descriptions.Item label="申请时间">{basic.created}</Descriptions.Item>
</Descriptions>
</MellowCard>
</Col>
{
(
channel.memberType === MEMBER_TYPE_CHANNEL_CORPORATE ||
channel.memberType === MEMBER_TYPE_CHANNEL_INDIVIDUAL
) && (
<Col span={24}>
<MellowCard
title="渠道信息"
>
{!channelRender ? (
<Descriptions column={2} className={styles.descriptions}>
<Descriptions.Item label="渠道级别">{channel.level}</Descriptions.Item>
<Descriptions.Item label="渠道类型">{channel.type}</Descriptions.Item>
<Descriptions.Item label="代理地市">
<Row gutter={[16, 16]} style={{ flex: 1 }}>
{
channel.areas ?
channel.areas.map(item => (
<Col key={item} span={12}>{item}</Col>
)) :
null
}
</Row>
</Descriptions.Item>
<Descriptions.Item label="渠道描述">{channel.desc}</Descriptions.Item>
</Descriptions>
) : channelRender}
</MellowCard>
</Col>
)
}
{extra.map((item, index) => (
<Col key={index} span={24}>
<MellowCard
title={item.groupName}
>
<Row gutter={20}>
<Col span={12}>
<Descriptions column={1} className={styles.descriptions}>
{item.elements.map((ele, index) => (index + 1) % 2 !== 0 ? (
<Descriptions.Item key={index} label={ele.fieldLocalName}>
{renderFieldTypeContent(ele.fieldType as any, ele.fieldValue)}
</Descriptions.Item>
) : null)}
</Descriptions>
</Col>
<Col span={12}>
<Descriptions column={1} className={styles.descriptions}>
{item.elements.map((ele, index) => (index + 1) % 2 === 0 ? (
<Descriptions.Item key={index} label={ele.fieldLocalName}>
{renderFieldTypeContent(ele.fieldType as any, ele.fieldValue)}
</Descriptions.Item>
) : null)}
</Descriptions>
</Col>
</Row>
</MellowCard>
</Col>
))}
<Col span={24}>
<FlowRecords
outerHistory={outerHistory}
innerHistory={innerHistory}
/>
</Col>
</Row>
</div>
);
};
export default BasicInfo;
@import '../../../../global/styles/utils.less';
.equityInfo {
.container {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 24px;
height: 192px;
background: #ffffff;
border-radius: 8px;
&-title {
margin-bottom: 20px;
line-height: 24px;
font-size: 14px;
font-weight: 500;
color: rgba(23, 43, 77, 1);
}
&-content {
}
}
.tofo {
display: flex;
padding: 0 0 4px;
margin: 0;
min-height: 101px;
overflow-x: auto;
.silkyScrollbar();
&-item {
flex: 0 0 33.333%;
list-style: none;
&-logo {
width: 40px;
height: 40px;
margin: 0 auto 4px;
text-align: center;
> img {
width: 100%;
height: 100%;
}
}
&-title {
margin-bottom: 9px;
line-height: 22px;
font-size: 12px;
font-weight: 400;
color: rgba(23, 43, 77, 1);
text-align: center;
:global {
.anticon {
margin-left: 4px;
}
}
}
&-extra {
text-align: center;
}
&-tag {
line-height: 22px;
padding: 0 6px;
font-size: 12px;
font-weight: 400;
text-align: center;
border-radius: 4px;
&-price {
color: rgba(101, 84, 192, 1);
background: rgba(234, 230, 255, 1);
}
&-recurrence {
color: rgba(230, 63, 59, 1);
background: rgba(255, 235, 230, 1);
}
&-integral {
color: rgba(255, 153, 31, 1);
background: rgba(255, 250, 230, 1);
}
}
}
}
.exhibition {
display: flex;
align-items: center;
&-left {
flex: 1;
}
&-right {
flex-shrink: 0;
}
&-title {
line-height: 20px;
margin-bottom: 24px;
font-size: 12px;
font-weight: 400;
color: rgba(107, 119, 140, 1);
}
&-amount {
font-size: 24px;
font-weight: 500;
color: rgba(23, 43, 77, 1);
line-height: 24px;
> span {
margin-left: 8px;
line-height: 12px;
font-size: 12px;
font-weight: 400;
color: rgba(107, 119, 140, 1);
}
}
&-logo {
width: 72px;
height: 72px;
> img {
width: 100%;
height: 100%;
}
}
}
}
\ No newline at end of file
This diff is collapsed.
import React from 'react';
import {
Tabs,
Badge,
} from 'antd';
import PolymericTable from '@/components/PolymericTable';
import { EditableColumns } from '@/components/PolymericTable/interface';
import MellowCard from '@/components/MellowCard';
import StatusTag from '@/components/StatusTag';
import {
MEMBER_INNER_STATUS_BADGE_COLOR,
MEMBER_OUTER_STATUS_TYPE,
} from '../../constant';
export interface InnerHistoryItem {
createTime?: string,
id?: number,
innerStatus?: number,
innerStatusName?: string,
operation?: string,
operatorJobTitle?: string,
operatorName?: string,
operatorOrgName?: string,
remark?: string,
};
export interface OuterHistoryItem {
createTime?: string,
id?: number,
operation?: string,
operatorRoleName?: string,
outerStatus?: number,
outerStatusName?: string,
remark?: string,
};
interface FlowRecordsProps {
outerHistory?: OuterHistoryItem[];
innerHistory?: InnerHistoryItem[];
};
const FlowRecords: React.FC<FlowRecordsProps> = ({ outerHistory = [], innerHistory = [] }) => {
const outerColumns: EditableColumns<OuterHistoryItem>[] = [
{
title: '序号',
dataIndex: 'index',
align: 'center',
render: (text, record, index) => index + 1,
},
{
title: '操作角色',
dataIndex: 'operatorRoleName',
align: 'center',
},
{
title: '状态',
dataIndex: 'outerStatusName',
align: 'center',
render: (text, record) => (
<StatusTag type={MEMBER_OUTER_STATUS_TYPE[record.outerStatus as number]} title={text} />
),
},
{
title: '操作',
dataIndex: 'operation',
align: 'center',
},
{
title: '操作时间',
dataIndex: 'createTime',
align: 'center',
ellipsis: true,
},
{
title: '审核意见',
dataIndex: 'remark',
align: 'center',
ellipsis: true,
},
];
const innerColumns: EditableColumns<InnerHistoryItem>[] = [
{
title: '序号',
dataIndex: 'index',
align: 'center',
render: (text, record, index) => index + 1,
},
{
title: '操作人',
dataIndex: 'operatorName',
align: 'center',
},
{
title: '部门',
dataIndex: 'operatorOrgName',
align: 'center',
},
{
title: '职位',
dataIndex: 'operatorJobTitle',
align: 'center',
},
{
title: '状态',
dataIndex: 'innerStatusName',
align: 'center',
render: (text, record) => (
<Badge color={MEMBER_INNER_STATUS_BADGE_COLOR[record.innerStatus as number] || '#606266'} text={text} />
),
},
{
title: '操作',
dataIndex: 'operation',
align: 'center',
},
{
title: '操作时间',
dataIndex: 'createTime',
align: 'center',
ellipsis: true,
},
{
title: '审核意见',
dataIndex: 'remark',
align: 'center',
ellipsis: true,
},
];
return (
<MellowCard>
<Tabs onChange={() => { }}>
<Tabs.TabPane tab="流转记录" key="1">
<PolymericTable
dataSource={outerHistory}
columns={outerColumns}
loading={false}
pagination={null}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="内部单据流转记录" key="2">
<PolymericTable
dataSource={innerHistory}
columns={innerColumns}
loading={false}
pagination={null}
/>
</Tabs.TabPane>
</Tabs>
</MellowCard>
);
};
export default FlowRecords;
\ No newline at end of file
.head {
display: flex;
align-items: center;
font-size: 18px;
font-weight: 500;
&-prefix {
width: 48px;
height: 48px;
line-height: 48px;
border-radius: 4px;
border: 1px solid #DFE1E6;
color: #fff;
text-align: center;
background-color: #8777D9;
}
&-name {
color: #303133;
margin: 0 8px 0 12px;
}
}
import React from 'react';
import LevelBrand from '../LevelBrand';
import styles from './index.less';
export interface HeadInfoProps {
info: {
name?: string,
level?: number,
};
extra?: React.ReactNode;
};
const HeadInfo: React.FC<HeadInfoProps> = ({ info, extra }) => (
<div className={styles.head}>
<div className={styles['head-prefix']}>
{ info && info.name && info.name.length ? info.name[0] : '' }
</div>
<div className={styles['head-name']}>
{info.name || ''}
</div>
{!extra ? (
<LevelBrand level={info.level} />
) : extra}
</div>
);
export default HeadInfo;
\ No newline at end of file
.brand {
display: inline-block;
width: 54px;
height: 16px;
line-height: 16px;
> img {
width: 100%;
height: 100%;
}
}
\ No newline at end of file
import React from 'react';
import p_level1 from '@/asserts/level1.png';
import p_level2 from '@/asserts/level2.png';
import p_level3 from '@/asserts/level3.png';
import p_level4 from '@/asserts/level4.png';
import styles from './index.less';
enum levelEnum {
'青铜会员' = 1,
'白银会员' = 2,
'黄金会员' = 3,
'钻石会员' = 4,
}
export interface LevelBrandProps {
level: levelEnum;
};
const PIC_MAP = {
1: p_level1,
2: p_level2,
3: p_level3,
4: p_level4,
};
const LevelBrand: React.FC<LevelBrandProps> = ({ level }) => {
const current = PIC_MAP[level] || '';
return (
<div className={styles.brand}>
{current && <img src={current} />}
</div>
);
};
export default LevelBrand;
\ No newline at end of file
@card-prefix: card;
.levelInfo {
.infoWrap {
display: flex;
&-left {
margin-right: 40px;
flex-shrink: 0;
}
&-right {
flex: 1;
}
}
.card {
width: 338px;
height: 190px;
padding: 32px 24px 16px 24px;
background: linear-gradient(135deg, rgba(255, 235, 225, 1) 0%, rgba(251, 196, 167, 1) 100%);
box-shadow: 0 1px 2px 0 rgba(23, 43, 77, 0.12);
border-radius: 8px;
position: relative;
z-index: 0;
&-name {
min-height: 33px;
margin-bottom: 48px;
line-height: 33px;
font-size: 24px;
font-weight: 500;
color: rgba(164, 114, 104, 1);
}
&-progress {
padding: 0 72px 0 0;
:global {
.ant-progress-bg {
background: rgba(164, 114, 104, 1) !important;
}
.ant-progress-inner {
background-color: rgb(233, 191, 169, 1) !important;
}
}
}
&-txt {
display: flex;
justify-content: space-between;
}
&-experience {
font-size: 12px;
font-weight: 400;
color: rgba(164, 114, 104, 1);
line-height: 20px;
}
&-higher {
font-size: 12px;
font-weight: 400;
color: rgba(186, 140, 115, 1);
line-height: 20px;
}
&-level2 {
background: linear-gradient(135deg, rgba(233, 244, 255, 1) 0%, rgba(164, 185, 223, 1) 100%);
box-shadow: 0 1px 2px 0 rgba(23, 43, 77, 0.12);
.@{card-prefix} {
&-name {
color: rgba(91, 111, 146, 1);
}
&-progress {
:global {
.ant-progress-bg {
background: rgba(87, 108, 143, 1) !important;
}
.ant-progress-inner {
background-color: rgba(171, 192, 220, 1) !important;
}
}
}
&-experience {
color: rgba(91, 111, 146, 1);
}
&-higher {
color: rgba(133, 150, 179, 1);
}
}
}
&-level3 {
background: linear-gradient(135deg, rgba(255, 245, 222, 1) 0%, rgba(212, 191, 146, 1) 100%);
box-shadow: 0 1px 2px 0 rgba(23, 43, 77, 0.12);
.@{card-prefix} {
&-name {
color: rgba(141, 107, 56, 1);
}
&-progress {
:global {
.ant-progress-bg {
background: rgba(141, 107, 56, 1) !important;
}
.ant-progress-inner {
background-color: rgba(214, 195, 157, 1) !important;
}
}
}
&-experience {
color: rgba(141, 107, 56, 1);
}
&-higher {
color: rgba(174, 154, 113, 1);
}
}
}
&-level4 {
background: linear-gradient(135deg, rgba(232, 239, 255, 1) 0%, rgba(157, 162, 194, 1) 100%);
box-shadow: 0 1px 2px 0 rgba(23, 43, 77, 0.12);
.@{card-prefix} {
&-name {
color: rgba(89, 91, 113, 1);
}
&-progress {
:global {
.ant-progress-bg {
background: rgba(89, 91, 113, 1) !important;
}
.ant-progress-inner {
background-color: rgba(167, 170, 198, 1) !important;
}
}
}
&-experience {
color: rgba(89, 91, 113, 1);
}
&-higher {
color: rgba(134, 136, 166, 1);
}
}
}
&::after {
content: '';
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 100px;
z-index: -1;
width: 100px;
transform: skewX(24deg);
background: linear-gradient(rgba(255, 255, 255, .2), rgba(255, 255, 255, 0));
}
}
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import {
Row,
Col,
Progress,
} from 'antd';
import classNames from 'classnames';
import { MiniArea } from '@/components/Charts';
import PolymericTable from '@/components/PolymericTable';
import { EditableColumns } from '@/components/PolymericTable/interface';
import MellowCard from '@/components/MellowCard';
import styles from './index.less';
const PAGE_SIZE = 8;
export interface DataProps {
id?: number;
createTime?: string;
ruleName?: string;
score?: number;
remark?: string;
};
export interface FetchParams {
current: number;
pageSize: number;
};
export interface LevelInfoProps {
levelInfo?: {
level?: string,
score?: number,
nextLevel?: string,
nextScore?: number,
};
chartData?: {
x: React.ReactText,
y: number,
}[];
fetchList?: (params: FetchParams) => Promise<{ data: DataProps[], totalCount: number }>;
}
const LevelInfo: React.FC<LevelInfoProps> = ({
levelInfo = {},
chartData = [],
fetchList,
}) => {
const score = levelInfo.score || 0;
const nextScore = levelInfo.nextScore || 0;
const [page, setPage] = useState(1);
const [size, setSize] = useState(PAGE_SIZE);
const [total, setTotal] = useState(0);
const [list, setList] = useState<DataProps[]>([]);
const [listLoading, setListLoading] = useState(false);
const columns: EditableColumns[] = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
},
{
title: '获取项目',
dataIndex: 'ruleName',
align: 'center',
},
{
title: '获取分值',
dataIndex: 'score',
align: 'center',
},
{
title: '获取时间',
dataIndex: 'createTime',
align: 'center',
},
{
title: '备注',
dataIndex: 'remark',
align: 'center',
ellipsis: true,
},
];
const getHistoryList = (params?) => {
if (fetchList) {
setListLoading(true);
fetchList({
current: page,
pageSize: size,
...params,
}).then(res => {
const { data = [], totalCount = 0 } = (res || {});
setList(data);
setTotal(totalCount);
}).finally(() => {
setListLoading(false);
});
}
};
useEffect(() => {
getHistoryList();
}, []);
const handlePaginationChange = (current: number, pageSize: number) => {
setPage(page);
setSize(size);
getHistoryList({
current,
pageSize,
});
};
return (
<div className={styles.levelInfo}>
<Row gutter={[0, 24]}>
<Col span={24}>
<MellowCard
title="会员等级"
headStyle={{
borderBottom: 'none',
}}
>
<div className={styles.infoWrap}>
<div className={styles['infoWrap-left']}>
<div className={classNames(styles.card, styles['card-level1'])}>
<div className={styles['card-name']}>{levelInfo?.level}</div>
<div className={styles['card-progress']}>
<Progress
strokeWidth={4}
strokeLinecap="square"
showInfo={false}
percent={nextScore ? (score / nextScore) * 100 : 100}
/>
</div>
<div className={styles['card-txt']}>
<div className={styles['card-experience']}>
{score}/{nextScore}
</div>
<div className={styles['card-higher']}>
{levelInfo.nextLevel}
</div>
</div>
<div className={styles['card-higher']}>
当前活跃分
</div>
</div>
</div>
<div className={styles['infoWrap-right']}>
<MiniArea
animate={false}
line
borderWidth={2}
height={180}
padding={[10, 20, 50, 60]}
scale={{
x: {
alias: `${new Date().getFullYear()}年`, // 别名
},
y: {
tickCount: 5,
alias: '活跃分', // 别名
},
}}
xAxis={{
tickLine: undefined,
label: undefined,
title: {
style: {
fontSize: 12,
fill: '#C0C4CC',
fontWeight: 400,
rotate: 90,
},
},
}}
yAxis={{
tickLine: undefined,
label: {
offset: 10,
},
title: {
style: {
fontSize: 12,
fill: '#C0C4CC',
fontWeight: 400,
rotate: 90,
},
},
grid: {
line: {
type: 'line',
style: {
stroke: "#d9d9d9",
lineWidth: 1,
lineDash: [2, 2]
}
}
},
}}
color="l(90) 0:#AAC5FC 1:#FFFFFF"
data={chartData}
/>
</div>
</div>
</MellowCard>
</Col>
<Col span={24}>
<MellowCard
title="活跃分获取记录"
headStyle={{
borderBottom: 'none',
}}
>
<PolymericTable
dataSource={list}
columns={columns}
loading={listLoading}
pagination={{
pageSize: size,
total,
}}
onPaginationChange={handlePaginationChange}
/>
</MellowCard>
</Col>
</Row>
</div>
);
};
export default LevelInfo;
.list {
display: flex;
align-items: center;
flex-wrap: wrap;
padding: 0;
margin: 0;
width: 100%;
overflow-x: auto;
&-item {
width: 175px;
height: 120px;
padding: 0 28px;
margin-bottom: 10px;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(235,236,240,1);
overflow: hidden;
list-style: none;
&:not(:last-child) {
margin-right: 16px;
}
> img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
\ No newline at end of file
import React from 'react';
import styles from './index.less';
interface PicWrapProps {
pics: string[];
};
const PicWrap: React.FC<PicWrapProps> = ({ pics = [] }) => (
<ul className={styles.list}>
{pics.map((item, index) => (
<li key={index} className={styles['list-item']}>
<img src={item} />
</li>
))}
</ul>
);
export default PicWrap;
\ No newline at end of file
.sincerityInfo {
.tofo {
height: 100%;
:global {
.antd-card {
height: 100%;
}
}
&-item {
width: 50%;
padding: 55px 24px;
&-logo {
width: 72px;
height: 72px;
}
}
}
}
.contentBox {
position: relative;
padding-right: 72px;
background: #ffffff;
&-main {
}
&-extra {
position: absolute;
top: 0;
right: 0;
bottom: 0;
}
.title {
margin-bottom: 20px;
line-height: 20px;
font-size: 12px;
font-weight: 400;
color: rgba(107, 119, 140, 1);
:global {
.anticon {
margin-left: 6px;
}
}
}
.txt {
line-height: 32px;
font-size: 24px;
font-weight: 500;
color: rgba(23, 43, 77, 1);
}
}
.record {
&-tabs {
:global {
.ant-tabs-nav::before {
border-bottom: none;
}
}
}
&-btns {
margin-bottom: 16px;
}
&-row {
:global {
.ant-table-cell:first-child {
background-color: rgba(250, 251, 252, 1);
}
}
}
}
\ No newline at end of file
This diff is collapsed.
.tag {
line-height: 22px;
padding: 0 8px;
font-size: 12px;
font-weight: 400;
color: #00B37A;
background: #EBF7F2;
border-radius: 4px;
&__success {
color: #00B37A;
background: #EBF7F2;
}
&__warnning {
color: #FF991F;
background: #FFFAE6;
}
&__default {
color: #606266;
background: #F4F5F7;
}
&__danger {
color: #E63F3B;
background: #FFEBE6;
}
&__primary {
color: #3F7ED2;
background: #F0F8FF;
}
}
\ No newline at end of file
/*
* @Author: XieZhiXiong
* @Date: 2020-08-31 17:52:14
* @LastEditors: XieZhiXiong
* @LastEditTime: 2020-09-08 16:32:03
* @Description: 状态 tag
*/
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
interface StatusTagProps {
type: 'success' | 'warnning' | 'default' | 'danger' | 'primary';
title?: string;
};
const StatusTag: React.FC<StatusTagProps> = ({ type, title }) => {
const cls = classNames(styles.tag, styles[`tag__${type}`]);
return (
<span className={cls}>{title}</span>
);
};
export default StatusTag;
\ No newline at end of file
import {
MEMBER_INNER_STATUS_TO_BE_COMMIT,
MEMBER_INNER_STATUS_COMMIT_NOT_PASSED,
MEMBER_INNER_STATUS_TO_BE_VERIFY_STEP1,
MEMBER_INNER_STATUS_VERIFY_STEP1_NOT_PASSED,
MEMBER_INNER_STATUS_TO_BE_VERIFY_STEP2,
MEMBER_INNER_STATUS_VERIFY_STEP2_NOT_PASSED,
MEMBER_INNER_STATUS_TO_CONFIRM,
MEMBER_INNER_STATUS_VERIFY_NOT_PASSED,
MEMBER_INNER_STATUS_VERIFY_PASSED,
MEMBER_OUTER_STATUS_TO_PLATFORM_VERIFY,
MEMBER_OUTER_STATUS_PLATFORM_VERIFYING,
MEMBER_OUTER_STATUS_PLATFORM_VERIFY_PASSED,
MEMBER_OUTER_STATUS_PLATFORM_VERIFY_NOT_PASSED,
MEMBER_OUTER_STATUS_DEPOSITING,
MEMBER_OUTER_STATUS_DEPOSITORY_PASSED,
MEMBER_OUTER_STATUS_DEPOSITORY_NOT_PASSED,
MEMBER_OUTER_STATUS_MODIFYING,
MEMBER_OUTER_STATUS_MODIFY_PASSED,
MEMBER_OUTER_STATUS_MODIFY_NOT_PASSED,
MEMBER_STATUS_NORMAL,
MEMBER_STATUS_FROZEN,
} from '@/constants/const/member';
export const STATUS_COLOR_MAP = {
0: '#669EDE',
1: '#41CC9E',
2: '#EF6260',
};
export const STATUS_COLOR_TXT = {
0: '待审核',
1: '审核通过',
2: '冻结',
};
// 会员状态 StatusTag map
export const MEMBER_STATUS_TAG_MAP = {
[MEMBER_STATUS_NORMAL]: 'success',
[MEMBER_STATUS_FROZEN]: 'default'
};
// 会员外部状态 StatusTag map
export const MEMBER_OUTER_STATUS_TYPE = {
[MEMBER_OUTER_STATUS_TO_PLATFORM_VERIFY]: 'default',
[MEMBER_OUTER_STATUS_PLATFORM_VERIFYING]: 'warning',
[MEMBER_OUTER_STATUS_PLATFORM_VERIFY_PASSED]: 'success',
[MEMBER_OUTER_STATUS_PLATFORM_VERIFY_NOT_PASSED]: 'danger',
[MEMBER_OUTER_STATUS_DEPOSITING]: 'warning',
[MEMBER_OUTER_STATUS_DEPOSITORY_PASSED]: 'success',
[MEMBER_OUTER_STATUS_DEPOSITORY_NOT_PASSED]: 'danger',
[MEMBER_OUTER_STATUS_MODIFYING]: 'warning',
[MEMBER_OUTER_STATUS_MODIFY_PASSED]: 'success',
[MEMBER_OUTER_STATUS_MODIFY_NOT_PASSED]: 'danger',
};
// 会员内部状态 Tag badge map
export const MEMBER_INNER_STATUS_BADGE_COLOR = {
[MEMBER_INNER_STATUS_TO_BE_COMMIT]: 'grey',
[MEMBER_INNER_STATUS_COMMIT_NOT_PASSED]: 'red',
[MEMBER_INNER_STATUS_TO_BE_VERIFY_STEP1]: 'orange',
[MEMBER_INNER_STATUS_VERIFY_STEP1_NOT_PASSED]: 'red',
[MEMBER_INNER_STATUS_TO_BE_VERIFY_STEP2]: 'orange',
[MEMBER_INNER_STATUS_VERIFY_STEP2_NOT_PASSED]: 'red',
[MEMBER_INNER_STATUS_TO_CONFIRM]: 'blue',
[MEMBER_INNER_STATUS_VERIFY_NOT_PASSED]: 'red',
[MEMBER_INNER_STATUS_VERIFY_PASSED]: 'green',
};
\ No newline at end of file
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
export const auditSchema: ISchema = {
type: 'object',
properties: {
MEGA_LAYOUT: {
type: 'object',
'x-component': 'mega-layout',
properties: {
topLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
grid: true,
},
properties: {
ctl: {
type: 'object',
'x-component': 'ControllerBtns',
},
name: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '搜索',
tip: '输入 会员名称 进行搜索',
},
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
colStyle: {
marginLeft: 20,
},
},
properties: {
memberTypeId: {
type: 'string',
default: undefined,
enum: [],
'x-component-props': {
placeholder: '会员类型(全部)',
allowClear: true,
style: {
width: 160,
},
},
},
// status: {
// type: 'string',
// default: undefined,
// enum: [],
// 'x-component-props': {
// placeholder: '会员状态(全部)',
// allowClear: true,
// },
// },
roleId: {
type: 'string',
default: undefined,
enum: [],
'x-component-props': {
placeholder: '会员角色(全部)',
allowClear: true,
style: {
width: 160,
},
},
},
level: {
type: 'string',
default: undefined,
enum: [],
'x-component-props': {
placeholder: '会员等级(全部)',
allowClear: true,
style: {
width: 160,
},
},
},
source: {
type: 'string',
default: undefined,
enum: [],
'x-component-props': {
placeholder: '申请来源(全部)',
allowClear: true,
style: {
width: 160,
},
},
},
'[startDate, endDate]': {
type: 'string',
default: '',
'x-component': 'dateSelect',
'x-component-props': {
placeholder: '时间范围(全部)',
allowClear: true,
style: {
width: 160,
},
},
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '查询',
},
},
},
},
},
},
},
};
export const auditModalSchema: ISchema = {
type: 'object',
properties: {
MEGA_LAYOUT: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelAlign: 'top',
},
properties: {
agree: {
type: 'string',
default: 1,
enum: [
{ label: '审核通过', value: 1 },
{ label: '审核不通过', value: 0 },
],
'x-component': 'radio',
'x-component-props': {},
},
reason: {
type: 'string',
title: '审核不通过原因',
'x-component': 'textarea',
required: true,
'x-component-props': {
placeholder: '在此输入你的内容,最长120个字符,60个汉字',
rows: 5,
},
'x-rules': [
{
limitByte: true, // 自定义校验规则
maxByte: 120,
}
],
},
},
},
},
};
import React from 'react';
import { PATTERN_MAPS } from '@/constants/regExp';
import PicWrap from './components/PicWrap';
export function coverColFiltersItem(
data: Array<{[key: string]: any}>,
dataIndex: string,
item: {[key: string]: any}
) {
const index = data.findIndex(i => i.dataIndex === dataIndex);
if (index !== -1) {
data.splice(index, 1, {
...data[index],
filters: item,
});
}
};
// 初始化内部流转记录
export function normalizeInnerHistory(source: {[key: string]: any}[]) {
const ret: any[] = [];
if (!Array.isArray(source)) {
return ret;
}
source.forEach(item => {
const atom = {
id: item.id,
operator: item.operatorName,
org: item.operatorOrgName,
jobTitle: item.operatorJobTitle,
innerStatusName: item.innerStatusName,
innerStatus: item.innerStatus,
operation: item.operation,
operateTime: item.createTime,
reason: item.remark,
};
ret.push(atom);
});
return ret;
};
// 初始化内部流转记录
export function normalizeOuterHistory(source: {[key: string]: any}[]) {
const ret: any[] = [];
if (!Array.isArray(source)) {
return ret;
}
source.forEach(item => {
const atom = {
id: item.id,
roleName: item.operatorRoleName,
status: item.outerStatus,
statusName: item.outerStatusName,
operation: item.operation,
operateTime: item.createTime,
reason: item.remark,
};
ret.push(atom);
});
return ret;
};
export type ElementType = {
/**
* 注册资料id
*/
id?: number,
/**
* 字段名称
*/
fieldName?: string,
/**
* 中文名称
*/
fieldLocalName?: string,
/**
* 字段类型
*/
fieldType?: string,
/**
* 字段类型附加属性(该参数为map)
*/
attr?: { [key: string]: any },
/**
* 字段长度
*/
fieldLength?: number,
/**
* 是否可为空0-不能为空1-可以为空
*/
fieldEmpty?: number,
/**
* 字段顺序
*/
fieldOrder?: number,
/**
* 帮助信息
*/
fieldRemark?: string,
/**
* 枚举标签列表
*/
fieldEnum?: {
value?: number,
label?: string,
}[],
/**
* 字段校验规则枚举:0-无校验规则,1-邮箱规则,2-手机号码规则,3-身份证规则,4-电话号码规则
*/
ruleEnum?: number,
/**
* 校验规则的正则表达式
*/
pattern?: string,
/**
* 校验错误的提示语
*/
msg?: string,
/**
* 值
*/
fieldValue?: any,
/**
* 是否禁用
*/
disabled?: boolean,
}
export type GroupItem = {
/**
* 组名
*/
groupName: string,
/**
* 元素
*/
elements: ElementType[],
}
export type FieldType = 'string' | 'long' | 'upload' | 'radio' | 'select' | 'checkbox' | 'area' | string & {};
// 判断表单元素是否真的有值
const fieldHasValue = (fieldType: FieldType, value: any): boolean => {
switch (fieldType) {
case 'string':
case 'long':
case 'radio':
case 'select':
return !!value;
case 'upload':
case 'checkbox':
return value && value.length > 0;
case 'area':
return value && !!value.provinceCode;
default:
return true;
}
};
// 字段校验规则枚举:0-无校验规则,1-邮箱规则,2-手机号码规则,3-身份证规则,4-电话号码规则
const RULE_REG_MAP = {
1: PATTERN_MAPS.email,
2: PATTERN_MAPS.phone,
3: PATTERN_MAPS.identity,
4: PATTERN_MAPS.tel,
};
const getFieldType = (field: ElementType, editable: boolean = true) => {
const isDisabled = (!editable && fieldHasValue(field.fieldType as string, field.fieldValue)) || !!field.disabled;
// 默认是 输入框
let description: { [key: string]: any } = {
'x-component-props': {
placeholder: field.fieldRemark,
disabled: isDisabled,
},
};
// 公共的属性
const common = {
type: 'string',
required: field.fieldEmpty === 0,
title: field.fieldLocalName,
default: field.fieldValue,
'x-rules': [
(
field.ruleEnum
? {
pattern: RULE_REG_MAP[field.ruleEnum],
message: field.msg,
}
: null
),
(
field.pattern
? {
pattern: field.pattern,
message: field.msg,
}
: null
),
].filter(Boolean),
};
switch (field.fieldType as FieldType) {
case 'upload': {
description = {
'x-component': 'CustomUpload',
'x-component-props': {
showDesc: false,
disabled: isDisabled,
},
};
break;
}
case 'radio': {
description = {
'x-component': 'RadioGroup',
enum: field.fieldEnum,
'x-component-props': {
disabled: isDisabled
},
};
break;
}
case 'select': {
description = {
enum: field.fieldEnum,
'x-component-props': {
disabled: isDisabled,
},
};
break;
}
case 'checkbox': {
description = {
'x-component': 'CheckboxGroup',
enum: field.fieldEnum,
'x-component-props': {
disabled: isDisabled,
},
};
break;
}
case 'area': {
description = {
'x-component': 'AreaSelect',
'x-component-props': {
disabled: isDisabled,
},
// 这里判断 省级 是否有值,没值给 undefined
// 后台没值是返回 {provinceCode: '', cityCode: '', districtCode: ''}
default: field.fieldValue ? field.fieldValue.provinceCode ? field.fieldValue : undefined : undefined,
};
break;
}
default:
break;
}
return Object.assign({}, common, description);
};
/**
* 根据后台生成注册资料 schema
* @param elements
* @param editable 有值的元素是否可编辑
* @returns
*/
export function createMemberSchema(elements: ElementType[], editable: boolean = true) {
const components = {};
if (!Array.isArray(elements)) {
return components;
}
for (let item of elements) {
components[item.fieldName as string] = getFieldType(item, editable);
}
return components;
};
// 根据 fieldType 渲染对应的内容
export function renderFieldTypeContent(fieldType: FieldType, fieldValue: any): React.ReactNode {
// 默认渲染 string
let node: React.ReactNode = fieldValue;
switch (fieldType) {
case 'upload':
node = (
<PicWrap
pics={[fieldValue]}
/>
);
break;
default:
break;
}
return node;
};
\ No newline at end of file
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