Commit a86b16bba66bd2283ef0acc24ff86ac1ef8adb67
1 parent
0bd81688e7
Exists in
master
auto commit the code by alias command
Showing
20 changed files
with
799 additions
and
138 deletions
Show diff stats
.env.production
mock/index.js
1 | 1 | import Mock from 'mockjs' |
2 | -import { param2Obj } from '../src/utils' | |
2 | +import { | |
3 | + param2Obj | |
4 | +} from '../src/utils' | |
3 | 5 | |
4 | 6 | import user from './user' |
5 | 7 | import role from './role' |
8 | +import prod from './prod' | |
6 | 9 | import article from './article' |
7 | 10 | import search from './remote-search' |
8 | - | |
11 | +//因为没有Mock上,所以会请求不到数据。报404 | |
9 | 12 | const mocks = [ |
10 | 13 | ...user, |
11 | 14 | ...role, |
12 | 15 | ...article, |
16 | + ...prod, | |
13 | 17 | ...search |
14 | 18 | ] |
15 | 19 | |
... | ... | @@ -20,7 +24,7 @@ export function mockXHR() { |
20 | 24 | // mock patch |
21 | 25 | // https://github.com/nuysoft/Mock/issues/300 |
22 | 26 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send |
23 | - Mock.XHR.prototype.send = function() { | |
27 | + Mock.XHR.prototype.send = function () { | |
24 | 28 | if (this.custom.xhr) { |
25 | 29 | this.custom.xhr.withCredentials = this.withCredentials || false |
26 | 30 | |
... | ... | @@ -32,10 +36,14 @@ export function mockXHR() { |
32 | 36 | } |
33 | 37 | |
34 | 38 | function XHR2ExpressReqWrap(respond) { |
35 | - return function(options) { | |
39 | + return function (options) { | |
36 | 40 | let result = null |
37 | 41 | if (respond instanceof Function) { |
38 | - const { body, type, url } = options | |
42 | + const { | |
43 | + body, | |
44 | + type, | |
45 | + url | |
46 | + } = options | |
39 | 47 | // https://expressjs.com/en/4x/api.html#req |
40 | 48 | result = respond({ |
41 | 49 | method: type, | ... | ... |
mock/prod.js
... | ... | @@ -10,21 +10,29 @@ const image_uri = 'https://wpimg.wallstcn.com/360e4842-4db5-42d0-b078-f9a84a8255 |
10 | 10 | |
11 | 11 | for (let i = 0; i < count; i++) { |
12 | 12 | List.push(Mock.mock({ |
13 | - pid: '@increment', | |
14 | - pname: '@title(5,10)', | |
15 | - timestamp: +Mock.Random.date('T'), | |
13 | + pid: '@increment', //产品id | |
14 | + pname: '01-8701志平防蓝光-防辐射电脑网课眼镜,TR90弹性漆,近视镜,青春潮流', //产品名移交 | |
15 | + timestamp: +Mock.Random.date('T'), //产品时间 | |
16 | 16 | shoper: '@first', //所属工厂 |
17 | 17 | salescount: '@first', //购买次数 |
18 | - importance: '@integer(1, 3)', //排序权重 | |
19 | - prod_info_weight: '@integer(1, 3)', //重量 | |
20 | - prod_info_leg_long: '@integer(1, 3)', //腿长 | |
21 | - prod_info_glass_width: '@integer(1, 3)', //镜宽 | |
22 | - prod_info_glass_height: '@integer(1, 3)', //镜高 | |
23 | - prod_info_frame_width: '@integer(1, 3)', //框宽 | |
24 | - prod_info_frame_height: '@integer(1, 3)', //框高 | |
25 | - prod_info_norse_width: '@integer(1, 3)', //鼻宽 | |
18 | + importance: '@integer(1, 3)', //排序-权重 | |
19 | + prod_info_weight: '@integer(1, 3)', //参数--->重量 | |
20 | + prod_info_leg_long: '@integer(1, 3)', //参数--->腿长 | |
21 | + prod_info_glass_width: '@integer(1, 3)', //参数--->镜宽 | |
22 | + prod_info_glass_height: '@integer(1, 3)', //参数--->镜高 | |
23 | + prod_info_frame_width: '@integer(1, 3)', //参数--->框宽 | |
24 | + prod_info_frame_height: '@integer(1, 3)', //参数--->框高 | |
25 | + prod_info_norse_width: '@integer(1, 3)', //参数--->鼻宽 | |
26 | 26 | prod_info_window_pic: [], //鼻宽 |
27 | - image_uri: image_uri | |
27 | + image_uri: image_uri, | |
28 | + prod_memo: '', | |
29 | + // 'type|1': ['CN', 'US', 'JP', 'EU'], | |
30 | + // 'status|1': ['published', 'draft'], | |
31 | + 'type|1': ['CN', 'US', 'JP', 'EU'], //本产品销售区域 | |
32 | + 'prod_status|1': ['published', 'draft'], //状态,上线-下线 | |
33 | + judge_list: [], //评论管理 | |
34 | + sku_list: [], //sku清单 | |
35 | + sku_attr: [], //sku属性 | |
28 | 36 | })) |
29 | 37 | } |
30 | 38 | |
... | ... | @@ -35,23 +43,22 @@ export default [{ |
35 | 43 | const { |
36 | 44 | importance, |
37 | 45 | type, |
38 | - title, | |
46 | + pname, | |
39 | 47 | page = 1, |
40 | 48 | limit = 20, |
41 | - sort | |
49 | + // sort | |
42 | 50 | } = config.query |
43 | 51 | |
44 | 52 | let mockList = List.filter(item => { |
45 | 53 | if (importance && item.importance !== +importance) return false |
46 | 54 | if (type && item.type !== type) return false |
47 | - if (title && item.title.indexOf(title) < 0) return false | |
55 | + if (pname && item.pname.indexOf(pname) < 0) return false | |
48 | 56 | return true |
49 | 57 | }) |
50 | 58 | |
51 | - if (sort === '-id') { | |
52 | - mockList = mockList.reverse() | |
53 | - } | |
54 | - | |
59 | + // if (sort === '-pid') { | |
60 | + // mockList = mockList.reverse() | |
61 | + // } | |
55 | 62 | const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) |
56 | 63 | |
57 | 64 | return { |
... | ... | @@ -63,16 +70,15 @@ export default [{ |
63 | 70 | } |
64 | 71 | } |
65 | 72 | }, |
66 | - | |
67 | 73 | { |
68 | 74 | url: '/yp/prod/detail', |
69 | 75 | type: 'get', |
70 | 76 | response: config => { |
71 | 77 | const { |
72 | - id | |
78 | + pid | |
73 | 79 | } = config.query |
74 | 80 | for (const prod of List) { |
75 | - if (prod.id === +id) { | |
81 | + if (prod.pid === +pid) { | |
76 | 82 | return { |
77 | 83 | code: 20000, |
78 | 84 | data: prod |
... | ... | @@ -82,34 +88,34 @@ export default [{ |
82 | 88 | } |
83 | 89 | }, |
84 | 90 | |
85 | - { | |
86 | - url: '/yp/prod/pv', | |
87 | - type: 'get', | |
88 | - response: _ => { | |
89 | - return { | |
90 | - code: 20000, | |
91 | - data: { | |
92 | - pvData: [{ | |
93 | - key: 'PC', | |
94 | - pv: 1024 | |
95 | - }, | |
96 | - { | |
97 | - key: 'mobile', | |
98 | - pv: 1024 | |
99 | - }, | |
100 | - { | |
101 | - key: 'ios', | |
102 | - pv: 1024 | |
103 | - }, | |
104 | - { | |
105 | - key: 'android', | |
106 | - pv: 1024 | |
107 | - } | |
108 | - ] | |
109 | - } | |
110 | - } | |
111 | - } | |
112 | - }, | |
91 | + // { | |
92 | + // url: '/yp/prod/pv', | |
93 | + // type: 'get', | |
94 | + // response: _ => { | |
95 | + // return { | |
96 | + // code: 20000, | |
97 | + // data: { | |
98 | + // pvData: [{ | |
99 | + // key: 'PC', | |
100 | + // pv: 1024 | |
101 | + // }, | |
102 | + // { | |
103 | + // key: 'mobile', | |
104 | + // pv: 1024 | |
105 | + // }, | |
106 | + // { | |
107 | + // key: 'ios', | |
108 | + // pv: 1024 | |
109 | + // }, | |
110 | + // { | |
111 | + // key: 'android', | |
112 | + // pv: 1024 | |
113 | + // } | |
114 | + // ] | |
115 | + // } | |
116 | + // } | |
117 | + // } | |
118 | + // }, | |
113 | 119 | |
114 | 120 | { |
115 | 121 | url: '/yp/prod/create', | ... | ... |
src/api/prod.js
... | ... | @@ -12,16 +12,16 @@ export function fetchList(query) { |
12 | 12 | return request({ |
13 | 13 | url: '/yp/prod/list', |
14 | 14 | method: 'post', |
15 | - params: query | |
15 | + data: query | |
16 | 16 | }) |
17 | 17 | } |
18 | 18 | |
19 | -export function fetchArticle(id) { | |
19 | +export function fetchProd(pid) { | |
20 | 20 | return request({ |
21 | 21 | url: '/yp/prod/detail', |
22 | 22 | method: 'get', |
23 | 23 | params: { |
24 | - id | |
24 | + pid | |
25 | 25 | } |
26 | 26 | }) |
27 | 27 | } | ... | ... |
src/components/Pagination/index.vue
src/lang/zh.js
src/router/modules/prod.js
... | ... | @@ -16,11 +16,31 @@ const prodRouter = { |
16 | 16 | children: [{ |
17 | 17 | path: 'list', |
18 | 18 | component: () => import('@/views/prod/list'), |
19 | - name: 'prodList', | |
19 | + name: 'ProdList', | |
20 | 20 | meta: { |
21 | 21 | title: 'prods.prodlist', |
22 | 22 | roles: ['admin', 'assistant', 'shoper', 'runner'] |
23 | 23 | } |
24 | + }, { | |
25 | + path: 'edit/:pid(\\d+)', | |
26 | + component: () => import('@/views/prod/edit'), | |
27 | + name: 'ProdEdit', | |
28 | + meta: { | |
29 | + title: 'prods.prodedit', | |
30 | + noCache: true, | |
31 | + activeMenu: '/prod/list' | |
32 | + }, | |
33 | + hidden: true | |
34 | + }, { | |
35 | + path: 'create/:fid(\\d+)', | |
36 | + component: () => import('@/views/prod/create'), | |
37 | + name: 'ProdCreate', | |
38 | + meta: { | |
39 | + title: 'prods.prodcreate', | |
40 | + noCache: true, | |
41 | + activeMenu: '/prod/list' | |
42 | + }, | |
43 | + hidden: true | |
24 | 44 | }] |
25 | 45 | } |
26 | 46 | ... | ... |
src/router/modules/sites.js
... | ... | @@ -7,7 +7,7 @@ const sitesRouter = { |
7 | 7 | alwaysShow: true, // will always show the root menu |
8 | 8 | name: 'Site', |
9 | 9 | meta: { |
10 | - title: 'sites', | |
10 | + title: 'sites.sites', | |
11 | 11 | icon: 'people', |
12 | 12 | roles: ['admin', 'assistant', 'runner'] // you can set roles in root nav |
13 | 13 | }, |
... | ... | @@ -16,7 +16,7 @@ const sitesRouter = { |
16 | 16 | component: () => import('@/views/site/list'), |
17 | 17 | name: 'SiteList', |
18 | 18 | meta: { |
19 | - title: '站点列表', | |
19 | + title: 'sites.siteList', | |
20 | 20 | roles: ['admin', 'runner'] |
21 | 21 | |
22 | 22 | } | ... | ... |
src/store/modules/settings.js
1 | 1 | import variables from '@/styles/element-variables.scss' |
2 | 2 | import defaultSettings from '@/settings' |
3 | 3 | |
4 | -const { showSettings, tagsView, fixedHeader, sidebarLogo, supportPinyinSearch } = defaultSettings | |
4 | +const { | |
5 | + showSettings, | |
6 | + tagsView, | |
7 | + fixedHeader, | |
8 | + sidebarLogo, | |
9 | + supportPinyinSearch | |
10 | +} = defaultSettings | |
5 | 11 | |
6 | 12 | const state = { |
7 | 13 | theme: variables.theme, |
... | ... | @@ -13,7 +19,10 @@ const state = { |
13 | 19 | } |
14 | 20 | |
15 | 21 | const mutations = { |
16 | - CHANGE_SETTING: (state, { key, value }) => { | |
22 | + CHANGE_SETTING: (state, { | |
23 | + key, | |
24 | + value | |
25 | + }) => { | |
17 | 26 | if (state.hasOwnProperty(key)) { |
18 | 27 | state[key] = value |
19 | 28 | } |
... | ... | @@ -21,7 +30,9 @@ const mutations = { |
21 | 30 | } |
22 | 31 | |
23 | 32 | const actions = { |
24 | - changeSetting({ commit }, data) { | |
33 | + changeSetting({ | |
34 | + commit | |
35 | + }, data) { | |
25 | 36 | commit('CHANGE_SETTING', data) |
26 | 37 | } |
27 | 38 | } |
... | ... | @@ -32,4 +43,3 @@ export default { |
32 | 43 | mutations, |
33 | 44 | actions |
34 | 45 | } |
35 | - | ... | ... |
src/utils/request.js
... | ... | @@ -40,21 +40,13 @@ service.interceptors.request.use( |
40 | 40 | 'Content-Type': 'application/x-www-form-urlencoded' |
41 | 41 | } |
42 | 42 | } |
43 | - // let options = { | |
44 | - // lock: true, | |
45 | - // fullscreen: false, | |
46 | - // text: '数据加载中……', | |
47 | - // // background: '#FFCC00', | |
48 | - // spinner: 'el-icon-loading' | |
49 | - // }; | |
50 | - const options = { | |
51 | - type: 'success', | |
52 | - message: baseUrl + config.url, | |
53 | - title: 'request axios ', | |
54 | - showClose: true, | |
55 | - duration: 3000 | |
56 | - } | |
57 | - Notification(options) | |
43 | + // Notification({ | |
44 | + // type: 'success', | |
45 | + // message: baseUrl + config.url, | |
46 | + // title: 'request axios ', | |
47 | + // showClose: true, | |
48 | + // duration: 3000 | |
49 | + // }) | |
58 | 50 | // loadingInstance = Loading.service(options); |
59 | 51 | config.method === 'post' |
60 | 52 | ? config.data = qs.stringify({ |
... | ... | @@ -75,7 +67,6 @@ service.interceptors.request.use( |
75 | 67 | // do something before request is sent |
76 | 68 | }, |
77 | 69 | error => { |
78 | - // do something with request error | |
79 | 70 | Message({ |
80 | 71 | message: error || 'Error', |
81 | 72 | type: 'error', |
... | ... | @@ -100,7 +91,7 @@ service.interceptors.response.use( |
100 | 91 | */ |
101 | 92 | response => { |
102 | 93 | const options = { |
103 | - type: 'error', | |
94 | + type: response.status == 200 ? 'success' : 'error', | |
104 | 95 | message: response.status, |
105 | 96 | title: 'response status value ', |
106 | 97 | showClose: true, | ... | ... |
src/views/example/list.vue
1 | 1 | <template> |
2 | 2 | <div class="app-container"> |
3 | - <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%"> | |
4 | - <el-table-column align="center" label="ID" width="80"> | |
3 | + <el-table | |
4 | + v-loading="listLoading" | |
5 | + :data="list" | |
6 | + border | |
7 | + fit | |
8 | + highlight-current-row | |
9 | + style="width: 100%" | |
10 | + > | |
11 | + <el-table-column | |
12 | + align="center" | |
13 | + label="ID" | |
14 | + width="80" | |
15 | + > | |
5 | 16 | <template slot-scope="scope"> |
6 | 17 | <span>{{ scope.row.id }}</span> |
7 | 18 | </template> |
8 | 19 | </el-table-column> |
9 | 20 | |
10 | - <el-table-column width="180px" align="center" label="Date"> | |
21 | + <el-table-column | |
22 | + width="180px" | |
23 | + align="center" | |
24 | + label="Date" | |
25 | + > | |
11 | 26 | <template slot-scope="scope"> |
12 | 27 | <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span> |
13 | 28 | </template> |
14 | 29 | </el-table-column> |
15 | 30 | |
16 | - <el-table-column width="120px" align="center" label="Author"> | |
31 | + <el-table-column | |
32 | + width="120px" | |
33 | + align="center" | |
34 | + label="Author" | |
35 | + > | |
17 | 36 | <template slot-scope="scope"> |
18 | 37 | <span>{{ scope.row.author }}</span> |
19 | 38 | </template> |
20 | 39 | </el-table-column> |
21 | 40 | |
22 | - <el-table-column width="100px" label="Importance"> | |
41 | + <el-table-column | |
42 | + width="100px" | |
43 | + label="Importance" | |
44 | + > | |
23 | 45 | <template slot-scope="scope"> |
24 | - <svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="meta-item__icon" /> | |
46 | + <svg-icon | |
47 | + v-for="n in +scope.row.importance" | |
48 | + :key="n" | |
49 | + icon-class="star" | |
50 | + class="meta-item__icon" | |
51 | + /> | |
25 | 52 | </template> |
26 | 53 | </el-table-column> |
27 | 54 | |
28 | - <el-table-column class-name="status-col" label="Status" width="110"> | |
55 | + <el-table-column | |
56 | + class-name="status-col" | |
57 | + label="Status" | |
58 | + width="110" | |
59 | + > | |
29 | 60 | <template slot-scope="{row}"> |
30 | 61 | <el-tag :type="row.status | statusFilter"> |
31 | 62 | {{ row.status }} |
... | ... | @@ -33,18 +64,32 @@ |
33 | 64 | </template> |
34 | 65 | </el-table-column> |
35 | 66 | |
36 | - <el-table-column min-width="300px" label="Title"> | |
67 | + <el-table-column | |
68 | + min-width="300px" | |
69 | + label="Title" | |
70 | + > | |
37 | 71 | <template slot-scope="{row}"> |
38 | - <router-link :to="'/example/edit/'+row.id" class="link-type"> | |
72 | + <router-link | |
73 | + :to="'/example/edit/'+row.id" | |
74 | + class="link-type" | |
75 | + > | |
39 | 76 | <span>{{ row.title }}</span> |
40 | 77 | </router-link> |
41 | 78 | </template> |
42 | 79 | </el-table-column> |
43 | 80 | |
44 | - <el-table-column align="center" label="Actions" width="120"> | |
81 | + <el-table-column | |
82 | + align="center" | |
83 | + label="Actions" | |
84 | + width="120" | |
85 | + > | |
45 | 86 | <template slot-scope="scope"> |
46 | 87 | <router-link :to="'/example/edit/'+scope.row.id"> |
47 | - <el-button type="primary" size="small" icon="el-icon-edit"> | |
88 | + <el-button | |
89 | + type="primary" | |
90 | + size="small" | |
91 | + icon="el-icon-edit" | |
92 | + > | |
48 | 93 | Edit |
49 | 94 | </el-button> |
50 | 95 | </router-link> |
... | ... | @@ -52,7 +97,13 @@ |
52 | 97 | </el-table-column> |
53 | 98 | </el-table> |
54 | 99 | |
55 | - <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" /> | |
100 | + <pagination | |
101 | + v-show="total>0" | |
102 | + :total="total" | |
103 | + :page.sync="listQuery.page" | |
104 | + :limit.sync="listQuery.limit" | |
105 | + @pagination="getList" | |
106 | + /> | |
56 | 107 | </div> |
57 | 108 | </template> |
58 | 109 | ... | ... |
src/views/prod/components/Dropdown/Comment.vue
... | ... | @@ -0,0 +1,41 @@ |
1 | +<template> | |
2 | + <el-dropdown :show-timeout="100" trigger="click"> | |
3 | + <el-button plain> | |
4 | + {{ !comment_disabled?'Comment: opened':'Comment: closed' }} | |
5 | + <i class="el-icon-caret-bottom el-icon--right" /> | |
6 | + </el-button> | |
7 | + <el-dropdown-menu slot="dropdown" class="no-padding"> | |
8 | + <el-dropdown-item> | |
9 | + <el-radio-group v-model="comment_disabled" style="padding: 10px;"> | |
10 | + <el-radio :label="true"> | |
11 | + Close comment | |
12 | + </el-radio> | |
13 | + <el-radio :label="false"> | |
14 | + Open comment | |
15 | + </el-radio> | |
16 | + </el-radio-group> | |
17 | + </el-dropdown-item> | |
18 | + </el-dropdown-menu> | |
19 | + </el-dropdown> | |
20 | +</template> | |
21 | + | |
22 | +<script> | |
23 | +export default { | |
24 | + props: { | |
25 | + value: { | |
26 | + type: Boolean, | |
27 | + default: false | |
28 | + } | |
29 | + }, | |
30 | + computed: { | |
31 | + comment_disabled: { | |
32 | + get() { | |
33 | + return this.value | |
34 | + }, | |
35 | + set(val) { | |
36 | + this.$emit('input', val) | |
37 | + } | |
38 | + } | |
39 | + } | |
40 | +} | |
41 | +</script> | ... | ... |
src/views/prod/components/Dropdown/Platform.vue
... | ... | @@ -0,0 +1,46 @@ |
1 | +<template> | |
2 | + <el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click"> | |
3 | + <el-button plain> | |
4 | + Platfroms({{ platforms.length }}) | |
5 | + <i class="el-icon-caret-bottom el-icon--right" /> | |
6 | + </el-button> | |
7 | + <el-dropdown-menu slot="dropdown" class="no-border"> | |
8 | + <el-checkbox-group v-model="platforms" style="padding: 5px 15px;"> | |
9 | + <el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key"> | |
10 | + {{ item.name }} | |
11 | + </el-checkbox> | |
12 | + </el-checkbox-group> | |
13 | + </el-dropdown-menu> | |
14 | + </el-dropdown> | |
15 | +</template> | |
16 | + | |
17 | +<script> | |
18 | +export default { | |
19 | + props: { | |
20 | + value: { | |
21 | + required: true, | |
22 | + default: () => [], | |
23 | + type: Array | |
24 | + } | |
25 | + }, | |
26 | + data() { | |
27 | + return { | |
28 | + platformsOptions: [ | |
29 | + { key: 'a-platform', name: 'a-platform' }, | |
30 | + { key: 'b-platform', name: 'b-platform' }, | |
31 | + { key: 'c-platform', name: 'c-platform' } | |
32 | + ] | |
33 | + } | |
34 | + }, | |
35 | + computed: { | |
36 | + platforms: { | |
37 | + get() { | |
38 | + return this.value | |
39 | + }, | |
40 | + set(val) { | |
41 | + this.$emit('input', val) | |
42 | + } | |
43 | + } | |
44 | + } | |
45 | +} | |
46 | +</script> | ... | ... |
src/views/prod/components/Dropdown/SourceUrl.vue
... | ... | @@ -0,0 +1,38 @@ |
1 | +<template> | |
2 | + <el-dropdown :show-timeout="100" trigger="click"> | |
3 | + <el-button plain> | |
4 | + Link | |
5 | + <i class="el-icon-caret-bottom el-icon--right" /> | |
6 | + </el-button> | |
7 | + <el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px"> | |
8 | + <el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri"> | |
9 | + <el-input v-model="source_uri" placeholder="Please enter the content"> | |
10 | + <template slot="prepend"> | |
11 | + URL | |
12 | + </template> | |
13 | + </el-input> | |
14 | + </el-form-item> | |
15 | + </el-dropdown-menu> | |
16 | + </el-dropdown> | |
17 | +</template> | |
18 | + | |
19 | +<script> | |
20 | +export default { | |
21 | + props: { | |
22 | + value: { | |
23 | + type: String, | |
24 | + default: '' | |
25 | + } | |
26 | + }, | |
27 | + computed: { | |
28 | + source_uri: { | |
29 | + get() { | |
30 | + return this.value | |
31 | + }, | |
32 | + set(val) { | |
33 | + this.$emit('input', val) | |
34 | + } | |
35 | + } | |
36 | + } | |
37 | +} | |
38 | +</script> | ... | ... |
src/views/prod/components/Dropdown/index.js
src/views/prod/components/ProdDetail.vue
... | ... | @@ -0,0 +1,388 @@ |
1 | +<template> | |
2 | + <div class="createPost-container"> | |
3 | + <el-form | |
4 | + ref="postForm" | |
5 | + :model="postForm" | |
6 | + :rules="rules" | |
7 | + class="form-container" | |
8 | + > | |
9 | + <sticky | |
10 | + :z-index="10" | |
11 | + :class-name="'sub-navbar '+postForm.status" | |
12 | + > | |
13 | + <CommentDropdown v-model="postForm.comment_disabled" /> | |
14 | + <PlatformDropdown v-model="postForm.platforms" /> | |
15 | + <SourceUrlDropdown v-model="postForm.source_uri" /> | |
16 | + <el-button | |
17 | + v-loading="loading" | |
18 | + style="margin-left: 10px;" | |
19 | + type="success" | |
20 | + @click="submitForm" | |
21 | + > | |
22 | + Publish | |
23 | + </el-button> | |
24 | + <el-button | |
25 | + v-loading="loading" | |
26 | + type="warning" | |
27 | + @click="draftForm" | |
28 | + > | |
29 | + Draft | |
30 | + </el-button> | |
31 | + </sticky> | |
32 | + | |
33 | + <div class="createPost-main-container"> | |
34 | + <el-row> | |
35 | + <!-- <Warning /> --> | |
36 | + <el-col :span="24"> | |
37 | + <el-form-item | |
38 | + style="margin-bottom: 40px;" | |
39 | + prop="title" | |
40 | + > | |
41 | + <MDinput | |
42 | + v-model="postForm.title" | |
43 | + :maxlength="100" | |
44 | + name="name" | |
45 | + required | |
46 | + > | |
47 | + Title | |
48 | + </MDinput> | |
49 | + </el-form-item> | |
50 | + | |
51 | + <div class="postInfo-container"> | |
52 | + <el-row> | |
53 | + <el-col :span="8"> | |
54 | + <el-form-item | |
55 | + label-width="60px" | |
56 | + label="shoper:" | |
57 | + class="postInfo-container-item" | |
58 | + > | |
59 | + <el-select | |
60 | + v-model="postForm.shoper" | |
61 | + :remote-method="getRemoteUserList" | |
62 | + filterable | |
63 | + default-first-option | |
64 | + remote | |
65 | + placeholder="Search user" | |
66 | + > | |
67 | + <el-option | |
68 | + v-for="(item,index) in userListOptions" | |
69 | + :key="item+index" | |
70 | + :label="item" | |
71 | + :value="item" | |
72 | + /> | |
73 | + </el-select> | |
74 | + </el-form-item> | |
75 | + </el-col> | |
76 | + | |
77 | + <el-col :span="10"> | |
78 | + <el-form-item | |
79 | + label-width="120px" | |
80 | + label="Publish Time:" | |
81 | + class="postInfo-container-item" | |
82 | + > | |
83 | + <el-date-picker | |
84 | + v-model="displayTime" | |
85 | + type="datetime" | |
86 | + format="yyyy-MM-dd HH:mm:ss" | |
87 | + placeholder="Select date and time" | |
88 | + /> | |
89 | + </el-form-item> | |
90 | + </el-col> | |
91 | + | |
92 | + <el-col :span="6"> | |
93 | + <el-form-item | |
94 | + label-width="90px" | |
95 | + label="Importance:" | |
96 | + class="postInfo-container-item" | |
97 | + > | |
98 | + <el-rate | |
99 | + v-model="postForm.importance" | |
100 | + :max="3" | |
101 | + :colors="['#99A9BF', '#F7BA2A', '#FF9900']" | |
102 | + :low-threshold="1" | |
103 | + :high-threshold="3" | |
104 | + style="display:inline-block" | |
105 | + /> | |
106 | + </el-form-item> | |
107 | + </el-col> | |
108 | + </el-row> | |
109 | + </div> | |
110 | + </el-col> | |
111 | + </el-row> | |
112 | + | |
113 | + <el-form-item | |
114 | + style="margin-bottom: 40px;" | |
115 | + label-width="70px" | |
116 | + label="Summary:" | |
117 | + > | |
118 | + <el-input | |
119 | + v-model="postForm.content_short" | |
120 | + :rows="1" | |
121 | + type="textarea" | |
122 | + class="article-textarea" | |
123 | + autosize | |
124 | + placeholder="Please enter the content" | |
125 | + /> | |
126 | + <span | |
127 | + v-show="contentShortLength" | |
128 | + class="word-counter" | |
129 | + >{{ contentShortLength }}words</span> | |
130 | + </el-form-item> | |
131 | + | |
132 | + <el-form-item | |
133 | + prop="content" | |
134 | + style="margin-bottom: 30px;" | |
135 | + > | |
136 | + <Tinymce | |
137 | + ref="editor" | |
138 | + v-model="postForm.content" | |
139 | + :height="400" | |
140 | + /> | |
141 | + </el-form-item> | |
142 | + | |
143 | + <el-form-item | |
144 | + prop="image_uri" | |
145 | + style="margin-bottom: 30px;" | |
146 | + > | |
147 | + <Upload v-model="postForm.image_uri" /> | |
148 | + </el-form-item> | |
149 | + </div> | |
150 | + </el-form> | |
151 | + </div> | |
152 | +</template> | |
153 | + | |
154 | +<script> | |
155 | +import Tinymce from '@/components/Tinymce' | |
156 | +import Upload from '@/components/Upload/SingleImage3' | |
157 | +import MDinput from '@/components/MDinput' | |
158 | +import Sticky from '@/components/Sticky' // 粘性header组件 | |
159 | +import { validURL } from '@/utils/validate' | |
160 | +import { fetchProd } from '@/api/prod' | |
161 | +import { searchUser } from '@/api/remote-search' | |
162 | +// import Warning from './Warning' | |
163 | +import { | |
164 | + CommentDropdown, | |
165 | + PlatformDropdown, | |
166 | + SourceUrlDropdown | |
167 | +} from './Dropdown' | |
168 | + | |
169 | +const defaultForm = { | |
170 | + status: 'draft', | |
171 | + title: '', // 文章题目 | |
172 | + content: '', // 文章内容 | |
173 | + content_short: '', // 文章摘要 | |
174 | + source_uri: '', // 文章外链 | |
175 | + image_uri: '', // 文章图片 | |
176 | + display_time: undefined, // 前台展示时间 | |
177 | + id: undefined, | |
178 | + platforms: ['a-platform'], | |
179 | + comment_disabled: false, | |
180 | + importance: 0 | |
181 | +} | |
182 | + | |
183 | +export default { | |
184 | + name: 'ProdDetail', | |
185 | + components: { | |
186 | + Tinymce, | |
187 | + MDinput, | |
188 | + Upload, | |
189 | + Sticky, | |
190 | + // Warning, | |
191 | + CommentDropdown, | |
192 | + PlatformDropdown, | |
193 | + SourceUrlDropdown | |
194 | + }, | |
195 | + props: { | |
196 | + isEdit: { | |
197 | + type: Boolean, | |
198 | + default: false | |
199 | + } | |
200 | + }, | |
201 | + data() { | |
202 | + const validateRequire = (rule, value, callback) => { | |
203 | + if (value === '') { | |
204 | + this.$message({ | |
205 | + message: rule.field + '为必传项', | |
206 | + type: 'error' | |
207 | + }) | |
208 | + callback(new Error(rule.field + '为必传项')) | |
209 | + } else { | |
210 | + callback() | |
211 | + } | |
212 | + } | |
213 | + const validateSourceUri = (rule, value, callback) => { | |
214 | + if (value) { | |
215 | + if (validURL(value)) { | |
216 | + callback() | |
217 | + } else { | |
218 | + this.$message({ | |
219 | + message: '外链url填写不正确', | |
220 | + type: 'error' | |
221 | + }) | |
222 | + callback(new Error('外链url填写不正确')) | |
223 | + } | |
224 | + } else { | |
225 | + callback() | |
226 | + } | |
227 | + } | |
228 | + return { | |
229 | + postForm: Object.assign({}, defaultForm), | |
230 | + loading: false, | |
231 | + userListOptions: [], | |
232 | + rules: { | |
233 | + image_uri: [{ validator: validateRequire }], | |
234 | + title: [{ validator: validateRequire }], | |
235 | + content: [{ validator: validateRequire }], | |
236 | + source_uri: [{ validator: validateSourceUri, trigger: 'blur' }] | |
237 | + }, | |
238 | + tempRoute: {} | |
239 | + } | |
240 | + }, | |
241 | + computed: { | |
242 | + contentShortLength() { | |
243 | + return this.postForm.content_short.length | |
244 | + }, | |
245 | + lang() { | |
246 | + return this.$store.getters.language | |
247 | + }, | |
248 | + displayTime: { | |
249 | + // set and get is useful when the data | |
250 | + // returned by the back end api is different from the front end | |
251 | + // back end return => "2013-06-25 06:59:25" | |
252 | + // front end need timestamp => 1372114765000 | |
253 | + get() { | |
254 | + return +new Date(this.postForm.display_time) | |
255 | + }, | |
256 | + set(val) { | |
257 | + this.postForm.display_time = new Date(val) | |
258 | + } | |
259 | + } | |
260 | + }, | |
261 | + created() { | |
262 | + if (this.isEdit) { | |
263 | + const pid = this.$route.params && this.$route.params.pid | |
264 | + this.fetchData(pid) | |
265 | + } | |
266 | + | |
267 | + // Why need to make a copy of this.$route here? | |
268 | + // Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page | |
269 | + // https://github.com/PanJiaChen/vue-element-admin/issues/1221 | |
270 | + this.tempRoute = Object.assign({}, this.$route) | |
271 | + }, | |
272 | + methods: { | |
273 | + fetchData(pid) { | |
274 | + fetchProd(pid) | |
275 | + .then(response => { | |
276 | + this.postForm = response.data | |
277 | + | |
278 | + // just for test | |
279 | + this.postForm.title += ` prod pId:${this.postForm.pid}` | |
280 | + this.postForm.content_short += ` prod Id:${this.postForm.pid}` | |
281 | + | |
282 | + // set tagsview title | |
283 | + this.setTagsViewTitle() | |
284 | + | |
285 | + // set page title | |
286 | + this.setPageTitle() | |
287 | + }) | |
288 | + .catch(err => { | |
289 | + console.log(err) | |
290 | + }) | |
291 | + }, | |
292 | + setTagsViewTitle() { | |
293 | + const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article' | |
294 | + const route = Object.assign({}, this.tempRoute, { | |
295 | + title: `${title}-${this.postForm.pid}` | |
296 | + }) | |
297 | + this.$store.dispatch('tagsView/updateVisitedView', route) | |
298 | + }, | |
299 | + setPageTitle() { | |
300 | + const title = 'Edit Article' | |
301 | + document.title = `${title} - ${this.postForm.pid}` | |
302 | + }, | |
303 | + submitForm() { | |
304 | + console.log('this.postForm', this.postForm) | |
305 | + this.$refs.postForm.validate(valid => { | |
306 | + if (valid) { | |
307 | + this.loading = true | |
308 | + this.$notify({ | |
309 | + title: '成功', | |
310 | + message: '发布文章成功', | |
311 | + type: 'success', | |
312 | + duration: 2000 | |
313 | + }) | |
314 | + this.postForm.status = 'published' | |
315 | + this.loading = false | |
316 | + } else { | |
317 | + console.log('error submit!!') | |
318 | + return false | |
319 | + } | |
320 | + }) | |
321 | + }, | |
322 | + draftForm() { | |
323 | + if ( | |
324 | + this.postForm.content.length === 0 || | |
325 | + this.postForm.title.length === 0 | |
326 | + ) { | |
327 | + this.$message({ | |
328 | + message: '请填写必要的标题和内容', | |
329 | + type: 'warning' | |
330 | + }) | |
331 | + return | |
332 | + } | |
333 | + this.$message({ | |
334 | + message: '保存成功', | |
335 | + type: 'success', | |
336 | + showClose: true, | |
337 | + duration: 1000 | |
338 | + }) | |
339 | + this.postForm.status = 'draft' | |
340 | + }, | |
341 | + getRemoteUserList(query) { | |
342 | + searchUser(query).then(response => { | |
343 | + if (!response.data.items) return | |
344 | + this.userListOptions = response.data.items.map(v => v.name) | |
345 | + }) | |
346 | + } | |
347 | + } | |
348 | +} | |
349 | +</script> | |
350 | + | |
351 | +<style lang="scss" scoped> | |
352 | +@import "~@/styles/mixin.scss"; | |
353 | + | |
354 | +.createPost-container { | |
355 | + position: relative; | |
356 | + | |
357 | + .createPost-main-container { | |
358 | + padding: 40px 45px 20px 50px; | |
359 | + | |
360 | + .postInfo-container { | |
361 | + position: relative; | |
362 | + @include clearfix; | |
363 | + margin-bottom: 10px; | |
364 | + | |
365 | + .postInfo-container-item { | |
366 | + float: left; | |
367 | + } | |
368 | + } | |
369 | + } | |
370 | + | |
371 | + .word-counter { | |
372 | + width: 40px; | |
373 | + position: absolute; | |
374 | + right: 10px; | |
375 | + top: 0px; | |
376 | + } | |
377 | +} | |
378 | + | |
379 | +.article-textarea /deep/ { | |
380 | + textarea { | |
381 | + padding-right: 40px; | |
382 | + resize: none; | |
383 | + border: none; | |
384 | + border-radius: 0px; | |
385 | + border-bottom: 1px solid #bfcbd9; | |
386 | + } | |
387 | +} | |
388 | +</style> | ... | ... |
src/views/prod/components/Warning.vue
src/views/prod/create.vue
... | ... | @@ -0,0 +1,16 @@ |
1 | +<template> | |
2 | + <prod-detail :is-edit="false" /> | |
3 | +</template> | |
4 | + | |
5 | +<script> | |
6 | +import ProdDetail from './components/ProdDetail' | |
7 | +// HTML 中的标签名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。 | |
8 | +// 这意味着当你使用 DOM 中的模板时, | |
9 | +// 使用驼峰命名法 的 prop 名需要使用其等价的短横线分隔命名方法或者全部使用小写。 | |
10 | +// 否者就会报上述错误。 | |
11 | +export default { | |
12 | + name: 'CreateForm', | |
13 | + components: { ProdDetail } | |
14 | +} | |
15 | +</script> | |
16 | + | ... | ... |
src/views/prod/edit.vue
src/views/prod/list.vue
1 | 1 | <template> |
2 | 2 | <div class="app-container"> |
3 | + <!-- <el-header> | |
4 | + | |
5 | + </el-header> --> | |
6 | + <template> | |
7 | + <router-link :to="'/prod/create/1111'"> | |
8 | + <el-button | |
9 | + type="primary" | |
10 | + size="small" | |
11 | + icon="el-icon-edit" | |
12 | + > | |
13 | + create | |
14 | + </el-button> | |
15 | + </router-link> | |
16 | + </template> | |
3 | 17 | <el-table |
4 | 18 | v-loading="listLoading" |
5 | 19 | :data="list" |
... | ... | @@ -10,39 +24,45 @@ |
10 | 24 | > |
11 | 25 | <el-table-column |
12 | 26 | align="center" |
13 | - label="ID" | |
14 | - width="80" | |
27 | + label="pid" | |
15 | 28 | > |
16 | 29 | <template slot-scope="scope"> |
17 | - <span>{{ scope.row.id }}</span> | |
30 | + <span>{{ scope.row.pid }}</span> | |
18 | 31 | </template> |
19 | 32 | </el-table-column> |
20 | - | |
21 | 33 | <el-table-column |
22 | - width="180px" | |
23 | 34 | align="center" |
24 | - label="Date" | |
35 | + label="pname" | |
25 | 36 | > |
26 | 37 | <template slot-scope="scope"> |
27 | - <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span> | |
38 | + <span>{{ scope.row.pname }}</span> | |
28 | 39 | </template> |
29 | 40 | </el-table-column> |
30 | - | |
31 | 41 | <el-table-column |
32 | - width="120px" | |
33 | 42 | align="center" |
34 | - label="Author" | |
43 | + label="参数信息" | |
35 | 44 | > |
36 | 45 | <template slot-scope="scope"> |
37 | - <span>{{ scope.row.author }}</span> | |
46 | + <span>框宽{{ scope.row.prod_info_frame_width }}mm</span><br> | |
47 | + <span>腿长{{ scope.row.prod_info_leg_long }}mm</span><br> | |
48 | + <span>镜宽{{ scope.row.prod_info_glass_width }}mm</span><br> | |
49 | + <span>镜高{{ scope.row.prod_info_glass_height }}mm</span><br> | |
50 | + <span>鼻宽{{ scope.row.prod_info_norse_width }}mm</span><br> | |
51 | + <span>重量{{ scope.row.prod_info_weight }}g</span> | |
38 | 52 | </template> |
39 | 53 | </el-table-column> |
40 | 54 | |
41 | 55 | <el-table-column |
42 | - width="100px" | |
43 | - label="Importance" | |
56 | + align="center" | |
57 | + label="Date" | |
44 | 58 | > |
45 | 59 | <template slot-scope="scope"> |
60 | + <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span> | |
61 | + </template> | |
62 | + </el-table-column> | |
63 | + | |
64 | + <el-table-column label="Importance"> | |
65 | + <template slot-scope="scope"> | |
46 | 66 | <svg-icon |
47 | 67 | v-for="n in +scope.row.importance" |
48 | 68 | :key="n" |
... | ... | @@ -54,43 +74,36 @@ |
54 | 74 | |
55 | 75 | <el-table-column |
56 | 76 | class-name="status-col" |
57 | - label="Status" | |
58 | - width="110" | |
77 | + label="prod_status" | |
59 | 78 | > |
60 | 79 | <template slot-scope="{row}"> |
61 | - <el-tag :type="row.status | statusFilter"> | |
62 | - {{ row.status }} | |
80 | + <el-tag :type="row.prod_status | statusFilter"> | |
81 | + {{ row.prod_status }} | |
63 | 82 | </el-tag> |
64 | 83 | </template> |
65 | 84 | </el-table-column> |
66 | 85 | |
67 | 86 | <el-table-column |
68 | - min-width="300px" | |
69 | - label="Title" | |
70 | - > | |
71 | - <template slot-scope="{row}"> | |
72 | - <router-link | |
73 | - :to="'/example/edit/'+row.id" | |
74 | - class="link-type" | |
75 | - > | |
76 | - <span>{{ row.title }}</span> | |
77 | - </router-link> | |
78 | - </template> | |
79 | - </el-table-column> | |
80 | - | |
81 | - <el-table-column | |
82 | 87 | align="center" |
83 | 88 | label="Actions" |
84 | - width="120" | |
85 | 89 | > |
86 | - <template slot-scope="scope"> | |
87 | - <router-link :to="'/example/edit/'+scope.row.id"> | |
90 | + <template slot-scope="{row}"> | |
91 | + <router-link :to="'/prod/edit/'+row.pid"> | |
92 | + <el-button | |
93 | + type="primary" | |
94 | + size="small" | |
95 | + icon="el-icon-edit" | |
96 | + > | |
97 | + Edit1 | |
98 | + </el-button> | |
99 | + </router-link> | |
100 | + <router-link :to="'/prod/del/'+row.pid"> | |
88 | 101 | <el-button |
89 | 102 | type="primary" |
90 | 103 | size="small" |
91 | 104 | icon="el-icon-edit" |
92 | 105 | > |
93 | - Edit | |
106 | + Edit2 | |
94 | 107 | </el-button> |
95 | 108 | </router-link> |
96 | 109 | </template> |
... | ... | @@ -108,20 +121,20 @@ |
108 | 121 | </template> |
109 | 122 | |
110 | 123 | <script> |
111 | -import { fetchList } from '@/api/article' | |
124 | +import { fetchList } from '@/api/prod' | |
125 | +// import { Pagination } from "@/components/Pagination"; // Secondary package based on el-pagination | |
112 | 126 | import Pagination from '@/components/Pagination' // Secondary package based on el-pagination |
113 | - | |
114 | 127 | export default { |
115 | - name: 'ArticleList', | |
128 | + name: 'ProdList', | |
116 | 129 | components: { Pagination }, |
117 | 130 | filters: { |
118 | - statusFilter(status) { | |
131 | + statusFilter(prod_status) { | |
119 | 132 | const statusMap = { |
120 | 133 | published: 'success', |
121 | 134 | draft: 'info', |
122 | 135 | deleted: 'danger' |
123 | 136 | } |
124 | - return statusMap[status] | |
137 | + return statusMap[prod_status] | |
125 | 138 | } |
126 | 139 | }, |
127 | 140 | data() { |
... | ... | @@ -141,6 +154,7 @@ export default { |
141 | 154 | methods: { |
142 | 155 | getList() { |
143 | 156 | this.listLoading = true |
157 | + console.log('getList', 'dddddd') | |
144 | 158 | fetchList(this.listQuery).then(response => { |
145 | 159 | this.list = response.data.items |
146 | 160 | this.total = response.data.total | ... | ... |