Commit 8a0e4eede07d9836ba6ec1ead345155edcab531d

Authored by BigBoss
1 parent fc0d443bb7
Exists in master

产品列表页

... ... @@ -2,7 +2,7 @@ import Mock from 'mockjs'
2 2 // import logoPath from "~@/assets/img/yp_logo.jpeg"
3 3  
4 4 const List = []
5   -const count = 100
  5 +const count = 50
6 6  
7 7 const baseContent = '<p>I am testing data, I am testing data.</p><p></p>'
8 8 // const image_uri = logoPath
... ...
src/views/application/appList.vue
... ... @@ -91,16 +91,16 @@ export default {
91 91 path: '',
92 92 nav_menu_data: [{
93 93 title: '产品列表',
94   - path: '/Menu/appList'
  94 + name: 'appList'
95 95 }, {
96 96 title: '订单列表',
97   - path: '/Menu/orderList'
  97 + name: 'orderList'
98 98 }, {
99 99 title: '用户列表',
100   - path: '/Menu/userList'
  100 + name: 'userList'
101 101 }, {
102 102 title: '运营分析',
103   - path: '/Menu/analys'
  103 + name: 'analys'
104 104 }],
105 105 prodListTableData: [{
106 106 prodInfo: 'pic',
... ...
src/views/prod/list.vue
1 1 <template>
2 2 <div class="app-container">
  3 + <div class="filter-container">
  4 + <!-- 搜索input -->
  5 + <el-input
  6 + v-model="listQuery.title"
  7 + :placeholder="$t('table.title')"
  8 + style="width: 200px;"
  9 + class="filter-item"
  10 + @keyup.enter.native="handleFilter"
  11 + />
  12 + <!-- type选择 -->
  13 + <el-select
  14 + v-model="listQuery.type"
  15 + :placeholder="$t('table.type')"
  16 + clearable
  17 + class="filter-item"
  18 + style="width: 110px"
  19 + >
  20 + <el-option
  21 + v-for="item in calendarTypeOptions"
  22 + :key="item.key"
  23 + :label="item.display_name"
  24 + :value="item.key"
  25 + />
  26 + </el-select>
  27 +
  28 + <!-- imp选择 -->
  29 + <el-select
  30 + v-model="listQuery.importance"
  31 + :placeholder="$t('table.importance')"
  32 + clearable
  33 + filterable
  34 + style="width: 130px"
  35 + class="filter-item"
  36 + >
  37 + <!-- 分组tag筛选 -->
  38 + <el-option-group
  39 + v-for="group in importanceOptions"
  40 + :key="group.label"
  41 + :label="group.label"
  42 + >
  43 + <el-option
  44 + v-for="item in group.options"
  45 + :key="item.value"
  46 + :label="item.label"
  47 + :value="item.value"
  48 + />
  49 + </el-option-group>
  50 + </el-select>
  51 + <!-- 排序 -->
  52 + <el-select
  53 + v-model="listQuery.sort"
  54 + style="width: 140px"
  55 + class="filter-item"
  56 + @change="handleFilter"
  57 + >
  58 + <el-option
  59 + v-for="item in sortOptions"
  60 + :key="item.key"
  61 + :label="item.label"
  62 + :value="item.key"
  63 + />
  64 + </el-select>
  65 + <!-- search按钮 -->
  66 + <el-button
  67 + v-waves
  68 + class="filter-item"
  69 + type="primary"
  70 + icon="el-icon-search"
  71 + @click="handleFilter"
  72 + >
  73 + {{ $t('table.search') }}
  74 + </el-button>
  75 + <!-- add按钮 -->
  76 + <el-button
  77 + class="filter-item"
  78 + style="margin-left: 10px;"
  79 + type="primary"
  80 + icon="el-icon-edit"
  81 + @click="handleCreate"
  82 + >
  83 + {{ $t('table.add') }}
  84 + </el-button>
  85 + <!-- 导出为excel按钮 -->
  86 + <el-button
  87 + v-waves
  88 + :loading="downloadLoading"
  89 + class="filter-item"
  90 + type="primary"
  91 + icon="el-icon-download"
  92 + @click="handleDownload"
  93 + >
  94 + {{ $t('table.export') }}
  95 + </el-button>
  96 + <!-- 是否显示审核人 -->
  97 + <el-checkbox
  98 + v-model="showReviewer"
  99 + class="filter-item"
  100 + style="margin-left:15px;"
  101 + @change="tableKey=tableKey+1"
  102 + >
  103 + {{ $t('table.reviewer') }}
  104 + </el-checkbox>
  105 + </div>
  106 +
  107 + <!-- 表格 -->
3 108 <el-table
  109 + :key="tableKey"
4 110 v-loading="listLoading"
5 111 :data="list"
6 112 border
7 113 fit
8 114 highlight-current-row
9   - style="width: 100%"
  115 + style="width: 100%;"
  116 + @sort-change="sortChange"
10 117 >
  118 + <!-- id列 -->
11 119 <el-table-column
  120 + :label="$t('table.id')"
  121 + prop="id"
  122 + sortable="custom"
12 123 align="center"
13   - label="ID"
14 124 width="80"
  125 + :class-name="getSortClass('id')"
15 126 >
16   - <template slot-scope="scope">
17   - <span>{{ scope.row.id }}</span>
  127 + <template slot-scope="{row}">
  128 + <span>{{ row.id }}</span>
18 129 </template>
19 130 </el-table-column>
20   -
  131 + <!-- date列 -->
21 132 <el-table-column
22   - width="180px"
  133 + :label="$t('table.date')"
  134 + width="150px"
23 135 align="center"
24   - label="Date"
25 136 >
26   - <template slot-scope="scope">
27   - <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
  137 + <template slot-scope="{row}">
  138 + <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
28 139 </template>
29 140 </el-table-column>
30   -
  141 + <!-- title列 -->
  142 + <el-table-column
  143 + :label="$t('table.title')"
  144 + min-width="150px"
  145 + >
  146 + <template slot-scope="{row}">
  147 + <span
  148 + class="link-type"
  149 + @click="handleUpdate(row)"
  150 + >{{ row.title }}</span>
  151 + <el-tag>{{ row.type | typeFilter }}</el-tag>
  152 + </template>
  153 + </el-table-column>
  154 + <!-- author列 -->
31 155 <el-table-column
32   - width="120px"
  156 + :label="$t('table.author')"
  157 + width="110px"
33 158 align="center"
34   - label="Author"
35 159 >
36   - <template slot-scope="scope">
37   - <span>{{ scope.row.author }}</span>
  160 + <template slot-scope="{row}">
  161 + <span>{{ row.author }}</span>
  162 + </template>
  163 + </el-table-column>
  164 + <!-- reviewer列 -->
  165 + <el-table-column
  166 + v-if="showReviewer"
  167 + :label="$t('table.reviewer')"
  168 + width="110px"
  169 + align="center"
  170 + >
  171 + <template slot-scope="{row}">
  172 + <span style="color:red;">{{ row.reviewer }}</span>
38 173 </template>
39 174 </el-table-column>
40   -
41 175 <el-table-column
42   - width="100px"
43   - label="Importance"
  176 + :label="$t('table.importance')"
  177 + width="80px"
44 178 >
45   - <template slot-scope="scope">
  179 + <template slot-scope="{row}">
46 180 <svg-icon
47   - v-for="n in +scope.row.importance"
  181 + v-for="n in +row.importance"
48 182 :key="n"
49 183 icon-class="star"
50 184 class="meta-item__icon"
51 185 />
52 186 </template>
53 187 </el-table-column>
54   -
  188 + <!-- 阅读数列 -->
55 189 <el-table-column
56   - class-name="status-col"
57   - label="Status"
58   - width="110"
  190 + :label="$t('table.readings')"
  191 + align="center"
  192 + width="95"
59 193 >
60 194 <template slot-scope="{row}">
61   - <el-tag :type="row.status | statusFilter">
62   - {{ row.status }}
63   - </el-tag>
  195 + <span
  196 + v-if="row.pageviews"
  197 + class="link-type"
  198 + @click="handleFetchPv(row.pageviews)"
  199 + >{{ row.pageviews }}</span>
  200 + <span v-else>0</span>
64 201 </template>
65 202 </el-table-column>
66 203  
  204 + <!-- 状态列 -->
67 205 <el-table-column
68   - min-width="300px"
69   - label="Title"
  206 + :label="$t('table.status')"
  207 + class-name="status-col"
  208 + width="100"
70 209 >
71 210 <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>
  211 + <el-tag :type="row.status | statusFilter">
  212 + {{ row.status }}
  213 + </el-tag>
78 214 </template>
79 215 </el-table-column>
80   -
  216 + <!-- 操作列 -->
81 217 <el-table-column
  218 + :label="$t('table.actions')"
82 219 align="center"
83   - label="Actions"
84   - width="120"
85   - >
86   - <template slot-scope="scope">
87   - <router-link :to="'/example/edit/'+scope.row.id">
88   - <el-button
89   - type="primary"
90   - size="small"
91   - icon="el-icon-edit"
92   - >
93   - Edit
94   - </el-button>
95   - </router-link>
  220 + width="230"
  221 + class-name="small-padding fixed-width"
  222 + >
  223 + <template slot-scope="{row,$index}">
  224 + <el-button
  225 + type="primary"
  226 + size="mini"
  227 + @click="handleUpdate(row)"
  228 + >
  229 + {{ $t('table.edit') }}
  230 + </el-button>
  231 + <el-button
  232 + v-if="row.status!='published'"
  233 + size="mini"
  234 + type="success"
  235 + @click="handleModifyStatus(row,'published')"
  236 + >
  237 + {{ $t('table.publish') }}
  238 + </el-button>
  239 + <el-button
  240 + v-if="row.status!='draft'"
  241 + size="mini"
  242 + @click="handleModifyStatus(row,'draft')"
  243 + >
  244 + {{ $t('table.draft') }}
  245 + </el-button>
  246 + <el-button
  247 + v-if="row.status!='deleted'"
  248 + size="mini"
  249 + type="danger"
  250 + @click="handleDelete(row,$index)"
  251 + >
  252 + {{ $t('table.delete') }}
  253 + </el-button>
96 254 </template>
97 255 </el-table-column>
98 256 </el-table>
99 257  
  258 + <!-- 分页器 -->
100 259 <pagination
101 260 v-show="total>0"
102 261 :total="total"
... ... @@ -104,16 +263,160 @@
104 263 :limit.sync="listQuery.limit"
105 264 @pagination="getList"
106 265 />
  266 +
  267 + <el-dialog
  268 + :title="textMap[dialogStatus]"
  269 + :visible.sync="dialogFormVisible"
  270 + >
  271 + <el-form
  272 + ref="dataForm"
  273 + :rules="rules"
  274 + :model="temp"
  275 + label-position="left"
  276 + label-width="70px"
  277 + style="width: 400px; margin-left:50px;"
  278 + >
  279 + <el-form-item
  280 + :label="$t('table.type')"
  281 + prop="type"
  282 + >
  283 + <el-select
  284 + v-model="temp.type"
  285 + class="filter-item"
  286 + placeholder="Please select"
  287 + >
  288 + <el-option
  289 + v-for="item in calendarTypeOptions"
  290 + :key="item.key"
  291 + :label="item.display_name"
  292 + :value="item.key"
  293 + />
  294 + </el-select>
  295 + </el-form-item>
  296 + <el-form-item
  297 + :label="$t('table.date')"
  298 + prop="timestamp"
  299 + >
  300 + <el-date-picker
  301 + v-model="temp.timestamp"
  302 + type="datetime"
  303 + placeholder="Please pick a date"
  304 + />
  305 + </el-form-item>
  306 + <el-form-item
  307 + :label="$t('table.title')"
  308 + prop="title"
  309 + >
  310 + <el-input v-model="temp.title" />
  311 + </el-form-item>
  312 + <el-form-item :label="$t('table.status')">
  313 + <el-select
  314 + v-model="temp.status"
  315 + class="filter-item"
  316 + placeholder="Please select"
  317 + >
  318 + <el-option
  319 + v-for="item in statusOptions"
  320 + :key="item"
  321 + :label="item"
  322 + :value="item"
  323 + />
  324 + </el-select>
  325 + </el-form-item>
  326 + <el-form-item :label="$t('table.importance')">
  327 + <el-rate
  328 + v-model="temp.importance"
  329 + :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
  330 + :max="3"
  331 + style="margin-top:8px;"
  332 + />
  333 + </el-form-item>
  334 + <el-form-item :label="$t('table.remark')">
  335 + <el-input
  336 + v-model="temp.remark"
  337 + :autosize="{ minRows: 2, maxRows: 4}"
  338 + type="textarea"
  339 + placeholder="Please input"
  340 + />
  341 + </el-form-item>
  342 + </el-form>
  343 + <div
  344 + slot="footer"
  345 + class="dialog-footer"
  346 + >
  347 + <el-button @click="dialogFormVisible = false">
  348 + {{ $t('table.cancel') }}
  349 + </el-button>
  350 + <el-button
  351 + type="primary"
  352 + @click="dialogStatus==='create'?createData():updateData()"
  353 + >
  354 + {{ $t('table.confirm') }}
  355 + </el-button>
  356 + </div>
  357 + </el-dialog>
  358 +
  359 + <el-dialog
  360 + :visible.sync="dialogPvVisible"
  361 + title="Reading statistics"
  362 + >
  363 + <el-table
  364 + :data="pvData"
  365 + border
  366 + fit
  367 + highlight-current-row
  368 + style="width: 100%"
  369 + >
  370 + <el-table-column
  371 + prop="key"
  372 + label="Channel"
  373 + />
  374 + <el-table-column
  375 + prop="pv"
  376 + label="Pv"
  377 + />
  378 + </el-table>
  379 + <span
  380 + slot="footer"
  381 + class="dialog-footer"
  382 + >
  383 + <el-button
  384 + type="primary"
  385 + @click="dialogPvVisible = false"
  386 + >{{ $t('table.confirm') }}</el-button>
  387 + </span>
  388 + </el-dialog>
107 389 </div>
108 390 </template>
109 391  
110 392 <script>
111   -import { fetchList } from '@/api/article'
112   -import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
  393 +import {
  394 + fetchList,
  395 + fetchPv,
  396 + createArticle,
  397 + updateArticle
  398 +} from '@/api/article'
  399 +import waves from '@/directive/waves' // waves directive
  400 +import { parseTime } from '@/utils'
  401 +import Pagination from '@/components/Pagination' // secondary package based on el-pagination
  402 +
  403 +const calendarTypeOptions = [
  404 + { key: 'CN', display_name: '成镜' },
  405 + { key: 'US', display_name: '镜片' },
  406 + { key: 'JP', display_name: '隐形眼镜' },
  407 + { key: 'EU', display_name: '特种镜' }
  408 +]
  409 +
  410 +// arr to obj, such as { CN : "China", US : "USA" }
  411 +const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
  412 + acc[cur.key] = cur.display_name
  413 + return acc
  414 +}, {})
113 415  
114 416 export default {
115   - name: 'ArticleList',
  417 + name: 'ComplexTable',
116 418 components: { Pagination },
  419 + directives: { waves },
117 420 filters: {
118 421 statusFilter(status) {
119 422 const statusMap = {
... ... @@ -122,17 +425,103 @@ export default {
122 425 deleted: 'danger'
123 426 }
124 427 return statusMap[status]
  428 + },
  429 + typeFilter(type) {
  430 + return calendarTypeKeyValue[type]
125 431 }
126 432 },
127 433 data() {
128 434 return {
  435 + tableKey: 0,
129 436 list: null,
130 437 total: 0,
131 438 listLoading: true,
132 439 listQuery: {
133 440 page: 1,
134   - limit: 20
135   - }
  441 + limit: 20,
  442 + importance: undefined,
  443 + title: undefined,
  444 + type: undefined,
  445 + sort: '+id'
  446 + },
  447 + importanceOptions: [{
  448 + label: '款式索引',
  449 + options: [{
  450 + value: 'young',
  451 + label: '青春学子风'
  452 + }, {
  453 + value: 'leisure',
  454 + label: '休闲'
  455 + }, {
  456 + value: 'business',
  457 + label: '商务'
  458 + }, {
  459 + value: 'Retro',
  460 + label: '复古'
  461 + }, {
  462 + value: 'fashion',
  463 + label: '时尚'
  464 + }]
  465 + }, {
  466 + label: '颜色',
  467 + options: [{
  468 + value: 'golden',
  469 + label: '金色'
  470 + }, {
  471 + value: 'silver',
  472 + label: '银色'
  473 + }, {
  474 + value: 'black',
  475 + label: '黑色'
  476 + }, {
  477 + value: 'multicolour',
  478 + label: '彩色'
  479 + }, {
  480 + value: 'gun',
  481 + label: '枪色'
  482 + }]
  483 + }],
  484 + calendarTypeOptions,
  485 + sortOptions: [
  486 + { label: 'ID Ascending', key: '+id' },
  487 + { label: 'ID Descending', key: '-id' }
  488 + ],
  489 + statusOptions: ['published', 'draft', 'deleted'],
  490 + showReviewer: false,
  491 + temp: {
  492 + id: undefined,
  493 + importance: 1,
  494 + remark: '',
  495 + timestamp: new Date(),
  496 + title: '',
  497 + type: '',
  498 + status: 'published'
  499 + },
  500 + dialogFormVisible: false,
  501 + dialogStatus: '',
  502 + textMap: {
  503 + update: 'Edit',
  504 + create: 'Create'
  505 + },
  506 + dialogPvVisible: false,
  507 + pvData: [],
  508 + rules: {
  509 + type: [
  510 + { required: true, message: 'type is required', trigger: 'change' }
  511 + ],
  512 + timestamp: [
  513 + {
  514 + type: 'date',
  515 + required: true,
  516 + message: 'timestamp is required',
  517 + trigger: 'change'
  518 + }
  519 + ],
  520 + title: [
  521 + { required: true, message: 'title is required', trigger: 'blur' }
  522 + ]
  523 + },
  524 + downloadLoading: false
136 525 }
137 526 },
138 527 created() {
... ... @@ -144,20 +533,153 @@ export default {
144 533 fetchList(this.listQuery).then(response => {
145 534 this.list = response.data.items
146 535 this.total = response.data.total
147   - this.listLoading = false
  536 +
  537 + // Just to simulate the time of the request
  538 + setTimeout(() => {
  539 + this.listLoading = false
  540 + }, 1.5 * 1000)
  541 + })
  542 + },
  543 + handleFilter() {
  544 + this.listQuery.page = 1
  545 + this.getList()
  546 + },
  547 + handleModifyStatus(row, status) {
  548 + this.$message({
  549 + message: '操作成功',
  550 + type: 'success'
  551 + })
  552 + row.status = status
  553 + },
  554 + sortChange(data) {
  555 + const { prop, order } = data
  556 + if (prop === 'id') {
  557 + this.sortByID(order)
  558 + }
  559 + },
  560 + sortByID(order) {
  561 + if (order === 'ascending') {
  562 + this.listQuery.sort = '+id'
  563 + } else {
  564 + this.listQuery.sort = '-id'
  565 + }
  566 + this.handleFilter()
  567 + },
  568 + resetTemp() {
  569 + this.temp = {
  570 + id: undefined,
  571 + importance: 1,
  572 + remark: '',
  573 + timestamp: new Date(),
  574 + title: '',
  575 + status: 'published',
  576 + type: ''
  577 + }
  578 + },
  579 + handleCreate() {
  580 + this.resetTemp()
  581 + this.dialogStatus = 'create'
  582 + this.dialogFormVisible = true
  583 + this.$nextTick(() => {
  584 + this.$refs['dataForm'].clearValidate()
148 585 })
  586 + },
  587 + createData() {
  588 + this.$refs['dataForm'].validate(valid => {
  589 + if (valid) {
  590 + this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
  591 + this.temp.author = '秀野堂主'
  592 + createArticle(this.temp).then(() => {
  593 + this.list.unshift(this.temp)
  594 + this.dialogFormVisible = false
  595 + this.$notify({
  596 + title: '成功',
  597 + message: '创建成功',
  598 + type: 'success',
  599 + duration: 2000
  600 + })
  601 + })
  602 + }
  603 + })
  604 + },
  605 + handleUpdate(row) {
  606 + this.temp = Object.assign({}, row) // copy obj
  607 + this.temp.timestamp = new Date(this.temp.timestamp)
  608 + this.dialogStatus = 'update'
  609 + this.dialogFormVisible = true
  610 + this.$nextTick(() => {
  611 + this.$refs['dataForm'].clearValidate()
  612 + })
  613 + },
  614 + updateData() {
  615 + this.$refs['dataForm'].validate(valid => {
  616 + if (valid) {
  617 + const tempData = Object.assign({}, this.temp)
  618 + tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
  619 + updateArticle(tempData).then(() => {
  620 + const index = this.list.findIndex(v => v.id === this.temp.id)
  621 + this.list.splice(index, 1, this.temp)
  622 + this.dialogFormVisible = false
  623 + this.$notify({
  624 + title: '成功',
  625 + message: '更新成功',
  626 + type: 'success',
  627 + duration: 2000
  628 + })
  629 + })
  630 + }
  631 + })
  632 + },
  633 + handleDelete(row, index) {
  634 + this.$notify({
  635 + title: '成功',
  636 + message: '删除成功',
  637 + type: 'success',
  638 + duration: 2000
  639 + })
  640 + this.list.splice(index, 1)
  641 + },
  642 + handleFetchPv(pv) {
  643 + fetchPv(pv).then(response => {
  644 + this.pvData = response.data.pvData
  645 + this.dialogPvVisible = true
  646 + })
  647 + },
  648 + handleDownload() {
  649 + this.downloadLoading = true
  650 + import('@/vendor/Export2Excel').then(excel => {
  651 + const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
  652 + const filterVal = [
  653 + 'timestamp',
  654 + 'title',
  655 + 'type',
  656 + 'importance',
  657 + 'status'
  658 + ]
  659 + const data = this.formatJson(filterVal)
  660 + excel.export_json_to_excel({
  661 + header: tHeader,
  662 + data,
  663 + filename: 'table-list'
  664 + })
  665 + this.downloadLoading = false
  666 + })
  667 + },
  668 + formatJson(filterVal) {
  669 + return this.list.map(v =>
  670 + filterVal.map(j => {
  671 + if (j === 'timestamp') {
  672 + return parseTime(v[j])
  673 + } else {
  674 + return v[j]
  675 + }
  676 + })
  677 + )
  678 + },
  679 + getSortClass: function(key) {
  680 + const sort = this.listQuery.sort
  681 + return sort === `+${key}` ? 'ascending' : 'descending'
149 682 }
150 683 }
151 684 }
152 685 </script>
153   -
154   -<style scoped>
155   -.edit-input {
156   - padding-right: 100px;
157   -}
158   -.cancel-btn {
159   - position: absolute;
160   - right: 15px;
161   - top: 10px;
162   -}
163   -</style>
... ...