merge from file-import
This commit is contained in:
commit
effe43a91c
|
@ -0,0 +1,15 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="p-id" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
|
@ -7,6 +7,7 @@
|
|||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"jsqr": "^1.3.1",
|
||||
"jszip": "^3.7.1",
|
||||
"medium-zoom": "^1.0.5",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.13.1",
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
import {actionTypes} from "../constant/ActionTypes";
|
||||
import {handleStyle} from "../utils/gaHelper";
|
||||
|
||||
export const genQRInfo = text => ({
|
||||
type: actionTypes.GENERATE_QR_INFO,
|
||||
text
|
||||
})
|
||||
export const genQRInfo = text => {
|
||||
if (Array.isArray(text)) {
|
||||
return {
|
||||
type: actionTypes.GENERATE_QR_INFO,
|
||||
text: text[0],
|
||||
textArray: text
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: actionTypes.GENERATE_QR_INFO,
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
export const changeStyle = (rendererIndex, value) => {
|
||||
handleStyle(value);
|
||||
|
|
|
@ -318,7 +318,7 @@ td {
|
|||
.Qr-input-upload {
|
||||
display: flex;
|
||||
max-width: calc(100vw - 46px);
|
||||
width: 20em;
|
||||
width: 22em;
|
||||
}
|
||||
|
||||
.Qr-input-upload-div {
|
||||
|
@ -360,7 +360,7 @@ td {
|
|||
height: calc(2em + 6px);
|
||||
font-size: calc(10px + 2vmin);
|
||||
border: var(--border-color) solid 2px;
|
||||
margin-left: 10px;
|
||||
margin: 0 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
@ -372,6 +372,7 @@ td {
|
|||
|
||||
.Qr-upload {
|
||||
color: var(--upload-color);
|
||||
fill: var(--upload-color);
|
||||
font-size: calc(10px + 2vmin);
|
||||
border: var(--border-color) solid 2px;
|
||||
width: calc(2em + 6px)!important;
|
||||
|
@ -409,6 +410,7 @@ td {
|
|||
.Qr-upload:hover {
|
||||
border-color: var(--border-color-focus);
|
||||
color: var(--border-color-focus);
|
||||
fill: var(--border-color-focus);
|
||||
}
|
||||
|
||||
.Qr-upload:active {
|
||||
|
@ -417,6 +419,7 @@ td {
|
|||
-moz-transition-duration: 0s;
|
||||
border-color: #3BBC9F;
|
||||
color: #3BBC9F;
|
||||
fill: #3BBC9F;
|
||||
}
|
||||
|
||||
.Qr-upload-svg {
|
||||
|
|
|
@ -4,6 +4,12 @@ import PropTypes from 'prop-types';
|
|||
import {isWeiXin} from "../../utils/navigatorUtils";
|
||||
import PresetModalViewer from "../../containers/preset/PresetModalViewer";
|
||||
|
||||
const CountComponent = ({ value }) => {
|
||||
if (isNaN(value)) return null;
|
||||
if (value >= 10000) value = (value / 10000).toFixed(1) + "万";
|
||||
return <sup className="Gray">{value}</sup>
|
||||
}
|
||||
|
||||
const WxMessage = () => {
|
||||
if (isWeiXin()) {
|
||||
return (
|
||||
|
@ -13,7 +19,7 @@ const WxMessage = () => {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
const ImgBox = ({ imgData }) => {
|
||||
|
@ -29,7 +35,7 @@ const ImgBox = ({ imgData }) => {
|
|||
return null
|
||||
}
|
||||
|
||||
const PartDownload = ({ value, downloadCount, onSvgDownload, onImgDownload, savePreset }) => {
|
||||
const PartDownload = ({ value, batchMode, downloadCount, onSvgDownload, onImgDownload, savePreset }) => {
|
||||
const [imgData, setImgData] = useState('');
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
|
@ -38,8 +44,10 @@ const PartDownload = ({ value, downloadCount, onSvgDownload, onImgDownload, save
|
|||
<div className="Qr-Centered title-margin">
|
||||
<div className="Qr-s-title">Downloads</div>
|
||||
<p className="Qr-s-subtitle">
|
||||
下载二维码 — {value}
|
||||
<sup className="Gray"> {downloadCount}</sup>
|
||||
<span>下载二维码</span>
|
||||
<span>({batchMode ? "批量模式" : "单文件模式"})</span>
|
||||
<span> — {value}</span>
|
||||
<CountComponent value={downloadCount} />
|
||||
</p>
|
||||
</div>
|
||||
<div className="Qr-Centered">
|
||||
|
|
|
@ -51,6 +51,7 @@ const PartMore = () => {
|
|||
</ScrollContainer>
|
||||
<div className="Qr-Centered">
|
||||
<h2>最新消息</h2>
|
||||
<p><b>2021.10.5</b><br/>新增批量生成二维码。</p>
|
||||
<p><b>2020.9.1</b><br/>新增 C3 样式、图标插入、 PNG 下载。</p>
|
||||
<p><b>2020.6.29</b><br/>新的反馈渠道!我们开始征集好玩的二维码设计啦,可以是推送尾图、海报等等,快来上传吧。<LinkTrace
|
||||
href='https://qrbtf-com.mikecrm.com/J2wjEEq' rel="noopener noreferrer"
|
||||
|
|
|
@ -17,7 +17,7 @@ const ParamUpload = ({ rendererIndex, paramIndex, onChange }) => (
|
|||
key={"input_" + rendererIndex + "_" + paramIndex}
|
||||
id="image_upload"
|
||||
hidden={true}
|
||||
accept=".jpg, .jpeg, .png"
|
||||
accept="image/*"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import * as React from "react"
|
||||
|
||||
function FileImport(props) {
|
||||
return (
|
||||
<svg width={30} d="1633445633242" className="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<style type="text/css"/>
|
||||
</defs>
|
||||
<path
|
||||
d="M182 841h-46C61.442 841 1 780.558 1 706V136C1 61.442 61.442 1 136 1h570c74.558 0 135 60.442 135 135v48h46c74.558 0 135 60.442 135 135v570c0 74.558-60.442 135-135 135H317c-74.558 0-135-60.442-135-135v-48z m569-657v-48c0-24.853-20.147-45-45-45H136c-24.853 0-45 20.147-45 45v570c0 24.853 20.147 45 45 45h46V319c0-74.558 60.442-135 135-135h434z m-434 90c-24.853 0-45 20.147-45 45v570c0 24.853 20.147 45 45 45h570c24.853 0 45-20.147 45-45V319c0-24.853-20.147-45-45-45H317z m239.71 278.5V402.79c0-24.854 20.148-45 45-45 24.854 0 45 20.146 45 45V552.5h149.712c24.852 0 45 20.147 45 45s-20.148 45-45 45H646.71v149.71c0 24.854-20.147 45-45 45s-45-20.146-45-45V642.5H407c-24.853 0-45-20.147-45-45s20.147-45 45-45h149.71z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default FileImport;
|
|
@ -4,6 +4,7 @@ import React, {useRef} from "react";
|
|||
import {isPicture} from "../../utils/imageUtils";
|
||||
import {decodeData} from "../../utils/qrcodeHandler";
|
||||
import { handleUpload, handleInputUrl } from "../../utils/gaHelper";
|
||||
import FileImport from "../../components/svg/FileImport";
|
||||
|
||||
const InputText = ({dispatch}) => {
|
||||
const textRef = useRef();
|
||||
|
@ -41,7 +42,6 @@ const InputText = ({dispatch}) => {
|
|||
decodeData(file).then((res) => {
|
||||
if (res) {
|
||||
textRef.current.value = res.data;
|
||||
console.log(res.data)
|
||||
dispatch(genQRInfo(res.data))
|
||||
}
|
||||
}).catch(console.err);
|
||||
|
@ -65,9 +65,39 @@ const InputText = ({dispatch}) => {
|
|||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="text_import" className="Qr-upload">
|
||||
<FileImport />
|
||||
</label>
|
||||
<input
|
||||
type="file"
|
||||
id="text_import"
|
||||
hidden={true}
|
||||
accept={".txt"}
|
||||
onClick={(e) => e.target.value = null}
|
||||
onChange={(e) => {
|
||||
if (e.target.files.length > 0) {
|
||||
const file = e.target.files[0];
|
||||
if (file.type === "text/plain") {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = function(event) {
|
||||
const lines = event.target.result
|
||||
.split(/[\r\n]+/g)
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0);
|
||||
console.log(lines)
|
||||
if (lines.length > 0) {
|
||||
textRef.current.value = lines[0];
|
||||
dispatch(genQRInfo(lines))
|
||||
}
|
||||
}
|
||||
fileReader.readAsText(file, "UTF-8");
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="Qr-input-hint">
|
||||
上传普通二维码或输入网址
|
||||
上传普通二维码、输入网址或上传 txt 文本批量生成
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>);
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import {connect} from 'react-redux';
|
||||
import PartDownload from "../../components/app/PartDownload";
|
||||
import {saveImg, saveSvg} from "../../utils/downloader";
|
||||
import {saveImg, saveSvg, startTask} from "../../utils/downloader";
|
||||
import {getDownloadCount, increaseDownloadData, recordDownloadDetail} from "../../api/TcbHandler";
|
||||
import {getParamDetailedValue, outerHtml} from "../../utils/util";
|
||||
import {fillEmptyWith, getParamDetailedValue, outerHtml} from "../../utils/util";
|
||||
import {handleDownloadImg, handleDownloadSvg} from "../../utils/gaHelper";
|
||||
import {appendPreset} from "../../utils/storageUtils";
|
||||
import {svgToBase64} from "../../utils/imageUtils";
|
||||
import React from "react";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import JSZip from "jszip";
|
||||
import {encodeData} from "../../utils/qrcodeHandler";
|
||||
|
||||
function saveDB(state, type, updateDownloadData) {
|
||||
return new Promise(resolve => {
|
||||
|
@ -42,20 +46,66 @@ function saveDB(state, type, updateDownloadData) {
|
|||
const mapStateToProps = (state, ownProps) => ({
|
||||
value: state.value,
|
||||
downloadCount: state.downloadData[state.value],
|
||||
onSvgDownload: () => {
|
||||
saveSvg(state.value, outerHtml(state.selectedIndex));
|
||||
saveDB(state, 'svg', ownProps.updateDownloadData).catch(console.error);
|
||||
handleDownloadSvg(state.value);
|
||||
},
|
||||
onImgDownload: (type) => {
|
||||
return new Promise(resolve => {
|
||||
saveImg(state.value, outerHtml(state.selectedIndex), 1500, 1500, type).then((res) => {
|
||||
saveDB(state, type, ownProps.updateDownloadData).then(() => {
|
||||
handleDownloadImg(state.value, type);
|
||||
resolve(res)
|
||||
batchMode: state.textUrlArray.length > 0,
|
||||
onSvgDownload: async () => {
|
||||
let data;
|
||||
if (state.textUrlArray.length === 0) {
|
||||
data = saveSvg(state.value, outerHtml(state.selectedIndex));
|
||||
const filename = "QRcode_" + state.value + ".svg";
|
||||
startTask(URL.createObjectURL(data), filename);
|
||||
} else {
|
||||
const zip = new JSZip();
|
||||
for (let i = 0; i < state.textUrlArray.length; i++) {
|
||||
const qrcode = encodeData({ text: state.textUrlArray[i], correctLevel: state.correctLevel });
|
||||
const el = React.createElement(state.rendererType, {
|
||||
qrcode: qrcode,
|
||||
params: fillEmptyWith(state.paramValue[state.selectedIndex].slice(), 0),
|
||||
title: state.title,
|
||||
icon: state.icon,
|
||||
setParamInfo: () => {}
|
||||
});
|
||||
});
|
||||
});
|
||||
data = saveSvg(state.value, ReactDOMServer.renderToString(el));
|
||||
console.log(data)
|
||||
const filename = "QRcode_" + state.value + "_" + i + ".svg";
|
||||
zip.file(filename, data);
|
||||
}
|
||||
const href = URL.createObjectURL(await zip.generateAsync({ type: "blob" }));
|
||||
startTask(href, "QRcode_" + state.value + ".zip");
|
||||
}
|
||||
|
||||
await saveDB(state, 'svg', ownProps.updateDownloadData);
|
||||
handleDownloadSvg(state.value);
|
||||
return data;
|
||||
},
|
||||
onImgDownload: async (type) => {
|
||||
let data;
|
||||
if (state.textUrlArray.length === 0) {
|
||||
data = await saveImg(state.value, outerHtml(state.selectedIndex), 1500, 1500, type);
|
||||
const filename = "QRcode_" + state.value + "." + type;
|
||||
startTask(data, filename);
|
||||
} else {
|
||||
const zip = new JSZip();
|
||||
for (let i = 0; i < state.textUrlArray.length; i++) {
|
||||
const qrcode = encodeData({ text: state.textUrlArray[i], correctLevel: state.correctLevel });
|
||||
const el = React.createElement(state.rendererType, {
|
||||
qrcode: qrcode,
|
||||
params: fillEmptyWith(state.paramValue[state.selectedIndex].slice(), 0),
|
||||
title: state.title,
|
||||
icon: state.icon,
|
||||
setParamInfo: () => {}
|
||||
});
|
||||
data = await saveImg(state.value, ReactDOMServer.renderToString(el), 1500, 1500, type);
|
||||
const startIdx = data.indexOf("base64,") + "base64,".length;
|
||||
const filename = "QRcode_" + state.value + "_" + i + "." + type;
|
||||
zip.file(filename, data.substring(startIdx), {base64: true});
|
||||
}
|
||||
const href = URL.createObjectURL(await zip.generateAsync({ type: "blob" }));
|
||||
startTask(href, "QRcode_" + state.value + ".zip");
|
||||
}
|
||||
|
||||
await saveDB(state, type, ownProps.updateDownloadData)
|
||||
handleDownloadImg(state.value, type);
|
||||
return data;
|
||||
},
|
||||
savePreset: () => {
|
||||
let preset = {
|
||||
|
|
|
@ -8,6 +8,7 @@ const initialState = {
|
|||
value: 'A1',
|
||||
correctLevel: 0,
|
||||
textUrl: QRBTF_URL,
|
||||
textUrlArray: [],
|
||||
history: [],
|
||||
downloadData: [],
|
||||
qrcode: encodeData({text: QRBTF_URL, correctLevel: 0}),
|
||||
|
@ -24,7 +25,8 @@ export default function appReducer(state = initialState, action) {
|
|||
if (!text || text.length === 0) text = QRBTF_URL;
|
||||
return Object.assign({}, state, {
|
||||
textUrl: text,
|
||||
qrcode: encodeData({text: text, correctLevel: state.correctLevel})
|
||||
qrcode: encodeData({text: text, correctLevel: state.correctLevel}),
|
||||
textUrlArray: action.textArray || []
|
||||
});
|
||||
}
|
||||
case actionTypes.CHANGE_STYLE: {
|
||||
|
|
|
@ -3,23 +3,25 @@ const svgHead = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n " +
|
|||
|
||||
const MIME = { "jpg": "image/jpeg", "png": "image/png" };
|
||||
|
||||
export function startTask(data, filename) {
|
||||
let a = document.createElement('a');
|
||||
a.setAttribute('href', data)
|
||||
a.setAttribute('target', 'download')
|
||||
a.setAttribute('download', filename);
|
||||
a.setAttribute('hidden', true);
|
||||
a.click();
|
||||
}
|
||||
|
||||
export function saveSvg(value, content) {
|
||||
let htmlContent = [svgHead + content]
|
||||
let bl = new Blob(htmlContent, {type: "image/svg+xml"})
|
||||
let a = document.createElement("a")
|
||||
let filename = "QRcode_" + value + ".svg"
|
||||
|
||||
a.href = URL.createObjectURL(bl)
|
||||
a.download = filename
|
||||
a.hidden = true
|
||||
a.click()
|
||||
return bl;
|
||||
}
|
||||
|
||||
export function saveImg(value, content, width, height, type) {
|
||||
if (!MIME[type]) throw "Error image type";
|
||||
|
||||
// Finish creating downloadable data
|
||||
let filename = "QRcode_" + value + "." + type;
|
||||
const wrap = document.createElement('div');
|
||||
wrap.innerHTML = content;
|
||||
|
||||
|
@ -51,12 +53,8 @@ export function saveImg(value, content, width, height, type) {
|
|||
// Will result in a download popup for chrome and the
|
||||
// image opening in a new tab for others.
|
||||
|
||||
let a = document.createElement('a');
|
||||
let data = canvas.toDataURL(MIME[type], 0.8);
|
||||
a.setAttribute('href', data)
|
||||
a.setAttribute('target', 'download')
|
||||
a.setAttribute('download', filename);
|
||||
a.click();
|
||||
// startTask(data, filename);
|
||||
|
||||
resolve(data)
|
||||
};
|
||||
|
|
|
@ -39,7 +39,6 @@ export function toBase64(file, aspectRatio) {
|
|||
height = img.height;
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
console.log(width + ' ' + height)
|
||||
canvas.setAttribute('width', width);
|
||||
canvas.setAttribute('height', height);
|
||||
|
||||
|
|
29
yarn.lock
29
yarn.lock
|
@ -5279,6 +5279,11 @@ ignore@^4.0.6:
|
|||
resolved "https://registry.npm.taobao.org/ignore/download/ignore-4.0.6.tgz?cache=0&sync_timestamp=1565775199290&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fignore%2Fdownload%2Fignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=
|
||||
|
||||
immediate@~3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.nlark.com/immediate/download/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
||||
|
||||
immer@1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.npm.taobao.org/immer/download/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
|
@ -6397,6 +6402,16 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3:
|
|||
array-includes "^3.0.3"
|
||||
object.assign "^4.1.0"
|
||||
|
||||
jszip@^3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.nlark.com/jszip/download/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9"
|
||||
integrity sha1-vWNAEiHBViWhIoxVbKimjab9o9k=
|
||||
dependencies:
|
||||
lie "~3.3.0"
|
||||
pako "~1.0.2"
|
||||
readable-stream "~2.3.6"
|
||||
set-immediate-shim "~1.0.1"
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npm.taobao.org/killable/download/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
|
@ -6488,6 +6503,13 @@ levn@^0.3.0, levn@~0.3.0:
|
|||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
|
||||
lie@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.nlark.com/lie/download/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||
integrity sha1-3Pgt7lRfRgdNryAMfBxaCOD0D2o=
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
lines-and-columns@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npm.taobao.org/lines-and-columns/download/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||
|
@ -7483,7 +7505,7 @@ p-try@^2.0.0:
|
|||
resolved "https://registry.npm.taobao.org/p-try/download/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||
integrity sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=
|
||||
|
||||
pako@~1.0.5:
|
||||
pako@~1.0.2, pako@~1.0.5:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.npm.taobao.org/pako/download/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=
|
||||
|
@ -9503,6 +9525,11 @@ set-blocking@^2.0.0:
|
|||
resolved "https://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
|
||||
set-immediate-shim@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.nlark.com/set-immediate-shim/download/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
|
||||
integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
|
||||
|
||||
set-value@^2.0.0, set-value@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npm.taobao.org/set-value/download/set-value-2.0.1.tgz?cache=0&sync_timestamp=1585775409029&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fset-value%2Fdownload%2Fset-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
||||
|
|
Loading…
Reference in New Issue