diff --git a/api/app/lib/controllers/file/index.js b/api/app/lib/controllers/file/index.js index 270447e9..8870da3e 100644 --- a/api/app/lib/controllers/file/index.js +++ b/api/app/lib/controllers/file/index.js @@ -59,30 +59,13 @@ async function delFileDir(ctx, next) { let rslt = [], fileDirIds = []; try { - const { type, id } = ctx.query, // type == parent / child + const { id } = ctx.query, // type == parent / child models = ctx.fs.dc.models; const transaction = await ctx.fs.dc.orm.transaction(); - if (type == 'parent') { - let fileTypes = await models.FileType.findAll({ where: { rId: id } }); - if (fileTypes && fileTypes.length) { - fileDirIds = fileTypes.map(item => item.fId); - } - - if (fileDirIds && fileDirIds.length) { - await models.Files.destroy({ - where: { - fId: { $in: fileDirIds } - }, - transaction - }) - } - await models.FileType.destroy({ where: { rId: id }, transaction }) - await models.FileRoad.destroy({ where: { rId: id }, transaction }) - } else { - await models.Files.destroy({ where: { fId: id }, transaction }) - await models.FileType.destroy({ where: { fId: id }, transaction }) - } + await models.FileRoad.destroy({ where: { rId: id }, transaction }) + await models.Files.destroy({ where: { roadId: id }, transaction }) + await transaction.commit(); error = null; diff --git a/api/log/development.log b/api/log/development.log index 2542724b..58b473fe 100644 --- a/api/log/development.log +++ b/api/log/development.log @@ -10656,3 +10656,6 @@ headers: {} 2022-07-28 18:34:55.231 - error: path: /publicity, error: TypeError: values.map is not a function 2022-07-28 18:35:45.669 - error: path: /publicity, error: TypeError: values.map is not a function 2022-07-28 18:37:40.324 - error: path: /publicity, error: TypeError: values.map is not a function +2022-07-28 21:48:22.652 - debug: [FS-LOGGER] Init. +2022-07-28 21:48:22.760 - info: [FS-ATTACHMENT] Inject attachment mw into router. +2022-07-28 21:48:22.760 - info: [FS-AUTH] Inject auth and api mv into router. diff --git a/web/client/src/components/Upload/index.js b/web/client/src/components/Upload/index.js index eac0a27a..0bd3af10 100644 --- a/web/client/src/components/Upload/index.js +++ b/web/client/src/components/Upload/index.js @@ -54,7 +54,7 @@ class Uploads extends Component { componentWillReceiveProps(np) { const { dispatch, value: thisEditData, onChange } = this.props; - const { value: nextEditData } = np; + const { value: nextEditData, clearFileList } = np; const setFileList = () => { let defaultFileList = []; @@ -92,6 +92,12 @@ class Uploads extends Component { } } } + + if (clearFileList) { + this.setState({ + fileList: [] + }); + } // else{ // this.setState({ // fileList:[], diff --git a/web/client/src/sections/fillion/actions/file.js b/web/client/src/sections/fillion/actions/file.js index e453a967..2b6adc7f 100644 --- a/web/client/src/sections/fillion/actions/file.js +++ b/web/client/src/sections/fillion/actions/file.js @@ -51,14 +51,14 @@ export function updateFileDir(query) { } -// query : {typeId, userId, userName, startDate, endDate, fileSize, fileName, fileUrl, fileExt} -export function uploadFile(query) { +// data : {typeId, userId, userName, startDate, endDate, fileSize, fileName, fileUrl, fileExt} +export function uploadFile(data) { return dispatch => basicAction({ type: 'post', dispatch: dispatch, actionType: 'UPLOAD_FILE', url: ApiTable.uploadFile, - query, + data, msg: { error: '上传文件失败' }, reducer: { name: 'uploadFile' } }); diff --git a/web/client/src/sections/fillion/components/file/functionMenu.js b/web/client/src/sections/fillion/components/file/functionMenu.js new file mode 100644 index 00000000..b4211d18 --- /dev/null +++ b/web/client/src/sections/fillion/components/file/functionMenu.js @@ -0,0 +1,94 @@ +import React, { useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { delFileDir, queryFileDir, updateFileDir, } from '../../actions/file'; +import './menu.less' +import { message, Modal } from 'antd'; +import { ExclamationCircleOutlined } from '@ant-design/icons' + +const { confirm } = Modal; + +const FunctionMenu = props => { + const { dispatch, onDelDir, selectRoad } = props; + + useEffect(() => { + const box = document.getElementById('tree-box'); + if (box) + box.oncontextmenu = function (e) { + //取消默认的浏览器自带右键 很重要!! + e.preventDefault(); + + //获取我们自定义的右键菜单 + var menu = document.querySelector("#rihgt-click-menu"); + + //根据事件对象中鼠标点击的位置,进行定位 + menu.style.left = e.clientX + 'px'; + menu.style.top = e.clientY + 'px'; + + //改变自定义菜单的高宽,让它显示出来 + menu.style.display = 'block'; + } + + //关闭右键菜单,很简单 + window.onclick = function (e) { + //用户触发click事件就可以关闭了,因为绑定在window上,按事件冒泡处理,不会影响菜单的功能 + document.querySelector('#rihgt-click-menu') ? document.querySelector('#rihgt-click-menu').style.display = 'none' : '' + } + }, [true]) + + const onDeleteDir = () => { + + if (selectRoad) { + const id = selectRoad + dispatch(delFileDir({ id })).then(res => { + const { type } = res; + if (type == 'DEL_FILE_DIR_SUCCESS') { + dispatch(queryFileDir()); + message.success('删除成功') + } else { + message.error('删除失败') + } + }); + } + } + + const showDeleteConfirm = () => { + + confirm({ + title: `是否确认删除该道路?`, + icon: , + // content: 'Some descriptions', + okText: '是', + okType: 'danger', + cancelText: '否', + onOk() { + onDeleteDir(); + }, + onCancel() { + }, + }); + } + + const refreshFileDir = () => { + dispatch(queryFileDir()); + } + + return ( +
+
+
+ 删除 +
+
+
+
+ 刷新 +
+
+
+ + ) +} + +FunctionMenu.propTypes = {} + +export default FunctionMenu \ No newline at end of file diff --git a/web/client/src/sections/fillion/components/file/menu.less b/web/client/src/sections/fillion/components/file/menu.less new file mode 100644 index 00000000..c143c3b8 --- /dev/null +++ b/web/client/src/sections/fillion/components/file/menu.less @@ -0,0 +1,43 @@ +#rihgt-click-menu { + display: none; + font-size: 1.1em; + position: fixed; + width: 200px; + height: auto; + padding: 5px 0px; + border-radius: 5px; + top: 10; + left: 10; + background-color: #fff; + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24); + color: #333; + z-index: 999; +} + +#rihgt-click-menu .context_item { + height: 32px; + line-height: 32px; + cursor: pointer; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +#rihgt-click-menu .context_item:hover { + background-color: #ddd; +} + +#rihgt-click-menu .context_item .inner_item { + margin: 0px 10px; +} + +#rihgt-click-menu .context_item .inner_item i { + margin: 0 5px 0 0; + font-weight: bold; +} + +#rihgt-click-menu .context_hr { + height: 1px; + border-top: 1px solid #bbb; + margin: 3px 10px; +} \ No newline at end of file diff --git a/web/client/src/sections/fillion/components/file/uploadModal.js b/web/client/src/sections/fillion/components/file/uploadModal.js index e57a0026..b14b5b49 100644 --- a/web/client/src/sections/fillion/components/file/uploadModal.js +++ b/web/client/src/sections/fillion/components/file/uploadModal.js @@ -32,8 +32,9 @@ const UploadModal = props => { maxFilesNum={1} maxFileSize={10} onChange={onFileUploaded} - // value - // onStateChange + clearFileList={isVisible} + // value + // onStateChange /> diff --git a/web/client/src/sections/fillion/components/fileTable.js b/web/client/src/sections/fillion/components/fileTable.js index 4052e40a..a0fecdcd 100644 --- a/web/client/src/sections/fillion/components/fileTable.js +++ b/web/client/src/sections/fillion/components/fileTable.js @@ -1,32 +1,40 @@ import { connect } from 'react-redux'; import './protable.less' -import { Card, Button, Popconfirm, Badge, Col, Row, DatePicker, Input, Modal, Spin, Image, message, Popover } from 'antd'; +import { Card, Button, Row, DatePicker, Input, Modal, message, Image } from 'antd'; import ProTable from '@ant-design/pro-table'; -import { getFileList, createFileDir, queryFileDir } from '../actions/file'; +import { getFileList, createFileDir, queryFileDir, uploadFile, deleteFile } from '../actions/file'; +import { ExclamationCircleOutlined } from '@ant-design/icons'; import React, { useEffect, useState } from 'react'; import { httpDel } from '@peace/utils' import { PinyinHelper } from '@peace/utils'; import RoadModal from './file/roadModal'; +import Pdfh5 from "pdfh5"; +import "pdfh5/css/pdfh5.css"; +import FunctionMenu from './file/functionMenu'; +const { confirm } = Modal; // @ts-ignore import UploadModal from './file/uploadModal'; +var pdfh5 = null; const DetailList = (props) => { - const { fileList, loading, dispatch, handleOpen, handelRefresh, onPageChange } = props; - const [visible, setVisible] = useState(false) - const [selectRecord, setSelectRecord] = useState(); - const checkDetail = (record) => { - // dispatch(getReportDetail(record.id)) - } + const { fileList, loading, dispatch, handelRefresh, onPageChange } = props; + const [imgSrc, setImgSrc] = useState({ imageView: false, imgSrc: '' }) + const [pdfView, setPdfView] = useState({ showPDF: false, pdfName: '', pdfurl: '' }) + var tyApiRoot = localStorage.getItem('tyApiRoot') + + useEffect(() => { + if (pdfView.showPDF) { + pdfh5 = new Pdfh5("#pdf-loader", { + pdfurl: pdfView.pdfurl + }) + } + }, [pdfView]) - const handleRemove = (record) => { - let url = 'report/{reportId}'; - const actionType = "DEL_REPORT_RECORD"; - const msg = {} + const handleRemove = (record, filePath) => { if (record) { - url = url.replace('{reportId}', record.id) - httpDel(dispatch, { url, actionType, msg }).then(res => { - if (res.success) { + dispatch(deleteFile(record.id)).then(res => { + if (res.type == 'DELETE_FILE_SUCCESS') { message.success("文件删除成功"); handelRefresh() } else { @@ -34,6 +42,30 @@ const DetailList = (props) => { } }) } + let url = `${tyApiRoot}/attachments`, msg = {}; + const actionType = "DEL_FILE_RECORD"; + if (filePath) { + httpDel(dispatch, { url, actionType, msg, query: { src: filePath } }) + } + } + const overviewPDF = (record, filePath) => { + setPdfView({ showPDF: true, pdfName: record.fileName, pdfurl: filePath }) + } + + const showDeleteConfirm = (record, filePath) => { + confirm({ + title: '是否确认删除该文件?', + icon: , + // content: 'Some descriptions', + okText: '是', + okType: 'danger', + cancelText: '否', + onOk() { + handleRemove(record, filePath); + }, + onCancel() { + }, + }); } const columns = [ @@ -93,30 +125,36 @@ const DetailList = (props) => { valueType: 'option', align: 'center', render: (text, record) => { - return [ - , - - - - - ]} - visible={selectRecord == record.id && visible} - trigger="click" - onClick={() => setSelectRecord(record.id)} - title="是否删除该记录?" - onVisibleChange={(newVisible) => setVisible(newVisible)} - > - - - ] - } + const regEx = /\bhttps?:\/\/[^:\/]+/ig; + const path = record.fileUrl; + const filename = path.substr(path.lastIndexOf("/") + 1); + const filePath = path.replace(regEx, ""); + const filePath_ = `${tyApiRoot}/attachments?src=${filePath}&filename=${filename}`; + + return + {/* {下载} */} + { { + window.open(filePath_); + }} >下载} + + { showDeleteConfirm(record, filePath) }}>删除 + { + ['.png', '.jpg'].some(item => item == record.fileExt) ? + [, + { setImgSrc({ imageView: true, imgSrc: path }) }}>预览] + : '' + } + { + ['.pdf'].some(item => item == record.fileExt) ? + [, + overviewPDF(record, path)}>预览] + : '' + } + + }, }, ]; - return ( + return [ { rowKey="key" toolBarRender={false} search={false} - /> - ); + />, + { + setImgSrc({ imageView: value, imgSrc: '' }); + }, + }} + />, + { + pdfh5 = null; + setPdfView({ showPDF: false, pdfName: '', pdfurl: '' }); + }} + > +
+ + + ]; }; @@ -143,7 +206,7 @@ const RoadNameList = (props) => { const [filterRoad, setFilterRoad] = useState([]); const [addVisible, setAddVisible] = useState(false); const [selectRoad, setSelectRoad] = useState(); - const { onChange, record, roads, loading, queryData, dispatch } = props; + const { onChange, roads, loading, queryData, dispatch } = props; const columns = [ { title: '道路名称', @@ -154,6 +217,13 @@ const RoadNameList = (props) => { ]; + useEffect(() => { + if (roads && roads instanceof Array) { + setSelectRoad(roads[0].rId) + onChange(roads[0]); + } + }, [roads]) + useEffect(() => { if (roads) { setFilterRoad(roads) @@ -184,8 +254,12 @@ const RoadNameList = (props) => { } + const onDelDir = () => { + + } + return ( -
+
{ onSubmit={createRoadDir} onCancel={() => { setAddVisible(false) }} /> + +
); @@ -228,10 +308,8 @@ const RoadNameList = (props) => { const FileTable = (props) => { - const { roads, fileList, dispatch, fileListLoading, roadsLoading } = props; + const { roads, fileList, dispatch, fileListLoading, roadsLoading, user } = props; const [record, setRecord] = useState(); - const [dateRange, setDateRange] = useState(); - const [detailVisible, setDetailVisible] = useState(false) const [activeTabKey1, setActiveTabKey1] = useState('1'); const [uploadVisible, setUploadVisible] = useState(false); const { RangePicker } = DatePicker; @@ -246,7 +324,7 @@ const FileTable = (props) => { if (record) { queryData(); } - }, [record, dateRange]) + }, [record]) const queryData = () => { const { rId } = record; @@ -265,14 +343,7 @@ const FileTable = (props) => { }, [activeTabKey1, record]) const handelRefresh = () => { - - } - - const handleClose = () => { - setDetailVisible(false) - } - const handleOpen = () => { - setDetailVisible(true) + queryData() } const tabList = [ @@ -304,8 +375,22 @@ const FileTable = (props) => { } setRecord(target); } - const hanleUpload = () => { - + const hanleUpload = (fileList) => { + let fileUrl, fileExt, fileName, fileSize; + if (fileList && fileList instanceof Array) { + const file = fileList[0]; + fileName = file.name; + fileExt = fileName.substr(fileName.lastIndexOf('.')); + fileUrl = file.url; + fileSize = file.size; + dispatch(uploadFile({ typeId: activeTabKey1, userId: user.id, fileSize, fileName, fileUrl, fileExt, roadId: record.rId })).then(res => { + if (res.type == 'UPLOAD_FILE_SUCCESS') { + message.success('文件新增成功'); + setUploadVisible(false); + queryData(); + } + }); + } } return ( @@ -331,13 +416,13 @@ const FileTable = (props) => { - + { setUploadVisible(false) }} - onConfirm={hanleUpload} + onSubmit={hanleUpload} />
@@ -345,12 +430,13 @@ const FileTable = (props) => { }; function mapStateToProps(state) { - const { fileDirs, fileList } = state; + const { fileDirs, fileList, auth } = state; return { roads: fileDirs.data, roadsLoading: fileDirs.isRequesting, fileList: fileList.data, fileListLoading: fileList.isRequesting, + user: auth.user, }; } export default connect(mapStateToProps)(FileTable); \ No newline at end of file diff --git a/web/client/src/sections/fillion/components/protable.less b/web/client/src/sections/fillion/components/protable.less index f788725f..6d66a5df 100644 --- a/web/client/src/sections/fillion/components/protable.less +++ b/web/client/src/sections/fillion/components/protable.less @@ -9,22 +9,19 @@ } -.list-row-actived{ +.list-row-actived { background-color: #7cafc6; font-weight: 600; } -// .spilce { -// .split-row-select-active { -// background-color: #7cafc6; -// font-weight: 600; -// } - -// th { -// display: none; -// } -// } +.ant-divider { + width: 0px; + height: 8px; + border-left: 1px solid gray; + margin: 0px 8px; + opacity: 0.8; +} .card-protable { display: flex; diff --git a/web/log/development.txt b/web/log/development.txt index 9b421be3..2bae49ab 100644 --- a/web/log/development.txt +++ b/web/log/development.txt @@ -30712,3 +30712,9 @@ 2022-07-28 16:51:10.270 - debug: [FS-LOGGER] Init. 2022-07-28 16:51:11.180 - info: [Router] Inject api: attachment/index >>>>>>> c5a4c8a87b2e23e891d4276a9cca953058b78bd7 +2022-07-28 19:02:55.701 - debug: [FS-LOGGER] Init. +2022-07-28 19:02:55.796 - info: [Router] Inject api: attachment/index +2022-07-28 19:04:01.860 - debug: [FS-LOGGER] Init. +2022-07-28 19:04:01.936 - info: [Router] Inject api: attachment/index +2022-07-28 20:47:45.618 - debug: [FS-LOGGER] Init. +2022-07-28 20:47:45.710 - info: [Router] Inject api: attachment/index diff --git a/web/package-lock.json b/web/package-lock.json index 25c2bba8..588c99ee 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -324,7 +324,7 @@ "@antv/g-svg": { "version": "0.5.6", "resolved": "http://npm.anxinyun.cn/@antv%2fg-svg/-/g-svg-0.5.6.tgz", - "integrity": "sha512-Xve1EUGk4HMbl2nq4ozR4QLh6GyoZ8Xw/+9kHYI4B5P2lIUQU95MuRsaLFfW5NNpZDx85ZeH97tqEmC9L96E7A==", + "integrity": "sha1-cLL6mAxDGzmtPFtLU+NqHWCVfWU=", "requires": { "@antv/g-base": "^0.5.3", "@antv/g-math": "^0.1.6", @@ -1921,7 +1921,7 @@ }, "@peace/utils": { "version": "0.0.51", - "resolved": "http://npm.anxinyun.cn/@peace%2futils/-/utils-0.0.51.tgz", + "resolved": "http://npm.anxinyun.cn:443/@peace%2futils/-/utils-0.0.51.tgz", "integrity": "sha512-+HeDYNCf4Cid2nWEIQxED2avueBgXL4AgY7SVngubfCS6qI2TKjyPuTrtDGHTvojuLQe5BlEiKMxIuiAMQmTag==", "requires": { "immutable": "^4.0.0-rc.12", @@ -4203,7 +4203,7 @@ "connect-history-api-fallback": { "version": "1.6.0", "resolved": "http://npm.anxinyun.cn/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + "integrity": "sha1-izIIk1kwjRERFdgcrT/Oq4iPl7w=" }, "connected-react-router": { "version": "6.9.3", @@ -4625,7 +4625,7 @@ "define-property": { "version": "2.0.2", "resolved": "http://npm.anxinyun.cn/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -6178,7 +6178,7 @@ "he": { "version": "1.2.0", "resolved": "http://npm.anxinyun.cn/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", "dev": true }, "history": { @@ -6924,7 +6924,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "http://npm.anxinyun.cn/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=" }, "jsbn": { "version": "0.1.1", @@ -7286,7 +7286,7 @@ "koa-static": { "version": "1.5.3", "resolved": "http://npm.anxinyun.cn/koa-static/-/koa-static-1.5.3.tgz", - "integrity": "sha512-FmfSFJOrtWGZ/Ae5Q7xeM+ck1IdofNEvIQhdPLvGHyTjilhYmFGoyRN1+BAbTknWnDoRRyHsGGq0FMRDTcCb1w==", + "integrity": "sha1-29IUbu5xeA3/0xLyPMSnYui839I=", "requires": { "debug": "^3.2.5", "koa-send": "~2.0.1" @@ -8359,6 +8359,11 @@ "through": "~2.3" } }, + "pdfh5": { + "version": "1.4.2", + "resolved": "http://npm.anxinyun.cn:443/pdfh5/-/pdfh5-1.4.2.tgz", + "integrity": "sha512-1BL8HIx/EEZowRPBgas7/WokbGEv1gxKNRmmHSimG113178mKxIBH4pxWBc0tj6d25Sy+EwnlQwv9cUUmQa42w==" + }, "perfect-scrollbar": { "version": "1.5.5", "resolved": "http://npm.anxinyun.cn/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", @@ -10518,7 +10523,7 @@ "source-map-resolve": { "version": "0.5.3", "resolved": "http://npm.anxinyun.cn/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -11101,7 +11106,7 @@ "type-is": { "version": "1.6.18", "resolved": "http://npm.anxinyun.cn/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -11341,7 +11346,7 @@ "use": { "version": "3.1.1", "resolved": "http://npm.anxinyun.cn/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=" }, "use-json-comparison": { "version": "1.0.6", diff --git a/web/package.json b/web/package.json index 3f139646..073500da 100644 --- a/web/package.json +++ b/web/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "mocha", "start": "cross-env NODE_ENV=development npm run start-params", - "start-params": "node server -p 5000 -u http://10.8.30.7:14000 --qndmn http://rfkimpwbb.hn-bkt.clouddn.com", + "start-params": "node server -p 5000 -u http://localhost:4000 --qndmn http://rfkimpwbb.hn-bkt.clouddn.com", "deploy": "export NODE_ENV=production&&npm run color && npm run build && node server", "build-dev": "export NODE_ENV=development&&webpack --config webpack.config.js", "build": "export NODE_ENV=production&&webpack --config webpack.config.prod.js", @@ -92,6 +92,7 @@ "swiper": "^8.3.1", "uuid": "^8.3.1", "webpack-dev-server": "^3.11.2", + "pdfh5": "^1.4.2", "xlsx": "^0.16.9" } }