项目总结以及思考

不是问题的问题

沟通(项目管理,产品经理,设计师,后台)

  1. 项目管理:项目完成排期,确认项目进度
  2. 产品经理: 对设计稿有什么疑惑直接寻找产品经理(逻辑部分)
  3. 设计师:(样式的更改,沟通解决),一开始就应该寻找设计师询问(生成app部分的滚动条)布局(自适应)
  4. 后台对接接口

笔记

  • react
  • 对组件的安排(整个产品里面的相似部分,尽量封装在一起,提高代码的利用率)
  • 接口组的安排(不影响其他业务的条件下)
  • 对接接口(后台小伙伴的沟通)
  • 以下一些知识点的掌握

预览,生成二维码,颜色选择器,前端生成图片(base64转化为文件格式),进度条,弹窗,上传图片,图片的裁剪(canvas截图),路由的安排,折线图(react-highcharts(svg), react-echarts(canvas)),图片上传文件过滤, 页面的架构搭建

颜色选择器

Alt text

  1. 颜色列表,hover的时候出现问题
1
2
3
4
5
6
7
8
9
10
11
12
13
&:hover {
box-sizing: border-box;
border: 1px solid @color-nav-bg;
box-shadow: @box-shadow-span;
border-radius: 3px;
}
```
![Alt text](http://obfnbicau.bkt.clouddn.com/box-sizing.jpg)
2. 颜色选择器的封装
3. 前端自动绘制logo(canvas实现)

createCanvas(color) {
const logo = this.logo;
const ctx = logo.getContext(‘2d’);
ctx.fillStyle = color;
ctx.fillRect(0, 0, 300, 300);
ctx.textAlign = ‘center’;
ctx.font = ‘90px Muna’;
ctx.fillStyle = ‘#FFF’;
ctx.fillText(this.state.val, 150, 110);
const dataURL = logo.toDataURL(‘image/png’);
if (this.props.onChange) {
this.props.onChange(dataURL);
}
}

1
2
3
4
5
生成base64的图片链接,后台给定接口是:
![Alt text](http://obfnbicau.bkt.clouddn.com/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87.jpg)
将base64格式图转化成file文件格式:[File 对象是特殊类型的 Blob](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob)

const debug = {hello: “world”};
const blob = new Blob([JSON.stringify(debug, null, 2)],
{type : ‘application/json’});

console.log(blob);

export const getBlobByDataURI = (data, type) => {
const binary = window.atob(data.split(‘,’)[1]);
const array = [];
for (let i = 0; i < binary.length; i += 1) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type });
};

export const base64ToFile = uri => getBlobByDataURI(uri, ‘image/png’);

export const uploadImage = (file, types, ds) => new Promise((resolve, reject) => {
if (types.length && types.every(item => (file.type.indexOf(item)) === -1)) {
reject();
} else {
const formData = new FormData();
formData.append(‘file’, file);
ds.resolve(‘pp/app.pics’, {
body: formData,
mode: ‘FormData’,
}).then((res) => {
resolve(http://pic.kuaizhan.com${res.body.data.url});
}).catch((error) => {
console.log(error);
});
}
});

1
2
3
4
5
6
### 进度条(生成app的进度显示)
![Alt text](http://obfnbicau.bkt.clouddn.com/%E8%BF%9B%E5%BA%A6%E6%9D%A1.jpg)
`具体实现:`

// stroke-dasharray=”0,10000” 第一个值就是填充的值,为0,第二段为不填充的长度,超过圆环的周长即可。此时进度条为0%:
const AppProgressCircle = (prop) => {
const {
percent = 0,
size = 111,
} = prop;
const circleLength = Math.floor(2 Math.PI 56);
const dashArray = ${(circleLength * percent) / 100},10000;
return (






{percent}%



);
};

export default AppProgressCircle;

1
2
`核心代码:`

// 最后记得页面卸载的时候清除定时器
componentWillUnmount() {
clearInterval(this.timerCheckApp);
}

checkAppTimer(ds, dispatch, siteId) {
const countDown = 5; // 5分钟请求验证检查app是否生成成功
const d = new Date(1111/1/1,0:${countDown}:0);
clearInterval(this.Timer);
// 在countDown时间间隔内进行循环的请求操作和进度条数字控制
this.timerCheckApp = setInterval(() => {
// const fast = 5000;
// const slow = 9000;
let m = d.getMinutes();
let s = d.getSeconds();
m = m < 10 ? 0${m} : m;
s = s < 10 ? 0${s} : s;
d.setSeconds(s - 1);
dispatch(checkAppGenerate(ds, siteId));
// 进度条的处理
this.setAppProcess();
// 定时器为0或者生成成功停止请求
if (m === ‘00’ && s === ‘00’) {
// 将最后的code赋值
this.setState({
code: this.props.appGenerate.code,
});
clearInterval(this.timerCheckApp);
this.timerCheckApp = undefined;
console.log(‘最后code’, this.props.appGenerate.code);
}
// 请求成功时清除定时器
if (this.props.appGenerate.code === 0) {
dispatch(getAppInfo(ds, siteId));
this.setAppProcess(100); // 将进度条设置为100
clearInterval(this.timerCheckApp);
}
console.log(‘定时器’, ${m}:${s});

              }, 5000);
}

// 设置进度条的数值
setAppProcess() {
if (this.state.appProcess < 99) {
this.setState({
appProcess: this.state.appProcess + 1,
});
}
if (this.state.appProcess === 99) {
// 到达99%时候清除定时器
clearInterval(this.timerCheckApp);
this.setState({
code: 30005, // 进度条99%时候请求未成功
});
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
### 图片裁剪([e.screenX.md--drag.html--drags.html](e.screenX.md))
![Alt text](http://obfnbicau.bkt.clouddn.com/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87111.jpg)
1. 图片裁剪位置确定,canvas截取图片,转化成文件,传递后台
2. 裁剪框的实现(4条线(东西南北方向,负边距定制位置),8个点位置方向的确定(绝对定位,负边距实现定位))
3. 将需要裁剪的图片作为一个背景图片
4. 将图像转绘制在canvas画布上面(注意将已有图片加载完成才绘制画布)
5. canvas的宽高和位置的确定
6. data-*自定义属性
7. 9个方向的位置计算(e.screenX计算,鼠标的移动位置mouseMove,mouseDown)
` 核心代码:` (drawImage定时器绘制视频帧)
ctx.drawImage(image, x, y, width, height, 0, 0, canvasW, canvasH);
![Alt text](http://obfnbicau.bkt.clouddn.com/drawImage.jpg)
handleConfirm() {
    const canvas = this.canvas;
    const ctx = canvas.getContext('2d');
    const image = new Image();
    image.onload = () => { //注意!!!:已有图片加载完成,才绘制图片
        const x = this.state.x / this.scaleRatio; // 裁剪图片的位置
        const y = this.state.y / this.scaleRatio;
        const width = this.state.width / this.scaleRatio; // 被裁剪图像的宽高
        const height = this.state.height / this.scaleRatio;
        const canvasW = this.props.scale ? this.state.width : width;
        const canvasH = this.props.scale ? this.state.height : height;
        canvas.height = canvasH; // 伸展或缩小图像
        canvas.width = canvasW;   //0,0在画布上放置图像的x,y坐标位置
        ctx.drawImage(image, x, y, width, height, 0, 0, canvasW, canvasH);
        this.setState({
            cropped: true,
        });
        this.props.handleCrop(canvas.toDataURL());
    };
    image.src = this.props.imgsrc;
}
1
2
> 9个方向的位置计算(e.screenX计算,鼠标的移动位置mouseMove,mouseDown)

handleMouseMove(e) {
let offsetX = e.screenX - this.moveStartX;
let offsetY = e.screenY - this.moveStartY;
this.moveStartX = e.screenX;
this.moveStartY = e.screenY;
const lastX = this.state.x;
const lastY = this.state.y;
const lastH = this.state.height;
const lastW = this.state.width;
const imageH = this.imageH;
const imageW = this.imageW;
const minOffset = 0;
const minUnit = 10;
switch (this.direction) {
case ‘all’: {
let nowX = lastX + offsetX;
const maxX = imageW - lastW;
nowX = compare(nowX, minOffset, maxX);

    let nowY = lastY + offsetY;
    const maxY = imageH - lastH;
    nowY = compare(nowY, minOffset, maxY);

    this.setState({ x: nowX, y: nowY });
    break;
}
1
2
3
> 裁剪布局的实现:




alt=”cropper”
/>




cropper



1
2
3
4
5
6
7
8
9
10
11
12
13
裁剪出现问题:canvas转化为,不接受跨域图片
![Alt text](http://obfnbicau.bkt.clouddn.com/%E8%A3%81%E5%89%AA%E5%87%BA%E9%94%99.jpg)
![Alt text](http://obfnbicau.bkt.clouddn.com/canvas%E8%B7%A8%E5%9F%9F.png)
### 上传图片
![Alt text](http://obfnbicau.bkt.clouddn.com/%E5%A4%A7%E5%9B%BE.jpg)
1. 上传图片的图片槽AppImgSlot组件,(图片列表,拖拽功能,替换功能,删除功能imgList=[],onDrag,onReplace,onRemove)
2. 图片上传功能:

input上传图片 uploadImage(file, types, this.context.ds)
.then((url) => {
afterUpload(url);
})

.catch(() => errorUpload('请上传合适尺寸/格式的图片'));)

export const uploadImage = (file, types, ds) => new Promise((resolve, reject) => {
if (types.length && types.every(item => (file.type.indexOf(item)) === -1)) {
reject();
} else {
const formData = new FormData();
formData.append(‘file’, file);
ds.resolve(‘pp/app.pics’, {
body: formData,
mode: ‘FormData’,
}).then((res) => {
resolve(http://pic.kuaizhan.com${res.body.data.url});
}).catch((error) => {
console.log(error);
});
}
});


// 定义接口组v1
ds.registerGroup(‘v1’, {
urlPrefix: conf.get(‘url_app_v1’),
requestProcessor: (req) => {
if (req.body) {
if (!req.headers) {
req.headers = {};
}
if (!(req.body instanceof FormData)) {
req.headers[‘Content-Type’] = ‘application/x-www-form-urlencoded;charset=UTF-8’;
req.body = encodeQueryString(req.body);
}
}
return req;
},
});

1
2
3. 图片拖拽功能:

(ev.dataTransfer.setData(‘Text’, index))}
// 接收数据
onDragOver={(ev) => { ev.preventDefault(); }}
onDrop={(ev) => {
if (!onDrag) { return; }
onDrag(ev.dataTransfer.getData(‘Text’), index);
}
} // 调用 preventDefault() 来避免浏览器对数据的默认处理(drop 事件的默认行为是以链接形式打开)
alt=”图片”
className=”img”

/>

//
handleDrag(from, to) {
const imgList = […this.state.imgList];
[imgList[from], imgList[to]] = [imgList[to], imgList[from]];
this.setState({ imgList });
this.props.onDrag(imgList);
}
handleRemove(index) {
const imgList = […this.state.imgList];
imgList.splice(index, 1);
this.setState({
imgList,
});
this.props.onRemove(imgList);
}
handleReplace(index) {
this.changeIndex = index;
this.uploader.upload();
}
handleAddImg(url) {
const imgList = […this.state.imgList];
imgList[this.changeIndex] = url;
this.setState({ imgList });
this.props.onUpload(imgList);
}
handleError(error) {
this.props.errorUpload(error);
}

1
2
3
4
5
6
7
8
![Alt text](http://obfnbicau.bkt.clouddn.com/%E6%8B%96%E6%94%BE.jpg)
### 预览
1. 第四步设置使用预览:
![Alt text](http://obfnbicau.bkt.clouddn.com/4%E9%A2%84%E8%A7%88.jpg)

{
   this.props.createPosition === 'createApp' ?
    <AppConfigPreview
                      color={this.props.currentConfig.appTitle}
                      src={this.props.currentConfig.domainName}
                      previewTop={PREVIEW_POSITION.previewTop}
                      previewRight={PREVIEW_POSITION.previewRight}
      /> :
     <AppConfigPreview
                      color={this.props.currentConfig.appTitle}
                      previewTop={PREVIEW_POSITIONM.previewTop}
                      previewRight={PREVIEW_POSITIONM.previewRight}
        />
          }
1
2
3
4
5
6
7
2. 站点列表预览'
![Alt text](http://obfnbicau.bkt.clouddn.com/%E7%AB%99%E7%82%B9%E9%A2%84%E8%A7%88.jpg)
`核心代码:`

1
2
3
4
5
### 生成二维码(网上解决办法)(生成二维码的源代码)
![Alt text](http://obfnbicau.bkt.clouddn.com/%E4%BA%8C%E7%BB%B4%E7%A0%81.jpg)
`核心代码:`

const qrcodeUrl = ‘/v1/common/encode-png?large=true&data=’;
二维码地址

1
2
3
### 图片上传文件的过滤(设计师的需求)
`核心代码`:
1
2
3
4
5
6
7
### 折线图(实现数据的统计)配置
![Alt text](http://obfnbicau.bkt.clouddn.com/%E6%8A%98%E7%BA%BF%E5%9B%BE.jpg)
1. 折线图(react-highcharts(svg), Echarts(canvas))
`核心代码`:

// 管理app数据表数据 todo
export const APPCHARTSCONFIG = {
// 图表区选项
chart: {
width: 850, // 自适应的宽度
height: 412, // 自适应的高度
plotBorderColor: ‘#D8D8D8’, // 主图表区边框的颜色,即X轴与Y轴围成的区域的边框颜色
},
legend: {
enabled: false, // x轴下面的系列名称
},
colors: [‘#4886FF’], // 颜色选项
xAxis: { // X轴选项
title: ‘’,
legend: ‘’,
tickWidth: 1, // 坐标轴的刻度
labels: { //
rotation: 0,
style: {
fontSize: ‘12px’,
color: ‘#CFCED4’,
},
},
categories: [
‘2017/10/07’,
‘2017/10/08’,
‘2017/10/09’,
‘2017/10/10’,
‘2017/10/11’,
‘2017/10/12’,
‘2017/10/13’,
],
lineColor: ‘#D8D8D8’, // 基线颜色
lineWidth: 1, // 基线宽度
},
yAxis: {
title: ‘’,
gridLineWidth: 1, // 设置横向分区线宽度
labels: { //
rotation: 0,
style: {
fontSize: ‘12px’,
color: ‘#CFCED4’,
},
},
},
series: [
{ // 数据列选项
data: [
13,
15,
12,
16,
16,
13,
11],
color: ‘#4886FF’, // 优先级颜色最高
},
],
tooltip: {
backgroundColor: ‘#FFF’,
borderColor: ‘#E0DFE7’,
shadow: ‘0 0 8px 0’,
borderRadius: 3,
},
title: {
text: null,
},
credits: {// 版权信息
enabled: false,
},
};

```

路由和页面结构的搭建

Alt text

路由的设置,组件的封装

代码复用(多用弹窗的设置)

弹窗1:

Alt text

弹窗2:

Alt text

弹窗3:

Alt text

弹窗4:

Alt text

代码规范

  1. eslint
    className的规范:

    • this.classname(‘app-img-solt’);方法的使用
  2. KZUIComponent

思考

  1. 养成写文档和记录,加注释的习惯,主动寻找问题的解决办法,记录博客,代码的优化
  2. 自己的一个知识体系和学习习惯
  3. 思考,解决,询问(最好的提问方式???)

    Alt text

最后

  1. 没有最后~~~~~~~~~
文章目录
  1. 1. 不是问题的问题
    1. 1.1. 沟通(项目管理,产品经理,设计师,后台)
    2. 1.2. 笔记
      1. 1.2.1. 颜色选择器
    3. 1.3. console.log(blob);
    4. 1.4. />
      1. 1.4.1. 路由和页面结构的搭建
      2. 1.4.2. 代码复用(多用弹窗的设置)
    5. 1.5. 代码规范
    6. 1.6. 思考
    7. 1.7. 最后
|