Reconstruct code completely

This commit is contained in:
CPunisher 2020-05-10 18:15:02 +08:00
parent fdc1578306
commit 1d81bba201
35 changed files with 561 additions and 205 deletions

135
package-lock.json generated
View File

@ -1071,6 +1071,24 @@
"to-fast-properties": "^2.0.0"
}
},
"@cloudbase/adapter-interface": {
"version": "0.4.0",
"resolved": "https://registry.npm.taobao.org/@cloudbase/adapter-interface/download/@cloudbase/adapter-interface-0.4.0.tgz",
"integrity": "sha1-xXk1tQPnv6zksVHfY6h9xWSumQc=",
"dev": true
},
"@cloudbase/database": {
"version": "0.9.11-rc.0",
"resolved": "https://registry.npm.taobao.org/@cloudbase/database/download/@cloudbase/database-0.9.11-rc.0.tgz",
"integrity": "sha1-ev1zOIzpzmct1bbZTm5XCNmRQ70=",
"dev": true,
"requires": {
"bson": "^4.0.2",
"lodash.clonedeep": "4.5.0",
"lodash.set": "4.3.2",
"lodash.unset": "4.5.2"
}
},
"@cnakazawa/watch": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/@cnakazawa/watch/download/@cnakazawa/watch-1.0.4.tgz",
@ -2384,6 +2402,41 @@
"resolved": "https://registry.npm.taobao.org/aws4/download/aws4-1.9.1.tgz?cache=0&sync_timestamp=1578959055063&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.9.1.tgz",
"integrity": "sha1-fjPY99RJs/ZzzXLeuavcVS2+Uo4="
},
"axios": {
"version": "0.19.2",
"resolved": "https://registry.npm.taobao.org/axios/download/axios-0.19.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faxios%2Fdownload%2Faxios-0.19.2.tgz",
"integrity": "sha1-PqNsXYgY0NX4qKl6bTa4bNwAyyc=",
"dev": true,
"requires": {
"follow-redirects": "1.5.10"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.5.10.tgz?cache=0&sync_timestamp=1585479417937&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.5.10.tgz",
"integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=",
"dev": true,
"requires": {
"debug": "=3.1.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"axobject-query": {
"version": "2.1.2",
"resolved": "https://registry.npm.taobao.org/axobject-query/download/axobject-query-2.1.2.tgz",
@ -3099,6 +3152,28 @@
"node-int64": "^0.4.0"
}
},
"bson": {
"version": "4.0.4",
"resolved": "https://registry.npm.taobao.org/bson/download/bson-4.0.4.tgz",
"integrity": "sha1-S9os7fKuehjRXLJO4e3ox5f47s8=",
"dev": true,
"requires": {
"buffer": "^5.1.0",
"long": "^4.0.0"
},
"dependencies": {
"buffer": {
"version": "5.6.0",
"resolved": "https://registry.npm.taobao.org/buffer/download/buffer-5.6.0.tgz?cache=0&sync_timestamp=1588706716358&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.6.0.tgz",
"integrity": "sha1-oxdJ3H2B2E2wir+Te2uMQDP2J4Y=",
"dev": true,
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
}
}
},
"buffer": {
"version": "4.9.2",
"resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-4.9.2.tgz",
@ -3452,6 +3527,15 @@
"shallow-clone": "^0.1.2"
}
},
"cloudbase-adapter-wx_mp": {
"version": "0.1.1",
"resolved": "https://registry.npm.taobao.org/cloudbase-adapter-wx_mp/download/cloudbase-adapter-wx_mp-0.1.1.tgz",
"integrity": "sha1-J1DtgUX4IWhB0lu7XWubSiFLhDg=",
"dev": true,
"requires": {
"@cloudbase/adapter-interface": "^0.4.0"
}
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz",
@ -3812,6 +3896,12 @@
"randomfill": "^1.0.3"
}
},
"crypto-js": {
"version": "3.3.0",
"resolved": "https://registry.npm.taobao.org/crypto-js/download/crypto-js-3.3.0.tgz?cache=0&sync_timestamp=1581508591511&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcrypto-js%2Fdownload%2Fcrypto-js-3.3.0.tgz",
"integrity": "sha1-hG3RzOL2iqz6FWyFePkmpgm3l2s=",
"dev": true
},
"css": {
"version": "2.2.4",
"resolved": "https://registry.npm.taobao.org/css/download/css-2.2.4.tgz",
@ -5565,6 +5655,12 @@
"locate-path": "^2.0.0"
}
},
"fingerprintjs2": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/fingerprintjs2/download/fingerprintjs2-2.1.0.tgz",
"integrity": "sha1-Idw/7ifTsZkFbvjrhz3rzNjgYyM=",
"dev": true
},
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/flat-cache/download/flat-cache-2.0.1.tgz",
@ -8172,11 +8268,23 @@
"resolved": "https://registry.npm.taobao.org/lodash._reinterpolate/download/lodash._reinterpolate-3.0.0.tgz",
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npm.taobao.org/lodash.clonedeep/download/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
"dev": true
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npm.taobao.org/lodash.memoize/download/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
},
"lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npm.taobao.org/lodash.set/download/lodash.set-4.3.2.tgz",
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=",
"dev": true
},
"lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npm.taobao.org/lodash.sortby/download/lodash.sortby-4.7.0.tgz",
@ -8204,11 +8312,23 @@
"resolved": "https://registry.npm.taobao.org/lodash.uniq/download/lodash.uniq-4.5.0.tgz",
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
},
"lodash.unset": {
"version": "4.5.2",
"resolved": "https://registry.npm.taobao.org/lodash.unset/download/lodash.unset-4.5.2.tgz",
"integrity": "sha1-Nw0dPoW3Kn4bDN8tJyEhMG8j5O0=",
"dev": true
},
"loglevel": {
"version": "1.6.8",
"resolved": "https://registry.npm.taobao.org/loglevel/download/loglevel-1.6.8.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floglevel%2Fdownload%2Floglevel-1.6.8.tgz",
"integrity": "sha1-iiX7ddCSIw7NRFcnDYC1TigBEXE="
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz",
"integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz",
@ -12624,6 +12744,21 @@
"resolved": "https://registry.npm.taobao.org/tapable/download/tapable-1.1.3.tgz",
"integrity": "sha1-ofzMBrWNth/XpF2i2kT186Pme6I="
},
"tcb-js-sdk": {
"version": "1.6.1",
"resolved": "https://registry.npm.taobao.org/tcb-js-sdk/download/tcb-js-sdk-1.6.1.tgz",
"integrity": "sha1-jaLnrtwR+iNYqtrr2YcSg4shFUM=",
"dev": true,
"requires": {
"@cloudbase/adapter-interface": "^0.4.0",
"@cloudbase/database": "0.9.11-rc.0",
"axios": "^0.19.1",
"cloudbase-adapter-wx_mp": "^0.1.0",
"crypto-js": "^3.1.9-1",
"fingerprintjs2": "^2.1.0",
"url": "^0.11.0"
}
},
"terser": {
"version": "4.6.12",
"resolved": "https://registry.npm.taobao.org/terser/download/terser-4.6.12.tgz?cache=0&sync_timestamp=1587902079221&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fterser%2Fdownload%2Fterser-4.6.12.tgz",

View File

@ -36,6 +36,7 @@
},
"homepage": "./",
"devDependencies": {
"redux-devtools": "^3.5.0"
"redux-devtools": "^3.5.0",
"tcb-js-sdk": "^1.6.1"
}
}

View File

@ -1,14 +1,39 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-165845289-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-165845289-1');
</script>
<script>
(function(){
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!--<meta name="viewport" content="width=device-width, initial-scale=1" />-->
<meta content="yes" name="apple-mobile-web-app-capable">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="theme-color" content="#000000" />
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="QRBTF 参数化二维码生成器 Parametric QR Code Generator"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--

View File

@ -5,9 +5,14 @@ export const genQRInfo = text => ({
text
})
export const changeStyle = index => ({
export const changeStyle = (rendererIndex, rendererType, value) => ({
type: actionTypes.CHANGE_STYLE,
index
rendererIndex, rendererType, value
})
export const changeCorrectLevel = (correctLevel) => ({
type: actionTypes.CHANGE_CORRECT_LEVEL,
correctLevel
})
export const createParam = (paramInfo, paramValue) => ({

52
src/api/db.js Normal file
View File

@ -0,0 +1,52 @@
import * as tcb from 'tcb-js-sdk';
const app = tcb.init({
env: 'qrbtf-1d845d'
});
const auth = app.auth();
async function login() {
await auth.signInAnonymously();
// const loginState = await auth.getLoginState();
}
login();
const db = app.database();
const _ = db.command
export function increaseDownloadData(value) {
db.collection('QRCounter').where({
value: _.eq(value)
}).get().then(res => {
if (res.data.length > 0) {
db.collection('QRCounter').where({
value: _.eq(value)
}).update({
count: _.inc(1),
date: new Date().toString()
}).then(res => {
})
}
else {
db.collection('QRCounter').add({
value: value,
count: 1,
date: new Date().toString()
}).then(res => {
})
}
})
}
export function recordDownloadDetail({text, value, type, params, history}) {
db.collection('QRDownloadData').add({
date: new Date().toString(),
text: text,
value: value,
type: type,
params: params,
history: history
}).then(res => {
})
}

View File

@ -84,6 +84,8 @@
.Qr-item-image {
/*padding: 23px;*/
overflow: hidden;
display: flex;
background-color: white;
width: calc((100vw - 56px) / 2);
height: calc((100vw - 56px) / 2);
@ -105,6 +107,7 @@
}
.Qr-item-image-inner {
justify-content: center;
-webkit-transition-timing-function: ease-in-out;
-moz-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;

View File

@ -1,46 +0,0 @@
/*eslint-disable*/
import React from "react";
import ReactDOMServer from 'react-dom/server'
import {getQrcodeData} from "../utils/qrcodeHandler";
import {saveImg, saveSvg} from "../utils/downloader";
import {isWeiXin} from "../utils/util";
import './Qrcode.css';
import logo from '../qrbtf-logo.svg';
import Footer from "./footer/Footer";
import QrItem from "./QrItem";
import QrRendererBase from "./QrRendererBase";
import QrRendererRound from "./QrRendererRound";
import QrRendererRandRound from "./QrRendererRandRound";
import QrRendererBlank from "./QrRendererBlank";
import QrRendererRandRect from "./QrRendererRandRect";
import QrRendererDSJ from "./QrRendererDSJ";
class Qrcode extends React.Component {
downloadSvg = (e) => {
const style = styleList[this.state.selectedIndex]
const el = React.createElement(style.renderer, {qrcode: this.state.qrcode, params: this.state.paramValue[this.state.selectedIndex]})
saveSvg(style.value, ReactDOMServer.renderToString(el))
}
downloadImg = (e) => {
const style = styleList[this.state.selectedIndex]
const el = React.createElement(style.renderer, {qrcode: this.state.qrcode, params: this.state.paramValue[this.state.selectedIndex]})
saveImg(style.value, ReactDOMServer.renderToString(el), 1500, 1500)
}
}
export default Qrcode;
window.onload = function(){
if(isWeiXin()){
const outer = document.getElementById("wx-message");
const inner = document.createElement("div");
inner.className = "note-font";
inner.id = "wx-message-inner";
inner.innerHTML = "当前客户端不支持下载,请在浏览器中打开。";
outer.appendChild(inner);
}
}

View File

@ -3,25 +3,25 @@ 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";
import PartParams from "./PartParams";
import PartDownload from "./PartDownload";
import PartDownloadViewer from "../../containers/app/PartDownloadViewer";
import PartStylesViewer from "../../containers/app/PartStylesViewer";
function App() {
return (
<div className="App">
<header className="App-header">
<div className="Layout">
<div className="Qr-outer">
<Header/>
<PartInput/>
<PartStyles/>
<PartStylesViewer/>
<PartParams/>
<PartDownload/>
<PartDownloadViewer/>
<PartMore/>
<Footer/>
</div>
</div>
</header>
</div>
);

View File

@ -1,20 +1,31 @@
import React from 'react';
import './App.css';
import DownloadSvg from "../../containers/download/DownloadSvg";
import DownloadJpg from "../../containers/download/DownloadJpg";
import {isWeiXin} from "../../utils/util";
const PartDownload = () => (
const WxMessage = () => {
if (isWeiXin()) {
return <div className="note-font" id="wx-message-inner">当前客户端不支持下载请在浏览器中打开</div>
}
return null
}
const PartDownload = ({ value }) => (
<div className="Qr-titled">
<div className="Qr-Centered title-margin">
<div className="Qr-s-title">Downloads</div>
<p className="Qr-s-subtitle">下载二维码</p>
<p className="Qr-s-subtitle">下载二维码 {value}</p>
</div>
<div className="Qr-Centered">
<div className="div-btn">
<button className="dl-btn">SVG</button>
<button className="dl-btn">JPG</button>
<DownloadSvg/>
<DownloadJpg/>
</div>
<div id="wx-message">
<WxMessage/>
</div>
<div id="wx-message"></div>
</div>
</div>
)

View File

@ -1,11 +0,0 @@
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

@ -10,9 +10,13 @@ const PartMore = () => (
</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/docs/donate"} value={"打赏 & 赞助"} />
<LinkButton href={"https://www.yuque.com/qrbtf/topics"} value={"问题反馈"} />
</div>
<div className="div-btn">
<LinkButton href={"https://www.yuque.com/qrbtf/docs/dev"} value={"开发与设计"} />
<LinkButton href={"https://www.yuque.com/qrbtf/docs/coop"} value={"商业合作"} />
</div>
</div>
</div>
)

View File

@ -1,6 +1,7 @@
import React from 'react';
import './App.css';
import ParamListViewer from "../../containers/param/ParamListViewer";
import ParamCorrectLevelViewer from "../../containers/param/ParamCorrectLevelViewer";
const PartParams = () => (
<div className="Qr-titled-nobg">
@ -12,22 +13,7 @@ const PartParams = () => (
<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}>25%</option>*/}
{/* <option value={2}>30%</option>*/}
{/* </select>*/}
{/* </td>*/}
{/*</tr>*/}
<ParamCorrectLevelViewer/>
<ParamListViewer/>
</tbody>
</table>

View File

@ -1,17 +1,23 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import './App.css';
import StyleListViewer from "../../containers/StyleListViewer";
import StyleListViewer from "../../containers/style/StyleListViewer";
const PartStyles = () => (
<div className="Qr-titled">
const PartStyles = ({ setParamInfo }) => {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
setLoaded(true);
}, [])
const styleList = React.createElement(StyleListViewer({setParamInfo}))
return (<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">
{React.createElement(StyleListViewer())}
{styleList}
</div>
</div>
)
</div>)
}
export default PartStyles;

View File

@ -8,14 +8,18 @@ const Footer = () => (
<div className="Qr-Centered Qr-footer note-font">
<div>
<strong>作者</strong>&emsp;
<a href="https://blog.ciaochaos.com/"
<a
href="https://blog.ciaochaos.com/"
rel="noopener noreferrer"
target="_blank">ciaochaos
</a>&emsp;
<a href="https://github.com/CPunisher/"
rel="noopener noreferrer"
target="_blank">CPunisher</a>
target="_blank">CPunisher
</a>&ensp;&ensp;
<a href="https://www.yuque.com/qrbtf/docs/contact" rel="noopener noreferrer" target="_blank">联系我们</a>
</div>
<div className="Gray">
Copyright © {currentYear} QRBTF. 保留所有权利
</div>

View File

@ -1,6 +1,7 @@
import React from "react";
import '../Qrcode.css';
import logo from "../../qrbtf-logo.svg";
import InputText from "../../containers/InputText";
const logoStyle = {
background: `url(${logo})`,
@ -13,7 +14,8 @@ const Header = () => (
<div style={logoStyle}>
<h1 className="Qr-title">&ensp;</h1>
</div>
<p className="Qr-subtitle">参数化二维码生成器</p>
<p className="Qr-subtitle">参数化二维码生成器 <sup className="Gray">测试版</sup></p>
<InputText/>
</div>
)

View File

@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types'
import '../Qrcode.css';
const ParamCorrectLevel = ({ value, onChange }) => (
<tr>
<td>容错率</td>
<td>
<select
className="Qr-select"
value={value}
onChange={onChange}>
<option value={1}>7%</option>
<option value={0}>15%</option>
<option value={3}>25%</option>
<option value={2}>30%</option>
</select>
</td>
</tr>
)
ParamCorrectLevel.propTypes = {
value: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired
}
export default ParamCorrectLevel;

View File

@ -9,7 +9,7 @@ const ParamList = ({ rendererIndex, paramInfo }) => (
switch (item.type) {
case ParamTypes.TEXT_EDITOR: {
return (
<tr>
<tr key={"tr_" + rendererIndex + "_" + paramIndex}>
<td>{item.key}</td>
<td>
<ParamTextViewer rendererIndex={rendererIndex} paramIndex={paramIndex}/>
@ -19,7 +19,7 @@ const ParamList = ({ rendererIndex, paramInfo }) => (
}
case ParamTypes.SELECTOR: {
return (
<tr>
<tr key={"tr_" + rendererIndex + "_" + paramIndex}>
<td>{item.key}</td>
<td>
<ParamSelectViewer rendererIndex={rendererIndex} paramIndex={paramIndex}/>

View File

@ -59,13 +59,20 @@ function getParamInfo() {
];
}
function viewBox(qrcode) {
if (!qrcode) return '0 0 0 0';
const nCount = qrcode.getModuleCount();
return String(-nCount) + ' ' + String(-nCount / 2) + ' ' + String(nCount * 2) + ' ' + String(nCount * 2);
}
const Renderer25D = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={viewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>

View File

@ -17,7 +17,8 @@ function listPoints(qrcode, params) {
let posType = params[2];
let id = 0;
if (width2 <= 0) width2 = 80;
if (width2 <= 0) width2 = 70;
if (width1 <= 0) width1 = 70;
let available = [];
let ava2 = [];

View File

@ -0,0 +1,11 @@
import React from "react";
const Renderer = ({ rendererType, ...other }) => (
React.createElement(rendererType, other)
)
function areEqual(prevProps, nextProps) {
return !(prevProps.selected == true || nextProps.selected == true)
}
export default React.memo(Renderer, areEqual)

View File

@ -1,6 +1,7 @@
export const actionTypes = {
GENERATE_QR_INFO: 'GENERATE_QR_INFO',
CHANGE_STYLE: 'CHANGE_STYLE',
CHANGE_CORRECT_LEVEL: 'CHANGE_CORRECT_LEVEL',
CREATE_PARAM: 'CREATE_PARAM',
CHANGE_PARAM: 'CHANGE_PARAM'
}

View File

@ -0,0 +1 @@
export const QRBTF_URL = 'https://qrbtf.com'

View File

@ -3,14 +3,14 @@ import { genQRInfo } from "../actions";
import React from "react";
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))}}
onKeyPress={(e) => {
if (e.key === 'Enter') dispatch(genQRInfo(e.target.value))
}}
/>
</div>
)
export default connect()(InputText);

View File

@ -1,16 +0,0 @@
import {connect} from 'react-redux';
import * as React from "react";
const mapStateToProps = (state, ownProps) => ({
qrcode: state.qrcode,
params: state.paramValue[ownProps.index],
rendererIndex: ownProps.index
})
const mapDispatchToProps = (dispatch, ownProps) => ({
setParamInfo: (params) => ownProps.setParamInfo(ownProps.index, params)
})
export default function Renderer(WrappedRenderer) {
return connect(mapStateToProps, mapDispatchToProps)(WrappedRenderer)
}

View File

@ -1,60 +0,0 @@
import {connect, useDispatch} from 'react-redux';
import {changeStyle, createParam} from "../actions";
import StyleList from "../components/style/StyleList";
import Renderer from "./Renderer";
import RendererBlank from "../components/renderer/RendererBlank";
import RendererBase from "../components/renderer/RendererBase";
import RendererDSJ from "../components/renderer/RendererDSJ";
import RendererRound from "../components/renderer/RendererRound";
import RendererRandRound from "../components/renderer/RendererRandRound";
import RendererRandRect from "../components/renderer/RendererRandRect";
import Renderer25D from "../components/renderer/Renderer25D";
import RendererImage from "../components/renderer/RendererImage";
import * as React from "react";
const styles = [
{value: "A1", renderer: RendererBase},
{value: "A2", renderer: RendererRound},
{value: "A3", renderer: RendererRandRound},
{value: "SP — 1", renderer: RendererDSJ},
{value: "SP — 2", renderer: RendererRandRect},
{value: "B1", renderer: Renderer25D},
{value: "C1", renderer: RendererImage},
{value: "D1", renderer: RendererBlank},
]
const paramInfoBuffer = new Array(16).fill(new Array(16))
const paramValueBuffer = new Array(16).fill(new Array(16))
const setParamInfo = (renderIndex, paramInfo) => {
paramInfoBuffer[renderIndex] = paramInfo
paramValueBuffer[renderIndex] = paramInfo.map(item => item.default)
}
const mapStateToProps = state => ({
styles: styles.map((style, index) => {
return {
value: style.value,
selected: state.selectedIndex == index,
renderer: React.createElement(Renderer(style.renderer), {
index: index,
setParamInfo: setParamInfo
})
}
})
})
const mapDispatchToProps = dispatch => ({
onSelected: index => {
dispatch(changeStyle(index))
}
})
const StyleListViewer = () => {
let res = connect(mapStateToProps, mapDispatchToProps)(StyleList)
const dispatch = useDispatch()
dispatch(createParam(paramInfoBuffer, paramValueBuffer))
return res;
}
export default StyleListViewer;

View File

@ -0,0 +1,8 @@
import { connect } from 'react-redux';
import PartDownload from "../../components/app/PartDownload";
const mapStateToProps = (state) => ({
value: state.value
})
export default connect(mapStateToProps, null)(PartDownload)

View File

@ -0,0 +1,9 @@
import {connect} from "react-redux";
import PartStyles from "../../components/app/PartStyles";
import {createParam} from "../../actions";
const mapDispatchToProps = (dispatch) => ({
setParamInfo: (paramInfo, paramValue) => dispatch(createParam(paramInfo, paramValue))
});
export default connect(null, mapDispatchToProps)(PartStyles)

View File

@ -0,0 +1,34 @@
import { connect } from 'react-redux';
import React from 'react';
import ReactDOMServer from 'react-dom/server'
import DownloadButton from "../../components/download/DownloadButton";
import {saveImg} from "../../utils/downloader";
import {increaseDownloadData, recordDownloadDetail} from "../../api/db";
import {getParamDetailedValue} from "../../utils/util";
const mapStateToProps = (state) => ({
value: 'JPG',
onClick: () => {
const el = React.createElement(state.rendererType, {
qrcode: state.qrcode,
params: state.paramValue[state.selectedIndex],
setParamInfo: () => {}
});
saveImg(state.value, ReactDOMServer.renderToString(el), 1500, 1500);
increaseDownloadData(state.value)
recordDownloadDetail({
text: state.textUrl,
value: state.value,
type: 'jpg',
params: state.paramInfo[state.selectedIndex].map((item, index) => {
return {
key: item.key,
value: getParamDetailedValue(item, state.paramValue[state.selectedIndex][index])
}
}),
history: state.history
});
}
});
export default connect(mapStateToProps, null)(DownloadButton)

View File

@ -0,0 +1,34 @@
import { connect } from 'react-redux';
import React from 'react';
import ReactDOMServer from 'react-dom/server'
import DownloadButton from "../../components/download/DownloadButton";
import {saveSvg} from "../../utils/downloader";
import {increaseDownloadData, recordDownloadDetail} from "../../api/db";
import {getParamDetailedValue} from "../../utils/util";
const mapStateToProps = (state) => ({
value: 'SVG',
onClick: () => {
const el = React.createElement(state.rendererType, {
qrcode: state.qrcode,
params: state.paramValue[state.selectedIndex],
setParamInfo: () => {}
});
saveSvg(state.value, ReactDOMServer.renderToString(el));
increaseDownloadData(state.value)
recordDownloadDetail({
text: state.textUrl,
value: state.value,
type: 'svg',
params: state.paramInfo[state.selectedIndex].map((item, index) => {
return {
key: item.key,
value: getParamDetailedValue(item, state.paramValue[state.selectedIndex][index])
}
}),
history: state.history
});
}
});
export default connect(mapStateToProps, null)(DownloadButton)

View File

@ -0,0 +1,15 @@
import {changeCorrectLevel} from "../../actions";
import ParamCorrectLevel from "../../components/param/ParamCorrectLevel";
import {connect} from "react-redux";
const mapStateToProps = (state) => ({
value: state.correctLevel
});
const mapDispatchToProps = (dispatch) => ({
onChange: (e) => {
dispatch(changeCorrectLevel(e.target.value));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ParamCorrectLevel)

View File

@ -5,7 +5,7 @@ import {changeParam} from "../../actions";
const mapStateToProps = (state, ownProps) => ({
rendererIndex: ownProps.rendererIndex,
paramIndex: ownProps.paramIndex,
value: state.paramValue[ownProps.rendererIndex][ownProps.paramIndex]
value: String(state.paramValue[ownProps.rendererIndex][ownProps.paramIndex])
})
const mapDispatchToProps = (dispatch, ownProps) => ({

View File

@ -0,0 +1,18 @@
import {connect} from 'react-redux';
import Renderer from "../../components/style/Renderer";
import {fillEmptyWith} from "../../utils/util";
const mapStateToProps = (state, ownProps) => ({
rendererType: ownProps.rendererType,
rendererIndex: ownProps.index,
qrcode: state.qrcode,
params: fillEmptyWith(state.paramValue[ownProps.index].slice(), 0),
selected: state.selectedIndex == ownProps.index,
})
const mapDispatchToProps = (dispatch, ownProps) => ({
setParamInfo: (params) => ownProps.setParamInfo(ownProps.index, params)
})
export default connect(mapStateToProps, mapDispatchToProps)(Renderer)

View File

@ -0,0 +1,56 @@
import {connect, useDispatch} from 'react-redux';
import {changeStyle, createParam} from "../../actions";
import StyleList from "../../components/style/StyleList";
import RendererViewer from "./RendererViewer";
import RendererBlank from "../../components/renderer/RendererBlank";
import RendererBase from "../../components/renderer/RendererBase";
import RendererDSJ from "../../components/renderer/RendererDSJ";
import RendererRound from "../../components/renderer/RendererRound";
import RendererRandRound from "../../components/renderer/RendererRandRound";
import RendererRandRect from "../../components/renderer/RendererRandRect";
import Renderer25D from "../../components/renderer/Renderer25D";
import RendererImage from "../../components/renderer/RendererImage";
import * as React from "react";
const styles = [
{value: "A1", renderer: RendererBase},
{value: "A2", renderer: RendererRound},
{value: "A3", renderer: RendererRandRound},
{value: "SP — 1", renderer: RendererDSJ},
{value: "SP — 2", renderer: RendererRandRect},
{value: "B1", renderer: Renderer25D},
{value: "C1", renderer: RendererImage},
{value: "D1", renderer: RendererBlank},
]
const paramInfoBuffer = new Array(16).fill(new Array(16))
const paramValueBuffer = new Array(16).fill(new Array(16))
const setParamInfo = (renderIndex, paramInfo) => {
paramInfoBuffer[renderIndex] = paramInfo
paramValueBuffer[renderIndex] = paramInfo.map(item => parseInt(item.default))
}
const mapStateToProps = state => ({
styles: styles.map((style, index) => {
return {
value: style.value,
selected: state.selectedIndex == index,
renderer: <RendererViewer rendererType={style.renderer} index={index} setParamInfo={setParamInfo}/>
}
})
})
const mapDispatchToProps = dispatch => ({
onSelected: rendererIndex => {
dispatch(changeStyle(rendererIndex, styles[rendererIndex].renderer, styles[rendererIndex].value))
}
})
const StyleListViewer = ({setParamInfo}) => {
let res = connect(mapStateToProps, mapDispatchToProps)(StyleList)
setParamInfo(paramInfoBuffer, paramValueBuffer);
return res;
}
export default StyleListViewer;

View File

@ -1,25 +1,45 @@
import {getQrcodeData} from "../utils/qrcodeHandler";
import {actionTypes} from "../constant/ActionTypes";
import {QRBTF_URL} from "../constant/References";
import RendererBase from "../components/renderer/RendererBase";
const initialState = {
selectedIndex: 0,
qrcode: null,
value: 'A1',
rendererType: RendererBase,
correctLevel: 0,
textUrl: QRBTF_URL,
history: [],
qrcode: getQrcodeData({text: QRBTF_URL, correctLevel: 0}),
paramInfo: new Array(16).fill(new Array(16)),
paramValue: new Array(16).fill(new Array(16))
}
export default function appReducer(state = initialState, action) {
console.log(state)
switch (action.type) {
case actionTypes.GENERATE_QR_INFO: {
let text = action.text;
if (!text || text.length == 0) text = QRBTF_URL;
return Object.assign({}, state, {
qrcode: getQrcodeData({text: action.text})
textUrl: text,
qrcode: getQrcodeData({text: text, correctLevel: state.correctLevel})
});
}
case actionTypes.CHANGE_STYLE: {
return Object.assign({}, state, {
selectedIndex: action.index
value: action.value,
rendererType: action.rendererType,
selectedIndex: action.rendererIndex,
history: state.history.slice().concat(action.value)
});
}
case actionTypes.CHANGE_CORRECT_LEVEL: {
return Object.assign({}, state, {
correctLevel: parseInt(action.correctLevel),
qrcode: getQrcodeData({text: state.textUrl, correctLevel: parseInt(action.correctLevel)})
})
}
case actionTypes.CREATE_PARAM: {
return Object.assign({}, state, {
paramInfo: action.paramInfo,
@ -34,7 +54,7 @@ export default function appReducer(state = initialState, action) {
}
const newItem = item.slice();
newItem[action.paramIndex] = action.value;
newItem[action.paramIndex] = parseInt(action.value);
return newItem;
})
});

View File

@ -1,3 +1,5 @@
import {ParamTypes} from "../constant/ParamTypes";
let seed = 0;
export function srand(sd) {
@ -20,6 +22,12 @@ export function defaultViewBox(qrcode) {
return String(-nCount / 5) + ' ' + String(-nCount / 5) + ' ' + String(nCount + nCount / 5 * 2) + ' ' + String(nCount + nCount / 5 * 2);
}
export function fillEmptyWith(arr, value) {
for (let i = 0; i < arr.length; i++)
if (!arr[i]) arr[i] = value;
return arr;
}
export function isWeiXin(){
const ua = window.navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) == 'micromessenger'){
@ -28,3 +36,8 @@ export function isWeiXin(){
return false;
}
}
export function getParamDetailedValue(item, paramValue) {
if (item.type == ParamTypes.SELECTOR) return item.choices[paramValue];
return paramValue;
}