Basic QRcode Generation
This commit is contained in:
parent
872a42b7a5
commit
4ea5215f4c
|
@ -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
|
||||
})
|
|
@ -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
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue