Authored by 李奇

Merge branch 'release/1.0' of git.yoho.cn:fe/yoho-shop-manage into release/1.0

@@ -6,4 +6,5 @@ node_modules/ @@ -6,4 +6,5 @@ node_modules/
6 *.log 6 *.log
7 .eslintcache 7 .eslintcache
8 app/bundle 8 app/bundle
9 -public/  
  9 +public/
  10 +.vscode/
1 <template> 1 <template>
2 <Row class="layout-header"> 2 <Row class="layout-header">
3 <Col :span="12" class="brand-title"> 3 <Col :span="12" class="brand-title">
4 - <i class="fa fa-bars" aria-hidden="true" @click="$emit('menu-trigger')"></i> 4 + <i class="iconfont icon-alignjustify" aria-hidden="true" @click="$emit('menu-trigger')"></i>
5 {{userInfo.name}} 5 {{userInfo.name}}
6 </Col> 6 </Col>
7 <Col :span="12" class="shop-info"> 7 <Col :span="12" class="shop-info">
8 - <span class="name">{{userInfo.currentShop.shopName}}</span> 8 + <span v-if="!showLoading" class="name">{{userInfo.currentShop.shopName}}</span>
  9 + <span v-if="showLoading" class="loading">切换中...</span>
9 <span>|</span> 10 <span>|</span>
10 <Dropdown @on-click="switchShop" trigger="click"> 11 <Dropdown @on-click="switchShop" trigger="click">
11 <a class="swtich-shop" href="javascript:void(0)"> 12 <a class="swtich-shop" href="javascript:void(0)">
@@ -22,12 +23,14 @@ @@ -22,12 +23,14 @@
22 23
23 <script> 24 <script>
24 import Vue from 'vue'; 25 import Vue from 'vue';
  26 +import userService from 'user-service';
25 27
26 export default { 28 export default {
27 name: 'UserInfo', 29 name: 'UserInfo',
28 data() { 30 data() {
29 return { 31 return {
30 - userInfo: this.$user 32 + userInfo: this.$user,
  33 + showLoading: false
31 }; 34 };
32 }, 35 },
33 methods: { 36 methods: {
@@ -36,9 +39,18 @@ export default { @@ -36,9 +39,18 @@ export default {
36 }, 39 },
37 switchShop(id) { 40 switchShop(id) {
38 if (this.userInfo.currentShop.shopsId !== id) { 41 if (this.userInfo.currentShop.shopsId !== id) {
39 - this.userInfo.currentShop = this.userInfo.shops.find(shop => shop.shopsId === id);  
40 - Vue.switchShop(id);  
41 - this.$emit('shop-change', this.userInfo.currentShop); 42 + this.showLoading = true;
  43 + userService.switchShop(id).then(res => {
  44 + this.showLoading = false;
  45 + if (res.code === 200) {
  46 + this.userInfo.currentShop = this.userInfo.shops.find(shop => shop.shopsId === id);
  47 + Vue.switchShop(id);
  48 + this.$emit('shop-change', this.userInfo.currentShop);
  49 + this.$Message.success(`当前店铺切换为:${this.userInfo.currentShop.shopName}`);
  50 + } else {
  51 + this.$Message.error(`切换失败:${res.message}`);
  52 + }
  53 + });
42 } 54 }
43 } 55 }
44 } 56 }
@@ -49,15 +61,15 @@ export default { @@ -49,15 +61,15 @@ export default {
49 .layout-header { 61 .layout-header {
50 height: 50px !important; 62 height: 50px !important;
51 background: #fff; 63 background: #fff;
52 - box-shadow: 0 1px 1px rgba(0, 0, 0, .1); 64 + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
53 font-size: 14px; 65 font-size: 14px;
54 line-height: 20px; 66 line-height: 20px;
55 padding: 15px; 67 padding: 15px;
56 position: relative; 68 position: relative;
57 69
58 &.user-collapse { 70 &.user-collapse {
59 - height: 0px !important;  
60 - padding: 0px !important; 71 + height: 0 !important;
  72 + padding: 0 !important;
61 overflow: hidden; 73 overflow: hidden;
62 74
63 .fa-bars { 75 .fa-bars {
@@ -67,7 +79,8 @@ export default { @@ -67,7 +79,8 @@ export default {
67 } 79 }
68 80
69 &.print-hide { 81 &.print-hide {
70 - display: none; } 82 + display: none;
  83 + }
71 84
72 .fa { 85 .fa {
73 font-size: 20px; 86 font-size: 20px;
@@ -75,7 +88,7 @@ export default { @@ -75,7 +88,7 @@ export default {
75 margin-right: 10px; 88 margin-right: 10px;
76 cursor: pointer; 89 cursor: pointer;
77 } 90 }
78 - 91 +
79 .shop-info { 92 .shop-info {
80 text-align: right; 93 text-align: right;
81 padding-right: 20px; 94 padding-right: 20px;
@@ -84,6 +97,10 @@ export default { @@ -84,6 +97,10 @@ export default {
84 margin-right: 5px; 97 margin-right: 5px;
85 } 98 }
86 99
  100 + .loading {
  101 + color: #ccc;
  102 + }
  103 +
87 .swtich-shop { 104 .swtich-shop {
88 margin-left: 5px; 105 margin-left: 5px;
89 font-size: 12px; 106 font-size: 12px;
@@ -91,7 +108,7 @@ export default { @@ -91,7 +108,7 @@ export default {
91 } 108 }
92 109
93 .logout { 110 .logout {
94 - color: #F44545; 111 + color: #f44545;
95 font-size: 12px; 112 font-size: 12px;
96 margin-left: 5px; 113 margin-left: 5px;
97 } 114 }
1 <template> 1 <template>
2 - <Select :value="handleValue" @on-change="updateValue" :disabled="disable" placeholder="请选择" style="width: 350px;"> 2 + <Select :value="handleValue" @on-change="updateValue" :disabled="disable" placeholder="请选择" style="width: 400px;">
3 <Option :value="season.id" v-for="season in seasonList" :key="season.id">{{season.label}}</Option> 3 <Option :value="season.id" v-for="season in seasonList" :key="season.id">{{season.label}}</Option>
4 </Select> 4 </Select>
5 </template> 5 </template>
1 import TableGoodSize from './table-good-size'; 1 import TableGoodSize from './table-good-size';
  2 +import TableCreateGoodSize from './table-create-good-size';
2 3
3 export { 4 export {
4 - TableGoodSize 5 + TableGoodSize,
  6 + TableCreateGoodSize
5 }; 7 };
  1 +<template>
  2 + <Table ref="sellerGoods" :context="self" :data="table.data" :columns="table.columns" stripe border></Table>
  3 +</template>
  4 +
  5 +<script>
  6 + import {ExamplePop} from 'components/pop';
  7 +
  8 + export default {
  9 + name: 'TableCreateGoodSize',
  10 + props: {
  11 + value: {
  12 + type: Array,
  13 + default() {
  14 + return [];
  15 + }
  16 + }
  17 + },
  18 + data() {
  19 + return {
  20 + self: this,
  21 + table: {
  22 + columns: [
  23 + {
  24 + title: '色系名称',
  25 + key: 'goodsName',
  26 + render(row, col, index) {
  27 + return `<div v-if="isExist(${index})">
  28 + <p>{{table.data[${index}].goodsName.name}}</p>
  29 + <Radio :value="table.data[${index}].goodsName.isDefault"
  30 + @on-change="clickDefault(${index})">主推</Radio>
  31 + </div>`;
  32 + }
  33 + },
  34 + {
  35 + title: '颜色展示名称',
  36 + key: 'factoryGoodsName',
  37 + render(row, col, index) {
  38 + return `<div v-if="isExist(${index})">
  39 + <i-input
  40 + v-model="row.factoryGoodsName"
  41 + :placeholder="row.goodsName.name">
  42 + </i-input>
  43 + </div>`;
  44 + }
  45 + },
  46 + {
  47 + title: '色卡图片',
  48 + key: 'goodsColorImage',
  49 + width: 170,
  50 + render(row, col, index) {
  51 + return `<div
  52 + :class="{'table-upload-item': true ,
  53 + 'table-upload-item-error':
  54 + row.goodsColorImage.showValidate && row.goodsColorImage.validate}"
  55 + >
  56 + <div class="table-upload-item-img">
  57 + <img v-if="row.goodsColorImage.value"
  58 + :src="row.goodsColorImage.value" alt="" width="84px" height="112px">
  59 + </div>
  60 + <div>
  61 + <file-upload :id="{index: ${index}}"
  62 + @on-success="uploadSuccess"
  63 + @on-error="uploadError"></file-upload>
  64 + <example-pop></example-pop>
  65 + </div>
  66 + <div v-if="row.goodsColorImage.showValidate && row.goodsColorImage.validate"
  67 + class="table-upload-item-tip">
  68 + 必须上传图片
  69 + </div>
  70 + </div>`;
  71 + },
  72 + renderHeader(col, index) {
  73 + return `<span class="table-header">${col.title}</span>`;
  74 + }
  75 + },
  76 + {
  77 + title: '款型编码',
  78 + key: 'factoryCode',
  79 + render(row, col, index) {
  80 + return `<div v-if="isExist(${index})">
  81 + <i-input
  82 + v-model="row.factoryCode"
  83 + placeholder="请输入..." >
  84 + </i-input>
  85 + </div>`;
  86 + }
  87 + },
  88 + {
  89 + title: '尺码',
  90 + key: 'sizeId',
  91 + width: 80,
  92 + render(row, col, index) {
  93 + return `<div class="size-id">
  94 + <div v-for="size in row.sizeId" class="row-span">
  95 + {{size.name}}
  96 + </div>
  97 + </div>`;
  98 + }
  99 + },
  100 + {
  101 + title: '商品条码',
  102 + key: 'sizeCode',
  103 + render(row, col, index) {
  104 + return `<div class='size-code'>
  105 + <div v-for="size,i in row.sizeCode" class="row-span">
  106 + <div style="position: relative">
  107 + <div :class="{'size-code-error': size.validate && !size.name}">
  108 + <i-input
  109 + v-model="size.name"
  110 + :disabled="!row.operator[i].value"
  111 + placeholder="请输入..."
  112 + />
  113 + </div>
  114 + <div class="size-code-tip" v-if="size.validate && !size.name">
  115 + 不能为空
  116 + </div>
  117 + </div>
  118 + </div>
  119 + </div>`;
  120 + },
  121 + renderHeader(col, index) {
  122 + return `<span class="table-header">${col.title}</span>`;
  123 + }
  124 + },
  125 + {
  126 + title: '操作',
  127 + key: 'operator',
  128 + width: 100,
  129 + render(row, col, index) {
  130 + return `<template v-if="isExist(${index})">
  131 + <div class="size-operator">
  132 + <div v-for="op,i in row.operator" class="row-span">
  133 + <i-button v-if="row.operator[i].value"
  134 + type="warning"
  135 + @click="clickOperator(row, i)">禁用</i-button>
  136 + <i-button v-else type="primary"
  137 + @click="clickOperator(row, i)">启用</i-button>
  138 + </div>
  139 + </div>
  140 + </template>`;
  141 + }
  142 + }
  143 + ],
  144 + data: this.value
  145 + }
  146 + };
  147 + },
  148 + methods: {
  149 + isExist(index) {
  150 + let row = this.table.data[index];
  151 +
  152 + if (row) {
  153 + return true;
  154 + }
  155 +
  156 + return false;
  157 + },
  158 + refreshTable() {
  159 + this.table.data = this.$refs.sellerGoods.rebuildData;
  160 + },
  161 + syncData() {
  162 + this.$emit('input', this.$refs.sellerGoods.rebuildData);
  163 + },
  164 + clickDefault(index) {
  165 + this.refreshTable();
  166 + let color = this.table.data[index];
  167 +
  168 +
  169 + color.goodsName.isDefault = true;
  170 + this.table.data.forEach((c) => {
  171 + if (c.colorId !== color.colorId) {
  172 + c.goodsName.isDefault = false;
  173 + }
  174 + });
  175 + },
  176 + clickOperator(row, itemIndex) {
  177 + this.refreshTable();
  178 + row.operator[itemIndex].value = row.operator[itemIndex].value ? false : true;
  179 +
  180 + if (row.operator[itemIndex].value) {
  181 + row.sizeCode[itemIndex].name = '';
  182 + row.sizeCode[itemIndex].validate = true;
  183 + } else {
  184 + row.sizeCode[itemIndex].name = '';
  185 + row.sizeCode[itemIndex].validate = false;
  186 + }
  187 +
  188 + },
  189 + uploadSuccess: function(attach, files) {
  190 + this.refreshTable();
  191 + this.table.data[attach.index].goodsColorImage.value = files[0];
  192 +
  193 + this.table.data[attach.index].goodsColorImage.validate = true;
  194 + if (this.table.data[attach.index].goodsColorImage.value) {
  195 + this.table.data[attach.index].goodsColorImage.showValidate = false;
  196 + } else {
  197 + this.table.data[attach.index].goodsColorImage.showValidate = true;
  198 + }
  199 + },
  200 + uploadError: function(attach, err) {
  201 + },
  202 + },
  203 + watch: {
  204 + value(newValue) {
  205 + this.table.data = newValue;
  206 + }
  207 + },
  208 + components: {
  209 + ExamplePop
  210 + }
  211 + };
  212 +
  213 +</script>
  214 +
  215 +<style lang="scss">
  216 +
  217 + @mixin row-span{
  218 + min-height: 30px;
  219 +
  220 + .row-span {
  221 + min-height: 30px;
  222 + border-bottom: 1px solid #e3e8ee;
  223 + padding-top: 20px ;
  224 + padding-bottom: 20px ;
  225 + margin-left: -18px;
  226 + margin-right: -18px;
  227 + padding-left: 18px ;
  228 + padding-right: 18px ;
  229 +
  230 + &:last-child {
  231 + border-bottom: none;
  232 + }
  233 + }
  234 + }
  235 +
  236 + .size-code {
  237 + @include row-span;
  238 +
  239 + &-error {
  240 + border: 1px solid #f30;
  241 + }
  242 +
  243 + &-tip {
  244 + position: absolute;
  245 + line-height: 1;
  246 + padding-top: 6px;
  247 + color: #f30;
  248 + }
  249 + }
  250 +
  251 + .size-id {
  252 + @include row-span;
  253 + text-align: center;
  254 + }
  255 +
  256 + .size-operator {
  257 + @include row-span;
  258 + text-align: center;
  259 + }
  260 +
  261 + .table-upload-item {
  262 + display: inline-block;
  263 + height: 220px;
  264 + width: 130px;
  265 + text-align: center;
  266 + margin: 30px 0;
  267 + }
  268 +
  269 + .table-upload-item-error {
  270 + border: 1px solid #f30;
  271 + }
  272 +
  273 + .table-upload-item-tip {
  274 + padding-top: 6px;
  275 + color: #f30;
  276 + top: 100%;
  277 + }
  278 +
  279 + .table-upload-item-img {
  280 + display: inline-block;
  281 + height: 116px;
  282 + width: 88px;
  283 + border: 2px solid #e8e8e8;
  284 + box-sizing: border-box;
  285 + }
  286 +
  287 + .table-header {
  288 + &:after {
  289 + content: '*';
  290 + display: inline-block;
  291 + margin-left: 4px;
  292 + line-height: 1;
  293 + font-family: SimSun;
  294 + font-size: 12px;
  295 + color: #f30;
  296 + }
  297 + }
  298 +
  299 +</style>
@@ -50,16 +50,16 @@ export default { @@ -50,16 +50,16 @@ export default {
50 { 50 {
51 title: '色卡图片', 51 title: '色卡图片',
52 key: 'goodsColorImage', 52 key: 'goodsColorImage',
53 - width: 250, 53 + width: 180,
54 render(row, col, index) { 54 render(row, col, index) {
55 return `<div 55 return `<div
56 - :class="{'upload-item': true ,  
57 - 'upload-item-error': 56 + :class="{'table-upload-item': true ,
  57 + 'table-upload-item-error':
58 row.goodsColorImage.showValidate && row.goodsColorImage.validate}"> 58 row.goodsColorImage.showValidate && row.goodsColorImage.validate}">
59 - <div class="upload-item-img"> 59 + <div class="table-upload-item-img">
60 <img v-if="row.goodsColorImage.value" 60 <img v-if="row.goodsColorImage.value"
61 :src="row.goodsColorImage.value" 61 :src="row.goodsColorImage.value"
62 - alt="" width="120px" height="122px"> 62 + alt="" width="84px" height="112px">
63 </div> 63 </div>
64 64
65 <div> 65 <div>
@@ -69,7 +69,7 @@ export default { @@ -69,7 +69,7 @@ export default {
69 <example-pop></example-pop> 69 <example-pop></example-pop>
70 </div> 70 </div>
71 <div v-if="row.goodsColorImage.showValidate && row.goodsColorImage.validate" 71 <div v-if="row.goodsColorImage.showValidate && row.goodsColorImage.validate"
72 - class="upload-item-tip"> 72 + class="table-upload-item-tip">
73 必须上传图片 73 必须上传图片
74 </div> 74 </div>
75 </div>`; 75 </div>`;
@@ -297,11 +297,11 @@ export default { @@ -297,11 +297,11 @@ export default {
297 } 297 }
298 } 298 }
299 299
300 -.upload-item-error { 300 +.table-upload-item-error {
301 border: 1px solid #f30; 301 border: 1px solid #f30;
302 } 302 }
303 303
304 -.upload-item-tip { 304 +.table-upload-item-tip {
305 padding-top: 6px; 305 padding-top: 6px;
306 color: #f30; 306 color: #f30;
307 top: 100%; 307 top: 100%;
@@ -50,7 +50,7 @@ export default { @@ -50,7 +50,7 @@ export default {
50 let mid = value[1]; 50 let mid = value[1];
51 let min = value[2]; 51 let min = value[2];
52 52
53 - if (!max.value) { 53 + if (!max || !max.value) {
54 return callback(new Error('一级类目不能为空')); 54 return callback(new Error('一级类目不能为空'));
55 } else if (!mid.value) { 55 } else if (!mid.value) {
56 56
@@ -84,6 +84,8 @@ export default { @@ -84,6 +84,8 @@ export default {
84 this.$refs.formData.validate((valid) => { 84 this.$refs.formData.validate((valid) => {
85 if (valid) { 85 if (valid) {
86 this.goNext(); 86 this.goNext();
  87 + } else {
  88 + this.$Message.error('请填写必填项!');
87 } 89 }
88 }); 90 });
89 }, 91 },
1 <template> 1 <template>
2 <div> 2 <div>
3 <Form ref="product" :model="product" :label-width="70" :rules="ruleValidate"> 3 <Form ref="product" :model="product" :label-width="70" :rules="ruleValidate">
4 -  
5 <Row> 4 <Row>
6 <div class="create-group"> 5 <div class="create-group">
7 <span class="create-group-indicator"></span> 6 <span class="create-group-indicator"></span>
@@ -10,78 +9,36 @@ @@ -10,78 +9,36 @@
10 </div> 9 </div>
11 </Row> 10 </Row>
12 11
13 - <Row>  
14 - <Col>  
15 - <div class="create-item-title">商品基本信息</div>  
16 - </Col>  
17 - </Row> 12 + <Row> <div class="create-item-title">商品基本信息</div> </Row>
18 13
19 - <Row>  
20 - <div class="ivu-form-item ivu-form-item-required">  
21 - <label class="ivu-form-item-label" style="width: 100px;">品牌</label>  
22 - <div style="float: left; line-height: 1; padding: 10px 12px 10px 0; box-sizing: border-box;">  
23 - <span>{{product.brandName}}</span>  
24 - </div>  
25 - </div>  
26 - </Row> 14 + <Form-item label="品牌"> <span>{{product.brandName}}</span> </Form-item>
27 15
28 - <Row>  
29 - <div class="ivu-form-item ivu-form-item-required">  
30 - <label class="ivu-form-item-label" style="width: 100px;">类目</label>  
31 - <span style="float: left; line-height: 1; padding: 10px 12px 10px 0; box-sizing: border-box;">  
32 - <span>{{sortName}}</span>  
33 - </span>  
34 - </div>  
35 - </Row> 16 + <Form-item label="类目"> <span>{{sortName}}</span> </Form-item>
36 17
37 - <Row>  
38 - <Col span="8">  
39 - <Form-item label="商品名称" prop="productName">  
40 - <Input v-model="product.productName" placeholder="请输入..."/>  
41 - </Form-item>  
42 - </Col>  
43 - </Row> 18 + <Form-item label="商品名称" prop="productName">
  19 + <Input v-model="product.productName" placeholder="请输入..." style="width: 400px;"/>
  20 + </Form-item>
44 21
45 - <Row>  
46 - <Col span="8">  
47 - <Form-item label="商品卖点">  
48 - <Input v-model="product.phrase" type="textarea" :rows="4" placeholder="请输入..."/>  
49 - </Form-item>  
50 - </Col>  
51 - </Row> 22 + <Form-item label="商品卖点">
  23 + <Input v-model="product.phrase" type="textarea" :rows="4" placeholder="请输入..." style="width: 400px;"/>
  24 + </Form-item>
52 25
53 - <Row>  
54 - <Col span="8">  
55 - <Form-item label="商家商品编码" prop="factoryCode">  
56 - <Input v-model="product.factoryCode" placeholder="请输入..."/>  
57 - </Form-item>  
58 - </Col>  
59 - </Row> 26 + <Form-item label="商家商品编码" prop="factoryCode">
  27 + <Input v-model="product.factoryCode" placeholder="请输入..." style="width: 400px;"/>
  28 + </Form-item>
60 29
61 - <Row>  
62 - <Col span="8">  
63 - <Form-item label="货品年" prop="goodsYears">  
64 - <Date-picker :value="product.goodsYears" @on-change="clickGoodsYear" type="year" placeholder="选择年" style="width: 200px">  
65 - </Date-picker>  
66 - </Form-item>  
67 - </Col>  
68 - </Row> 30 + <Form-item label="货品年" prop="goodsYears">
  31 + <Date-picker :value="product.goodsYears" @on-change="clickGoodsYear" type="year" placeholder="选择年" style="width: 400px">
  32 + </Date-picker>
  33 + </Form-item>
69 34
70 - <Row>  
71 - <Col span="8">  
72 - <Form-item label="货品季" prop="goodsSeason">  
73 - <SelectSeason v-model="product.goodsSeason"></SelectSeason>  
74 - </Form-item>  
75 - </Col>  
76 - </Row> 35 + <Form-item label="货品季" prop="goodsSeason">
  36 + <SelectSeason v-model="product.goodsSeason"></SelectSeason>
  37 + </Form-item>
77 38
78 - <Row>  
79 - <Col span="8">  
80 - <Form-item label="上市日期" prop="expectSaleTimeStr">  
81 - <Date-picker :value="product.expectSaleTimeStr" @on-change="clickSaleDate" type="date" placeholder="选择日期" style="width: 200px"></Date-picker>  
82 - </Form-item>  
83 - </Col>  
84 - </Row> 39 + <Form-item label="上市日期" prop="expectSaleTimeStr">
  40 + <Date-picker :value="product.expectSaleTimeStr" @on-change="clickSaleDate" type="date" placeholder="选择日期" style="width: 400px"></Date-picker>
  41 + </Form-item>
85 42
86 <Form-item label="性别" prop="gender"> 43 <Form-item label="性别" prop="gender">
87 <RadioGender v-model="product.gender"></RadioGender> 44 <RadioGender v-model="product.gender"></RadioGender>
@@ -91,20 +48,16 @@ @@ -91,20 +48,16 @@
91 <RadioSeason v-model="product.seasons"></RadioSeason> 48 <RadioSeason v-model="product.seasons"></RadioSeason>
92 </Form-item> 49 </Form-item>
93 50
94 -  
95 <Form-item label="年龄层" prop="ageLevel"> 51 <Form-item label="年龄层" prop="ageLevel">
96 <CheckboxAge v-model="product.ageLevel"></CheckboxAge> 52 <CheckboxAge v-model="product.ageLevel"></CheckboxAge>
97 </Form-item> 53 </Form-item>
98 54
99 <Row> 55 <Row>
100 - <Col>  
101 - <div class="create-item-title">商品规格  
102 - <span class="create-group-sub-title">(颜色名称只能填写中文,最多5个汉字。款型编码和条码只能填写英文和数字,不区分大小写)</span>  
103 - </div>  
104 - </Col> 56 + <div class="create-item-title">商品规格
  57 + <span class="create-group-sub-title">(颜色名称只能填写中文,最多5个汉字。款型编码和条码只能填写英文和数字,不区分大小写)</span>
  58 + </div>
105 </Row> 59 </Row>
106 60
107 -  
108 <Form-item label="颜色" prop="selectColor"> 61 <Form-item label="颜色" prop="selectColor">
109 <CheckboxColor v-model="product.selectColor" @on-add="addColor" @on-remove="removeColor"></CheckboxColor> 62 <CheckboxColor v-model="product.selectColor" @on-add="addColor" @on-remove="removeColor"></CheckboxColor>
110 </Form-item> 63 </Form-item>
@@ -113,45 +66,25 @@ @@ -113,45 +66,25 @@
113 <CheckboxSize v-model="product.selectSize" :sort-id="product.smallSortId" @on-add="addSize" @on-remove="removeSize"></CheckboxSize> 66 <CheckboxSize v-model="product.selectSize" :sort-id="product.smallSortId" @on-add="addSize" @on-remove="removeSize"></CheckboxSize>
114 </Form-item> 67 </Form-item>
115 68
116 - <Row>  
117 - <Form-item >  
118 - <Table ref="sellerGoods" :context="self" :data="table.data" :columns="table.columns" stripe border></Table>  
119 - </Form-item>  
120 - </Row>  
121 -  
122 - <Row>  
123 - <Col>  
124 - <div class="create-item-title">商品价格</div>  
125 - </Col>  
126 - </Row>  
127 -  
128 - <Row>  
129 - <Col span="8">  
130 - <Form-item label="吊牌价" prop="retailPrice">  
131 - <Input v-model="product.retailPrice" :number="true" placeholder="请输入..."/>  
132 - </Form-item>  
133 - </Col>  
134 - </Row> 69 + <Form-item >
  70 + <TableCreateGoodSize ref="sellerGoods" v-model="table.data"></TableCreateGoodSize>
  71 + </Form-item>
135 72
  73 + <Row> <div class="create-item-title">商品价格</div> </Row>
136 74
137 - <Row>  
138 - <Col span="8">  
139 - <Form-item label="销售价" prop="salesPrice">  
140 - <Input v-model="product.salesPrice" :number="true" placeholder="请输入..."/>  
141 - </Form-item>  
142 - </Col>  
143 - </Row> 75 + <Form-item label="吊牌价" prop="retailPrice">
  76 + <Input v-model="product.retailPrice" :number="true" placeholder="请输入..." style="width: 400px;"/>
  77 + </Form-item>
144 78
  79 + <Form-item label="销售价" prop="salesPrice">
  80 + <Input v-model="product.salesPrice" :number="true" placeholder="请输入..." style="width: 400px;"/>
  81 + </Form-item>
145 </Form> 82 </Form>
146 83
147 - <Row>  
148 - <Col>  
149 - <div style="text-align: center">  
150 - <Button type="primary" @click="nextStep(1)" size="large">下一步</Button>  
151 - <Button type="primary" @click="nextStep(0)" size="large">保存并退出</Button>  
152 - </div>  
153 - </Col>  
154 - </Row> 84 + <div style="text-align: center">
  85 + <Button type="primary" @click="nextStep(1)" size="large">下一步</Button>
  86 + <Button type="primary" @click="nextStep(0)" size="large">保存并退出</Button>
  87 + </div>
155 </div> 88 </div>
156 </template> 89 </template>
157 90
@@ -163,6 +96,7 @@ import {CheckboxAge, CheckboxColor, CheckboxSize} from 'components/checkbox'; @@ -163,6 +96,7 @@ import {CheckboxAge, CheckboxColor, CheckboxSize} from 'components/checkbox';
163 import {RadioSeason, RadioGender} from 'components/radio'; 96 import {RadioSeason, RadioGender} from 'components/radio';
164 import {SelectSeason} from 'components/select'; 97 import {SelectSeason} from 'components/select';
165 import {ExamplePop} from 'components/pop'; 98 import {ExamplePop} from 'components/pop';
  99 +import {TableCreateGoodSize} from 'components/table';
166 100
167 const makeColor = () => { 101 const makeColor = () => {
168 return { 102 return {
@@ -238,128 +172,6 @@ export default { @@ -238,128 +172,6 @@ export default {
238 self: this, 172 self: this,
239 colors: [], 173 colors: [],
240 table: { 174 table: {
241 - columns: [  
242 - {  
243 - title: '色系名称',  
244 - key: 'goodsName',  
245 - render(row, col, index) {  
246 - return `<div v-if="isExist(${index})">  
247 - <p>{{table.data[${index}].goodsName.name}}</p>  
248 - <Radio :value="table.data[${index}].goodsName.isDefault"  
249 - @on-change="clickDefault(${index})">主推</Radio>  
250 - </div>`;  
251 - }  
252 - },  
253 - {  
254 - title: '颜色展示名称',  
255 - key: 'factoryGoodsName',  
256 - render(row, col, index) {  
257 - return `<div v-if="isExist(${index})">  
258 - <i-input  
259 - v-model="row.factoryGoodsName"  
260 - :placeholder="row.goodsName.name">  
261 - </i-input>  
262 - </div>`;  
263 - }  
264 - },  
265 - {  
266 - title: '色卡图片',  
267 - key: 'goodsColorImage',  
268 - width: 170,  
269 - render(row, col, index) {  
270 - return `<div  
271 - :class="{'upload-item': true ,  
272 - 'upload-item-error':  
273 - row.goodsColorImage.showValidate && row.goodsColorImage.validate}"  
274 - >  
275 - <div class="upload-item-img">  
276 - <img v-if="row.goodsColorImage.value"  
277 - :src="row.goodsColorImage.value" alt="" width="120px" height="122px">  
278 - </div>  
279 - <div>  
280 - <file-upload :id="{index: ${index}}"  
281 - @on-success="uploadSuccess"  
282 - @on-error="uploadError"></file-upload>  
283 - <example-pop></example-pop>  
284 - </div>  
285 - <div v-if="row.goodsColorImage.showValidate && row.goodsColorImage.validate"  
286 - class="upload-item-tip">  
287 - 必须上传图片  
288 - </div>  
289 - </div>`;  
290 - },  
291 - renderHeader(col, index) {  
292 - return `<span class="table-header">${col.title}</span>`;  
293 - }  
294 - },  
295 - {  
296 - title: '款型编码',  
297 - key: 'factoryCode',  
298 - render(row, col, index) {  
299 - return `<div v-if="isExist(${index})">  
300 - <i-input  
301 - v-model="row.factoryCode"  
302 - placeholder="请输入..." >  
303 - </i-input>  
304 - </div>`;  
305 - }  
306 - },  
307 - {  
308 - title: '尺码',  
309 - key: 'sizeId',  
310 - width: 80,  
311 - render(row, col, index) {  
312 - return `<div class="size-id">  
313 - <div v-for="size in row.sizeId" class="row-span">  
314 - {{size.name}}  
315 - </div>  
316 - </div>`;  
317 - }  
318 - },  
319 - {  
320 - title: '商品条码',  
321 - key: 'sizeCode',  
322 - render(row, col, index) {  
323 - return `<div class='size-code'>  
324 - <div v-for="size,i in row.sizeCode" class="row-span">  
325 - <div style="position: relative">  
326 - <div :class="{'size-code-error': size.validate && !size.name}">  
327 - <i-input  
328 - v-model="size.name"  
329 - :disabled="!row.operator[i].value"  
330 - placeholder="请输入..."  
331 - />  
332 - </div>  
333 - <div class="size-code-tip" v-if="size.validate && !size.name">  
334 - 不能为空  
335 - </div>  
336 - </div>  
337 - </div>  
338 - </div>`;  
339 - },  
340 - renderHeader(col, index) {  
341 - return `<span class="table-header">${col.title}</span>`;  
342 - }  
343 - },  
344 - {  
345 - title: '操作',  
346 - key: 'operator',  
347 - width: 100,  
348 - render(row, col, index) {  
349 - return `<template v-if="isExist(${index})">  
350 - <div class="size-operator">  
351 - <div v-for="op,i in row.operator" class="row-span">  
352 - <i-button v-if="row.operator[i].value"  
353 - type="warning"  
354 - @click="clickOperator(row, i)">禁用</i-button>  
355 - <i-button v-else type="primary"  
356 - @click="clickOperator(row, i)">启用</i-button>  
357 - </div>  
358 - </div>  
359 - </template>`;  
360 - }  
361 - }  
362 - ],  
363 data: [], 175 data: [],
364 selectedSizes: [], 176 selectedSizes: [],
365 selectedColors: [], 177 selectedColors: [],
@@ -449,7 +261,7 @@ export default { @@ -449,7 +261,7 @@ export default {
449 this.beforeSubmit(); 261 this.beforeSubmit();
450 return api.saveBaseProductInfo(this.product); 262 return api.saveBaseProductInfo(this.product);
451 }, () => { 263 }, () => {
452 - this.$Message.error('表单验证失败!'); 264 + this.$Message.error('请填写必填项!');
453 }); 265 });
454 }, 266 },
455 nextStep: function(go) { 267 nextStep: function(go) {
@@ -489,31 +301,6 @@ export default { @@ -489,31 +301,6 @@ export default {
489 saveAndQuit: function() { 301 saveAndQuit: function() {
490 this.$router.push({name: 'product.offsale'}); 302 this.$router.push({name: 'product.offsale'});
491 }, 303 },
492 - clickDefault: function(index) {  
493 - this.refreshTable();  
494 - let color = this.table.data[index];  
495 -  
496 -  
497 - color.goodsName.isDefault = true;  
498 - this.table.data.forEach((c) => {  
499 - if (c.colorId !== color.colorId) {  
500 - c.goodsName.isDefault = false;  
501 - }  
502 - });  
503 - },  
504 - clickOperator: function(row, itemIndex) {  
505 - this.refreshTable();  
506 - row.operator[itemIndex].value = row.operator[itemIndex].value ? false : true;  
507 -  
508 - if (row.operator[itemIndex].value) {  
509 - row.sizeCode[itemIndex].name = '';  
510 - row.sizeCode[itemIndex].validate = true;  
511 - } else {  
512 - row.sizeCode[itemIndex].name = '';  
513 - row.sizeCode[itemIndex].validate = false;  
514 - }  
515 -  
516 - },  
517 clickGoodsYear: function(value) { 304 clickGoodsYear: function(value) {
518 this.product.goodsYears = value; 305 this.product.goodsYears = value;
519 }, 306 },
@@ -535,22 +322,6 @@ export default { @@ -535,22 +322,6 @@ export default {
535 322
536 _.first(this.table.data).goodsName.isDefault = true; 323 _.first(this.table.data).goodsName.isDefault = true;
537 }, 324 },
538 - changeFactoryGoodsName: function(row, index) {  
539 - this.table.data[index].factoryGoodsName = row.factoryGoodsName;  
540 - },  
541 - changeFactoryCode: function(row, index) {  
542 - this.table.data[index].factoryCode = row.factoryCode;  
543 - },  
544 - changeSizeCode: function(row, rowIndex, sizeIndex) {  
545 - this.table.data[rowIndex].sizeCode[sizeIndex].name = row.sizeCode[sizeIndex].name;  
546 -  
547 - this.table.data[rowIndex].sizeCode[sizeIndex].validate = true;  
548 - if (this.table.data[rowIndex].sizeCode[sizeIndex].name) {  
549 - this.table.data[rowIndex].sizeCode[sizeIndex].showValidate = false;  
550 - } else {  
551 - this.table.data[rowIndex].sizeCode[sizeIndex].showValidate = true;  
552 - }  
553 - },  
554 addColor: function(color) { 325 addColor: function(color) {
555 this.refreshTable(); 326 this.refreshTable();
556 this.addColorData(color); 327 this.addColorData(color);
@@ -646,33 +417,11 @@ export default { @@ -646,33 +417,11 @@ export default {
646 }); 417 });
647 418
648 color.operator.push({ 419 color.operator.push({
649 - value: true 420 + value: false
650 }); 421 });
651 }, 422 },
652 - isExist: function(index) {  
653 - let row = this.table.data[index];  
654 -  
655 - if (row) {  
656 - return true;  
657 - }  
658 -  
659 - return false;  
660 - },  
661 - uploadSuccess: function(attach, files) {  
662 - this.refreshTable();  
663 - this.table.data[attach.index].goodsColorImage.value = files[0];  
664 -  
665 - this.table.data[attach.index].goodsColorImage.validate = true;  
666 - if (this.table.data[attach.index].goodsColorImage.value) {  
667 - this.table.data[attach.index].goodsColorImage.showValidate = false;  
668 - } else {  
669 - this.table.data[attach.index].goodsColorImage.showValidate = true;  
670 - }  
671 - },  
672 refreshTable() { 423 refreshTable() {
673 - this.table.data = this.$refs.sellerGoods.rebuildData;  
674 - },  
675 - uploadError: function(attach, err) { 424 + this.$refs.sellerGoods.syncData();
676 }, 425 },
677 validateOtherProps: function() { 426 validateOtherProps: function() {
678 return new Promise((resolve, reject) => { 427 return new Promise((resolve, reject) => {
@@ -782,124 +531,12 @@ export default { @@ -782,124 +531,12 @@ export default {
782 SelectSeason, 531 SelectSeason,
783 CheckboxColor, 532 CheckboxColor,
784 CheckboxSize, 533 CheckboxSize,
785 - ExamplePop 534 + ExamplePop,
  535 + TableCreateGoodSize
786 } 536 }
787 }; 537 };
788 </script> 538 </script>
789 539
790 <style lang="scss"> 540 <style lang="scss">
791 -@mixin row-span{  
792 - min-height: 30px;  
793 -  
794 - .row-span {  
795 - min-height: 30px;  
796 - border-bottom: 1px solid #e3e8ee;  
797 - padding-top: 20px ;  
798 - padding-bottom: 20px ;  
799 - margin-left: -18px;  
800 - margin-right: -18px;  
801 - padding-left: 18px ;  
802 - padding-right: 18px ;  
803 -  
804 - &:last-child {  
805 - border-bottom: none;  
806 - }  
807 - }  
808 -}  
809 -  
810 -.squre {  
811 - display: inline-block;  
812 - height: 30px;  
813 - margin-right: 10px;  
814 - cursor: pointer;  
815 - border: 1px solid white;  
816 -}  
817 -  
818 -.squre-selected {  
819 - border: 1px solid black;  
820 -}  
821 -  
822 -.squre-disabled {  
823 - color: gray;  
824 - opacity: 0.5;  
825 - cursor: not-allowed;  
826 - border: 1px solid gray;  
827 -}  
828 -  
829 -.squre-color {  
830 - display: inline-block;  
831 - height: 28px;  
832 - width: 28px;  
833 -}  
834 -  
835 -.squre-name {  
836 - display: inline-block;  
837 - height: 30px;  
838 - line-height: 30px;  
839 - vertical-align: top;  
840 -}  
841 -  
842 -.size-code {  
843 - @include row-span;  
844 -  
845 - &-error {  
846 - border: 1px solid #f30;  
847 - }  
848 -  
849 - &-tip {  
850 - position: absolute;  
851 - line-height: 1;  
852 - padding-top: 6px;  
853 - color: #f30;  
854 - }  
855 -}  
856 -  
857 -.size-id {  
858 - @include row-span;  
859 - text-align: center;  
860 -}  
861 -  
862 -.size-operator {  
863 - @include row-span;  
864 - text-align: center;  
865 -}  
866 -  
867 -.upload-item {  
868 - display: inline-block;  
869 - height: 220px;  
870 - width: 130px;  
871 - text-align: center;  
872 - margin: 30px 0;  
873 -}  
874 -  
875 -.upload-item-error {  
876 - border: 1px solid #f30;  
877 -}  
878 -  
879 -.upload-item-tip {  
880 - padding-top: 6px;  
881 - color: #f30;  
882 - top: 100%;  
883 -}  
884 -  
885 -.upload-item-img {  
886 - display: inline-block;  
887 - height: 126px;  
888 - width: 124px;  
889 - border: 2px solid #e8e8e8;  
890 - box-sizing: border-box;  
891 -}  
892 -  
893 -.table-header {  
894 - &:after {  
895 - content: '*';  
896 - display: inline-block;  
897 - margin-left: 4px;  
898 - line-height: 1;  
899 - font-family: SimSun;  
900 - font-size: 12px;  
901 - color: #f30;  
902 - }  
903 -}  
904 541
905 </style> 542 </style>
@@ -129,6 +129,14 @@ import api from 'product-create/api'; @@ -129,6 +129,14 @@ import api from 'product-create/api';
129 import serial from 'product-create/serialize'; 129 import serial from 'product-create/serialize';
130 130
131 export default { 131 export default {
  132 + props: {
  133 + from: {
  134 + type: String,
  135 + default() {
  136 + return 'product.offsale';
  137 + }
  138 + }
  139 + },
132 data() { 140 data() {
133 return { 141 return {
134 showLoading: true, 142 showLoading: true,
@@ -159,6 +167,8 @@ export default { @@ -159,6 +167,8 @@ export default {
159 }; 167 };
160 }, 168 },
161 created() { 169 created() {
  170 + this.from = this.$route.query.from || this.from;
  171 +
162 service.getProduct(this.$route.params.id).then((result) => { 172 service.getProduct(this.$route.params.id).then((result) => {
163 _.update(result, 'goodsSeason', (s) => `${s}`); 173 _.update(result, 'goodsSeason', (s) => `${s}`);
164 if (!_.has(result, 'materialList')) { 174 if (!_.has(result, 'materialList')) {
@@ -493,7 +503,7 @@ export default { @@ -493,7 +503,7 @@ export default {
493 desc: '该商品保存成功!' 503 desc: '该商品保存成功!'
494 }); 504 });
495 505
496 - this.$router.push({ name: 'product.offsale' }); 506 + this.go(this.from);
497 } else { 507 } else {
498 this.$Notice.error({ 508 this.$Notice.error({
499 title: '保存错误', 509 title: '保存错误',
@@ -503,13 +513,16 @@ export default { @@ -503,13 +513,16 @@ export default {
503 } 513 }
504 }); 514 });
505 }, 515 },
  516 + go(from) {
  517 + this.$router.push({ name: from });
  518 + },
506 submit() { 519 submit() {
507 this.validate() 520 this.validate()
508 .then(([r1, r2]) => { 521 .then(([r1, r2]) => {
509 if (r1 & r2) { 522 if (r1 & r2) {
510 return Promise.resolve(); 523 return Promise.resolve();
511 } else { 524 } else {
512 - this.$Message.error('验证未通过'); 525 + this.$Message.error('请填写必填项');
513 return Promise.reject(); 526 return Promise.reject();
514 } 527 }
515 }) 528 })
@@ -295,7 +295,10 @@ @@ -295,7 +295,10 @@
295 this.$router.push({ 295 this.$router.push({
296 name: 'product.edit', 296 name: 'product.edit',
297 params: { 297 params: {
298 - id: skn 298 + id: skn,
  299 + },
  300 + query: {
  301 + from: 'product.offsale'
299 } 302 }
300 }); 303 });
301 }, 304 },
@@ -273,6 +273,9 @@ @@ -273,6 +273,9 @@
273 name: 'product.edit', 273 name: 'product.edit',
274 params: { 274 params: {
275 id: skn 275 id: skn
  276 + },
  277 + query: {
  278 + from: 'product.onsale'
276 } 279 }
277 }); 280 });
278 }, 281 },
@@ -103,7 +103,7 @@ @@ -103,7 +103,7 @@
103 return service.getCategoryList(this.pageData.current, this.pageData.pageSize).then((result) => { 103 return service.getCategoryList(this.pageData.current, this.pageData.pageSize).then((result) => {
104 if (result.code === 200) { 104 if (result.code === 200) {
105 this.pageData.total = result.data.total; 105 this.pageData.total = result.data.total;
106 - this.pageData.current = result.data.currentPage; 106 + this.pageData.current = result.data.currentPage || 1;
107 this.tableData = result.data.rows; 107 this.tableData = result.data.rows;
108 } 108 }
109 }); 109 });
@@ -19,9 +19,7 @@ const plugin = { @@ -19,9 +19,7 @@ const plugin = {
19 checkPurview() { 19 checkPurview() {
20 return Promise.resolve(); 20 return Promise.resolve();
21 21
22 - // let pUrl = `/${_.split(to.name, '.').join('/')}`;  
23 -  
24 - // let pur = _.find(purviews, p => p.menu_url === pUrl); 22 + // let pur = _.find(Vue.$purviews, p => p.menu_url === to.path);
25 23
26 // if (pur) { 24 // if (pur) {
27 // return Promise.resolve(); 25 // return Promise.resolve();
@@ -42,6 +40,8 @@ const plugin = { @@ -42,6 +40,8 @@ const plugin = {
42 user = Rsa.decrypt(user, Object); 40 user = Rsa.decrypt(user, Object);
43 return this.initPurview(Vue, user).then(() => { 41 return this.initPurview(Vue, user).then(() => {
44 next(); 42 next();
  43 + }, () => {
  44 + next();
45 }); 45 });
46 } 46 }
47 next(); 47 next();
@@ -65,7 +65,7 @@ const plugin = { @@ -65,7 +65,7 @@ const plugin = {
65 return next('/login.html'); 65 return next('/login.html');
66 } 66 }
67 67
68 - return this.checkPurview(Vue.$purviews, to).then(() => { 68 + return this.checkPurview(Vue, to).then(() => {
69 return next(); 69 return next();
70 }, () => { 70 }, () => {
71 return next('/401.html'); 71 return next('/401.html');
@@ -31,6 +31,13 @@ const userService = { @@ -31,6 +31,13 @@ const userService = {
31 } 31 }
32 }); 32 });
33 return purs; 33 return purs;
  34 + },
  35 + switchShop(shopId) {
  36 + return axios.post('/switchShop', {
  37 + shopId
  38 + }).then(res => {
  39 + return res.data;
  40 + });
34 } 41 }
35 }; 42 };
36 43
1 { 1 {
2 "name": "yoho-shop-manage", 2 "name": "yoho-shop-manage",
3 - "version": "1.0.5", 3 + "version": "1.0.7",
4 "description": "", 4 "description": "",
5 "main": "app.js", 5 "main": "app.js",
6 "scripts": { 6 "scripts": {
@@ -55,7 +55,8 @@ let domainApis = { @@ -55,7 +55,8 @@ let domainApis = {
55 getRemoteImageUrlBySku: '/product/getRemoteImageUrlBySku' 55 getRemoteImageUrlBySku: '/product/getRemoteImageUrlBySku'
56 }, 56 },
57 shop: { 57 shop: {
58 - login: '/loginInter' 58 + login: '/loginInter',
  59 + switchShop: '/index/ajaxshop'
59 } 60 }
60 }; 61 };
61 62
@@ -83,7 +83,8 @@ class Api extends Context { @@ -83,7 +83,8 @@ class Api extends Context {
83 logger.error(`api call ${err.statusCode} [${err.options.method}] 83 logger.error(`api call ${err.statusCode} [${err.options.method}]
84 ${err.options.url} ${err.response.body || ''}`); 84 ${err.options.url} ${err.response.body || ''}`);
85 } else { 85 } else {
86 - logger.error(`api call ${err}`); 86 + logger.error(`api call [${options.method || 'get'}] ${options.url}
  87 + ${JSON.stringify(options.qs || options.body)} ${err}`);
87 } 88 }
88 return Promise.reject(API_INTERNAL_ERROR); 89 return Promise.reject(API_INTERNAL_ERROR);
89 }); 90 });
@@ -10,6 +10,8 @@ const Express = require('express'); @@ -10,6 +10,8 @@ const Express = require('express');
10 const UserController = require('./user-controller'); 10 const UserController = require('./user-controller');
11 const FileController = require('./file-controller'); 11 const FileController = require('./file-controller');
12 const middleware = require('../framework/middleware'); 12 const middleware = require('../framework/middleware');
  13 +const before = require('../middleware/before');
  14 +const auth = require('../middleware/auth');
13 const multipart = require('connect-multiparty'); 15 const multipart = require('connect-multiparty');
14 const multipartMiddleware = multipart(); 16 const multipartMiddleware = multipart();
15 17
@@ -17,7 +19,8 @@ let router = Express.Router(); // eslint-disable-line @@ -17,7 +19,8 @@ let router = Express.Router(); // eslint-disable-line
17 19
18 router.post('/login', middleware(UserController, 'login')); 20 router.post('/login', middleware(UserController, 'login'));
19 router.post('/logout', middleware(UserController, 'logout')); 21 router.post('/logout', middleware(UserController, 'logout'));
20 -router.post('/upload/image', multipartMiddleware, middleware(FileController, 'uploadImage'));  
21 -router.post('/upload/xlsx', multipartMiddleware, middleware(FileController, 'uploadXlsx')); 22 +router.post('/switchShop', before, auth, middleware(UserController, 'switchShop'));
  23 +router.post('/upload/image', before, auth, multipartMiddleware, middleware(FileController, 'uploadImage'));
  24 +router.post('/upload/xlsx', before, auth, multipartMiddleware, middleware(FileController, 'uploadXlsx'));
22 25
23 module.exports = router; 26 module.exports = router;
@@ -26,9 +26,8 @@ class UserController extends Context { @@ -26,9 +26,8 @@ class UserController extends Context {
26 let currentShop = _.first(result.data); 26 let currentShop = _.first(result.data);
27 27
28 this.syncSession({req, res}, Object.assign(user, { 28 this.syncSession({req, res}, Object.assign(user, {
29 - shops: result.data,  
30 - currentShop: currentShop  
31 - }), sess); 29 + shops: result.data
  30 + }), sess, currentShop);
32 31
33 return res.json({ 32 return res.json({
34 code: 200, 33 code: 200,
@@ -68,26 +67,62 @@ class UserController extends Context { @@ -68,26 +67,62 @@ class UserController extends Context {
68 data: '登出成功' 67 data: '登出成功'
69 }); 68 });
70 } 69 }
  70 + switchShop(req, res) {
  71 + let shopId = req.body.shopId;
71 72
72 - syncSession(context, user, sess) { 73 + if (!shopId) {
  74 + return res.json({
  75 + code: 400,
  76 + message: '参数错误'
  77 + });
  78 + }
  79 + let shop = _.find(req.session.USER.shops, s => s.shopsId === shopId);
  80 +
  81 + if (!shop) {
  82 + return res.json({
  83 + code: 400,
  84 + message: '不存在的店铺'
  85 + });
  86 + }
  87 + this.userService.switchShop({
  88 + shopId,
  89 + cookies: {
  90 + PHPSESSID: encodeURIComponent(req.cookies.PHPSESSID),
  91 + 'connect.sid': encodeURIComponent(req.cookies['connect.sid'])
  92 + }
  93 + }).then(response => {
  94 + this.syncShopSession({
  95 + req,
  96 + res
  97 + }, response);
  98 + return res.json({
  99 + code: 200
  100 + });
  101 + });
  102 + }
  103 +
  104 + syncSession(context, user, sess, currentShop) {
73 context.req.session.USER = user; 105 context.req.session.USER = user;
74 context.req.session.LOGIN_UID = user.pid; // pid 为用户名 106 context.req.session.LOGIN_UID = user.pid; // pid 为用户名
75 107
  108 + this.syncShopSession(context, sess);
  109 + context.res.cookie('_isLogin', true, {
  110 + path: '/'
  111 + });
  112 + context.res.cookie('_sign', currentShop.shopsId, {
  113 + path: '/'
  114 + });
  115 + }
  116 +
  117 + syncShopSession(context, sess) {
76 _.each(sess, (v, k) => { 118 _.each(sess, (v, k) => {
77 context.res.cookie(k, v, { 119 context.res.cookie(k, v, {
78 path: '/', 120 path: '/',
79 domain: '.yohobuy.com', 121 domain: '.yohobuy.com',
80 httpOnly: true, 122 httpOnly: true,
81 - overwrite: false,  
82 encode: val => val 123 encode: val => val
83 }); 124 });
84 }); 125 });
85 - context.res.cookie('_isLogin', true, {  
86 - path: '/'  
87 - });  
88 - context.res.cookie('_sign', user.currentShop.shopsId, {  
89 - path: '/'  
90 - });  
91 } 126 }
92 } 127 }
93 128
@@ -39,24 +39,28 @@ class UserService extends Context { @@ -39,24 +39,28 @@ class UserService extends Context {
39 password: password 39 password: password
40 } 40 }
41 }).then(response => { 41 }).then(response => {
42 - let sessId = '', sid = ''; 42 + return this._getShopsSession(response.rawHeaders);
  43 + });
  44 + }
  45 +
  46 + _getShopsSession(rawHeaders) {
  47 + let sessId = '', sid = '';
43 48
44 - _.each(response.rawHeaders, header => {  
45 - let sessIdMatched = header.match(new RegExp(regSession.replace('${0}', 'PHPSESSID')));  
46 - let sidMatched = header.match(new RegExp(regSession.replace('${0}', 'connect\.sid'))); 49 + _.each(rawHeaders, header => {
  50 + let sessIdMatched = header.match(new RegExp(regSession.replace('${0}', 'PHPSESSID')));
  51 + let sidMatched = header.match(new RegExp(regSession.replace('${0}', 'connect\.sid')));
47 52
48 - if (sessIdMatched) {  
49 - sessId = sessIdMatched[1];  
50 - }  
51 - if (sidMatched) {  
52 - sid = sidMatched[1];  
53 - }  
54 - });  
55 - return {  
56 - PHPSESSID: sessId,  
57 - 'connect.sid': sid  
58 - }; 53 + if (sessIdMatched) {
  54 + sessId = sessIdMatched[1];
  55 + }
  56 + if (sidMatched) {
  57 + sid = sidMatched[1];
  58 + }
59 }); 59 });
  60 + return {
  61 + PHPSESSID: sessId,
  62 + 'connect.sid': sid
  63 + };
60 } 64 }
61 65
62 getShops(pid) { 66 getShops(pid) {
@@ -68,6 +72,22 @@ class UserService extends Context { @@ -68,6 +72,22 @@ class UserService extends Context {
68 }); 72 });
69 } 73 }
70 74
  75 + switchShop(opts) {
  76 + let options = {
  77 + url: `${config.apiDomain.shop.switchShop}?shops_id=${opts.shopId}`,
  78 + resolveWithFullResponse: true,
  79 + };
  80 + let jar = rp.jar();
  81 +
  82 + _.each(opts.cookies, (val, key) => {
  83 + jar.setCookie(rp.cookie(`${key}=${val}`), options.url);
  84 + });
  85 + options.jar = jar;
  86 + return rp.get(options).then(response => {
  87 + return this._getShopsSession(response.rawHeaders);
  88 + });
  89 + }
  90 +
71 profile(pid) { 91 profile(pid) {
72 return this.instance(Api).get(config.apiDomain.shop.profile.url, {userId: pid}); 92 return this.instance(Api).get(config.apiDomain.shop.profile.url, {userId: pid});
73 } 93 }