Merge branch 'dev-redux' of https://github.com/ciaochaos/qrbtf into dev-redux

 Conflicts:
	package-lock.json
	package.json
	public/index.html
	src/api/db.js
	src/components/Footer.js
	src/components/QrRendererBase.js
	src/components/QrRendererBlank.js
	src/components/Qrcode.js
	src/components/renderer/RendererDSJ.js
	src/components/renderer/RendererRandRect.js
	src/components/renderer/RendererRandRound.js
	src/components/renderer/RendererRound.js
	src/utils/util.js
This commit is contained in:
ciaochaos 2020-05-11 20:27:16 +08:00
commit 71562c1142
60 changed files with 1576 additions and 752 deletions

123
package-lock.json generated
View File

@ -1074,12 +1074,14 @@
"@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="
"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",
@ -1140,6 +1142,11 @@
"@hapi/hoek": "^8.3.0"
}
},
"@icons/material": {
"version": "0.2.4",
"resolved": "https://registry.npm.taobao.org/@icons/material/download/@icons/material-0.2.4.tgz",
"integrity": "sha1-6QyfcXaLNzbnbX3WeD/Gwq+oi8g="
},
"@jest/console": {
"version": "24.9.0",
"resolved": "https://registry.npm.taobao.org/@jest/console/download/@jest/console-24.9.0.tgz",
@ -2404,6 +2411,7 @@
"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"
},
@ -2412,6 +2420,7 @@
"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"
}
@ -2420,6 +2429,7 @@
"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"
}
@ -2427,7 +2437,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
@ -3150,6 +3161,7 @@
"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"
@ -3159,6 +3171,7 @@
"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"
@ -3523,6 +3536,7 @@
"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"
}
@ -3890,7 +3904,8 @@
"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="
"integrity": "sha1-hG3RzOL2iqz6FWyFePkmpgm3l2s=",
"dev": true
},
"css": {
"version": "2.2.4",
@ -5648,7 +5663,8 @@
"fingerprintjs2": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/fingerprintjs2/download/fingerprintjs2-2.1.0.tgz",
"integrity": "sha1-Idw/7ifTsZkFbvjrhz3rzNjgYyM="
"integrity": "sha1-Idw/7ifTsZkFbvjrhz3rzNjgYyM=",
"dev": true
},
"flat-cache": {
"version": "2.0.1",
@ -6172,6 +6188,14 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npm.taobao.org/hoist-non-react-statics/download/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha1-7OCsr3HWLClpwuxZ/v9CpLGoW0U=",
"requires": {
"react-is": "^16.7.0"
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.8.tgz",
@ -8252,7 +8276,8 @@
"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="
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
"dev": true
},
"lodash.memoize": {
"version": "4.1.2",
@ -8262,7 +8287,8 @@
"lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npm.taobao.org/lodash.set/download/lodash.set-4.3.2.tgz",
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=",
"dev": true
},
"lodash.sortby": {
"version": "4.7.0",
@ -8294,7 +8320,8 @@
"lodash.unset": {
"version": "4.5.2",
"resolved": "https://registry.npm.taobao.org/lodash.unset/download/lodash.unset-4.5.2.tgz",
"integrity": "sha1-Nw0dPoW3Kn4bDN8tJyEhMG8j5O0="
"integrity": "sha1-Nw0dPoW3Kn4bDN8tJyEhMG8j5O0=",
"dev": true
},
"loglevel": {
"version": "1.6.8",
@ -8304,7 +8331,8 @@
"long": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz",
"integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg="
"integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
@ -8392,6 +8420,11 @@
"object-visit": "^1.0.0"
}
},
"material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npm.taobao.org/material-colors/download/material-colors-1.2.6.tgz",
"integrity": "sha1-bRlYhxEmmSzuzHL0vMTY8BCGX0Y="
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz",
@ -10768,6 +10801,19 @@
}
}
},
"react-color": {
"version": "2.18.1",
"resolved": "https://registry.npm.taobao.org/react-color/download/react-color-2.18.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-color%2Fdownload%2Freact-color-2.18.1.tgz",
"integrity": "sha1-LNqMyOBqnixSrTkaMN2tMZckcvQ=",
"requires": {
"@icons/material": "^0.2.4",
"lodash": "^4.17.11",
"material-colors": "^1.2.1",
"prop-types": "^15.5.10",
"reactcss": "^1.2.0",
"tinycolor2": "^1.4.1"
}
},
"react-dev-utils": {
"version": "10.2.1",
"resolved": "https://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-10.2.1.tgz?cache=0&sync_timestamp=1584750310214&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dev-utils%2Fdownload%2Freact-dev-utils-10.2.1.tgz",
@ -11022,6 +11068,18 @@
"resolved": "https://registry.npm.taobao.org/react-is/download/react-is-16.13.1.tgz",
"integrity": "sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ="
},
"react-redux": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/react-redux/download/react-redux-7.2.0.tgz",
"integrity": "sha1-+XD2IZKzmBZC/sRv0NsYoHT+h50=",
"requires": {
"@babel/runtime": "^7.5.5",
"hoist-non-react-statics": "^3.3.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^16.9.0"
}
},
"react-scripts": {
"version": "3.4.1",
"resolved": "https://registry.npm.taobao.org/react-scripts/download/react-scripts-3.4.1.tgz",
@ -11082,6 +11140,14 @@
"workbox-webpack-plugin": "4.3.1"
}
},
"reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npm.taobao.org/reactcss/download/reactcss-1.2.3.tgz",
"integrity": "sha1-wAATh15Vexzw39mjaKHD2rO1SN0=",
"requires": {
"lodash": "^4.0.1"
}
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/read-pkg/download/read-pkg-3.0.0.tgz",
@ -11184,6 +11250,36 @@
"strip-indent": "^3.0.0"
}
},
"redux": {
"version": "4.0.5",
"resolved": "https://registry.npm.taobao.org/redux/download/redux-4.0.5.tgz?cache=0&sync_timestamp=1577154206919&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fredux%2Fdownload%2Fredux-4.0.5.tgz",
"integrity": "sha1-TbXeWBbheJHeioDEJCMtBvBR2T8=",
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
}
},
"redux-devtools": {
"version": "3.5.0",
"resolved": "https://registry.npm.taobao.org/redux-devtools/download/redux-devtools-3.5.0.tgz",
"integrity": "sha1-1pq3bU8Pir320kvPWVTXoaoraCc=",
"dev": true,
"requires": {
"lodash": "^4.2.0",
"prop-types": "^15.5.7",
"redux-devtools-instrument": "^1.9.0"
}
},
"redux-devtools-instrument": {
"version": "1.9.6",
"resolved": "https://registry.npm.taobao.org/redux-devtools-instrument/download/redux-devtools-instrument-1.9.6.tgz",
"integrity": "sha1-a0EllfdLnUjP1OzBPlhbFYjtbn4=",
"dev": true,
"requires": {
"lodash": "^4.2.0",
"symbol-observable": "^1.0.2"
}
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npm.taobao.org/regenerate/download/regenerate-1.4.0.tgz",
@ -12631,6 +12727,11 @@
"util.promisify": "~1.0.0"
}
},
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npm.taobao.org/symbol-observable/download/symbol-observable-1.2.0.tgz",
"integrity": "sha1-wiaIrtTqs83C3+rLtWFmBWCgCAQ="
},
"symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npm.taobao.org/symbol-tree/download/symbol-tree-3.2.4.tgz",
@ -12678,6 +12779,7 @@
"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",
@ -12884,6 +12986,11 @@
"resolved": "https://registry.npm.taobao.org/timsort/download/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npm.taobao.org/tinycolor2/download/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz",

View File

@ -6,10 +6,13 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-color": "^2.18.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react-scripts": "3.4.1",
"tcb-js-sdk": "^1.6.1"
"redux": "^4.0.5"
},
"scripts": {
"start": "react-scripts start",
@ -32,5 +35,9 @@
"last 1 safari version"
]
},
"homepage": "./"
"homepage": "./",
"devDependencies": {
"redux-devtools": "^3.5.0",
"tcb-js-sdk": "^1.6.1"
}
}

View File

@ -4,42 +4,66 @@
<!-- 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());
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-165845289-1');
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);
})();
(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="QRBTF 参数化二维码生成器 Parametric QR Code Generator"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>QRBTF 参数化二维码生成器</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -1,18 +0,0 @@
import React from 'react';
import './App.css';
import Layout from './containers/Layout'
import Qrcode from "./components/Qrcode";
function App() {
return (
<div className="App">
<header className="App-header">
<Layout>
<Qrcode></Qrcode>
</Layout>
</header>
</div>
);
}
export default App;

View File

@ -1,9 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

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

@ -0,0 +1,26 @@
import {actionTypes} from "../constant/ActionTypes";
export const genQRInfo = text => ({
type: actionTypes.GENERATE_QR_INFO,
text
})
export const changeStyle = (rendererIndex, rendererType, value) => ({
type: actionTypes.CHANGE_STYLE,
rendererIndex, rendererType, value
})
export const changeCorrectLevel = (correctLevel) => ({
type: actionTypes.CHANGE_CORRECT_LEVEL,
correctLevel
})
export const createParam = (paramInfo, paramValue) => ({
type: actionTypes.CREATE_PARAM,
paramInfo, paramValue
})
export const changeParam = (rendererIndex, paramIndex, value) => ({
type: actionTypes.CHANGE_PARAM,
rendererIndex, paramIndex, value
})

View File

@ -7,7 +7,11 @@ const auth = app.auth();
async function login() {
await auth.signInAnonymously();
<<<<<<< HEAD
const loginState = await auth.getLoginState()
=======
// const loginState = await auth.getLoginState();
>>>>>>> 47e31499e13fa4a29b3e69e6551268f3badb67b2
}
login();
@ -15,7 +19,11 @@ login();
const db = app.database();
const _ = db.command
<<<<<<< HEAD
export function increaseDownloadData(value, date) {
=======
export function increaseDownloadData(value) {
>>>>>>> 47e31499e13fa4a29b3e69e6551268f3badb67b2
db.collection('QRCounter').where({
value: _.eq(value)
}).get().then(res => {
@ -24,7 +32,11 @@ export function increaseDownloadData(value, date) {
value: _.eq(value)
}).update({
count: _.inc(1),
<<<<<<< HEAD
date: date
=======
date: new Date().toString()
>>>>>>> 47e31499e13fa4a29b3e69e6551268f3badb67b2
}).then(res => {
})
}
@ -32,7 +44,11 @@ export function increaseDownloadData(value, date) {
db.collection('QRCounter').add({
value: value,
count: 1,
<<<<<<< HEAD
date: date
=======
date: new Date().toString()
>>>>>>> 47e31499e13fa4a29b3e69e6551268f3badb67b2
}).then(res => {
})
}

View File

@ -1,20 +0,0 @@
import React from "react";
import './Qrcode.css';
const currentYear = new Date().getFullYear();
class Footer extends React.Component {
render() {
return (
<div className="Qr-titled">
<div className="Qr-Centered Qr-footer note-font">
<div><strong>作者</strong>&emsp;<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>&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>
<div className="Gray"><a href="http://www.beian.miit.gov.cn/" rel="noopener noreferrer" target="_blank"> ICP 19005869 </a></div>
</div>
</div>
);
}
}
export default Footer

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

@ -1,14 +0,0 @@
import React from "react";
import './Qrcode.css'
import {defaultViewBox} from "../utils/util";
export default class QrRendererBlank extends React.Component {
render() {
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(this.props.qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
</svg>
);
}
}

View File

@ -1,118 +0,0 @@
import React from "react";
import './Qrcode.css'
import {getTypeTable, QRPointType} from "../utils/qrcodeHandler";
import {rand, defaultRenderer, defaultViewBox} from "../utils/util";
function listPoint(props) {
if (!props.qrcode) return []
const qrcode = props.qrcode;
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = new Array(nCount);
let type = props.params[0];
let size = props.params[1] / 100;
let opacity = props.params[2] / 100;
let posType = props.params[3];
let id = 0;
const vw = [3, -3];
const vh = [3, -3];
if (size <= 0) size = 1.0
for (let x = 0; x < nCount; x++) {
for (let y = 0; y < nCount; y++) {
if (qrcode.isDark(x, y) == false) continue;
if (typeTable[x][y] == QRPointType.ALIGN_CENTER || typeTable[x][y] == QRPointType.ALIGN_OTHER || typeTable[x][y] == QRPointType.TIMING) {
if (type == 0)
pointList.push(<rect opacity={opacity} width={size} height={size} key={id++} fill="black" x={x + (1 - size)/2} y={y + (1 - size)/2}/>)
else if (type == 1)
pointList.push(<circle opacity={opacity} r={size / 2} key={id++} fill="black" cx={x + 0.5} cy={y + 0.5}/>)
else if (type == 2)
pointList.push(<circle key={id++} opacity={opacity} fill="black" cx={x + 0.5} cy={y + 0.5} r={size / 2} />)
}
else if (typeTable[x][y] == QRPointType.POS_CENTER) {
if (posType == 0) {
pointList.push(<rect width={1} height={1} key={id++} fill="black" x={x} y={y}/>);
} else if (posType == 1) {
pointList.push(<circle key={id++} fill="black" cx={x + 0.5} cy={y + 0.5} r={1.5} />)
pointList.push(<circle key={id++} fill="none" strokeWidth="1" stroke="black" cx={x + 0.5} cy={y + 0.5} r={3} />)
} else if (posType == 2) {
pointList.push(<circle key={id++} fill="black" cx={x + 0.5} cy={y + 0.5} r={1.5} />)
pointList.push(<circle key={id++} fill="none" strokeWidth="0.15" strokeDasharray="0.5,0.5" stroke="black" cx={x + 0.5} cy={y + 0.5} r={3} />)
for (let w = 0; w < vw.length; w++) {
pointList.push(<circle key={id++} fill="black" cx={x + vw[w] + 0.5} cy={y + 0.5} r={0.5} />)
}
for (let h = 0; h < vh.length; h++) {
pointList.push(<circle key={id++} fill="black" cx={x + 0.5} cy={y + vh[h] + 0.5} r={0.5} />)
}
}
}
else if (typeTable[x][y] == QRPointType.POS_OTHER) {
if (posType == 0) {
pointList.push(<rect width={1} height={1} key={id++} fill="black" x={x} y={y}/>);
}
}
else {
if (type == 0)
pointList.push(<rect opacity={opacity} width={size} height={size} key={id++} fill="black" x={x + (1 - size)/2} y={y + (1 - size)/2}/>)
else if (type == 1)
pointList.push(<circle opacity={opacity} r={size / 2} key={id++} fill="black" cx={x + 0.5} cy={y + 0.5}/>)
else if (type == 2)
pointList.push(<circle opacity={opacity} key={id++} fill="black" cx={x + 0.5} cy={y + 0.5} r={0.5 * rand(0.33,1.0)} />)
}
}
}
return pointList;
}
export default class QrRendererRandRound extends React.Component {
constructor(props) {
super(props);
if (this.props.setParamInfo) {
this.props.setParamInfo([
{
key: '信息点样式',
default: 2,
choices: [
"矩形",
"圆形",
"随机"
]
},
{
key: '信息点缩放',
default: 80
},
{
key: '信息点不透明度',
default: 100,
},
{
key: '定位点样式',
default: 2,
choices: [
"矩形",
"圆形",
"行星",
]
},
]
);
}
}
render() {
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(this.props.qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoint(this.props)}
</svg>
);
}
}

View File

@ -434,3 +434,7 @@ select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}
.Qr-color-picker {
width: 200px;
}

View File

@ -1,317 +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";
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";
import QrRenderer25D from "./QrRenderer25D";
import QrRendererImage from "./QrRendererImage";
import {recordDownloadDetail} from "../api/db";
const logoStyle = {
background: `url(${logo})`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'left'
};
const styleList = [
{value: "A1", renderer: QrRendererBase},
{value: "A2", renderer: QrRendererRound},
{value: "A3", renderer: QrRendererRandRound},
{value: "SP — 1", renderer: QrRendererDSJ},
{value: "SP — 2", renderer: QrRendererRandRect},
{value: "B1", renderer: QrRenderer25D},
{value: "C1", renderer: QrRendererImage},
{value: "D1", renderer: QrRendererBlank},
];
class Qrcode extends React.Component {
paramInfoBuffer;
paramValueBuffer;
constructor(props) {
super(props);
this.state = {
text: '',
selectedIndex: 0,
options: {text: ''},
qrcode: null,
paramInfo: [],
paramValue: [],
correctLevel: 0,
history: []
};
this.paramInfoBuffer = new Array(16).fill(new Array(16));
this.paramValueBuffer = new Array(16).fill(new Array(16));
}
componentDidMount() {
const text = 'https://qrbtf.com/';
this.setState({
paramInfo: this.paramInfoBuffer,
paramValue: this.paramValueBuffer,
text: text,
options: {text: text},
qrcode: getQrcodeData({text: text, correctLevel: this.state.correctLevel})
});
}
setParamInfo = (index) => {
const _this = this;
return function (params) {
_this.paramInfoBuffer[index] = params;
_this.paramValueBuffer[index] = params.map(p => {
return p.default
});
}
}
setParamValue = (valueIndex, value) => {
const newValue = this.state.paramValue.slice();
newValue[this.state.selectedIndex][valueIndex] = value;
this.setState({paramValue: newValue});
}
handleCreate = (e) => {
let text = this.state.text
if (text.length <= 0) text = 'https://qrbtf.com/';
this.setState({text: text, options: {text: text}, qrcode: getQrcodeData({text: text, correctLevel: this.state.correctLevel})});
if (e) e.target.blur();
}
downloadSvg = (e) => {
const selected = this.state.selectedIndex
const style = styleList[selected]
const el = React.createElement(style.renderer, {qrcode: this.state.qrcode, params: this.state.paramValue[selected]})
saveSvg(style.value, ReactDOMServer.renderToString(el))
recordDownloadDetail({
text: this.state.text,
value: styleList[selected],
type: 'svg',
params: this.state.paramInfo[selected].map((item, index) => {
return {
key: item.key,
value: item.choices ? item.choices[this.state.paramValue[selected][index]] : this.state.paramValue[selected][index]
}
}),
history: this.state.history
});
}
downloadImg = (e) => {
const selected = this.state.selectedIndex
const style = styleList[selected]
const el = React.createElement(style.renderer, {qrcode: this.state.qrcode, params: this.state.paramValue[selected]})
saveImg(style.value, ReactDOMServer.renderToString(el), 1500, 1500)
recordDownloadDetail({
text: this.state.text,
value: styleList[selected],
type: 'jpg',
params: this.state.paramInfo[selected].map((item, index) => {
return {
key: item.key,
value: item.choices ? item.choices[this.state.paramValue[selected][index]] : this.state.paramValue[selected][index]
}
}),
history: this.state.history
});
}
renderParamEditor = (info, index) => {
if (info.choices) {
return (
<select
className="Qr-select"
key={"select_" + this.state.selectedIndex + "_" + index}
value={this.state.paramValue[this.state.selectedIndex][index]}
onChange={(e) => this.setParamValue(index, e.target.value)}>
{
info.choices.map((choice, index) => {
return (
<option key={"option_" + this.state.selectedIndex + "_" + index}
value={index}>
{choice}
</option>
);
})
}
</select>
);
}
else {
return (
<input
type="number"
key={"input_" + this.state.selectedIndex + "_" + index}
className="Qr-input small-input"
placeholder="10"
defaultValue={this.state.paramValue[this.state.selectedIndex][index]}
onBlur={(e) => this.setParamValue(index, e.target.value)}
onKeyPress={(e) => {if(e.key === 'Enter') {this.setParamValue(index, e.target.value); e.target.blur()}}}/>
);
}
}
renderAdjustment = () => {
const target = this.state.paramInfo[this.state.selectedIndex];
if (target instanceof Array) {
return target.map((info, index) => {
return (
<tr key={"tr_" + index}>
<td key={"title_" + index}>{info.key}</td>
<td key={"editor_" + index}>{this.renderParamEditor(info, index)}</td>
</tr>
)
})
}
}
changeStyle = (index) => {
const newHistory = this.state.history.slice();
newHistory.push(styleList[index].value);
this.setState({selectedIndex: index, history: newHistory})
}
render() {
return (
<div className="Qr-outer">
<div className="Qr-Centered">
<div style={logoStyle}>
<h1 className="Qr-title">&ensp;</h1>
</div>
<p className="Qr-subtitle">参数化二维码生成器 <sup className="Gray">测试版</sup></p>
<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.changeStyle}
/>
})
}
</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}>25%</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/donate" 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">
<a href="https://www.yuque.com/qrbtf/docs/dev" rel="noopener noreferrer" target="_blank">
<button className="dl-btn">开发与设计</button>
</a>
<a href="https://www.yuque.com/qrbtf/docs/coop" rel="noopener noreferrer" target="_blank">
<button className="dl-btn">商业合作</button>
</a>
</div>
</div>
</div>
<Footer />
</div>
);
}
}
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);
}
}

30
src/components/app/App.js Normal file
View File

@ -0,0 +1,30 @@
import React from 'react';
import './App.css';
import '../Qrcode.css';
import Footer from "../footer/Footer";
import Header from "../header/Header";
import PartMore from "./PartMore";
import PartParams from "./PartParams";
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/>
<PartStylesViewer/>
<PartParams/>
<PartDownloadViewer/>
<PartMore/>
<Footer/>
</div>
</div>
</header>
</div>
);
}
export default App;

View File

@ -0,0 +1,32 @@
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 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">下载二维码 {value}</p>
</div>
<div className="Qr-Centered">
<div className="div-btn">
<DownloadSvg/>
<DownloadJpg/>
</div>
<div id="wx-message">
<WxMessage/>
</div>
</div>
</div>
)
export default PartDownload;

View File

@ -0,0 +1,24 @@
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/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>
)
export default PartMore;

View File

@ -0,0 +1,25 @@
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">
<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>
<ParamCorrectLevelViewer/>
<ParamListViewer/>
</tbody>
</table>
</div>
</div>
</div>
)
export default PartParams;

View File

@ -0,0 +1,23 @@
import React, {useEffect, useState} from 'react';
import './App.css';
import StyleListViewer from "../../containers/style/StyleListViewer";
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">
{styleList}
</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,36 @@
import React from "react";
import '../Qrcode.css';
const currentYear = new Date().getFullYear();
const Footer = () => (
<div className="Qr-titled">
<div className="Qr-Centered Qr-footer note-font">
<div>
<strong>作者</strong>&emsp;
<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>&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>
<div className="Gray">
<a href="http://www.beian.miit.gov.cn/" rel="noopener noreferrer" target="_blank">
ICP 19005869
</a>
</div>
</div>
</div>
)
export default Footer

View File

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

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,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import {SliderPicker} from 'react-color';
import '../Qrcode.css';
const ParamColor = ({ rendererIndex, paramIndex, value, onChange }) => (
<SliderPicker
key={"input_" + rendererIndex + "_" + paramIndex}
className="Qr-color-picker"
color={value}
onChangeComplete={onChange}
/>
)
ParamColor.propTypes = {
rendererIndex: PropTypes.number.isRequired,
paramIndex: PropTypes.number.isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
}
export default ParamColor;

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

@ -0,0 +1,50 @@
import React from 'react';
import {ParamTypes} from "../../constant/ParamTypes";
import PropTypes from "prop-types"
import ParamTextViewer from "../../containers/param/ParamTextViewer";
import ParamSelectViewer from "../../containers/param/ParamSelectViewer";
import ParamColorViewer from "../../containers/param/ParamColorViewer";
const ParamList = ({ rendererIndex, paramInfo }) => (
paramInfo.map((item, paramIndex) => {
switch (item.type) {
case ParamTypes.TEXT_EDITOR: {
return (
<tr key={"tr_" + rendererIndex + "_" + paramIndex}>
<td>{item.key}</td>
<td>
<ParamTextViewer rendererIndex={rendererIndex} paramIndex={paramIndex}/>
</td>
</tr>
)
}
case ParamTypes.SELECTOR: {
return (
<tr key={"tr_" + rendererIndex + "_" + paramIndex}>
<td>{item.key}</td>
<td>
<ParamSelectViewer rendererIndex={rendererIndex} paramIndex={paramIndex}/>
</td>
</tr>
)
}
case ParamTypes.COLOR_EDITOR: {
return (
<tr key={"tr_" + rendererIndex + "_" + paramIndex}>
<td>{item.key}</td>
<td>
<ParamColorViewer rendererIndex={rendererIndex} paramIndex={paramIndex}/>
</td>
</tr>
)
}
}
})
)
ParamList.propTypes = {
rendererIndex: PropTypes.number.isRequired,
paramInfo: PropTypes.array
}
export default ParamList;

View File

@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types'
import '../Qrcode.css';
const ParamList = ({ rendererIndex, paramIndex, value, info, onChange }) => (
<select
className="Qr-select"
key={"select_" + rendererIndex + "_" + paramIndex}
value={value}
onChange={onChange}>
{
info.choices.map((choice, index) => {
return (
<option key={"option_" + rendererIndex + "_" + paramIndex + "_" + index } value={index}>
{choice}
</option>
);
})
}
</select>
)
ParamList.propTypes = {
rendererIndex: PropTypes.number.isRequired,
paramIndex: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
info: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired
}
export default ParamList;

View File

@ -0,0 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types'
import '../Qrcode.css';
const ParamText = ({ rendererIndex, paramIndex, value, onBlur, onKeyPress }) => (
<input
type="number"
key={"input_" + rendererIndex + "_" + paramIndex}
className="Qr-input small-input"
placeholder={value}
defaultValue={value}
onBlur={onBlur}
onKeyPress={onKeyPress}
/>
)
ParamText.propTypes = {
rendererIndex: PropTypes.number.isRequired,
paramIndex: PropTypes.number.isRequired,
value: PropTypes.string.isRequired,
onBlur: PropTypes.func.isRequired,
onKeyPress: PropTypes.func.isRequired
}
export default ParamText;

View File

@ -0,0 +1,82 @@
import React, { useEffect } from "react";
import {defaultViewBox} from "../../utils/util";
import {ParamTypes} from "../../constant/ParamTypes";
import {getTypeTable, QRPointType} from "../../utils/qrcodeHandler";
function listPoints(qrcode, params) {
if (!qrcode) return []
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = new Array(nCount);
let size = 1.001;
let size2 = 1.001;
let height = params[0];
let height2 = params[1];
let id = 0;
const X = [-Math.sqrt(3)/2, 1/2];
const Y = [ Math.sqrt(3)/2, 1/2];
const Z = [0, 0];
const matrixString = 'matrix(' + String(X[0]) + ', ' + String(X[1]) + ', ' + String(Y[0]) + ', ' + String(Y[1]) + ', ' + String(Z[0]) + ', ' + String(Z[1]) + ')'
if (height <= 0) height = 1.0;
if (height2 <= 0) height2 = 1.0;
for (let x = 0; x < nCount; x++) {
for (let y = 0; y < nCount; y++) {
if (qrcode.isDark(x, y) == false) continue;
else if (typeTable[x][y] == QRPointType.POS_OTHER || typeTable[x][y] == QRPointType.POS_CENTER) {
pointList.push(<rect width={size2} height={size2} key={id++} fill="#FF7F89" x={x + (1 - size2)/2} y={y + (1 - size2)/2} transform={matrixString}/>);
pointList.push(<rect width={height2} height={size2} key={id++} fill="#FFEBF3" x={0} y={0} transform={matrixString+'translate('+String(x + (1 - size2)/2 + size2)+','+String(y + (1 - size2)/2)+') '+'skewY(45) '}/>);
pointList.push(<rect width={size2} height={height2} key={id++} fill="#FFD7D9" x={0} y={0} transform={matrixString+'translate('+String(x + (1 - size2)/2)+','+String(y + size2 + (1 - size2)/2)+') '+'skewX(45) '}/>);
}
else {
pointList.push(<rect width={size} height={size} key={id++} fill="#FF7F89" x={x + (1 - size)/2} y={y + (1 - size)/2} transform={matrixString}/>);
pointList.push(<rect width={height} height={size} key={id++} fill="#FFEBF3" x={0} y={0} transform={matrixString+'translate('+String(x + (1 - size)/2 + size)+','+String(y + (1 - size)/2)+') '+'skewY(45) '}/>);
pointList.push(<rect width={size} height={height} key={id++} fill="#FFD7D9" x={0} y={0} transform={matrixString+'translate('+String(x + (1 - size)/2)+','+String(y + size + (1 - size)/2)+') '+'skewX(45) '}/>);
}
}
}
return pointList;
}
function getParamInfo() {
return [
{
type: ParamTypes.TEXT_EDITOR,
key: '柱体高度',
default: 0.5,
},
{
type: ParamTypes.TEXT_EDITOR,
key: '定位点柱体高度',
default: 0.5,
},
];
}
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={viewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default Renderer25D

View File

@ -0,0 +1,132 @@
import React, { useEffect } from "react";
import {defaultViewBox, rand} from "../../utils/util";
import {ParamTypes} from "../../constant/ParamTypes";
import {getTypeTable, QRPointType} from "../../utils/qrcodeHandler";
function listPoints(qrcode, params) {
if (!qrcode) return []
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = new Array(nCount);
let type = params[0];
let size = params[1] / 100;
let opacity = params[2] / 100;
let posType = params[3];
let id = 0;
let otherColor = params[4];
let posColor = params[5];
const vw = [3, -3];
const vh = [3, -3];
if (size <= 0) size = 1.0
for (let x = 0; x < nCount; x++) {
for (let y = 0; y < nCount; y++) {
if (qrcode.isDark(x, y) == false) continue;
if (typeTable[x][y] == QRPointType.ALIGN_CENTER || typeTable[x][y] == QRPointType.ALIGN_OTHER || typeTable[x][y] == QRPointType.TIMING) {
if (type == 0)
pointList.push(<rect opacity={opacity} width={size} height={size} key={id++} fill={otherColor} x={x + (1 - size)/2} y={y + (1 - size)/2}/>)
else if (type == 1)
pointList.push(<circle opacity={opacity} r={size / 2} key={id++} fill={otherColor} cx={x + 0.5} cy={y + 0.5}/>)
else if (type == 2)
pointList.push(<circle key={id++} opacity={opacity} fill={otherColor} cx={x + 0.5} cy={y + 0.5} r={size / 2} />)
}
else if (typeTable[x][y] == QRPointType.POS_CENTER) {
if (posType == 0) {
pointList.push(<rect width={1} height={1} key={id++} fill={posColor} x={x} y={y}/>);
} else if (posType == 1) {
pointList.push(<circle key={id++} fill={posColor} cx={x + 0.5} cy={y + 0.5} r={1.5} />)
pointList.push(<circle key={id++} fill={posColor} strokeWidth="1" stroke="black" cx={x + 0.5} cy={y + 0.5} r={3} />)
} else if (posType == 2) {
pointList.push(<circle key={id++} fill={posColor} cx={x + 0.5} cy={y + 0.5} r={1.5} />)
pointList.push(<circle key={id++} fill="none" strokeWidth="0.15" strokeDasharray="0.5,0.5" stroke="black" cx={x + 0.5} cy={y + 0.5} r={3} />)
for (let w = 0; w < vw.length; w++) {
pointList.push(<circle key={id++} fill={posColor} cx={x + vw[w] + 0.5} cy={y + 0.5} r={0.5} />)
}
for (let h = 0; h < vh.length; h++) {
pointList.push(<circle key={id++} fill={posColor} cx={x + 0.5} cy={y + vh[h] + 0.5} r={0.5} />)
}
}
}
else if (typeTable[x][y] == QRPointType.POS_OTHER) {
if (posType == 0) {
pointList.push(<rect width={1} height={1} key={id++} fill={posColor} x={x} y={y}/>);
}
}
else {
if (type == 0)
pointList.push(<rect opacity={opacity} width={size} height={size} key={id++} fill={otherColor} x={x + (1 - size)/2} y={y + (1 - size)/2}/>)
else if (type == 1)
pointList.push(<circle opacity={opacity} r={size / 2} key={id++} fill={otherColor} cx={x + 0.5} cy={y + 0.5}/>)
else if (type == 2)
pointList.push(<circle opacity={opacity} key={id++} fill={otherColor} cx={x + 0.5} cy={y + 0.5} r={0.5 * rand(0.33,1.0)} />)
}
}
}
return pointList;
}
function getParamInfo() {
return [
{
type: ParamTypes.SELECTOR,
key: '信息点样式',
default: 0,
choices: [
"矩形",
"圆形",
"随机"
]
},
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点缩放',
default: 100
},
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点不透明度',
default: 100,
},
{
type: ParamTypes.SELECTOR,
key: '定位点样式',
default: 0,
choices: [
"矩形",
"圆形",
"行星",
]
},
{
type: ParamTypes.COLOR_EDITOR,
key: '信息点颜色',
default: '#000000'
},
{
type: ParamTypes.COLOR_EDITOR,
key: '定位点点颜色',
default: '#000000'
}
];
}
const RendererBase = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default RendererBase

View File

@ -0,0 +1,25 @@
import React, { useEffect } from "react";
import {defaultViewBox} from "../../utils/util";
function listPoints(qrcode, params) {
return []
}
function getParamInfo() {
return [];
}
const RenderBlank = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default RenderBlank

View File

@ -1,21 +1,20 @@
import React from "react";
import './Qrcode.css'
import {getTypeTable, QRPointType} from "../utils/qrcodeHandler";
import {defaultRenderer, defaultViewBox, rand} from "../utils/util";
import React, { useEffect } from "react";
import {defaultViewBox} from "../../utils/util";
import {ParamTypes} from "../../constant/ParamTypes";
import {getTypeTable, QRPointType} from "../../utils/qrcodeHandler";
function listPoint(props) {
if (!props.qrcode) return []
function listPoints(qrcode, params) {
if (!qrcode) return []
const qrcode = props.qrcode;
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = [];
const g1 = [];
const g2 = [];
let width2 = props.params[0] / 100;
let width1 = props.params[1] / 100;
let posType = props.params[2];
let width2 = params[0] / 100;
let width1 = params[1] / 100;
let posType = params[2];
let id = 0;
if (width2 <= 0) width2 = 70;
@ -159,39 +158,41 @@ function listPoint(props) {
return pointList;
}
export default class QrRendererDSJ extends React.Component {
constructor(props) {
super(props);
if (this.props.setParamInfo) {
this.props.setParamInfo([
{
key: '信息点缩放',
default: 70,
},
{
key: 'x 宽度',
default: 70,
},
{
key: '定位点样式',
default: 1,
choices: [
"矩形",
"DSJ",
]
},
]
);
}
}
render() {
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(this.props.qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoint(this.props)}
</svg>
);
}
function getParamInfo() {
return [
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点缩放',
default: 70,
},
{
type: ParamTypes.TEXT_EDITOR,
key: 'x 宽度',
default: 70,
},
{
type: ParamTypes.SELECTOR,
key: '定位点样式',
default: 1,
choices: [
"矩形",
"DSJ",
]
},
]
}
const RenderDSJ = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default RenderDSJ

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,11 @@
import React from "react";
import './Qrcode.css'
import {defaultRenderer, defaultViewBox, rand} from "../utils/util";
import React, { useEffect } from "react";
import {defaultViewBox, rand} from "../../utils/util";
import {ParamTypes} from "../../constant/ParamTypes";
import {getTypeTable, QRPointType} from "../../utils/qrcodeHandler";
function listPoint(props) {
if (!props.qrcode) return []
function listPoints(qrcode, params) {
if (!qrcode) return []
const qrcode = props.qrcode;
const nCount = qrcode.getModuleCount();
const pointList = [];
let id = 0;
@ -38,14 +38,21 @@ function listPoint(props) {
return pointList;
}
export default class QrRendererRandRect extends React.Component {
render() {
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(this.props.qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoint(this.props)}
</svg>
);
}
function getParamInfo() {
return []
}
const RendererRandRect = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default RendererRandRect

View File

@ -1,20 +1,19 @@
import React from "react";
import './Qrcode.css'
import {getTypeTable, QRPointType} from "../utils/qrcodeHandler";
import {defaultRenderer, defaultViewBox, rand} from "../utils/util";
import React, { useEffect } from "react";
import {defaultViewBox, rand} from "../../utils/util";
import {ParamTypes} from "../../constant/ParamTypes";
import {getTypeTable, QRPointType} from "../../utils/qrcodeHandler";
function listPoint(props) {
if (!props.qrcode) return []
function listPoints(qrcode, params) {
if (!qrcode) return []
const qrcode = props.qrcode;
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = new Array(nCount);
let type = props.params[0];
let size = props.params[1] / 100;
let opacity = props.params[2] / 100;
let posType = props.params[3];
let type = params[0];
let size = params[1] / 100;
let opacity = params[2] / 100;
let posType = params[3];
let id = 0;
const vw = [3, -3];
@ -70,49 +69,52 @@ function listPoint(props) {
return pointList;
}
export default class QrRendererBase extends React.Component {
constructor(props) {
super(props);
if (this.props.setParamInfo) {
this.props.setParamInfo([
{
key: '信息点样式',
default: 0,
choices: [
"矩形",
"圆形",
"随机"
]
},
{
key: '信息点缩放',
default: 100
},
{
key: '信息点不透明度',
default: 100,
},
{
key: '定位点样式',
default: 0,
choices: [
"矩形",
"圆形",
"行星",
]
},
]
);
}
}
render() {
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(this.props.qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoint(this.props)}
</svg>
);
}
function getParamInfo() {
return [
{
type: ParamTypes.SELECTOR,
key: '信息点样式',
default: 2,
choices: [
"矩形",
"圆形",
"随机"
]
},
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点缩放',
default: 80
},
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点不透明度',
default: 100,
},
{
type: ParamTypes.SELECTOR,
key: '定位点样式',
default: 2,
choices: [
"矩形",
"圆形",
"行星",
]
},
];
}
const RendererRandRound = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default RendererRandRound

View File

@ -1,20 +1,19 @@
import React from "react";
import './Qrcode.css'
import {getTypeTable, QRPointType} from "../utils/qrcodeHandler";
import {defaultRenderer, defaultViewBox, rand} from "../utils/util";
import React, { useEffect } from "react";
import {defaultViewBox, rand} from "../../utils/util";
import {ParamTypes} from "../../constant/ParamTypes";
import {getTypeTable, QRPointType} from "../../utils/qrcodeHandler";
function listPoint(props) {
if (!props.qrcode) return []
function listPoints(qrcode, params) {
if (!qrcode) return []
const qrcode = props.qrcode;
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = new Array(nCount);
let type = props.params[0];
let size = props.params[1] / 100;
let opacity = props.params[2] / 100;
let posType = props.params[3];
let type = params[0];
let size = params[1] / 100;
let opacity = params[2] / 100;
let posType = params[3];
let id = 0;
const vw = [3, -3];
@ -70,49 +69,52 @@ function listPoint(props) {
return pointList;
}
export default class QrRendererRound extends React.Component {
constructor(props) {
super(props);
if (this.props.setParamInfo) {
this.props.setParamInfo([
{
key: '信息点样式',
default: 1,
choices: [
"矩形",
"圆形",
"随机"
]
},
{
key: '信息点缩放',
default: 50
},
{
key: '信息点不透明度',
default: 30,
},
{
key: '定位点样式',
default: 1,
choices: [
"矩形",
"圆形",
"行星",
]
},
]
);
}
}
render() {
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(this.props.qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoint(this.props)}
</svg>
);
}
function getParamInfo() {
return [
{
type: ParamTypes.SELECTOR,
key: '信息点样式',
default: 1,
choices: [
"矩形",
"圆形",
"随机"
]
},
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点缩放',
default: 50
},
{
type: ParamTypes.TEXT_EDITOR,
key: '信息点不透明度',
default: 30,
},
{
type: ParamTypes.SELECTOR,
key: '定位点样式',
default: 1,
choices: [
"矩形",
"圆形",
"行星",
]
},
]
}
const RendererRound = ({ qrcode, params, setParamInfo}) => {
useEffect(() => {
setParamInfo(getParamInfo());
}, [setParamInfo]);
return (
<svg className="Qr-item-svg" width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(qrcode, params)}
</svg>
)
}
export default RendererRound

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

@ -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, onSelected }) => (
<div className={calClassName(selected)}
onMouseDown={onSelected}>
<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,
onSelected: 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}
onSelected={() => 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,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,6 @@
export const ParamTypes = {
TEXT_EDITOR: 1,
SELECTOR: 2,
COLOR_EDITOR: 3,
MULTI_CHECK: 4,
}

View File

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

View File

@ -0,0 +1,16 @@
import {connect} from 'react-redux';
import {genQRInfo} from "../actions";
import React from "react";
const InputText = ({dispatch}) => (
<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))
}}
/>
)
export default connect()(InputText);

View File

@ -1,4 +0,0 @@
.Layout {
margin: 0;
padding: 0;
}

View File

@ -1,12 +0,0 @@
import React from "react";
import './Layout.css'
const Layout = (props) => {
return (
<div className="Layout">
{props.children}
</div>
);
}
export default Layout;

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,17 @@
import { connect } from 'react-redux';
import {changeParam} from "../../actions";
import ParamColor from "../../components/param/ParamColor";
const mapStateToProps = (state, ownProps) => ({
rendererIndex: ownProps.rendererIndex,
paramIndex: ownProps.paramIndex,
value: state.paramValue[ownProps.rendererIndex][ownProps.paramIndex]
})
const mapDispatchToProps = (dispatch, ownProps) => ({
onChange: (color) => {
dispatch(changeParam(ownProps.rendererIndex, ownProps.paramIndex, color.hex))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ParamColor);

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

@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import ParamList from "../../components/param/ParamList";
const mapStateToProps = (state) => ({
rendererIndex: state.selectedIndex,
paramInfo: state.paramInfo[state.selectedIndex]
})
export default connect(mapStateToProps, null)(ParamList)

View File

@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import {changeParam} from "../../actions";
import ParamSelect from "../../components/param/ParamSelect";
const mapStateToProps = (state, ownProps) => ({
rendererIndex: ownProps.rendererIndex,
paramIndex: ownProps.paramIndex,
value: state.paramValue[ownProps.rendererIndex][ownProps.paramIndex],
info: state.paramInfo[ownProps.rendererIndex][ownProps.paramIndex],
})
const mapDispatchToProps = (dispatch, ownProps) => ({
onChange: (e) => {
dispatch(changeParam(ownProps.rendererIndex, ownProps.paramIndex, e.target.value))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ParamSelect);

View File

@ -0,0 +1,21 @@
import { connect } from 'react-redux';
import ParamText from "../../components/param/ParamText";
import {changeParam} from "../../actions";
const mapStateToProps = (state, ownProps) => ({
rendererIndex: ownProps.rendererIndex,
paramIndex: ownProps.paramIndex,
value: String(state.paramValue[ownProps.rendererIndex][ownProps.paramIndex])
})
const mapDispatchToProps = (dispatch, ownProps) => ({
onBlur: (e) => dispatch(changeParam(ownProps.rendererIndex, ownProps.paramIndex, e.target.value)),
onKeyPress: (e) => {
if(e.key === 'Enter') {
dispatch(changeParam(ownProps.rendererIndex, ownProps.paramIndex, e.target.value));
e.target.blur()
}
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ParamText);

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 => 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,12 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import App from './components/app/App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
import {createStore} from "redux";
const store = createStore(rootReducer);
ReactDOM.render(
<React.StrictMode>
<App />
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);

64
src/reducers/index.js Normal file
View File

@ -0,0 +1,64 @@
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,
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) {
switch (action.type) {
case actionTypes.GENERATE_QR_INFO: {
let text = action.text;
if (!text || text.length == 0) text = QRBTF_URL;
return Object.assign({}, state, {
textUrl: text,
qrcode: getQrcodeData({text: text, correctLevel: state.correctLevel})
});
}
case actionTypes.CHANGE_STYLE: {
return Object.assign({}, state, {
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,
paramValue: action.paramValue
})
}
case actionTypes.CHANGE_PARAM: {
return Object.assign({}, state, {
paramValue: state.paramValue.map((item, index) => {
if (index != action.rendererIndex) {
return item;
}
const newItem = item.slice();
newItem[action.paramIndex] = isNaN(action.value) ? action.value : parseInt(action.value);
return newItem;
})
});
}
default: return state
}
}

View File

@ -17,6 +17,7 @@
//---------------------------------------------------------------------
// QR8bitByte
//---------------------------------------------------------------------
/* eslint-disable */
function QR8bitByte(data) {
this.mode = QRMode.MODE_8BIT_BYTE;

View File

@ -1,4 +1,4 @@
import React from "react";
import {ParamTypes} from "../constant/ParamTypes";
let seed = 0;
@ -22,6 +22,11 @@ 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();
@ -31,3 +36,8 @@ export function isWeiXin(){
return false;
}
}
export function getParamDetailedValue(item, paramValue) {
if (item.type == ParamTypes.SELECTOR) return item.choices[paramValue];
return paramValue;
}