使用到的资源
react-selectable-fast
antd
react
以下为完整代码,有冗余,没有整理
/**
* @file modules/adlib/plan/EditFormantd
* @author shj
*/
import {
Form,
Input,
Switch,
DatePicker,
message,
Button
} from 'antd';
import {utils} from 'baidu-acu-react-common';
import React from 'react';
import moment from 'moment';
import {getInitDate} from 'modules/common/helper';
import ScheduleChange from 'containers/adlib/CyclePanel';
const FormItem = Form.Item;
const {timeToUtc} = utils.timeUtil;
const formItemLayout = {
labelCol: {
xs: {span: 24},
sm: {span: 4}
},
wrapperCol: {
xs: {span: 24},
sm: {span: 16}
}
};
let data = getInitDate();
class EditFormantd extends React.Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
payload: {},
isNewAD: false,
cycle: 'ffffffffffffffffffffffffffffffffffffffffff'
}
componentDidMount() {
if (this.props.item) {
this.loadDetail();
}
if (this.props.parent) {
this.setState({isNewAD: false});
}
else {
this.setState({isNewAD: true});
}
}
loadDetail = () => {
const {actions, item} = this.props;
actions.getPlanDetail({id: item.planId}).then(res => {
this.setState({payload: res, cycle: res.planCycle});
});
}
changeFormatPlanCycle(planCycle) {
// 将数组中的值给读取出来 按照二进制来存储
let arr = '';
for (let i = 0; i < 24; i++) {
if (planCycle.includes(i + '')) {
arr += 1;
continue;
}
arr += 0;
}
// 转为16进制
const str = parseInt(this.reverseStr(arr), 2).toString(16);
return this.formatHexData(str).repeat(7);
}
reverseStr(str) {
return str.split('').reverse().join('');
}
formatHexData(data) {
// 判断字符串的长度,然后补0
return '0'.repeat(6 - data.length) + data;
}
decodePlayCycle(cycle) {
// 将传输的字段给解析
const subSyc = parseInt(cycle.substring(0, 6), 16).toString(2);
let arr = this.reverseStr(subSyc).split('');
let data = [];
for (let i = 0; i < 24; i++) {
if (arr[i] === '1') {
data.push(i + '');
continue;
}
}
return data.length > 1 ? data : ['0'];
}
decodeCycle(cycle) {
// 将传输的字段给解析
const subSyc = parseInt(cycle, 16).toString(2);
let arr = this.reverseStr(subSyc).split('');
return arr;
}
doSubmit = e => {
e && e.preventDefault();
const {actions, parent} = this.props;
const {payload} = this.state;
return new Promise((resolve, reject) => {
this.props.form.validateFieldsAndScroll((err, values) => {
values.startTime = timeToUtc(values.startTime);
values.endTime = timeToUtc(values.endTime);
values.budget = values.budget * 100;
if (!err) {
if (payload.planId) {
values.cost = payload.cost;
values.createTime = payload.createTime;
values.planId = payload.planId;
values.status = payload.status;
values.unitCount = payload.unitCount;
values.userId = payload.userId;
}
const promise = payload.planId ? actions.updatePlan(values)
: actions.createPlan(values);
promise.then(
() => {
parent.handleCancel();
parent.loadList();
},
res => {
let msg = '';
if (res.field) {
msg = res.field.planName;
}
message.error('操作失败, ' + msg);
reject();
}
);
}
else {
reject();
}
});
});
}
compareToStartTime= (rule, value, callback) => {
const form = this.props.form;
if (value && (moment(value).isBefore(form.getFieldValue('startTime')))) {
callback('结束时间必须大于开始时间!');
}
else {
callback();
}
}
compareToEndTime= (rule, value, callback) => {
const form = this.props.form;
if (value && (moment(value).isAfter(form.getFieldValue('endTime')))) {
callback('开始时间必须小于结束时间!');
}
else {
callback();
}
}
// 校验开始时间(大于昨天,小于10年)
disabledStartDate = startTime => {
return startTime > moment().add(10, 'year') || startTime < moment().subtract(1, 'day');
}
// 校验结束时间(小于10年)
disabledEndDate = endTime => {
const form = this.props.form;
return endTime > moment().add(11, 'year') || endTime < form.getFieldValue('startTime');
}
render() {
const {form} = this.props;
const {payload, isNewAD, cycle} = this.state;
const startInit = payload.startTime ? moment(payload.startTime) : moment();
const endInit = payload.endTime ? moment(payload.endTime) : moment().add(10, 'year');
const getFieldDecorator = form.getFieldDecorator;
return (
<Form onSubmit={this.doSubmit}>
<FormItem {...formItemLayout} label="计划名称" extra='请输入计划名称,不超过100个字符'>
{getFieldDecorator('planName', {
initialValue: payload.planName || '',
rules: [
{
pattern: /^[0-9a-zA-Z_\u4e00-\u9fa5]+$/,
message: '只能输入数字字母下划线和汉字'
},
{
max: 100,
message: '最多输入100个字符'
},
{
required: true,
message: '计划名称必填'
}
]
})(<Input placeholder='请输入计划名称' style={{width: 200}} />)}
</FormItem>
<FormItem {...formItemLayout} label="每日预算(元)" >
{getFieldDecorator('budget', {
initialValue: payload.budget ? payload.budget / 100 : undefined,
rules: [
{pattern: /^[1-9][0-9]{0,6}([.]{1}[0-9]{1,2})?$/,
message: '仅可输入正数,整数部分最多7位,小数部分最多2位'
},
{
required: true,
message: '每日预算(元)必填'
}
]
})(<Input placeholder='请输入每日预算' style={{width: 200}} />)}
</FormItem>
<FormItem {...formItemLayout} label="匀速消耗" >
{getFieldDecorator('enableUniform', {
initialValue: payload.enableUniform ? payload.enableUniform : false,
rules: [
{
required: true,
message: '匀速消耗必填'
}
]
})(<Switch checkedChildren="开" unCheckedChildren="关" defaultunChecked />)}
</FormItem>
<FormItem {...formItemLayout} label="开始时间" >
{getFieldDecorator('startTime', {
initialValue: startInit,
rules: [
{
required: true,
message: '开始时间必填'
},
{
validator: this.compareToEndTime
}
]
})(<DatePicker showTime disabledDate={this.disabledStartDate} format='YYYY-MM-DD HH:mm:ss' />)}
</FormItem>
<FormItem {...formItemLayout} label="结束时间" >
{getFieldDecorator('endTime', {
initialValue: endInit,
rules: [
{
required: true,
message: '结束时间必填'
},
{
validator: this.compareToStartTime
}
]
})(<DatePicker showTime disabledDate={this.disabledEndDate} format='YYYY-MM-DD HH:mm:ss' />)}
</FormItem>
<FormItem {...formItemLayout} label="投放时间" >
{getFieldDecorator('planCycle', {
initialValue: cycle,
rules: [
{
required: true,
message: '投放时间'
}
]
})(<ScheduleChange
cycle={cycle}
parent={this}
/>)}
</FormItem>
{!isNewAD && <FormItem>
<Button type="primary" size='large' htmlType="submit" >
提交
</Button>
</FormItem>}
</Form>
);
}
}
export default Form.create()(EditFormantd);
/**
* @file CyclePanel
* @author shj
*/
import _ from 'lodash';
import {Checkbox} from 'antd';
import {SelectableGroup} from 'react-selectable-fast';
import {Component} from 'react';
import {parseHexString, getHexString, getFullArray} from 'modules/common/helper';
import './adlib.less';
import GroupItem from './GroupItem';
const weeksArray = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
const quckTips = [{text: '全周投放', id: [0, 1, 2, 3, 4, 5, 6]},
{text: '周一到周五投放', id: [0, 1, 2, 3, 4]},
{text: '周末投放', id: [5, 6]}
];
export default class CyclePanel extends Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
allData: [],
dayHours: [],
days: []
}
componentDidMount() {
const {cycle} = this.props;
let daysAll = [];
let daysNo = [];
// 将总的数据转为数组, 每个代表一小时, 1表示选中,0表示不显示
let data = parseHexString(cycle).reverse().join('');
let allData = this.getAllData(data);
const dayHours = [];
for (let i = 0; i <= 24; i++) {
dayHours.push(i);
}
for (let i = 0; i < 24; i++) {
daysAll.push(1);
daysNo.push(0);
}
let days = this.checkSelectedAll(allData);
this.setState({dayHours, allData, daysNo, daysAll, days, data});
}
changeradio = e => {
let {allData, daysNo, daysAll} = this.state;
// 对应的是周几全选
for (let i = 0; i < 7; i++) {
if (e.indexOf(i) > -1) {
// 全选
allData[i] = daysAll;
}
else {
// 全不选
allData[i] = daysNo;
}
}
this.setState({allData, days: e});
}
handleSelectionFinish = e => {
let selected = e.map(d => d.props);
let cycle = this.getData(selected);
let allData = this.getAllData(cycle);
let days = this.checkSelectedAll(allData);
const {parent} = this.props;
this.setState({allData, days});
parent.setState({cycle: getHexString(getFullArray(cycle.split('')).reverse())});
}
checkSelectedAll = allData => {
let days = [];
_.map(allData, (val, index) => {
if (val.indexOf('0') === -1) {
// 全部是选中
days.push(index);
}
});
return days;
}
getData = e => {
let allArr = [];
let selectArr = e.map(d => d.id);
for (let i = 0; i < 168; i++) {
if (selectArr.indexOf(i) > -1) {
allArr.push(1);
}
else {
allArr.push(0);
}
}
return allArr.join('');
}
getAllData = data => {
let allData = [];
for (let i = 0; i < data.length; i = i + 24) {
allData.push(data.substr(i, 24).split(''));
}
return allData;
}
render() {
const {allData, dayHours, days} = this.state;
return (
<div className="heatmap">
<div className="heatmap-body">
<div className="heatmap-time-line" >
{_.map(dayHours, (val, index) => {
if (index % 2 === 0) {
return <div className='heatmap-time-head'>{val}:00</div>;
}
})}
</div>
<div className="heatmap-time-body">
<div className='heatmap-line'>
<Checkbox.Group onChange={this.changeradio} value={days}>
{_.map(weeksArray, (dayText, index) => (
<Checkbox id={dayText} value={index}>
<span className='week-box'>{dayText}</span>
</Checkbox>
))}
</Checkbox.Group>
</div>
{allData && <div className='select-tab'>
<SelectableGroup
className="main"
clickClassName="tick"
enableDeselect
allowClickWithoutSelected={false}
onSelectionFinish={this.handleSelectionFinish}
>
{allData.map((d, index) => (
<div key={d[index] + weeksArray[index]}>
{d.map((val, i) => {
let id = index * 24 + i;
let select = Number(val) === 1;
return (
<GroupItem item={val} key={id} isSelected={select} id={id} />
);
})}
</div>
))}
</SelectableGroup>
</div>}
</div>
</div>
<div className='heatmap-time-foot'>
快速设定:
<span
className='slot-btn'
title={quckTips[0].text}
onClick={() => this.changeradio(quckTips[0].id)}
>{quckTips[0].text}</span>
<span
className='slot-btn'
title={quckTips[1].text}
onClick={() => this.changeradio(quckTips[1].id)}
>{quckTips[1].text}</span>
<span
className='slot-btn'
title={quckTips[2].text}
onClick={() => this.changeradio(quckTips[2].id)}
>{quckTips[2].text}</span>
<div className="ui-schedule-chart-help">
<div className="item selected"></div>
<div className="text">投放时间段</div>
<div className="item"></div>
<div className="text">暂停时间段</div>
</div>
</div>
</div>
);
}
}
/**
* @file common/common
*/
import {Select} from 'antd';
import _ from 'lodash';
const {Option} = Select;
export function getInitDate() {
let data = [];
for (let i = 0; i < 24; i++) {
// const text = i + '~' + (i + 1);
let text;
if (i < 9) {
text = '0' + i + ':00 ~ ' + '0' + (i + 1) + ':00';
}
else if (i === 9) {
text = '09:00 ~ ' + '10:00';
}
else {
text = i + ':00 ~ ' + (i + 1) + ':00';
}
data.push(<Option key={i}>{text}</Option>);
}
return data;
}
export function getCookie(name) {
let cookies = document.cookie;
let result = 1;
if (cookies.indexOf(name) > -1) {
let cookieArr = cookies.split(';');
cookieArr.map(d => {
let index = d.indexOf(name);
if (index > -1) {
result = d.split('=')[1];
}
});
}
return result;
}
export function getPagination(tabData, pageSize) {
return (tabData ? {
total: tabData.totalCount,
pageSize: pageSize,
pageSizeOptions: ['10', '20', '30', '50', '100'],
defaultPageSize: pageSize,
showSizeChanger: true,
showTotal() {
return '共 ' + tabData.totalCount + ' 条数据';
}
} : {});
}
export function formatRegionconf(param, fun) {
_.each(param, function (val) {
val.value = val.id;
val.key = 'val.id' + val.id + val.text;
val.title = val.text;
if (_.has(val, 'children')) {
formatRegionconf(val.children, fun);
}
});
}
export function getRegionIds(regionConf, list, fun) {
let nameList = [];
const flag = list.length > 0;
_.each(regionConf, function (val) {
if (flag) {
if (list.indexOf(val.id) > -1) {
nameList.push(val.id);
// 遍历children
let child = getChildren(val.children, fun);
nameList = nameList.concat(child);
}
if (val.children) {
let child = getRegionIds(val.children, list, fun);
nameList = nameList.concat(child);
}
}
else {
nameList.push(val.id);
let child = getRegionIds(val.children, [], fun);
nameList = nameList.concat(child);
}
});
return nameList;
}
export function getChildren(param, fun) {
let idList = [];
_.each(param, function (val) {
idList.push(val.id);
let child = getChildren(val.children, fun);
idList = idList.concat(child);
});
return idList;
}
export function getFullArray(rawValue) {
return [].concat.apply([], rawValue);
}
export function getRawValue(fullArray) {
let hourCount = 24;
let rawValue = [];
for (let i = 0; i < fullArray.length; i += hourCount) {
rawValue.push(fullArray.slice(i, i + hourCount));
}
return rawValue;
}
export function getHexString(fullArray) {
let itemLength = 4;
let hexStr = '';
for (let i = 0; i < fullArray.length; i += itemLength) {
hexStr += parseInt(fullArray.slice(i, i + itemLength).join(''), 2).toString(16);
}
return hexStr;
}
export function parseHexString(hexStr) {
let itemLength = 4;
return hexStr.split('').map(function (hexChar) {
let binStr = parseInt(hexChar, 16).toString(2);
return new Array(itemLength - binStr.length + 1).join('0') + binStr;
}).join('').split('').map(Number);
}
.heatmap {
font-size: 12px;
width: 700px;
padding: 6px 0 8px 0;
background: #fff;
border: 1px solid #CDCDCD;
position: relative;
overflow-x: scroll;
overflow-y: hidden;
.heatmap-body {
height: 200px;
.heatmap-time-line {
height: 30px;
padding: 0 0 0 40px;
.heatmap-time-head {
border: 0;
width: 50px;
text-align: left;
position: relative;
color: #000;
display: inline-block;
font-size: 12px;
height: 23px;
line-height: 23px;
-moz-user-select: none;
-webkit-user-select: none;
}
.long {
width: 125px;
}
}
.heatmap-day-head {
float: left;
width: 100%;
margin: 0;
.heatmap-day {
line-height: 25px;
padding-right: 5px;
height: 25px;
text-align: right;
}
}
.heatmap-time-body {
.heatmap-line {
height: 25px;
width: 60px;
.week-box {
padding: 0;
line-height: 25px;
}
.ant-checkbox-wrapper + .ant-checkbox-wrapper {
margin-left: 0;
}
}
.select-tab {
margin-left: 50px;
margin-top: -25px;
width: 600px;
}
.selected {
background: #dcfacf;
border: 1px solid #c2e8a6;
color: #fff;
font-size: 0;
}
.noselected {
background: #fff;
border: 1px solid #dcdcdc;
color: #E6E6E6;
font-size: 0;
}
}
}
.heatmap-head {
height: 28px;
width: 100%;
line-height: 28px;
padding-top: 10px;
position: relative;
.heatmap-help {
float: right;
width: 200px;
.heatmap-help-text {
float: left;
line-height: 16px;
padding: 5px 8px 0 3px;
}
}
}
// 配置格子
.heatmap-time {
background: #fff;
border: 1px solid #F7F8FC;
color: #fff;
cursor: pointer;
float: left;
// display: inline-block;
font-size: 12px;
height: 25px;
line-height: 25px;
text-align: center;
width: 25px;
-moz-user-select: none;
-webkit-user-select: none;
position: relative;
}
.heatmap-time-day {
float: left;
//display: inline-block;
font-size: 12px;
height: 23px;
line-height: 23px;
text-align: center;
-moz-user-select: none;
-webkit-user-select: none;
position: relative;
}
.heatmap-time-foot {
float: left;
margin-top: 10px;
width: 100%;
//display: inline-block;
font-size: 12px;
margin-left: -40px;
height: 23px;
line-height: 23px;
text-align: center;
-moz-user-select: none;
-webkit-user-select: none;
position: relative;
.slot-btn {
border: 1px solid #888f9b;
margin-left: 10px;
padding: 2px 10px;
}
.ui-schedule-chart-help {
float: right;
width: 250px;
& > div {
float: left;
margin-left: 10px;
}
.item {
border: 1px solid #E2E5EC;
width: 25px;
height: 25px;
position: relative;
top: 3px;
}
.selected {
margin-left: 20px;
background-color: #dcfacf;
}
}
}
.heatmap-time-selected {
background: #108cee;
color: #fff;
}
}
/**
* @file GroupItem
* @author shj
*/
import _ from 'lodash';
import {createSelectable} from 'react-selectable-fast';
import {Component} from 'react';
import './adlib.less';
import cs from 'classnames';
class GroupItem extends Component {
render() {
const {item} = this.props;
let {selectableRef, isSelecting, id, isSelected} = this.props;
let title = item.title + ':00-' + (item.title + 1) + ':00\n点击/拖动鼠标选择';
return (
<div
ref={selectableRef}
className={cs('heatmap-time', {selected: isSelected, selecting: isSelecting})}
title={title}
>
{item.id}
</div>
);
}
}
export default createSelectable(GroupItem);
效果
可以通过鼠标拖动,实现数据的加载