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() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="Qr-Centered">
|
<div></div>
|
||||||
|
// <div className="Qr-titled">
|
||||||
<input
|
// <div className="Qr-Centered title-margin">
|
||||||
className="Qr-input big-input"
|
// <div className="Qr-s-title">Downloads</div>
|
||||||
placeholder="Input your URL here"
|
// <p className="Qr-s-subtitle">下载二维码 — {styleList[this.state.selectedIndex].value}</p>
|
||||||
onChange={(e) => this.setState({text: e.target.value})}
|
// </div>
|
||||||
onBlur={this.handleCreate}
|
// <div className="Qr-Centered">
|
||||||
onKeyPress={(e) => {if(e.key === 'Enter') this.handleCreate(e)}}
|
// <div className="div-btn">
|
||||||
/>
|
// <button className="dl-btn" onClick={this.downloadSvg}>SVG</button>
|
||||||
</div>
|
// <button className="dl-btn" onClick={this.downloadImg}>JPG</button>
|
||||||
<div className="Qr-titled">
|
// </div>
|
||||||
<div className="Qr-Centered title-margin">
|
// <div id="wx-message"></div>
|
||||||
<div className="Qr-s-title">Styles</div>
|
// </div>
|
||||||
<p className="Qr-s-subtitle">点击选择样式</p>
|
//
|
||||||
</div>
|
// </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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ import './App.css';
|
||||||
import '../Qrcode.css';
|
import '../Qrcode.css';
|
||||||
import Footer from "../footer/Footer";
|
import Footer from "../footer/Footer";
|
||||||
import Header from "../header/Header";
|
import Header from "../header/Header";
|
||||||
|
import PartInput from "./PartInput";
|
||||||
|
import PartStyles from "./PartStyles";
|
||||||
|
import PartMore from "./PartMore";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -10,7 +13,9 @@ function App() {
|
||||||
<header className="App-header">
|
<header className="App-header">
|
||||||
<div className="Qr-outer">
|
<div className="Qr-outer">
|
||||||
<Header/>
|
<Header/>
|
||||||
|
<PartInput/>
|
||||||
|
<PartStyles/>
|
||||||
|
<PartMore/>
|
||||||
<Footer/>
|
<Footer/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</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