Showing
11 changed files
with
143 additions
and
46 deletions
1 | <template> | 1 | <template> |
2 | - <div> | ||
3 | - <input type="file" accept=".xlsx" @change="handleChange"> | 2 | + <div @click="handleClick" style="display: inline-block;"> |
3 | + <input ref="input" type="file" accept=".xlsx" @change="handleChange"> | ||
4 | <slot></slot> | 4 | <slot></slot> |
5 | </div> | 5 | </div> |
6 | </template> | 6 | </template> |
@@ -9,9 +9,13 @@ | @@ -9,9 +9,13 @@ | ||
9 | import request from 'axios'; | 9 | import request from 'axios'; |
10 | 10 | ||
11 | export default { | 11 | export default { |
12 | + name: 'XlsxUpload', | ||
12 | props: { | 13 | props: { |
13 | action: { | 14 | action: { |
14 | - type: String | 15 | + type: String, |
16 | + default() { | ||
17 | + return '/upload/xlsx'; | ||
18 | + } | ||
15 | }, | 19 | }, |
16 | onSuccess: { | 20 | onSuccess: { |
17 | type: Function, | 21 | type: Function, |
@@ -44,6 +48,9 @@ | @@ -44,6 +48,9 @@ | ||
44 | }; | 48 | }; |
45 | }, | 49 | }, |
46 | methods: { | 50 | methods: { |
51 | + handleClick() { | ||
52 | + this.$refs.input.click(); | ||
53 | + }, | ||
47 | handleChange(e) { | 54 | handleChange(e) { |
48 | this.files = e.target.files; | 55 | this.files = e.target.files; |
49 | this.onChange(this.files); | 56 | this.onChange(this.files); |
@@ -110,7 +110,7 @@ | @@ -110,7 +110,7 @@ | ||
110 | </Form-item> | 110 | </Form-item> |
111 | 111 | ||
112 | <Form-item label="尺寸" prop="selectSize"> | 112 | <Form-item label="尺寸" prop="selectSize"> |
113 | - <CheckboxSize v-model="table.selectedSizes" :sort-id="product.smallSortId" @on-add="addSize" @on-remove="removeSize"></CheckboxSize> | 113 | + <CheckboxSize v-model="product.selectSize" :sort-id="product.smallSortId" @on-add="addSize" @on-remove="removeSize"></CheckboxSize> |
114 | </Form-item> | 114 | </Form-item> |
115 | 115 | ||
116 | <Row> | 116 | <Row> |
@@ -387,11 +387,11 @@ export default { | @@ -387,11 +387,11 @@ export default { | ||
387 | salesPrice: [ | 387 | salesPrice: [ |
388 | { required: true, trigger: 'blur', validator: validateMustLessRetailPrice}, | 388 | { required: true, trigger: 'blur', validator: validateMustLessRetailPrice}, |
389 | ], | 389 | ], |
390 | - selectSize: [ | ||
391 | - { required: true, trigger: 'change', validator: validateColor}, | ||
392 | - ], | ||
393 | selectColor: [ | 390 | selectColor: [ |
394 | - { required: true, trigger: 'change', validator: validateSize}, | 391 | + { required: true, trigger: 'NONE', validator: validateColor}, |
392 | + ], | ||
393 | + selectSize: [ | ||
394 | + { required: true, trigger: 'NONE', validator: validateSize}, | ||
395 | ], | 395 | ], |
396 | }, | 396 | }, |
397 | years: 2018 | 397 | years: 2018 |
@@ -590,6 +590,8 @@ export default { | @@ -590,6 +590,8 @@ export default { | ||
590 | this.refreshTable(); | 590 | this.refreshTable(); |
591 | let changeSize = _.first(sizes); | 591 | let changeSize = _.first(sizes); |
592 | 592 | ||
593 | + this.table.selectedSizes.push(changeSize); | ||
594 | + | ||
593 | this.table.data.forEach((c) => { | 595 | this.table.data.forEach((c) => { |
594 | this.addSizeData(c, changeSize); | 596 | this.addSizeData(c, changeSize); |
595 | }); | 597 | }); |
@@ -613,6 +615,7 @@ export default { | @@ -613,6 +615,7 @@ export default { | ||
613 | color.sizeId.splice(index, 1); | 615 | color.sizeId.splice(index, 1); |
614 | color.sizeCode.splice(index, 1); | 616 | color.sizeCode.splice(index, 1); |
615 | color.operator.splice(index, 1); | 617 | color.operator.splice(index, 1); |
618 | + this.table.selectedSizes.splice(index, 1); | ||
616 | } | 619 | } |
617 | }, | 620 | }, |
618 | addSizeData: function(color, findSize) { | 621 | addSizeData: function(color, findSize) { |
@@ -755,22 +758,6 @@ export default { | @@ -755,22 +758,6 @@ export default { | ||
755 | return this.product.maxSortName + '/' + this.product.middleSortName + '/' + this.product.smallSortName; | 758 | return this.product.maxSortName + '/' + this.product.middleSortName + '/' + this.product.smallSortName; |
756 | } | 759 | } |
757 | }, | 760 | }, |
758 | - watch: { | ||
759 | - 'table.selectedSizes': function(newVal, oldVal) { | ||
760 | - if (newVal.length === 0) { | ||
761 | - this.table.showValidateSize = true; | ||
762 | - } else { | ||
763 | - this.table.showValidateSize = false; | ||
764 | - } | ||
765 | - }, | ||
766 | - 'table.selectedColors': function(newVal) { | ||
767 | - if (newVal.length === 0) { | ||
768 | - this.table.showValidateColor = true; | ||
769 | - } else { | ||
770 | - this.table.showValidateColor = false; | ||
771 | - } | ||
772 | - } | ||
773 | - }, | ||
774 | components: { | 761 | components: { |
775 | CheckboxAge, | 762 | CheckboxAge, |
776 | RadioSeason, | 763 | RadioSeason, |
@@ -10,11 +10,12 @@ | @@ -10,11 +10,12 @@ | ||
10 | <Form-item label="上传文件:"> | 10 | <Form-item label="上传文件:"> |
11 | <XlsxUpload | 11 | <XlsxUpload |
12 | ref="upload" | 12 | ref="upload" |
13 | - action="//jsonplaceholder.typicode.com/posts/" | ||
14 | :on-success="uploadSuccess" | 13 | :on-success="uploadSuccess" |
15 | - :on-error="uploadError"> | 14 | + :on-error="uploadError" |
15 | + :on-change="uploadChange"> | ||
16 | <Button type="ghost" icon="ios-cloud-upload-outline">上传文件</Button> | 16 | <Button type="ghost" icon="ios-cloud-upload-outline">上传文件</Button> |
17 | </XlsxUpload> | 17 | </XlsxUpload> |
18 | + <span>{{name}}</span> | ||
18 | </Form-item> | 19 | </Form-item> |
19 | <Form-item label="说明:"> | 20 | <Form-item label="说明:"> |
20 | <div style="display: inline-block"> | 21 | <div style="display: inline-block"> |
@@ -22,7 +23,7 @@ | @@ -22,7 +23,7 @@ | ||
22 | <li>1、上传文件必须是 <span style="color: red;">.xlsx</span> 文件</li> | 23 | <li>1、上传文件必须是 <span style="color: red;">.xlsx</span> 文件</li> |
23 | <li>2、第一行为标题栏:<strong>skn | sku | 库存数量</strong></li> | 24 | <li>2、第一行为标题栏:<strong>skn | sku | 库存数量</strong></li> |
24 | <li>3、每次最多导入1000条数据</li> | 25 | <li>3、每次最多导入1000条数据</li> |
25 | - <li>4、<a href="">下载样例</a></li> | 26 | + <li>4、<a href="/example.xlsx">下载样例</a></li> |
26 | </ul> | 27 | </ul> |
27 | </div> | 28 | </div> |
28 | </Form-item> | 29 | </Form-item> |
@@ -30,8 +31,8 @@ | @@ -30,8 +31,8 @@ | ||
30 | </div> | 31 | </div> |
31 | 32 | ||
32 | <div slot="footer" style="text-align: center"> | 33 | <div slot="footer" style="text-align: center"> |
33 | - <Button type="primary" size="large" :loading="modal_loading" @click="submit">保存</Button> | ||
34 | - <Button type="primary" size="large" @click="cancel">取消</Button> | 34 | + <Button type="primary" size="large" :loading="modal_loading" @click="save">保存</Button> |
35 | + <Button type="primary" size="large" @click="close">取消</Button> | ||
35 | </div> | 36 | </div> |
36 | </Modal> | 37 | </Modal> |
37 | </template> | 38 | </template> |
@@ -40,30 +41,60 @@ | @@ -40,30 +41,60 @@ | ||
40 | export default { | 41 | export default { |
41 | data() { | 42 | data() { |
42 | return { | 43 | return { |
43 | - url: '', | ||
44 | - model: true, | 44 | + model: false, |
45 | modal_loading: false, | 45 | modal_loading: false, |
46 | + fileName: '' | ||
46 | }; | 47 | }; |
47 | }, | 48 | }, |
48 | methods: { | 49 | methods: { |
49 | show() { | 50 | show() { |
51 | + this.reset(); | ||
50 | this.model = true; | 52 | this.model = true; |
51 | }, | 53 | }, |
52 | - beforeUpload() { | ||
53 | - if (this.$refs.upload.fileList !== 1) { | ||
54 | - this.$refs.upload.fileList.splice(0, 1); | 54 | + uploadSuccess(result) { |
55 | + this.modal_loading = false; | ||
56 | + | ||
57 | + if (result.code === 200) { | ||
58 | + this.$Notice.success({ | ||
59 | + title: '更新库存成功', | ||
60 | + }); | ||
61 | + | ||
62 | + this.close(); | ||
63 | + } else { | ||
64 | + this.$Notice.error({ | ||
65 | + title: '保存失败,请重试', | ||
66 | + }); | ||
55 | } | 67 | } |
56 | }, | 68 | }, |
57 | - uploadSuccess(response, file, fileList) { | ||
58 | - }, | ||
59 | - uploadError(error, file, fileList) { | 69 | + uploadError() { |
70 | + this.modal_loading = false; | ||
71 | + | ||
72 | + this.$Notice.error({ | ||
73 | + title: '保存失败,请重试', | ||
74 | + }); | ||
60 | }, | 75 | }, |
61 | - submit() { | 76 | + uploadChange(files) { |
77 | + if (files.length === 0) { | ||
78 | + return; | ||
79 | + } | ||
80 | + | ||
81 | + this.fileName = files[0].name; | ||
62 | }, | 82 | }, |
63 | - cancel() { | 83 | + close() { |
84 | + this.model = false; | ||
64 | }, | 85 | }, |
65 | save() { | 86 | save() { |
66 | - | 87 | + this.modal_loading = true; |
88 | + return this.$refs.upload.upload(); | ||
89 | + }, | ||
90 | + reset() { | ||
91 | + this.modal_loading = false; | ||
92 | + this.fileName = ''; | ||
93 | + } | ||
94 | + }, | ||
95 | + computed: { | ||
96 | + name() { | ||
97 | + return this.fileName || '未选择任何文件'; | ||
67 | } | 98 | } |
68 | } | 99 | } |
69 | }; | 100 | }; |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | 15 | ||
16 | <FilterItem> | 16 | <FilterItem> |
17 | <Button type="primary" @click="filterSearch">筛选</Button> | 17 | <Button type="primary" @click="filterSearch">筛选</Button> |
18 | - <Button type="primary" @click="showImportStore">导入库存</Button> | 18 | + <Button type="warning" @click="showImportStore">导入库存</Button> |
19 | <Button @click="clearFilter">清空条件</Button> | 19 | <Button @click="clearFilter">清空条件</Button> |
20 | </FilterItem> | 20 | </FilterItem> |
21 | </LayoutFilter> | 21 | </LayoutFilter> |
@@ -146,11 +146,10 @@ function getProduct(skn) { | @@ -146,11 +146,10 @@ function getProduct(skn) { | ||
146 | } | 146 | } |
147 | 147 | ||
148 | function addNoneItem(objArray) { | 148 | function addNoneItem(objArray) { |
149 | - if (objArray.inputType === 'checkbox') { | ||
150 | - return objArray; | ||
151 | - } | ||
152 | - | ||
153 | objArray.forEach(obj => { | 149 | objArray.forEach(obj => { |
150 | + if (obj.inputType === 'checkbox') { | ||
151 | + return; | ||
152 | + } | ||
154 | obj.idNameList && obj.idNameList.unshift({id: 0, text: '请选择' + obj.attributeName}); | 153 | obj.idNameList && obj.idNameList.unshift({id: 0, text: '请选择' + obj.attributeName}); |
155 | }); | 154 | }); |
156 | 155 |
@@ -40,6 +40,7 @@ | @@ -40,6 +40,7 @@ | ||
40 | "vue-html5-editor": "^1.1.1", | 40 | "vue-html5-editor": "^1.1.1", |
41 | "vue-router": "^2.2.0", | 41 | "vue-router": "^2.2.0", |
42 | "vue-template-compiler": "^2.2.6", | 42 | "vue-template-compiler": "^2.2.6", |
43 | + "xlsx2json": "^1.0.0", | ||
43 | "yoho-cookie": "^1.2.0", | 44 | "yoho-cookie": "^1.2.0", |
44 | "yoho-md5": "^2.0.0", | 45 | "yoho-md5": "^2.0.0", |
45 | "yoho-node-lib": "^0.2.18", | 46 | "yoho-node-lib": "^0.2.18", |
@@ -41,6 +41,7 @@ app.use(cookieSession({ | @@ -41,6 +41,7 @@ app.use(cookieSession({ | ||
41 | 41 | ||
42 | app.use(compression()); | 42 | app.use(compression()); |
43 | app.use(favicon(path.join(__dirname, '/favicon.ico'))); | 43 | app.use(favicon(path.join(__dirname, '/favicon.ico'))); |
44 | +app.use(Express.static(path.join(__dirname, 'public'))); | ||
44 | app.use(bodyParser.json()); | 45 | app.use(bodyParser.json()); |
45 | app.use(bodyParser.urlencoded({extended: false})); | 46 | app.use(bodyParser.urlencoded({extended: false})); |
46 | app.use(cookieParser()); | 47 | app.use(cookieParser()); |
@@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
7 | 7 | ||
8 | const Context = require('../framework/context'); | 8 | const Context = require('../framework/context'); |
9 | const FileService = require('../service/file-service'); | 9 | const FileService = require('../service/file-service'); |
10 | +const XlsxService = require('../service/xlsx-service'); | ||
10 | const Api = require('../common/api'); | 11 | const Api = require('../common/api'); |
11 | const apiDomain = require('../common/api-domain'); | 12 | const apiDomain = require('../common/api-domain'); |
12 | const fs = require('fs'); | 13 | const fs = require('fs'); |
@@ -17,6 +18,7 @@ class FileController extends Context { | @@ -17,6 +18,7 @@ class FileController extends Context { | ||
17 | super(); | 18 | super(); |
18 | this.api = this.instance(Api); | 19 | this.api = this.instance(Api); |
19 | this.fileService = this.instance(FileService); | 20 | this.fileService = this.instance(FileService); |
21 | + this.xlsxService = this.instance(XlsxService); | ||
20 | } | 22 | } |
21 | uploadImage(req, res, next) { | 23 | uploadImage(req, res, next) { |
22 | let files = req.files && req.files.file || []; | 24 | let files = req.files && req.files.file || []; |
@@ -79,6 +81,23 @@ class FileController extends Context { | @@ -79,6 +81,23 @@ class FileController extends Context { | ||
79 | }).catch(next); | 81 | }).catch(next); |
80 | } | 82 | } |
81 | } | 83 | } |
84 | + uploadXlsx(req, res, next) { | ||
85 | + let file = req.files && req.files.file; | ||
86 | + | ||
87 | + return this.xlsxService.handle(file.path).then((result) => { | ||
88 | + if (result.code === 200) { | ||
89 | + return res.json({ | ||
90 | + code: 200, | ||
91 | + message: '保存成功' | ||
92 | + }); | ||
93 | + } else { | ||
94 | + return res.json({ | ||
95 | + code: 500, | ||
96 | + message: result.message | ||
97 | + }); | ||
98 | + } | ||
99 | + }).catch(next); | ||
100 | + } | ||
82 | } | 101 | } |
83 | 102 | ||
84 | module.exports = FileController; | 103 | module.exports = FileController; |
@@ -18,6 +18,7 @@ let router = Express.Router(); // eslint-disable-line | @@ -18,6 +18,7 @@ let router = Express.Router(); // eslint-disable-line | ||
18 | router.post('/login', middleware(UserController, 'login')); | 18 | router.post('/login', middleware(UserController, 'login')); |
19 | router.post('/logout', middleware(UserController, 'logout')); | 19 | router.post('/logout', middleware(UserController, 'logout')); |
20 | router.post('/upload/image', multipartMiddleware, middleware(FileController, 'uploadImage')); | 20 | router.post('/upload/image', multipartMiddleware, middleware(FileController, 'uploadImage')); |
21 | +router.post('/upload/xlsx', multipartMiddleware, middleware(FileController, 'uploadXlsx')); | ||
21 | 22 | ||
22 | 23 | ||
23 | module.exports = router; | 24 | module.exports = router; |
server/service/xlsx-service.js
0 → 100644
1 | +/** | ||
2 | + * Created by TaoHuang on 2017/5/17. | ||
3 | + */ | ||
4 | +const Context = require('../framework/context'); | ||
5 | +const xlsx2json = require('xlsx2json'); | ||
6 | +const Api = require('../common/api'); | ||
7 | +const apiDomain = global.yoho.apiDomain; | ||
8 | +const _ = require('lodash'); | ||
9 | + | ||
10 | +const xlsxRead = (fileName) => { | ||
11 | + return xlsx2json(fileName, { | ||
12 | + dataStartingRow: 2, | ||
13 | + mapping: { | ||
14 | + productSkn: 'A', | ||
15 | + productSku: 'B', | ||
16 | + number: 'c' | ||
17 | + } | ||
18 | + }); | ||
19 | +}; | ||
20 | + | ||
21 | +class XlsxService extends Context { | ||
22 | + constructor() { | ||
23 | + super(); | ||
24 | + this.api = this.instance(Api); | ||
25 | + } | ||
26 | + | ||
27 | + handle(fileName) { | ||
28 | + return xlsxRead(fileName).then((jsonArray) => { | ||
29 | + let data = _.first(jsonArray); | ||
30 | + | ||
31 | + if (!data) { | ||
32 | + return Promise.reject(new Error('数据为空')); | ||
33 | + } | ||
34 | + | ||
35 | + return this.postJitStore(data); | ||
36 | + }); | ||
37 | + } | ||
38 | + | ||
39 | + postJitStore(params) { | ||
40 | + let data = { | ||
41 | + brandAuthData: null, | ||
42 | + authLevel: true, | ||
43 | + storageType: 3, | ||
44 | + virtualInventoryBos: params | ||
45 | + }; | ||
46 | + | ||
47 | + return this.api.post(apiDomain.erp.importJitStorage, data); | ||
48 | + } | ||
49 | +} | ||
50 | + | ||
51 | +module.exports = XlsxService; |
-
Please register or login to post a comment