Basic QRcode Generation

This commit is contained in:
CPunisher 2020-05-06 23:59:56 +08:00
parent 872a42b7a5
commit 4ea5215f4c
18 changed files with 306 additions and 166 deletions

14
src/actions/index.js Normal file
View File

@ -0,0 +1,14 @@
export const actionTypes = {
GENERATE_QR_INFO: 'GENERATE_QR_INFO',
CHANGE_STYLE: 'CHANGE_STYLE'
}
export const genQRInfo = text => ({
type: actionTypes.GENERATE_QR_INFO,
text
})
export const changeStyle = index => ({
type: actionTypes.CHANGE_STYLE,
index
})

View File

@ -1,22 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const InputInfo = ({ onChange, onBlur, onKeyPress }) => (
<div className="Qr-Centered">
<input
className="Qr-input big-input"
placeholder="Input your URL here"
onChange={onChange}
onBlur={onBlur}
onKeyPress={onKeyPress}
/>
</div>
)
InputInfo.propTypes = {
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func.isRequired,
onKeyPress: PropTypes.func.isRequired
}
export default InputInfo

View File

@ -1,41 +0,0 @@
import React from "react";
import './Qrcode.css'
function calClassName(props) {
if (props.selected === true) return 'Qr-item Qr-item-selected';
return 'Qr-item';
}
export default class QrItem extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.forceUpdate()
}
handleClick = (e) => {
this.props.onSelected(this.props.index);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return (nextProps.selected || this.props.selected) && (this.props.text == nextProps.text || this.props.text.length == 0)
}
render() {
return (
<div className={calClassName(this.props)} onClick={this.handleClick}>
<div className="Qr-item-image">
<div className="Qr-item-image-inner">
{this.props.renderer}
</div>
</div>
<div className="Qr-item-detail">
{this.props.value}
</div>
</div>
);
}
}

View File

@ -145,108 +145,21 @@ class Qrcode extends React.Component {
render() {
return (
<div className="Qr-Centered">
<input
className="Qr-input big-input"
placeholder="Input your URL here"
onChange={(e) => this.setState({text: e.target.value})}
onBlur={this.handleCreate}
onKeyPress={(e) => {if(e.key === 'Enter') this.handleCreate(e)}}
/>
</div>
<div className="Qr-titled">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">Styles</div>
<p className="Qr-s-subtitle">点击选择样式</p>
</div>
<div className="Qr-s">
<div className="Qr-box">
{
styleList.map((style, index) => {
return <QrItem
key={style.value}
value={style.value}
index={index}
qrcode={this.state.qrcode}
renderer={React.createElement(style.renderer, {
qrcode: this.state.qrcode,
params: this.state.paramValue[index],
setParamInfo: this.setParamInfo(index)
})}
text={this.state.text}
selected={index == this.state.selectedIndex}
onSelected={() => this.setState({selectedIndex: index})}
/>
})
}
</div>
</div>
</div>
<div className="Qr-titled-nobg">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">Parameters</div>
<p className="Qr-s-subtitle">参数调整</p>
</div>
<div className="Qr-Centered">
<div className="Qr-div-table">
<table className="Qr-table">
<tbody>
<tr>
<td>容错率</td>
<td>
<select
className="Qr-select"
value={this.state.correctLevel}
onChange={(e) => {
this.setState({correctLevel: parseInt(e.target.value)}, () => this.handleCreate())
}}>
<option value={1}>7%</option>
<option value={0}>15%</option>
<option value={3}>20%</option>
<option value={2}>30%</option>
</select>
</td>
</tr>
{this.renderAdjustment()}
</tbody>
</table>
</div>
</div>
</div>
<div className="Qr-titled">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">Downloads</div>
<p className="Qr-s-subtitle">下载二维码 {styleList[this.state.selectedIndex].value}</p>
</div>
<div className="Qr-Centered">
<div className="div-btn">
<button className="dl-btn" onClick={this.downloadSvg}>SVG</button>
<button className="dl-btn" onClick={this.downloadImg}>JPG</button>
</div>
<div id="wx-message"></div>
</div>
</div>
<div className="Qr-titled-nobg">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">More</div>
<p className="Qr-s-subtitle">更多</p>
</div>
<div className="Qr-Centered btn-row">
<div className="div-btn">
<a href="https://www.yuque.com/qrbtf/docs" rel="noopener noreferrer" target="_blank">
<button className="dl-btn">使用手册</button>
</a>
<a href="https://www.yuque.com/qrbtf/topics" rel="noopener noreferrer" target="_blank">
<button className="dl-btn">问题反馈</button>
</a>
</div>
<div className="div-btn">
<button disabled className="dl-btn">提交样式</button>
</div>
</div>
</div>
<div></div>
// <div className="Qr-titled">
// <div className="Qr-Centered title-margin">
// <div className="Qr-s-title">Downloads</div>
// <p className="Qr-s-subtitle">下载二维码 — {styleList[this.state.selectedIndex].value}</p>
// </div>
// <div className="Qr-Centered">
// <div className="div-btn">
// <button className="dl-btn" onClick={this.downloadSvg}>SVG</button>
// <button className="dl-btn" onClick={this.downloadImg}>JPG</button>
// </div>
// <div id="wx-message"></div>
// </div>
//
// </div>
);
}
}

View File

@ -3,6 +3,9 @@ import './App.css';
import '../Qrcode.css';
import Footer from "../footer/Footer";
import Header from "../header/Header";
import PartInput from "./PartInput";
import PartStyles from "./PartStyles";
import PartMore from "./PartMore";
function App() {
return (
@ -10,7 +13,9 @@ function App() {
<header className="App-header">
<div className="Qr-outer">
<Header/>
<PartInput/>
<PartStyles/>
<PartMore/>
<Footer/>
</div>
</header>

View File

@ -0,0 +1,11 @@
import React from 'react';
import './App.css';
import InputText from "../../containers/InputText";
const PartInput = () => (
<div className="Qr-Centered">
<InputText/>
</div>
)
export default PartInput;

View File

@ -0,0 +1,20 @@
import React from 'react';
import './App.css';
import LinkButton from "../link/LinkButton";
const PartMore = () => (
<div className="Qr-titled-nobg">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">More</div>
<p className="Qr-s-subtitle">更多</p>
</div>
<div className="Qr-Centered btn-row">
<div className="div-btn">
<LinkButton href={"https://www.yuque.com/qrbtf/docs"} value={"使用手册"} />
<LinkButton href={"https://www.yuque.com/qrbtf/topics"} value={"问题反馈"} />
</div>
</div>
</div>
)
export default PartMore;

View File

@ -0,0 +1,17 @@
import React from 'react';
import './App.css';
import StyleListViewer from "../../containers/StyleListViewer";
const PartStyles = () => (
<div className="Qr-titled">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">Styles</div>
<p className="Qr-s-subtitle">点击选择样式</p>
</div>
<div className="Qr-s">
<StyleListViewer/>
</div>
</div>
)
export default PartStyles;

View File

@ -0,0 +1,16 @@
import React from "react";
import PropTypes from 'prop-types'
import '../Qrcode.css';
const DownloadButton = ({ onClick, value }) => (
<button className="dl-btn" onClick={onClick}>
{value}
</button>
)
DownloadButton.propTypes = {
onClick: PropTypes.func.isRequired,
value: PropTypes.string.isRequired
}
export default DownloadButton;

View File

@ -0,0 +1,16 @@
import React from "react";
import PropTypes from 'prop-types'
import '../Qrcode.css';
const LinkButton = ({ href, value }) => (
<a href={href} rel="noopener noreferrer" target="_blank">
<button className="dl-btn">{value}</button>
</a>
)
LinkButton.propTypes = {
href: PropTypes.string.isRequired,
value: PropTypes.string.isRequired
}
export default LinkButton;

View File

@ -0,0 +1,30 @@
import React from "react";
import PropTypes from 'prop-types';
import {defaultRenderer} from "../../utils/util";
function listPoints(qrcode) {
if (!qrcode) return[]
const nCount = qrcode.getModuleCount();
const pointList = []
let id = 0
for (let x = 0; x < nCount; x++) {
for (let y = 0; y < nCount; y++) {
if (qrcode.isDark(x, y)) {
pointList.push(<rect key={id++} width={1} height={1} fill={"black"} x={x} y={y}/>)
}
}
}
return pointList;
}
const RendererBase = ({ qrcode }) => (
defaultRenderer(qrcode, listPoints(qrcode))
);
RendererBase.prototype = {
qrcode: PropTypes.object.isRequired
}
export default RendererBase;

View File

@ -0,0 +1,13 @@
import React from "react";
import PropTypes from 'prop-types';
import {defaultRenderer} from "../../utils/util";
const RendererBlank = ({ qrcode }) => (
defaultRenderer(qrcode, [])
);
RendererBlank.prototype = {
qrcode: PropTypes.object.isRequired
}
export default RendererBlank;

View File

@ -0,0 +1,30 @@
import React from 'react'
import PropTypes from 'prop-types'
function calClassName(selected) {
if (selected === true) return 'Qr-item Qr-item-selected';
return 'Qr-item';
}
const Style = ({ value, renderer, selected, onClick }) => (
<div className={calClassName(selected)}
onClick={onClick}>
<div className="Qr-item-image">
<div className="Qr-item-image-inner">
{renderer}
</div>
</div>
<div className="Qr-item-detail">
{value}
</div>
</div>
);
Style.propTypes = {
value: PropTypes.string.isRequired,
renderer: PropTypes.object.isRequired,
selected: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
}
export default Style;

View File

@ -0,0 +1,26 @@
import React from 'react'
import PropTypes from 'prop-types'
import Style from "./Style";
const StyleList = ({ styles, onSelected }) => (
<div className="Qr-box">
{styles.map((style, index) =>
<Style
key={style.value}
{...style}
onClick={() => onSelected(index)}
/>
)}
</div>
);
StyleList.propTypes = {
styles: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string.isRequired,
renderer: PropTypes.object.isRequired,
selected: PropTypes.bool.isRequired
}).isRequired).isRequired,
onSelected: PropTypes.func.isRequired
}
export default StyleList;

View File

@ -0,0 +1,16 @@
import React from "react";
import { connect } from 'react-redux';
import { genQRInfo } from "../actions";
const InputText = ({ dispatch }) => (
<div className="Qr-Centered">
<input
className="Qr-input big-input"
placeholder="Input your URL here"
onBlur={e => dispatch(genQRInfo(e.target.value))}
onKeyPress={(e) => {if(e.key === 'Enter') dispatch(genQRInfo(e.target.value))}}
/>
</div>
)
export default connect()(InputText);

View File

@ -0,0 +1,14 @@
import React from "react";
import { connect } from 'react-redux';
import {changeStyle} from "../actions";
const mapStateToProp = state => ({
qrcode: state.qrcode
})
const mapDispatchToProps = dispatch => ({
})
export default function Renderer(WrappedRenderer) {
return connect(mapStateToProp, mapDispatchToProps())(WrappedRenderer)
}

View File

@ -0,0 +1,36 @@
import React from "react";
import { connect } from 'react-redux';
import {changeStyle} from "../actions";
import StyleList from "../components/style/StyleList";
import Renderer from "./Renderer";
import RendererBlank from "../components/renderer/RendererBlank";
import RendererBase from "../components/renderer/RendererBase";
const styles = [
{value: 'A1', renderer: RendererBase},
{value: 'A2', renderer: RendererBlank},
{value: 'B1', renderer: RendererBlank},
{value: 'B2', renderer: RendererBlank},
{value: 'C1', renderer: RendererBlank},
{value: 'C2', renderer: RendererBlank},
{value: 'D1', renderer: RendererBlank},
]
const mapStateToProp = state => ({
styles: styles.map((style, index) => {
return {
value: style.value,
selected: state.selectedIndex == index,
renderer: React.createElement(Renderer(style.renderer))
}
})
})
const mapDispatchToProps = dispatch => ({
onSelected: index => dispatch(changeStyle(index))
})
export default connect(
mapStateToProp,
mapDispatchToProps
)(StyleList)

View File

@ -0,0 +1,26 @@
import { combineReducers } from "redux";
import {actionTypes} from "../actions";
import {getQrcodeData} from "../utils/qrcodeHandler";
const initialState = {
selectedIndex: 0,
qrcode: null
}
export default function appReducer(state = initialState, action) {
console.log(state)
switch (action.type) {
case actionTypes.GENERATE_QR_INFO: {
return Object.assign({}, state, {
qrcode: getQrcodeData({text: action.text})
});
}
case actionTypes.CHANGE_STYLE: {
return Object.assign({}, state, {
selectedIndex: action.index
})
}
default: return state
}
}