Authored by 陈峰

Merge branch 'feature/invoice-import' into 'master'

Feature/invoice import



See merge request !17
... ... @@ -3,11 +3,11 @@
<div v-if="canChange">
<p class="retail-price">吊牌价:{{retailPrice}}</p>
<p v-if="!showChange">
<i-button type="info" size="small" @click="changeClick">改</i-button>
<!--<i-button type="info" size="small" @click="changeClick">改</i-button>-->
销售价:{{salesPrice}}
</p>
<p v-else>
<i-button type="warning" size="small" @click="saveClick">存</i-button>
<!--<i-button type="warning" size="small" @click="saveClick">存</i-button>-->
销售价:<span class="price-input">
<Input v-model.number="newPrice"></Input></span>
</p>
... ... @@ -69,7 +69,7 @@
text-align: left;
.retail-price {
margin-left: 30px;
// margin-left: 30px;
}
.price-input {
... ...
... ... @@ -83,6 +83,7 @@
},
clear() {
this.files = null;
this.$refs.input.value = '';
},
post(file, params) {
let _this = this;
... ...
... ... @@ -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
};
... ...
<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>
... ...
... ... @@ -38,7 +38,7 @@
<span style="font-size:16px;">&nbsp; &nbsp; &nbsp; 针对JIT供应商提供的服务还包括:</span>
</p>
<p style="text-align:left;">
<span style="font-size:16px;">&nbsp; &nbsp; &nbsp; 外采商品调拨-调拨单、调拨发货</span>
<span style="font-size:16px;">&nbsp; &nbsp; &nbsp; 销售调拨-调拨单、调拨发货</span>
</p>
<p style="text-align:left;">
<span style="font-size:16px;">&nbsp; &nbsp; &nbsp; 仓库管理-可调拨库存</span>
... ... @@ -156,7 +156,7 @@
</p>
<p style="text-align:left;">
&nbsp; &nbsp; &nbsp; &nbsp;
<span style="font-size:16px;">操作功能菜单位置在 外采商品调拨-调拨发货</span>
<span style="font-size:16px;">操作功能菜单位置在 销售调拨-调拨发货</span>
<span style="font-size:16px;"></span>
</p>
<p style="text-align:left;">
... ...
... ... @@ -10,6 +10,6 @@ export default {
component: () => import(/* webpackChunkName: "product.create" */'./create'),
children: childrenViews,
meta: {
pageName: '新增商品',
pageName: '发布新商品',
}
};
... ...
... ... @@ -3,6 +3,6 @@ export default {
name: 'onsale',
component: () => import(/* webpackChunkName: "product.onsale" */'./onsale'),
meta: {
pageName: '在售商品'
pageName: '已上架商品'
}
};
... ...
... ... @@ -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() {
... ...
... ... @@ -8,6 +8,6 @@ export default {
name: 'diff',
component: () => import(/* webpackChunkName: "repository.diff" */'./diff'),
meta: {
pageName: '差异库存'
pageName: '发货入库差异'
}
};
... ...
... ... @@ -7,13 +7,13 @@ export default [{
name: 'list',
component: () => import(/* webpackChunkName: "repository.express" */'./list'),
meta: {
pageName: '入库物流管理'
pageName: '发货物流'
}
}, {
path: '/:id.html',
name: 'info',
component: () => import(/* webpackChunkName: "repository.express" */'./info'),
meta: {
pageName: '入库物流详情'
pageName: '发货物流详情'
}
}];
... ...
... ... @@ -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;
});
}
... ...
... ... @@ -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,139 @@
</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 =>
_.trim(row.productSku) &&
_.trim(row.productSkn) &&
_.trim(row.num));
goods = _.each(goods, row => {
row.productSkn = _.parseInt(row.productSkn);
row.productSku = _.parseInt(row.productSku);
row.num = _.parseInt(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>
... ...
... ... @@ -6,7 +6,7 @@ export default [{
name: 'list',
component: () => import(/* webpackChunkName: "repository.invoice" */'./list'),
meta: {
pageName: '发货入库管理'
pageName: '发货入库'
}
}, {
path: '/edit/:id.html',
... ...
... ... @@ -47,7 +47,7 @@
</layout-filter>
<layout-action>
<Button type="warning" @click="onClickCreate">创建入库单</Button>
<Button type="primary" @click="onClickCreate">创建入库单</Button>
</layout-action>
<layout-list>
... ...
<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,66 @@
</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 =>
_.trim(row.productSku) &&
_.trim(row.productSkn) &&
_.trim(row.number));
storages = _.each(storages, row => {
row.productSkn = _.parseInt(row.productSkn);
row.productSku = _.parseInt(row.productSku);
row.number = _.parseInt(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 +142,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>
... ...
... ... @@ -3,13 +3,13 @@ export default [{
name: 'list',
component: () => import(/* webpackChunkName: "repository.prodReturn" */'./list'),
meta: {
pageName: '请退单列表'
pageName: '商品退库'
}
}, {
path: '/detail/:id.html',
name: 'detail',
component: () => import(/* webpackChunkName: "repository.prodReturn" */'./detail'),
meta: {
pageName: '请退单详情'
pageName: '商品退库详情'
}
}];
... ...
... ... @@ -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>
... ...
... ... @@ -3,6 +3,6 @@ export default {
name: 'supplement',
component: () => import(/* webpackChunkName: "repository.supplement" */'./supplement'),
meta: {
pageName: '补货入库'
pageName: '补货建议'
}
};
... ...
... ... @@ -6,13 +6,13 @@ export default [{
name: 'list',
component: () => import(/* webpackChunkName: "shop.category" */'./list'),
meta: {
pageName: '商品分类'
pageName: '店铺商品分类'
}
}, {
path: '/edit.html',
name: 'edit',
component: () => import(/* webpackChunkName: "shop.category" */'./edit'),
meta: {
pageName: '商品分类编辑'
pageName: '关联商品'
}
}];
... ...
... ... @@ -22,10 +22,15 @@ export default function() {
holder: ''
},
prodCode: {
label: '商品编码',
label: 'SKN编码',
model: '',
holder: 'SKN'
},
skuCode: {
label: 'SKU编码',
model: '',
holder: 'SKU'
},
merChantCode: {
label: '商家编码',
model: '',
... ...
... ... @@ -23,12 +23,17 @@ export default function() {
fieldSpan: 18
},
prodCode: {
label: '商品编码',
label: 'SKN编码',
labelSpan: 6,
model: '',
holder: 'SKN',
fieldSpan: 18
},
skuCode: {
label: 'SKU编码',
model: '',
holder: 'SKU'
},
prodBarCode: {
label: '商品条码',
labelSpan: 6,
... ...
... ... @@ -19,10 +19,15 @@ export default function() {
holder: '',
},
prodCode: {
label: '商品编码',
label: 'SKN编码',
model: '',
holder: 'SKN'
},
skuCode: {
label: 'SKU编码',
model: '',
holder: 'SKU'
},
merChantCode: {
label: '商家编码',
model: '',
... ...
... ... @@ -13,6 +13,10 @@
<Input v-model.trim.number="filters.prodCode.model"
:placeholder="filters.prodCode.holder" :maxlength="9"></Input>
</filter-item>
<filter-item :label="filters.skuCode.label">
<Input v-model.trim.number="filters.skuCode.model"
:placeholder="filters.skuCode.holder" :maxlength="9"></Input>
</filter-item>
<filter-item :label="filters.merChantCode.label">
<Input v-model.trim="filters.merChantCode.model"
:placeholder="filters.merChantCode.holder"></Input>
... ... @@ -36,7 +40,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>
... ... @@ -95,6 +99,7 @@
proRequisitionFormId: 'orderNo',
expressNumber: 'expressNo',
productSkn: 'prodCode',
productSku: 'skuCode',
sknFactoryCode: 'merChantCode',
skuFactoryCode: 'prodBarCode',
startTime: 'startTime',
... ... @@ -119,6 +124,12 @@
return;
}
if (typeof values.productSku !== 'undefined' &&
!_.isFinite(+values.productSku)) {
this.$Message.error('SKU编码只能是数字', 3);
return;
}
const ot = values.isOvertime;
const start = values.startTime;
const end = values.endTime;
... ...
... ... @@ -13,6 +13,10 @@
<Input v-model.trim.number="filters.prodCode.model"
:placeholder="filters.prodCode.holder" :maxlength="9"></Input>
</filter-item>
<filter-item :label="filters.skuCode.label">
<Input v-model.trim.number="filters.skuCode.model"
:placeholder="filters.skuCode.holder" :maxlength="9"></Input>
</filter-item>
<filter-item :label="filters.prodBarCode.label">
<Input v-model.trim="filters.prodBarCode.model"
:placeholder="filters.prodBarCode.holder"></Input>
... ... @@ -94,6 +98,7 @@
};
const mapKeys = {
skn: 'prodCode',
sku: 'skuCode',
proReqFormId: 'orderNo',
expressNumber: 'expressNo',
skuFactoryCode: 'prodBarCode'
... ... @@ -124,6 +129,12 @@
return false;
}
if (typeof data.sku !== 'undefined' &&
!_.isFinite(+data.sku)) {
this.$Message.error('SKU编码只能是数字', 3);
return;
}
return data;
},
goExpDetail(expressNo) {
... ...
... ... @@ -9,6 +9,10 @@
<Input v-model.trim.number="filters.prodCode.model"
:placeholder="filters.prodCode.holder" :maxlength="9"></Input>
</filter-item>
<filter-item :label="filters.skuCode.label">
<Input v-model.trim.number="filters.skuCode.model"
:placeholder="filters.skuCode.holder" :maxlength="9"></Input>
</filter-item>
<filter-item :label="filters.merChantCode.label">
<Input v-model.trim="filters.merChantCode.model"
:placeholder="filters.merChantCode.holder"></Input>
... ... @@ -33,7 +37,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>
... ... @@ -105,6 +109,7 @@
const keyMap = {
proReqFormId: 'orderNo',
productSkn: 'prodCode',
productSku: 'skuCode',
sknFactoryCode: 'merChantCode',
skuFactoryCode: 'prodBarCode',
startTime: 'startTime',
... ... @@ -133,6 +138,12 @@
return;
}
if (typeof values.productSku !== 'undefined' &&
!_.isFinite(+values.productSku)) {
this.$Message.error('SKU编码只能是数字', 3);
return;
}
const ot = values.isOvertime;
const start = values.startTime;
const end = values.endTime;
... ...
... ... @@ -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;
... ...
... ... @@ -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');
... ...
... ... @@ -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',
... ...
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)) {
... ...
... ... @@ -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;
... ...
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;
... ...
... ... @@ -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;
... ...
... ... @@ -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);
}
... ...
No preview for this file type
No preview for this file type
... ... @@ -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;
... ...