Commit 71af921b authored by 彭俊龙's avatar 彭俊龙

批量下载二维码

parent dd7c2e61
Pipeline #95155 failed with stages
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
"jszip": "^3.10.1", "jszip": "^3.10.1",
"lodash": "4.17.11", "lodash": "4.17.11",
"minimist": "1.2.0", "minimist": "1.2.0",
"panda-xform": "6.10.69", "panda-xform": "6.10.77",
"parseForm": "^2.3.8", "parseForm": "^2.3.8",
"prop-types": "15.7.2", "prop-types": "15.7.2",
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
......
This diff was suppressed by a .gitattributes entry.
import React, { useRef } from 'react';
import { useState, useEffect, useMemo } from 'react';
import { Button, Input, Checkbox, Tooltip, Modal, Pagination, Spin } from 'antd';
import styles from './index.less';
import QRCode from 'qrcode.react';
import { GetAllDeviceDetails } from '@/services/flow/flow';
import html2canvas from 'html2canvas';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
const downloadFileUrl = '/PandaWorkFlow/WorkFlow/AccountManage/DownloadFiles';
const QRCodeView = props => {
const { style, values, size, id } = props;
const QRCodeValue = useMemo(() => {
return `${
window.location.origin
}/civ_mobile/product/workflow/equipment/devicePatrol?DeviceCode=${
values.deviceCode
}&AccountName=${values.accountName}`;
}, [values.deviceCode, values.accountName]);
return (
<div className={styles.QRCode} style={style || {}} id={id}>
<div className={styles.company}>{values.corporateName || '智慧水务'}</div>
<div className={styles.title}>{values.title || '设备二维码'}</div>
<div className={styles.QR}>
<QRCode
value={QRCodeValue}
size={size || 290} // 二维码的大小
level="H" // 设置容错等级为最高
fgColor="#000000" // 二维码的颜色
style={{ margin: 'auto' }}
/>
<div className={styles.QRLogo}>
{values.isSystemLogo === '系统LOGO' ? (
<img
style={{ width: '100%', height: '100%' }}
src={`${
window.origin
}/civweb4/assets/images/icon/%E7%86%8A%E7%8C%AB-%E8%93%9D%E8%89%B2.png`}
/>
) : (
<img
style={{
width: '100%',
height: '100%',
display: values.labelLogoUrl ? 'block' : 'none',
}}
src={`${window.origin}${downloadFileUrl}?filePath=${values.labelLogoUrl}`}
/>
)}
</div>
</div>
<div className={styles.pandaLogo}>
<div
className={styles.pandaLogoC}
style={{ display: values.isPandaLogo ? 'block' : 'none' }}
>
<img
style={{ width: '100%', height: '100%' }}
src={require('@/assets/images/bsmanager/patrolMaintenance/maintenance/熊猫logo.png')}
/>
</div>
</div>
<div className={styles.info}>
<div className={styles.DeviceName}>{values.deviceName}</div>
<div style={{ margin: '2%' }} />
<div className={styles.DeviceCode}>{values.deviceCode}</div>
</div>
</div>
);
};
const QRCodeViewSuzhou = props => {
const { style, values, size, id } = props;
const QRCodeValue = useMemo(() => {
return `${
window.location.origin
}/civ_mobile/product/workflow/equipment/devicePatrol?DeviceCode=${
values.deviceCode
}&AccountName=${values.accountName}`;
}, [values.deviceCode, values.accountName]);
return (
<div className={styles.QRCodeSuzhou} style={style || {}} id={id}>
<div className={styles.info}>
<div className={styles.DeviceName}>{values.deviceName}</div>
<div style={{ margin: '2%' }} />
<div className={styles.DeviceCode}>{values.deviceCode}</div>
<div className={styles.infoTip}>
<img
src={require('@/assets/images/bsmanager/patrolMaintenance/maintenance/宿州tip.png')}
/>
</div>
</div>
<div className={styles.QR}>
{/* 8888888 */}
<QRCode
value={QRCodeValue}
size={size || 310}
level="H"
fgColor="#000000"
style={{ margin: 'auto' }}
/>
<div className={styles.QRLogo}>
{values.isSystemLogo === '系统LOGO' ? (
<img
alt="系统logo"
style={{ width: '100%', height: '100%' }}
src={`${
window.origin
}/civweb4/assets/images/icon/%E7%86%8A%E7%8C%AB-%E8%93%9D%E8%89%B2.png`}
/>
) : (
<img
alt="自定义logo"
style={{
width: '100%',
height: '100%',
display: values.labelLogoUrl ? 'block' : 'none',
}}
src={`${window.origin}${downloadFileUrl}?filePath=${values.labelLogoUrl}`}
/>
)}
</div>
</div>
<div className={styles.company}>{values.corporateName}</div>
</div>
);
};
const QrcodeManager = () => {
const [keys, setKeys] = useState([]);
const [dataSource, setDataSource] = useState([]);
const [checkedData, setCheckedData] = useState([]);
const [card, setCard] = useState(null);
const [show, setShow] = useState(false);
const [page, setPage] = useState({ pageIndex: 1, pageSize: 20 });
const [total, setTotal] = useState(0);
const [searchVal, setSearchVal] = useState('');
const [loadding, setLoadding] = useState(false);
useEffect(() => {
getData({ ...page, ...searchVal });
}, []);
const getData = async params => {
const res = await GetAllDeviceDetails(params);
if (res.code === 0) {
setDataSource(res.data.list || []);
setTotal(res.data.totalCount);
}
};
const itemCheck = (e, item) => {
let checked = e.target.checked;
if (checked) {
setKeys([...keys, item.deviceCode]);
setCheckedData([...checkedData, item]);
} else {
setKeys(keys.filter(k => k !== item.deviceCode));
setCheckedData(checkedData.filter(v => v.deviceCode !== item.deviceCode));
}
};
const checkAll = e => {
let checked = e.target.checked;
setKeys(checked ? dataSource.map(v => v.deviceCode) : []);
setCheckedData(checked ? dataSource : []);
};
const showPreview = v => {
setCard(v);
setShow(true);
};
const onSearch = value => {
setSearchVal(value);
setPage({ pageIndex: 1, pageSize: 20 });
getData({ pageIndex: 1, pageSize: 20, accountName: value });
};
const pageChange = (pageIndex, pageSize) => {
setPage({ pageIndex, pageSize });
getData({ pageIndex, pageSize, accountName: searchVal });
};
const Allexport = async () => {
setLoadding(true);
let zip = new JSZip();
if (checkedData.length) {
for (let i = 0; i < checkedData.length; i++) {
let row = checkedData[i];
const canvas = document.getElementById(row.deviceCode);
const can = await html2canvas(canvas, {
allowTaint: false,
useCORS: true,
width: 610,
scale: 6,
});
let img = new Image();
img.src = can.toDataURL('image/png');
zip.file(`${row.deviceName}_${row.deviceCode}.png`, dataURLtoBlob(img.src));
}
}
zip.generateAsync({ type: 'blob' }).then(res => {
saveAs(res, `${searchVal || '批量下载'}.zip`);
setLoadding(false);
});
};
const dataURLtoBlob = dataurl => {
var arr = dataurl.split(',');
var mime = arr[0].match(/:(.*?);/)[1];
var bstr = atob(arr[1]);
var n = bstr.length;
var u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
};
return (
<div className={styles.valvePanel}>
<div className={styles.mainPage}>
<div className={styles.top}>
<div className={styles.pleft}>
<Checkbox style={{ width: '100px' }} onChange={e => checkAll(e)}>
全选
</Checkbox>
<Input.Search
style={{ minWidth: '280px' }}
placeholder="请输入检索内容"
onSearch={onSearch}
allowClear
/>
<Button style={{ marginLeft: '10px' }} type="primary" onClick={() => Allexport()}>
批量下载二维码
</Button>
</div>
</div>
<div className={styles.bottom}>
<Spin spinning={loadding} tip="下载中...">
<div className={styles.cards}>
{dataSource.map(v => {
return (
<div className={styles.card} key={`${v.deviceCode}`}>
<div className={styles.cardBg}>
<div className={styles.header}>
<div className={styles.rleft}>
<span className={styles.ticon} />
<span>{v.deviceName}</span>
<span>{`(${v.deviceCode})`}</span>
</div>
<div className={styles.rright}>
<Checkbox
checked={keys.some(s => s === v.deviceCode)}
onChange={e => itemCheck(e, v)}
/>
</div>
</div>
<div className={styles.content}>
<div
className={styles.timg}
onClick={() => {
showPreview(v);
}}
>
{v.template === 'default' || !v.template ? (
<QRCodeView style={{ zoom: 0.2 }} size={330} values={v} />
) : (
<QRCodeViewSuzhou style={{ zoom: 0.2 }} size={330} values={v} />
)}
</div>
<div className={styles.items}>
<div className={styles.item}>
<div className={styles.label}>台账名称</div>
<div className={styles.value}>
<Tooltip title={v.accountName}>{v.accountName}</Tooltip>
</div>
</div>
<div className={styles.item}>
<div className={styles.label}>设备名称</div>
<div className={styles.value}>
<Tooltip title={v.deviceName}>{v.deviceName}</Tooltip>
</div>
</div>
<div className={styles.item}>
<div className={styles.label}>设备编码</div>
<div className={styles.value}>{v.deviceCode}</div>
</div>
<div className={styles.item}>
<div className={styles.label}>设备类型</div>
<div className={styles.value}>
<Tooltip title={v.deviceType}>{v.deviceType}</Tooltip>
</div>
</div>
{/* <div className={styles.item}>
<div className={styles.label}>最近维护</div>
<div className={styles.value} style={{ overflow: 'hidden' }}>{v.lastModified}</div>
</div> */}
</div>
</div>
</div>
</div>
);
})}
</div>
</Spin>
<div className={styles.footer}>
<Pagination
pageSizeOptions={[10, 20, 50, 100, 200]}
current={page.pageIndex}
pageSize={page.pageSize}
total={total}
showSizeChanger
showTotal={count => `共计 ${count} 条`}
onChange={pageChange}
/>
</div>
</div>
</div>
<Modal
wrapClassName={styles.QRCodeMinModal}
onCancel={() => setShow(false)}
visible={show}
footer={null}
width={620}
bodyStyle={{ width: '100%', height: '980px', padding: '0' }}
>
{card?.template === 'default' || !card?.template ? (
<QRCodeView values={card} />
) : (
<QRCodeViewSuzhou values={card} />
)}
</Modal>
<div
id={'QRCodeBoxs'}
style={{ position: 'fixed', left: '-1200px', top: '-1200px', background: '#fff' }}
>
{checkedData.map((row, i) => {
const Component =
row.template === 'default' || !row.template ? QRCodeView : QRCodeViewSuzhou;
return (
<Component
key={i}
id={row.deviceCode}
values={row}
style={{ width: '610px', height: '980px', margin: '0 auto' }}
/>
);
})}
</div>
</div>
);
};
export default QrcodeManager;
@imgSrc: '@/assets/images/bsmanager/patrolMaintenance/maintenance';
.valvePanel {
width: 100%;
height: 100%;
padding: 0.3%;
.mainPage {
width: 100%;
height: 100%;
.top {
width: 100%;
height: 50px;
padding: 0 10px;
background: white;
display: flex;
align-items: center;
justify-content: space-between;
.pleft {
display: flex;
align-items: center;
}
}
.bottom {
width: 100%;
height: calc(100% - 115px);
overflow: auto;
.cards {
width: 100%;
display: flex;
flex-wrap: wrap;
.card {
margin-top: 7px;
width: 24.7%;
height: 240px;
border: 1px solid #96C5FB;
border-radius: 5px;
margin-left: 0.3%;
background: linear-gradient(to bottom, #D8EBFF, #FFFFFF);
.cardBg {
width: 100%;
height: 100%;
padding: 10px;
background: url('./img/卡片背景.png') no-repeat;
background-size: 50% 50%;
background-position: right 0 top 0;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
.rleft {
display: flex;
align-items: center;
padding-left: 10px;
// .ticon {
// display: inline-block;
// width: 30px;
// height: 28px;
// background: url('@{imgSrc}/阀门.png');
// background-size: 100% 100%;
// margin-right: 5px;
// }
}
}
.content {
margin-top: 2px;
width: 100%;
height: calc(100% - 29px);
display: flex;
.timg {
width: 140px;
height: 100%;
display: flex;
justify-content: center;
position: relative;
transition: all 0.3s ease-in-out;
&:hover {
cursor: pointer;
transform: scale(1.1);
}
.timgtext {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: white;
opacity: 0.8;
}
}
.items {
flex: 1;
margin-left: 5px;
padding: 15px 5px;
.item {
display: flex;
height: 31.5px;
line-height: 31.5px;
&:nth-child(1) {
border-top: 1px solid #D9E1EA;
}
.label {
width: 80px;
background: #F1F7FD;
border: 1px solid #D9E1EA;
border-top: none;
border-right: none;
padding-left: 7px;
}
.value {
flex: 1;
background: white;
border: 1px solid #D9E1EA;
border-top: none;
padding-left: 7px;
overflow: hidden;
}
}
}
}
}
}
.lists {
height: 100%;
background: white;
padding: 10px;
.rowLayerName {
background: #EEF6FF;
color: #2B8FFF;
padding: 2px 7px;
border-radius: 3px;
border: 1px solid;
}
.fileView {
display: flex;
justify-content: center;
align-items: center;
.imgSpan {
position: relative;
transition: all 0.2s ease-in;
&:hover {
cursor: pointer;
transform: scale(1.2);
}
.img {
max-width: 40px;
max-height: 40px;
}
.imgSub {
position: absolute;
top: 0;
right: 0;
background: red;
color: white;
width: 14px;
height: 14px;
text-align: center;
line-height: 14px;
font-size: 12px;
border-radius: 50%;
}
}
}
:global {
.@{ant-prefix}-table-wrapper {
height: 100%;
.@{ant-prefix}-spin-nested-loading {
height: 100%;
}
.@{ant-prefix}-spin-container {
height: 100%;
}
.@{ant-prefix}-table {
height: 100%;
.@{ant-prefix}-table-container {
height: 100%;
}
}
}
.@{ant-prefix}-table.@{ant-prefix}-table-bordered>.@{ant-prefix}-table-container {
border-left: 1px solid #dbe7fb;
.ant-table-header>table {
border-top: 1px solid #dbe7fb;
}
}
.@{ant-prefix}-table-thead>tr {
height: 40px;
th {
text-align: center;
border-top: 1px solid #dbe7fb;
font-weight: bold;
overflow: inherit;
user-select: none;
border-bottom: 1px solid #dbe7fb;
background: linear-gradient(to bottom, #FFFFFF, #E5F0FD);
&:nth-child(1) {
border-left: 1px solid #dbe7fb;
}
&:last-child {
border-right: 1px solid #dbe7fb;
}
}
}
.@{ant-prefix}-table-tbody>tr {
&:nth-child(2n-1) {
td {
background: #f6f9fe;
}
}
td {
border-bottom: 1px solid #dbe7fb;
border-right: 1px solid #dbe7fb !important;
&:nth-child(1) {
border-left: 1px solid #dbe7fb;
}
&:last-child {
border-right: 1px solid #dbe7fb;
}
}
&:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) {
td {
background: rgb(237, 242, 255) !important;
}
}
}
.@{ant-prefix}-table-body {
overflow-x: scroll;
&::-webkit-scrollbar {
width: 0;
height: 12px;
}
&::-webkit-scrollbar {
width: 0;
height: 6px;
}
&::-webkit-scrollbar-thumb {
background: rgb(41, 166, 255);
}
&::-webkit-scrollbar-track {
background: #f7f4f5;
padding: 0 3px;
}
}
}
}
.footer {
height: 50px;
padding-right: 10px;
background: white;
display: flex;
align-items: center;
justify-content: flex-end;
position: absolute;
width: 100%;
bottom: 0;
}
}
}
}
.QRCode {
width: 100%;
height: 100%;
background: url('@{imgSrc}/二维码背景.png');
background-size: 100% 100%;
position: relative;
.company {
height: 100px;
color: white;
padding: 3%;
text-align: center;
font-size: 40px;
}
.title {
text-align: center;
color: white;
font-weight: 700;
font-size: 52px;
font-style: italic;
}
.QR {
position: absolute;
top: 37.5%;
left: 26.2%;
width: 47.3%;
height: 29.7%;
overflow: hidden;
.QRLogo {
z-index: 99;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 86px;
height: 86px;
.systemLogo {
width: 100%;
height: 100%;
background: url('@{imgSrc}/熊猫-蓝色.png');
background-size: 100% 100%;
}
}
}
.pandaLogo {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 22%;
width: 47%;
height: 5%;
.pandaLogoC {
// width: 100%;
// height: 100%;
// background: url('@{imgSrc}/熊猫logo.png');
// background-size: 100% 100%;
}
}
.info {
position: absolute;
left: 50%;
bottom: 6%;
transform: translateX(-50%);
width: 84%;
height: 10%;
.tag {
margin-left: 29%;
padding: 4px 20px;
border-radius: 2px;
}
.DeviceName {
text-align: center;
font-weight: bold;
font-size: 30px;
}
.DeviceCode {
text-align: center;
font-weight: bold;
font-size: 30px;
}
}
}
.QRCodeSuzhou {
width: 100%;
height: 100%;
background: url('@{imgSrc}/宿州二维码背景.png');
background-size: 100% 90%;
position: relative;
.company {
height: 100px;
width: 100%;
color: #0086D1;
padding: 3%;
text-align: center;
background: white;
position: absolute;
bottom: 0;
font-family: 'FZZYK', sans-serif;
font-size: 40px;
}
.title {
text-align: center;
color: white;
font-weight: 700;
font-style: italic;
font-size: 52px;
}
.QR {
position: absolute;
top: 32.5%;
left: 22%;
// transform: translate(-50%, -50%);
width: 56.3%;
height: 35.7%;
overflow: hidden;
background: #fff;
border-radius: 25px;
display: flex;
justify-content: center;
align-items: center;
// position: absolute;
// top: 37.5%;
// left: 26.2%;
// width: 47.3%;
// height: 29.7%;
// overflow: hidden;
.QRLogo {
z-index: 99;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 86px;
height: 86px;
.systemLogo {
width: 100%;
height: 100%;
background: url('@{imgSrc}/熊猫-蓝色.png');
background-size: 100% 100%;
}
}
}
.pandaLogo {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 22%;
width: 47%;
height: 5%;
}
.info {
position: absolute;
left: 50%;
top: 6%;
transform: translateX(-50%);
width: 84%;
height: 15%;
background: url('@{imgSrc}/宿州标题.png');
background-size: 100% 40%;
background-repeat: no-repeat;
background-position-y: 100%;
text-align: center;
.infoTip {
position: absolute;
bottom: 5px;
left: 50%;
transform: translate(-50%, 120%);
img {
width: 60px;
}
}
.tag {
// margin-left: 29%;
padding: 4px 20px;
border-radius: 2px;
}
.DeviceName {
text-align: center;
font-weight: bold;
color: #fff;
font-size: 30px;
}
.DeviceCode {
text-align: center;
font-weight: bold;
color: #0086D1;
position: absolute;
left: 50%;
bottom: 4px;
font-size: 30px;
transform: translateX(-50%);
}
}
}
.QRCodeMinModal {
.ant-modal {
zoom: 0.5;
}
.ant-modal-close-x {
width: 40px;
height: 40px;
color: white;
}
}
\ No newline at end of file
...@@ -157,6 +157,9 @@ const Holidays = asyncComponent(() => import('@/pages/bsmanager/workOrder/holida ...@@ -157,6 +157,9 @@ const Holidays = asyncComponent(() => import('@/pages/bsmanager/workOrder/holida
const Maintenance = asyncComponent(() => const Maintenance = asyncComponent(() =>
import('@/pages/bsmanager/patrolMaintenance/maintenance/maintenance'), import('@/pages/bsmanager/patrolMaintenance/maintenance/maintenance'),
); );
const QrcodeManager = asyncComponent(() =>
import('@/pages/bsmanager/patrolMaintenance/qrcodeManager'),
);
const MaintenanceOld = asyncComponent(() => const MaintenanceOld = asyncComponent(() =>
import('@/pages/bsmanager/patrolMaintenance/maintenance/maintenanceOld'), import('@/pages/bsmanager/patrolMaintenance/maintenance/maintenanceOld'),
); );
...@@ -392,6 +395,11 @@ export default { ...@@ -392,6 +395,11 @@ export default {
name: '设备巡检(旧)', name: '设备巡检(旧)',
component: MaintenanceOld, component: MaintenanceOld,
}, },
{
path: '/biz/patrolMaintenance/qrcodeManger',
name: '二维码管理',
component: QrcodeManager,
},
], ],
}, },
], ],
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* @LastEditTime: 2022-08-17 10:10:50 * @LastEditTime: 2022-08-17 10:10:50
* @LastEditors: leizhe * @LastEditors: leizhe
*/ */
import { get, post, PUBLISH_SERVICE, PANDA_GIS } from '@/services/index'; import { get, post, PUBLISH_SERVICE, PANDA_GIS, PANDAWORKFLOW } from '@/services/index';
// 工单流程列表查询 // 工单流程列表查询
export const reloadFlows = param => export const reloadFlows = param =>
get(`${PUBLISH_SERVICE}/WorkOrderCenter/CM_Flow_ReloadFlows`, param); get(`${PUBLISH_SERVICE}/WorkOrderCenter/CM_Flow_ReloadFlows`, param);
...@@ -120,3 +120,5 @@ export const BatchEditDeviceQRCodeConfig = query => post(`${PUBLISH_SERVICE}/Wor ...@@ -120,3 +120,5 @@ export const BatchEditDeviceQRCodeConfig = query => post(`${PUBLISH_SERVICE}/Wor
//修改流程节点顺序 //修改流程节点顺序
export const UpdateFlowNodeOrder = query => get(`${PUBLISH_SERVICE}/WorkOrderCenter/UpdateFlowNodeOrder`, query); export const UpdateFlowNodeOrder = query => get(`${PUBLISH_SERVICE}/WorkOrderCenter/UpdateFlowNodeOrder`, query);
export const GetAllDeviceDetails = query => get(`${PANDAWORKFLOW}/DevicePatrol/GetAllDeviceDetails`, query);
...@@ -16,6 +16,7 @@ const WebSERVICE = '/PandaConfiguration/Raw'; ...@@ -16,6 +16,7 @@ const WebSERVICE = '/PandaConfiguration/Raw';
const CoreSERVICE = '/PandaCore/GCK'; const CoreSERVICE = '/PandaCore/GCK';
const PANDA_GIS = '/PandaGIS/MapServer'; const PANDA_GIS = '/PandaGIS/MapServer';
const PandaCore = '/PandaCore/GateWay'; const PandaCore = '/PandaCore/GateWay';
const PANDAWORKFLOW = '/PandaWorkFlow/WorkFlow';
const get = async (url, params, options = {}) => const get = async (url, params, options = {}) =>
request({ request({
url, url,
...@@ -48,4 +49,5 @@ export { ...@@ -48,4 +49,5 @@ export {
CoreSERVICE, CoreSERVICE,
PANDA_GIS, PANDA_GIS,
PandaCore, PandaCore,
PANDAWORKFLOW
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment