From e92941cb0d911c7ebc5cebccc0ed38a5026d362e Mon Sep 17 00:00:00 2001 From: 陈峰 <feng.chen@yoho.cn> Date: Tue, 4 Jul 2017 17:20:04 +0800 Subject: [PATCH] import --- app/components/global/upload-xlsx.vue | 1 + app/components/modal/index.js | 4 +++- app/components/modal/modal-import.vue | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/pages/product/output/output.vue | 37 ++++++++++++++++++++++--------------- app/pages/repository/invoice/deliver.vue | 2 +- app/pages/repository/invoice/edit.vue | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------- app/pages/repository/invoice/list.vue | 2 +- app/pages/repository/jit/components/import-store.vue | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------ app/pages/repository/return/list.vue | 2 +- app/pages/trade/allot/views/done.vue | 2 +- app/pages/trade/allot/views/undone.vue | 2 +- app/services/repository/invoice-service.js | 9 ++++++++- server/app.js | 3 +++ server/common/api-domain.js | 3 ++- server/common/api.js | 16 +--------------- server/controllers/file-controller.js | 28 ---------------------------- server/controllers/import-controller.js | 28 ++++++++++++++++++++++++++++ server/controllers/index.js | 8 +++----- server/middleware/proxy.js | 31 ++++++++++++++++++++++++++----- server/public/example.xlsx | Bin 6272 -> 0 bytes server/public/invoice-import.xlsx | Bin 0 -> 8506 bytes server/service/xlsx-service.js | 61 ++++++++++++++++++++----------------------------------------- 22 files changed, 419 insertions(+), 304 deletions(-) create mode 100644 app/components/modal/modal-import.vue create mode 100644 server/controllers/import-controller.js create mode 100644 server/public/invoice-import.xlsx diff --git a/app/components/global/upload-xlsx.vue b/app/components/global/upload-xlsx.vue index 4ceb0fc..f36fc2e 100644 --- a/app/components/global/upload-xlsx.vue +++ b/app/components/global/upload-xlsx.vue @@ -83,6 +83,7 @@ }, clear() { this.files = null; + this.$refs.input.value = ''; }, post(file, params) { let _this = this; diff --git a/app/components/modal/index.js b/app/components/modal/index.js index 36975b6..96968b5 100644 --- a/app/components/modal/index.js +++ b/app/components/modal/index.js @@ -2,10 +2,12 @@ import ModalSizeEdit from './modal-size-edit'; import ModalStockOut from './modal-stock-out'; import ModalDeliver from './modal-deliver'; import ModalExample from './modal-example'; +import ModalImport from './modal-import'; export default { ModalSizeEdit, ModalStockOut, ModalDeliver, - ModalExample + ModalExample, + ModalImport }; diff --git a/app/components/modal/modal-import.vue b/app/components/modal/modal-import.vue new file mode 100644 index 0000000..371e28f --- /dev/null +++ b/app/components/modal/modal-import.vue @@ -0,0 +1,109 @@ +<template> + <Modal v-model="model" class-name="vertical-center-modal"> + <p slot="header"> + {{title}} + </p> + <Form label-position="left" :label-width="80"> + <Form-item label="上传文件:"> + <upload-xlsx + ref="upload" + :data="importParams" + :action="action" + :on-success="uploadSuccess" + :on-error="uploadError" + :on-change="uploadChange"> + <Button type="ghost" icon="ios-cloud-upload-outline">上传文件</Button> + </upload-xlsx> + <span>{{name}}</span> + </Form-item> + <slot name="remark"> + </slot> + </Form> + <slot name="report"></slot> + + <div slot="footer" style="text-align: center"> + <Button type="primary" size="large" :disabled="disableSave" :loading="modelLoading" @click="upload">保存</Button> + <Button type="primary" size="large" @click="close">取消</Button> + </div> + </Modal> +</template> + +<script> +import _ from 'lodash'; + +export default { + name: 'modal-import', + props: { + value: { + type: Boolean, + default: false + }, + action: { + type: String, + default: '/import' + }, + title: { + type: String, + default: '导入' + }, + transParams: { + type: Object, + default: () => { + return {}; + } + } + }, + data() { + return { + model: this.value, + modelLoading: false, + disableSave: true, + fileName: '' + }; + }, + methods: { + uploadSuccess(result) { + this.$emit('import', result); + }, + uploadError(err) { + this.$emit('upload-error', err); + }, + uploadChange(files) { + this.fileName = files[0].name; + this.disableSave = false; + }, + upload() { + this.modelLoading = true; + this.$refs.upload.upload(); + }, + close() { + this.model = false; + this.disableSave = true; + this.fileName = ''; + this.$refs.upload.clear(); + this.modelLoading = false; + }, + show() { + this.model = true; + } + }, + computed: { + name() { + return this.fileName || '未选择任何文件'; + }, + importParams() { + let ps = {}; + + _.map(this.transParams, (v, k) => { + if (typeof v === 'object') { + ps[k] = JSON.stringify(v); + } else { + ps[k] = v; + } + }); + return ps; + } + } +}; +</script> + diff --git a/app/pages/product/output/output.vue b/app/pages/product/output/output.vue index 05954bf..6a8c0ec 100644 --- a/app/pages/product/output/output.vue +++ b/app/pages/product/output/output.vue @@ -16,7 +16,7 @@ </select-category> </filter-item> <filter-item> - <Button type="primary" @click="exportFile">导出</Button> + <Button type="warning" @click="exportFile">导出</Button> </filter-item> </layout-filter> @@ -34,11 +34,11 @@ </filter-item> <filter-item label="品牌"> - <select-brand v-model="brandId" :selectWhenOnlyOne="true"></select-brand> + <select-brand v-model="uploadParams.brandId" :selectWhenOnlyOne="true"></select-brand> </filter-item> <filter-item label="销售类型"> - <select-sell-type v-model="sellType" :brandId="brandId" :selectWhenOnlyOne="true"></select-sell-type> + <select-sell-type v-model="uploadParams.sellType" :brandId="uploadParams.brandId" :selectWhenOnlyOne="true"></select-sell-type> </filter-item> <filter-item label="上传文件"> @@ -46,6 +46,7 @@ <upload-xlsx ref="xlsxUpload" + :data="uploadParams" :action="action" :on-change="onChange" :on-success="onSuccess" @@ -57,7 +58,7 @@ </filter-item> <filter-item> - <Button type="primary" :loading="loading" @click="upload">开始上传</Button> + <Button type="warning" :loading="loading" @click="upload">开始上传</Button> </filter-item> </layout-filter> @@ -95,9 +96,12 @@ ] }, xlsxFile: '', - brandId: '', - sellType: '', - action: '/importSeller', + uploadParams: { + type: 'sellerProductImport', + brandId: '', + sellType: '' + }, + action: '/platform/importSeller', sort: { first: { label: '选择类目', @@ -151,10 +155,7 @@ }, upload() { this.loading = true; - this.$refs.xlsxUpload.upload({ - brandId: this.brandId, - sellType: this.sellType - }).then(() => { + this.$refs.xlsxUpload.upload().then(() => { this.loading = false; }).catch(() => { this.loading = false; @@ -162,14 +163,20 @@ }, onSuccess(data) { if (data.code === 200) { - if (_.has(data.data, 'failFileReason')) { - this.$Message.error('部分导入成功'); + if (_.get(data.data, 'failFileReason', []).length) { + this.$Notice.warning({ + title: '部分导入成功' + }); this.importInfo = data.data.failFileReason; } else { - this.$Message.info('全部导入成功'); + this.$Notice.success({ + title: '全部导入成功' + }); } } else { - this.$Message.error('导入失败'); + this.$Notice.error({ + title: '导入失败' + }); } }, onError() { diff --git a/app/pages/repository/invoice/deliver.vue b/app/pages/repository/invoice/deliver.vue index f2b5d91..d229055 100644 --- a/app/pages/repository/invoice/deliver.vue +++ b/app/pages/repository/invoice/deliver.vue @@ -63,8 +63,8 @@ }).then((res) => { if (res.code === 200) { this.tableData = (res.data.goodsList || []).map((i) => { - i.num = null; i._disabled = i.shipmentsNums === i.buyingNums; + i.num = i._disabled ? null : i.buyingNums - i.shipmentsNums; return i; }); } diff --git a/app/pages/repository/invoice/edit.vue b/app/pages/repository/invoice/edit.vue index f49180a..18463e8 100644 --- a/app/pages/repository/invoice/edit.vue +++ b/app/pages/repository/invoice/edit.vue @@ -11,6 +11,7 @@ <filter-item > <Button type="primary" @click="saveOrder">保存</Button> + <Button type="warning" @click="showImport">导入商品</Button> <Button type="ghost" @click="back">返回发货入库列表</Button> </filter-item> @@ -34,80 +35,131 @@ </Tabs> </layout-list> + <modal-import + ref="imporGoods" + title="导入入库单" + :trans-params="transParams" + @import="importGoods"> + <div slot="remark"> + <Form-item label="说明:"> + <div style="display: inline-block"> + <ul> + <li>1、上传文件必须是 <span style="color: red;">.xlsx</span> 文件</li> + <li>2、第一行为标题栏:<strong>skn | sku | 库存数量</strong></li> + <li>3、每次最多导入1000条数据</li> + <li>4、<a href="/invoice-import.xlsx">下载样例</a></li> + </ul> + </div> + </Form-item> + </div> + </modal-import> </layout-body> </template> <script> +import _ from 'lodash'; +import ProductList from './components/product-list'; +import OrderInfo from './components/order-info'; +import DatePick from './components/date-pick'; +import InvoiceService from 'services/repository/invoice-service'; +import moment from 'moment'; - import ProductList from './components/product-list'; - import OrderInfo from './components/order-info'; - import DatePick from './components/date-pick'; - import InvoiceService from 'services/repository/invoice-service'; - import moment from 'moment'; - - export default { - props: ['isEdit'], - data() { - return { - count: 0, - id: '', - storeId: '', - arrivalTime: null, - brandId: '', - info: null - }; - }, - created() { - this.invoiceService = new InvoiceService(); - this.id = this.$route.params.id; - this.brandId = this.$route.query.brandId; - }, - computed: { - arrival: { - get() { - return moment.unix(this.arrivalTime).format('YYYY-MM-DD'); - }, - set(newVal) { - this.arrivalTime = moment(newVal, 'YYYY-MM-DD').unix(); +export default { + props: ['isEdit'], + data() { + return { + importAction: '', + transParams: { + cols: { + productSkn: 'product_skn', + productSku: 'product_sku', + num: '数量' } + }, + count: 0, + id: '', + storeId: '', + arrivalTime: null, + brandId: '', + info: null + }; + }, + created() { + this.invoiceService = new InvoiceService(); + this.id = this.$route.params.id; + this.brandId = this.$route.query.brandId; + }, + computed: { + arrival: { + get() { + return moment.unix(this.arrivalTime).format('YYYY-MM-DD'); + }, + set(newVal) { + this.arrivalTime = moment(newVal, 'YYYY-MM-DD').unix(); } + } + }, + methods: { + showImport() { + this.$refs.imporGoods.show(); }, - methods: { - back() { - this.$router.push({name: 'repository.invoice.list'}); - }, - saveOrder() { - this.invoiceService.updateOrder({ - proRequisitionFormId: this.id, - predictArrivalTime: this.arrivalTime, - storeroomId: this.storeId - }).then((result) => { - if (result.code === 200) { - this.$Message.info('更新成功'); - } else { - this.$Message.error('更新失败'); - } + importGoods(goods) { + goods = _.filter(goods, row => row.productSku && row.productSkn && row.num); + if (!goods.length) { + return this.$Notice.warning({ + title: '导入商品为空' }); - }, - onTabsClick(index) { - if (index === 0) { + } + this.invoiceService.importGoods(goods, this.id).then(result => { + if (result.code === 200) { + this.$Notice.success({ + title: '导入成功' + }); + this.$refs.imporGoods.close(); this.$refs.selectProduct.search(); } else { - this.$refs.allProduct.search(); + this.$Notice.error({ + title: '导入失败:', + desc: result.message + }); } - }, - onChangeInfo(data) { - this.storeId = data.storeroomId; - this.arrivalTime = data.predictArrivalTime; - }, - refreshInfo() { - this.$refs.info.getOrderInfo(); + }); + }, + back() { + this.$router.push({name: 'repository.invoice.list'}); + }, + saveOrder() { + this.invoiceService.updateOrder({ + proRequisitionFormId: this.id, + predictArrivalTime: this.arrivalTime, + storeroomId: this.storeId + }).then((result) => { + if (result.code === 200) { + this.$Message.info('更新成功'); + } else { + this.$Message.error('更新失败'); + } + }); + }, + onTabsClick(index) { + if (index === 0) { + this.$refs.selectProduct.search(); + } else { + this.$refs.allProduct.search(); } }, - components: { - ProductList, - OrderInfo, - DatePick + onChangeInfo(data) { + this.storeId = data.storeroomId; + this.arrivalTime = data.predictArrivalTime; + }, + refreshInfo() { + this.$refs.info.getOrderInfo(); } - }; + }, + components: { + ProductList, + OrderInfo, + DatePick + } +}; </script> diff --git a/app/pages/repository/invoice/list.vue b/app/pages/repository/invoice/list.vue index 69dcfb3..ac71afc 100644 --- a/app/pages/repository/invoice/list.vue +++ b/app/pages/repository/invoice/list.vue @@ -47,7 +47,7 @@ </layout-filter> <layout-action> - <Button type="warning" @click="onClickCreate">创建入库单</Button> + <Button type="primary" @click="onClickCreate">创建入库单</Button> </layout-action> <layout-list> diff --git a/app/pages/repository/jit/components/import-store.vue b/app/pages/repository/jit/components/import-store.vue index b049145..8ffed66 100644 --- a/app/pages/repository/jit/components/import-store.vue +++ b/app/pages/repository/jit/components/import-store.vue @@ -1,34 +1,22 @@ <template> - <Modal v-model="model" - class-name="vertical-center-modal"> - <p slot="header"> - 导入库存 - </p> - - <div> - <Form label-position="left" :label-width="80"> - <Form-item label="上传文件:"> - <upload-xlsx - ref="upload" - :on-success="uploadSuccess" - :on-error="uploadError" - :on-change="uploadChange"> - <Button type="ghost" icon="ios-cloud-upload-outline">上传文件</Button> - </upload-xlsx> - <span>{{name}}</span> - </Form-item> - <Form-item label="说明:"> - <div style="display: inline-block"> + <modal-import + ref="importSeller" + title="导入库存" + :trans-params="transParams" + @import="importSeller"> + <div slot="remark"> + <Form-item label="说明:"> + <div style="display: inline-block"> <ul> <li>1、上传文件必须是 <span style="color: red;">.xlsx</span> 文件</li> <li>2、第一行为标题栏:<strong>skn | sku | 库存数量</strong></li> <li>3、每次最多导入1000条数据</li> <li>4、<a href="/example.xlsx">下载样例</a></li> </ul> - </div> - </Form-item> - </Form> - + </div> + </Form-item> + </div> + <div slot="report"> <Form label-position="left" :label-width="130" v-if="failList"> <div class="report"> <div class="title">库存导入结果报告</div> @@ -39,7 +27,6 @@ </li> </ul> </Form-item> - <Form-item label="SKN 不属于该店铺:" v-if="sknNotInShop"> <ul class="err"> <li v-for="sku in sknNotInShop" :key="sku" v-if="sku"> @@ -47,8 +34,6 @@ </li> </ul> </Form-item> - - <Form-item label="SKU与SKN不匹配:" v-if="skuNotInSkn"> <ul class="err"> <li v-for="sku in skuNotInSkn" :key="sku"> @@ -56,8 +41,6 @@ </li> </ul> </Form-item> - - <Form-item label="SKN不存在:" v-if="sknNotExist"> <ul class="err"> <li v-for="sku in sknNotExist" :key="sku"> @@ -65,8 +48,6 @@ </li> </ul> </Form-item> - - <Form-item label="SKU不存在:" v-if="skuNotExist"> <ul class="err"> <li v-for="sku in skuNotExist" :key="sku"> @@ -74,8 +55,6 @@ </li> </ul> </Form-item> - - <Form-item label="库存数据非法:" v-if="storageNumError"> <ul class="err"> <li v-for="sku in storageNumError" :key="sku"> @@ -84,52 +63,58 @@ </ul> </Form-item> </div> - </Form> - </div> - - <div slot="footer" style="text-align: center"> - <Button type="primary" size="large" :loading="modal_loading" v-if="!failList" @click="save">保存</Button> - <Button type="primary" size="large" v-else @click="close">关闭</Button> - <Button type="primary" size="large" @click="close">取消</Button> + </Form> </div> - </Modal> + </modal-import> </template> <script> - import _ from 'lodash'; - - export default { - name: 'import-store', - data() { - return { - model: false, - modal_loading: false, - fileName: '', - successList: null, - sknNotInShop: null, - skuNotInSkn: null, - sknNotExist: null, - skuNotExist: null, - storageNumError: null, - failList: null - }; - }, - methods: { - show() { - this.reset(); - this.model = true; +import _ from 'lodash'; +import JitService from 'services/repository/jit-service'; + +export default { + name: 'import-store', + created() { + this.jitService = new JitService(); + }, + data() { + return { + transParams: { + cols: { + productSku: 'sku', + productSkn: 'skn', + number: 'number' + } }, - uploadSuccess(result) { - this.modal_loading = false; - + fileName: '', + successList: null, + sknNotInShop: null, + skuNotInSkn: null, + sknNotExist: null, + skuNotExist: null, + storageNumError: null, + failList: null + }; + }, + methods: { + show() { + this.reset(); + this.$refs.importSeller.show(); + }, + importSeller(storages) { + storages = _.filter(storages, row => row.productSku && row.productSkn && row.number); + if (!storages.length) { + return this.$Message.error('导入存储为空'); + } + this.jitService.updateStorage(storages).then(result => { if (result.code === 200) { this.$Notice.success({ title: '导入成功', }); this.$emit('on-success'); - this.close(); + this.$refs.importSeller.close(); } else if (result.code === 500) { this.$Notice.error({ title: '导入失败', @@ -149,69 +134,33 @@ this.storageNumError = this.handleArray(result.data.storageNumError); this.failList = this.handleArray(result.data.failList); } - }, - handleArray(array) { - if (!array) { - return null; - } - - if (_.isEmpty(array)) { - return null; - } - - let newArray = array.filter(i => i); - - if (_.isEmpty(newArray)) { - return null; - } - - return newArray; - }, - uploadError(err) { - this.modal_loading = false; - - this.$Notice.error({ - title: '保存失败,请重试', - desc: err - }); - }, - uploadChange(files) { - if (files.length === 0) { - return; - } + }); + }, + handleArray(array) { + if (!array) { + return null; + } + if (_.isEmpty(array)) { + return null; + } + let newArray = array.filter(i => i); - this.fileName = files[0].name; - this.resetErr(); - }, - close() { - this.model = false; - this.$emit('on-success'); - }, - save() { - this.modal_loading = true; - return this.$refs.upload.upload(); - }, - reset() { - this.modal_loading = false; - this.fileName = ''; - this.resetErr(); - }, - resetErr() { - this.successList = null; - this.sknNotInShop = null; - this.skuNotInSkn = null; - this.sknNotExist = null; - this.skuNotExist = null; - this.storageNumError = null; - this.failList = null; + if (_.isEmpty(newArray)) { + return null; } + return newArray; }, - computed: { - name() { - return this.fileName || '未选择任何文件'; - } + reset() { + this.successList = null; + this.sknNotInShop = null; + this.skuNotInSkn = null; + this.sknNotExist = null; + this.skuNotExist = null; + this.storageNumError = null; + this.failList = null; } - }; + } +}; </script> <style lang="scss" scoped> diff --git a/app/pages/repository/return/list.vue b/app/pages/repository/return/list.vue index 5d07bb9..22b0370 100644 --- a/app/pages/repository/return/list.vue +++ b/app/pages/repository/return/list.vue @@ -23,7 +23,7 @@ </filter-item> <filter-item> <Button type="primary" @click="filterSearch">筛选</Button> - <Button type="primary" @click="exportList">导出</Button> + <Button type="warning" @click="exportList">导出</Button> <Button @click="clearFilter">清空条件</Button> </filter-item> </layout-filter> diff --git a/app/pages/trade/allot/views/done.vue b/app/pages/trade/allot/views/done.vue index 1662348..f247ec4 100644 --- a/app/pages/trade/allot/views/done.vue +++ b/app/pages/trade/allot/views/done.vue @@ -36,7 +36,7 @@ </filter-item> <filter-item> <Button type="primary" @click="filterSearch">筛选</Button> - <Button type="primary" @click="exportList">导出</Button> + <Button type="warning" @click="exportList">导出</Button> <Button @click="clearFilter">清空条件</Button> </filter-item> </layout-filter> diff --git a/app/pages/trade/allot/views/undone.vue b/app/pages/trade/allot/views/undone.vue index 627fae8..6155d9e 100644 --- a/app/pages/trade/allot/views/undone.vue +++ b/app/pages/trade/allot/views/undone.vue @@ -33,7 +33,7 @@ </filter-item> <filter-item> <Button type="primary" @click="filterSearch">筛选</Button> - <Button type="primary" @click="exportList">导出</Button> + <Button type="warning" @click="exportList">导出</Button> <Button @click="clearFilter">清空条件</Button> </filter-item> </layout-filter> diff --git a/app/services/repository/invoice-service.js b/app/services/repository/invoice-service.js index 4978019..6fb414e 100644 --- a/app/services/repository/invoice-service.js +++ b/app/services/repository/invoice-service.js @@ -19,7 +19,8 @@ const apiUrl = { addGoods: '/erp/invoiceGoodsAdd', updateGoods: '/erp/invoiceGoodsUpdate', deleteGoods: 'erp/invoiceGoodsDelete', - supplementProductList: '/erp/supplementProductList' + supplementProductList: '/erp/supplementProductList', + importGoods: '/erp/invoiceGoodsImport' }; class InvoiceService extends Service { @@ -108,5 +109,11 @@ class InvoiceService extends Service { return {}; }); } + importGoods(goods, invoiceId) { + return this.post(apiUrl.importGoods, { + goodsList: goods, + proRequisitionFormId: invoiceId + }); + } } export default InvoiceService; diff --git a/server/app.js b/server/app.js index 72ad686..f58f159 100644 --- a/server/app.js +++ b/server/app.js @@ -15,6 +15,8 @@ const pkg = require('../package.json'); const cookieSession = require('cookie-session'); const favicon = require('serve-favicon'); const path = require('path'); +const multipart = require('connect-multiparty'); +const multipartMiddleware = multipart(); global.env = { @@ -47,6 +49,7 @@ app.use(Express.static(path.join(__dirname, 'public'))); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(cookieParser()); +app.use(multipartMiddleware); const middleware = require('./middleware'); const controllers = require('./controllers'); diff --git a/server/common/api-domain.js b/server/common/api-domain.js index 5b21509..afa078b 100644 --- a/server/common/api-domain.js +++ b/server/common/api-domain.js @@ -39,7 +39,8 @@ let domainApis = { invoiceGoodsAdd: '/erp-shop-web/invoice/goods/add', invoiceGoodsUpdate: '/erp-shop-web/invoice/goods/update', invoiceGoodsDelete: '/erp-shop-web/invoice/goods/delete', - supplementProductList: '/erp-shop-web/supplementProduct/list' + supplementProductList: '/erp-shop-web/supplementProduct/list', + invoiceGoodsImport: '/erp-shop-web/invoice/goods/import' }, platform: { queryShopsByAdminPid: '/SellerShopController/queryShopsByAdminPid', diff --git a/server/common/api.js b/server/common/api.js index ad53d93..7c7ca7f 100644 --- a/server/common/api.js +++ b/server/common/api.js @@ -1,4 +1,3 @@ -const _ = require('lodash'); const Context = require('../framework/context'); const rp = require('request-promise'); const request = require('request'); @@ -42,20 +41,7 @@ class Api extends Context { headers: Object.assign(defaultHeader, headers) }); } - proxy(url, data, options) { - let params = _.merge({ - url, - headers: { - 'Content-Type': 'application/json' - } - }, options); - - if (options.method === 'get') { - params.qs = data; - } else { - params.body = JSON.stringify(data); - } - + proxy(params) { return request(params, (error, response) => { if (!error) { if (response && /^20\d/.test(response.statusCode)) { diff --git a/server/controllers/file-controller.js b/server/controllers/file-controller.js index 0efc0f3..78b875d 100644 --- a/server/controllers/file-controller.js +++ b/server/controllers/file-controller.js @@ -81,34 +81,6 @@ class FileController extends Context { }).catch(next); } } - uploadXlsx(req, res, next) { - let file = req.files && req.files.file; - - return this.xlsxService.handle(file.path).then((result) => { - if (result.code === 500) { - return res.json(result); - } - - req.body = result.body; - req.url = result.path; - - next(); - }).catch(next); - } - - importSeller(req, res, next) { - let file = req.files && req.files.file; - - this.api.upload(apiDomain.platform.importSeller, { - brandId: req.body.brandId, - shopsId: req.cookies._sign, - userId: req.session.LOGIN_UID, - file: fs.createReadStream(file.path), - type: 'sellerProductImport' - }).then((result) => { - res.json(result); - }).catch(next); - } } module.exports = FileController; diff --git a/server/controllers/import-controller.js b/server/controllers/import-controller.js new file mode 100644 index 0000000..7bd14a2 --- /dev/null +++ b/server/controllers/import-controller.js @@ -0,0 +1,28 @@ +const Context = require('../framework/context'); +const Api = require('../common/api'); +const XlsxService = require('../service/xlsx-service'); + +class FileController extends Context { + constructor() { + super(); + this.api = this.instance(Api); + this.xlsxService = this.instance(XlsxService); + } + import(req, res, next) { + let file = req.files && req.files.file; + + if (!file) { + return res.json({ + code: 400, + message: '请上传文件' + }); + } + let cols = JSON.parse(req.body.cols); + + this.xlsxService.resolveExcel(file.path, cols).then(excelData => { + return res.json(excelData); + }).catch(next); + } +} + +module.exports = FileController; diff --git a/server/controllers/index.js b/server/controllers/index.js index 2381c94..2a3c37b 100644 --- a/server/controllers/index.js +++ b/server/controllers/index.js @@ -9,20 +9,18 @@ const Express = require('express'); const UserController = require('./user-controller'); const FileController = require('./file-controller'); +const ImportController = require('./import-controller'); const middleware = require('../framework/middleware'); const before = require('../middleware/before'); const auth = require('../middleware/auth'); -const multipart = require('connect-multiparty'); -const multipartMiddleware = multipart(); let router = Express.Router(); // eslint-disable-line router.post('/login', middleware(UserController, 'login')); router.post('/logout', middleware(UserController, 'logout')); router.post('/switchShop', before, auth, middleware(UserController, 'switchShop')); -router.post('/upload/image', before, auth, multipartMiddleware, middleware(FileController, 'uploadImage')); -router.post('/upload/xlsx', before, auth, multipartMiddleware, middleware(FileController, 'uploadXlsx')); -router.post('/importSeller', before, auth, multipartMiddleware, middleware(FileController, 'importSeller')); +router.post('/upload/image', before, auth, middleware(FileController, 'uploadImage')); +router.post('/import', before, auth, middleware(ImportController, 'import')); router.post('/config', middleware(UserController, 'config')); module.exports = router; diff --git a/server/middleware/proxy.js b/server/middleware/proxy.js index 6075e67..f0f4d53 100644 --- a/server/middleware/proxy.js +++ b/server/middleware/proxy.js @@ -5,6 +5,7 @@ */ const Api = require('../common/api'); const _ = require('lodash'); +const fs = require('fs'); const blacklist = require('../common/api-blacklist'); const config = global.yoho.config; const logger = global.yoho.logger; @@ -54,15 +55,35 @@ module.exports = (req, res, next) => { platform_id: config.platform, userId: req.session.LOGIN_UID }; - let params = Object.assign(baseParams, req.query, req.body); - - return api.proxy(apiUrl, params, { + let reqParams = Object.assign({ + url: apiUrl, method: req.method.toLowerCase(), headers: { 'x-shop-id': currentShop.shopsId, - 'x-user-id': req.session.LOGIN_UID + 'x-user-id': req.session.LOGIN_UID, + 'Content-Type': 'application/json' } - }).on('error', error => { + }); + let files = req.files && req.files.file || []; + + if (!_.isArray(files)) { + files = [files]; + } + + if (req.method.toLowerCase() === 'get') { + reqParams.qs = Object.assign(baseParams, req.query, req.body); + } else if (files.length) { + let reqFiles = {}; + + _.each(files, file => { + reqFiles[file.fieldName] = fs.createReadStream(file.path); + }); + reqParams.formData = Object.assign(baseParams, req.query, req.body, reqFiles); + } else { + reqParams.body = JSON.stringify(Object.assign(baseParams, req.query, req.body)); + } + + return api.proxy(reqParams).on('error', error => { next({code: 500, message: error}); }).pipe(res); } diff --git a/server/public/example.xlsx b/server/public/example.xlsx index 131bbe2..dbf2a3c 100644 Binary files a/server/public/example.xlsx and b/server/public/example.xlsx differ diff --git a/server/public/invoice-import.xlsx b/server/public/invoice-import.xlsx new file mode 100644 index 0000000..77efc82 Binary files /dev/null and b/server/public/invoice-import.xlsx differ diff --git a/server/service/xlsx-service.js b/server/service/xlsx-service.js index 98b1311..8722ef6 100644 --- a/server/service/xlsx-service.js +++ b/server/service/xlsx-service.js @@ -6,54 +6,33 @@ const xlsx2json = require('xlsx2json'); const Api = require('../common/api'); const _ = require('lodash'); -const xlsxRead = (fileName) => { - return xlsx2json(fileName, { - dataStartingRow: 2, - mapping: { - productSkn: 'A', - productSku: 'B', - number: 'c' - } - }); -}; - class XlsxService extends Context { constructor() { super(); this.api = this.instance(Api); } - - handle(fileName) { - return xlsxRead(fileName).then((jsonArray) => { - let data = _.first(jsonArray); - - if (!data) { - return {code: 500, message: '数据为空'}; - } - - if (data.length > 1000) { - return {code: 500, message: '数据超出限制'}; - } - - data = data.filter(d => d.productSkn && d.productSku && d.number); - - return this.postJitStore(data); + resolveExcel(path, cols) { + return xlsx2json(path).then(excelData => { + let sheetData = _.first(excelData); + let header = sheetData[0]; + let rows = _.slice(sheetData, 1); + let columns = _.map(cols, (v, k) => { + return { + key: k, + col: _.findKey(header, hv => _.trim(hv) === v) + }; + }); + + return _.map(rows, row => { + let item = {}; + + _.each(columns, col => { + item[col.key] = row[col.col]; + }); + return item; + }); }); } - - postJitStore(params) { - let data = { - body: { - brandAuthData: null, - authLevel: true, - storageType: 3, - virtualInventoryBos: params - }, - path: '/erp/importJitStorage' - }; - - return Promise.resolve(data); - } } module.exports = XlsxService; -- libgit2 0.24.0