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