Commit f3ec4dd955193ce14f27df0ebc15c1a078b2f546
1 parent
96898eae45
Exists in
master
auto commit the code by alias command
Showing
4 changed files
with
11 additions
and
792 deletions
Show diff stats
src/router/index.js
| 1 | import Vue from 'vue' | 1 | import Vue from 'vue' |
| 2 | import Router from 'vue-router' | 2 | import Router from 'vue-router' |
| 3 | 3 | ||
| 4 | Vue.use(Router) | 4 | Vue.use(Router) |
| 5 | 5 | ||
| 6 | /* Layout */ | 6 | /* Layout */ |
| 7 | import Layout from '@/layout' | 7 | import Layout from '@/layout' |
| 8 | 8 | ||
| 9 | /* Router Modules */ | 9 | /* Router Modules */ |
| 10 | // import componentsRouter from './modules/components' | 10 | // import componentsRouter from './modules/components' |
| 11 | // import chartsRouter from './modules/charts' | 11 | // import chartsRouter from './modules/charts' |
| 12 | // import tableRouter from './modules/table' | 12 | // import tableRouter from './modules/table' |
| 13 | // import nestedRouter from './modules/nested' | 13 | // import nestedRouter from './modules/nested' |
| 14 | import userRouter from './modules/user' | 14 | import userRouter from './modules/user' |
| 15 | import systemRouter from './modules/system' | 15 | import systemRouter from './modules/system' |
| 16 | import prodRouter from './modules/prod' | 16 | import prodRouter from './modules/prod' |
| 17 | import orderRouter from './modules/order' | ||
| 17 | import metaRouter from './modules/meta' | 18 | import metaRouter from './modules/meta' |
| 18 | import sitesRouter from './modules/sites' | 19 | import sitesRouter from './modules/sites' |
| 19 | 20 | ||
| 20 | /** | 21 | /** |
| 21 | * Note: sub-menu only appear when route children.length >= 1 | 22 | * Note: sub-menu only appear when route children.length >= 1 |
| 22 | * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html | 23 | * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html |
| 23 | * | 24 | * |
| 24 | * hidden: true if set true, item will not show in the sidebar(default is false) | 25 | * hidden: true if set true, item will not show in the sidebar(default is false) |
| 25 | * alwaysShow: true if set true, will always show the root menu | 26 | * alwaysShow: true if set true, will always show the root menu |
| 26 | * if not set alwaysShow, when item has more than one children route, | 27 | * if not set alwaysShow, when item has more than one children route, |
| 27 | * it will becomes nested mode, otherwise not show the root menu | 28 | * it will becomes nested mode, otherwise not show the root menu |
| 28 | * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb | 29 | * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb |
| 29 | * name:'router-name' the name is used by <keep-alive> (must set!!!) | 30 | * name:'router-name' the name is used by <keep-alive> (must set!!!) |
| 30 | * meta : { | 31 | * meta : { |
| 31 | roles: ['admin','assistant','runner', 'shoper'] control the page roles (you can set multiple roles) | 32 | roles: ['admin','assistant','runner', 'shoper'] control the page roles (you can set multiple roles) |
| 32 | title: 'title' the name show in sidebar and breadcrumb (recommend set) | 33 | title: 'title' the name show in sidebar and breadcrumb (recommend set) |
| 33 | icon: 'svg-name' the icon show in the sidebar | 34 | icon: 'svg-name' the icon show in the sidebar |
| 34 | noCache: true if set true, the page will no be cached(default is false) | 35 | noCache: true if set true, the page will no be cached(default is false) |
| 35 | affix: true if set true, the tag will affix in the tags-view | 36 | affix: true if set true, the tag will affix in the tags-view |
| 36 | breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) | 37 | breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) |
| 37 | activeMenu: '/example/list' if set path, the sidebar will highlight the path you set | 38 | activeMenu: '/example/list' if set path, the sidebar will highlight the path you set |
| 38 | } | 39 | } |
| 39 | */ | 40 | */ |
| 40 | 41 | ||
| 41 | /** | 42 | /** |
| 42 | * constantRoutes | 43 | * constantRoutes |
| 43 | * a base page that does not have permission requirements | 44 | * a base page that does not have permission requirements |
| 44 | * all roles can be accessed | 45 | * all roles can be accessed |
| 45 | */ | 46 | */ |
| 46 | export const constantRoutes = [{ | 47 | export const constantRoutes = [{ |
| 47 | path: '/redirect', | 48 | path: '/redirect', |
| 48 | component: Layout, | 49 | component: Layout, |
| 49 | hidden: true, | 50 | hidden: true, |
| 50 | children: [{ | 51 | children: [{ |
| 51 | path: '/redirect/:path*', | 52 | path: '/redirect/:path*', |
| 52 | component: () => import('@/views/redirect/index') | 53 | component: () => import('@/views/redirect/index') |
| 53 | }] | 54 | }] |
| 54 | }, | 55 | }, |
| 55 | { | 56 | { |
| 56 | path: '/login', | 57 | path: '/login', |
| 57 | component: () => import('@/views/login/index'), | 58 | component: () => import('@/views/login/index'), |
| 58 | hidden: true | 59 | hidden: true |
| 59 | }, | 60 | }, |
| 60 | { | 61 | { |
| 61 | path: '/auth-redirect', | 62 | path: '/auth-redirect', |
| 62 | component: () => import('@/views/login/auth-redirect'), | 63 | component: () => import('@/views/login/auth-redirect'), |
| 63 | hidden: true | 64 | hidden: true |
| 64 | }, | 65 | }, |
| 65 | { | 66 | { |
| 66 | path: '/404', | 67 | path: '/404', |
| 67 | component: () => import('@/views/error-page/404'), | 68 | component: () => import('@/views/error-page/404'), |
| 68 | hidden: true | 69 | hidden: true |
| 69 | }, | 70 | }, |
| 70 | { | 71 | { |
| 71 | path: '/500', | 72 | path: '/500', |
| 72 | component: () => import('@/views/error-page/500'), | 73 | component: () => import('@/views/error-page/500'), |
| 73 | hidden: true | 74 | hidden: true |
| 74 | }, | 75 | }, |
| 75 | { | 76 | { |
| 76 | path: '/401', | 77 | path: '/401', |
| 77 | component: () => import('@/views/error-page/401'), | 78 | component: () => import('@/views/error-page/401'), |
| 78 | hidden: true | 79 | hidden: true |
| 79 | }, | 80 | }, |
| 80 | { | 81 | { |
| 81 | path: '/', | 82 | path: '/', |
| 82 | component: Layout, | 83 | component: Layout, |
| 83 | redirect: '/dashboard', | 84 | redirect: '/dashboard', |
| 84 | children: [{ | 85 | children: [{ |
| 85 | path: 'dashboard', | 86 | path: 'dashboard', |
| 86 | component: () => import('@/views/dashboard/index'), | 87 | component: () => import('@/views/dashboard/index'), |
| 87 | name: 'Dashboard', | 88 | name: 'Dashboard', |
| 88 | meta: { | 89 | meta: { |
| 89 | title: 'dashboard', | 90 | title: 'dashboard', |
| 90 | icon: 'dashboard', | 91 | icon: 'dashboard', |
| 91 | affix: true | 92 | affix: true |
| 92 | } | 93 | } |
| 93 | }] | 94 | }] |
| 94 | }, | 95 | }, |
| 95 | // { | 96 | // { |
| 96 | // path: '/documentation', | 97 | // path: '/documentation', |
| 97 | // component: Layout, | 98 | // component: Layout, |
| 98 | // children: [ | 99 | // children: [ |
| 99 | // { | 100 | // { |
| 100 | // path: 'index', | 101 | // path: 'index', |
| 101 | // component: () => import('@/views/documentation/index'), | 102 | // component: () => import('@/views/documentation/index'), |
| 102 | // name: 'Documentation', | 103 | // name: 'Documentation', |
| 103 | // meta: { title: 'documentation', icon: 'documentation', affix: true } | 104 | // meta: { title: 'documentation', icon: 'documentation', affix: true } |
| 104 | // } | 105 | // } |
| 105 | // ] | 106 | // ] |
| 106 | // }, | 107 | // }, |
| 107 | { | 108 | { |
| 108 | path: '/guide', | 109 | path: '/guide', |
| 109 | component: Layout, | 110 | component: Layout, |
| 110 | redirect: '/guide/index', | 111 | redirect: '/guide/index', |
| 111 | children: [{ | 112 | children: [{ |
| 112 | path: 'index', | 113 | path: 'index', |
| 113 | component: () => import('@/views/guide/index'), | 114 | component: () => import('@/views/guide/index'), |
| 114 | name: 'Guide', | 115 | name: 'Guide', |
| 115 | meta: { | 116 | meta: { |
| 116 | title: 'guide', | 117 | title: 'guide', |
| 117 | icon: 'guide', | 118 | icon: 'guide', |
| 118 | noCache: true | 119 | noCache: true |
| 119 | } | 120 | } |
| 120 | }] | 121 | }] |
| 121 | } | 122 | } |
| 122 | // { | 123 | // { |
| 123 | // path: '/profile', | 124 | // path: '/profile', |
| 124 | // component: Layout, | 125 | // component: Layout, |
| 125 | // redirect: '/profile/index', | 126 | // redirect: '/profile/index', |
| 126 | // hidden: true, | 127 | // hidden: true, |
| 127 | // children: [ | 128 | // children: [ |
| 128 | // { | 129 | // { |
| 129 | // path: 'index', | 130 | // path: 'index', |
| 130 | // component: () => import('@/views/profile/index'), | 131 | // component: () => import('@/views/profile/index'), |
| 131 | // name: 'Profile', | 132 | // name: 'Profile', |
| 132 | // meta: { title: 'profile', icon: 'user', noCache: true } | 133 | // meta: { title: 'profile', icon: 'user', noCache: true } |
| 133 | // } | 134 | // } |
| 134 | // ] | 135 | // ] |
| 135 | // } | 136 | // } |
| 136 | ] | 137 | ] |
| 137 | 138 | ||
| 138 | /** | 139 | /** |
| 139 | * asyncRoutes | 140 | * asyncRoutes |
| 140 | * the routes that need to be dynamically loaded based on user roles | 141 | * the routes that need to be dynamically loaded based on user roles |
| 141 | */ | 142 | */ |
| 142 | export const asyncRoutes = [ | 143 | export const asyncRoutes = [ |
| 143 | // { | 144 | // { |
| 144 | // path: '/permission', | 145 | // path: '/permission', |
| 145 | // component: Layout, | 146 | // component: Layout, |
| 146 | // redirect: '/permission/page', | 147 | // redirect: '/permission/page', |
| 147 | // alwaysShow: true, // will always show the root menu | 148 | // alwaysShow: true, // will always show the root menu |
| 148 | // name: 'Permission', | 149 | // name: 'Permission', |
| 149 | // meta: { | 150 | // meta: { |
| 150 | // title: 'permission', | 151 | // title: 'permission', |
| 151 | // icon: 'lock', | 152 | // icon: 'lock', |
| 152 | // roles: ['admin', 'assistant'] // you can set roles in root nav | 153 | // roles: ['admin', 'assistant'] // you can set roles in root nav |
| 153 | // }, | 154 | // }, |
| 154 | // children: [ | 155 | // children: [ |
| 155 | // { | 156 | // { |
| 156 | // path: 'page', | 157 | // path: 'page', |
| 157 | // component: () => import('@/views/permission/page'), | 158 | // component: () => import('@/views/permission/page'), |
| 158 | // name: 'PagePermission', | 159 | // name: 'PagePermission', |
| 159 | // meta: { | 160 | // meta: { |
| 160 | // title: 'pagePermission', | 161 | // title: 'pagePermission', |
| 161 | // roles: ['admin','assistant'] // or you can only set roles in sub nav | 162 | // roles: ['admin','assistant'] // or you can only set roles in sub nav |
| 162 | // } | 163 | // } |
| 163 | // }, | 164 | // }, |
| 164 | // { | 165 | // { |
| 165 | // path: 'directive', | 166 | // path: 'directive', |
| 166 | // component: () => import('@/views/permission/directive'), | 167 | // component: () => import('@/views/permission/directive'), |
| 167 | // name: 'DirectivePermission', | 168 | // name: 'DirectivePermission', |
| 168 | // meta: { | 169 | // meta: { |
| 169 | // title: 'directivePermission', | 170 | // title: 'directivePermission', |
| 170 | // roles: ['admin', 'shoper'] | 171 | // roles: ['admin', 'shoper'] |
| 171 | // // if do not set roles, means: this page does not require permission | 172 | // // if do not set roles, means: this page does not require permission |
| 172 | // } | 173 | // } |
| 173 | // }, | 174 | // }, |
| 174 | // { | 175 | // { |
| 175 | // path: 'role', | 176 | // path: 'role', |
| 176 | // component: () => import('@/views/permission/role'), | 177 | // component: () => import('@/views/permission/role'), |
| 177 | // name: 'RolePermission', | 178 | // name: 'RolePermission', |
| 178 | // meta: { | 179 | // meta: { |
| 179 | // title: 'rolePermission', | 180 | // title: 'rolePermission', |
| 180 | // roles: ['admin', 'runner'] | 181 | // roles: ['admin', 'runner'] |
| 181 | // } | 182 | // } |
| 182 | // } | 183 | // } |
| 183 | // ] | 184 | // ] |
| 184 | // }, | 185 | // }, |
| 185 | // tableRouter, | 186 | // tableRouter, |
| 186 | metaRouter, | 187 | metaRouter, |
| 187 | userRouter, | 188 | userRouter, |
| 188 | prodRouter, | 189 | prodRouter, |
| 189 | { | 190 | orderRouter, |
| 190 | path: '/orders', | ||
| 191 | component: Layout, | ||
| 192 | redirect: '/order/page', | ||
| 193 | alwaysShow: true, // will always show the root menu | ||
| 194 | name: 'Order', | ||
| 195 | meta: { | ||
| 196 | title: 'orders', | ||
| 197 | icon: 'shopping', | ||
| 198 | roles: ['admin', 'assistant', 'runner', 'shoper'] // you can set roles in root nav | ||
| 199 | }, | ||
| 200 | children: [{ | ||
| 201 | path: 'page', | ||
| 202 | component: () => import('@/views/permission/page'), | ||
| 203 | name: 'OrderList', | ||
| 204 | meta: { | ||
| 205 | title: 'OrderList', | ||
| 206 | roles: ['admin', 'assistant', 'runner', 'shoper'] // or you can only set roles in sub nav | ||
| 207 | } | ||
| 208 | }, | ||
| 209 | { | ||
| 210 | path: 'defined', | ||
| 211 | component: () => import('@/views/permission/directive'), | ||
| 212 | name: 'OrderDefiend', | ||
| 213 | meta: { | ||
| 214 | title: 'OrderDefiend', | ||
| 215 | roles: ['admin', 'assistant', 'runner', 'shoper'] | ||
| 216 | // if do not set roles, means: this page does not require permission | ||
| 217 | } | ||
| 218 | } | ||
| 219 | ] | ||
| 220 | }, | ||
| 221 | sitesRouter, | 191 | sitesRouter, |
| 222 | 192 | ||
| 223 | // { | 193 | // { |
| 224 | // path: '/icon', | 194 | // path: '/icon', |
| 225 | // component: Layout, | 195 | // component: Layout, |
| 226 | // children: [ | 196 | // children: [ |
| 227 | // { | 197 | // { |
| 228 | // path: 'index', | 198 | // path: 'index', |
| 229 | // component: () => import('@/views/icons/index'), | 199 | // component: () => import('@/views/icons/index'), |
| 230 | // name: 'Icons', | 200 | // name: 'Icons', |
| 231 | // meta: { title: 'icons', icon: 'icon', noCache: true } | 201 | // meta: { title: 'icons', icon: 'icon', noCache: true } |
| 232 | // } | 202 | // } |
| 233 | // ] | 203 | // ] |
| 234 | // }, | 204 | // }, |
| 235 | systemRouter, | 205 | systemRouter, |
| 236 | /** when your routing map is too long, you can split it into small modules **/ | 206 | /** when your routing map is too long, you can split it into small modules **/ |
| 237 | // componentsRouter, | 207 | // componentsRouter, |
| 238 | // chartsRouter, | 208 | // chartsRouter, |
| 239 | // nestedRouter, | 209 | // nestedRouter, |
| 240 | // tableRouter, | 210 | // tableRouter, |
| 241 | 211 | ||
| 242 | // { | 212 | // { |
| 243 | // path: '/example', | 213 | // path: '/example', |
| 244 | // component: Layout, | 214 | // component: Layout, |
| 245 | // redirect: '/example/list', | 215 | // redirect: '/example/list', |
| 246 | // name: 'Example', | 216 | // name: 'Example', |
| 247 | // meta: { | 217 | // meta: { |
| 248 | // title: 'example', | 218 | // title: 'example', |
| 249 | // icon: 'example' | 219 | // icon: 'example' |
| 250 | // }, | 220 | // }, |
| 251 | // children: [ | 221 | // children: [ |
| 252 | // { | 222 | // { |
| 253 | // path: 'create', | 223 | // path: 'create', |
| 254 | // component: () => import('@/views/example/create'), | 224 | // component: () => import('@/views/example/create'), |
| 255 | // name: 'CreateArticle', | 225 | // name: 'CreateArticle', |
| 256 | // meta: { title: 'createArticle', icon: 'edit' } | 226 | // meta: { title: 'createArticle', icon: 'edit' } |
| 257 | // }, | 227 | // }, |
| 258 | // { | 228 | // { |
| 259 | // path: 'edit/:id(\\d+)', | 229 | // path: 'edit/:id(\\d+)', |
| 260 | // component: () => import('@/views/example/edit'), | 230 | // component: () => import('@/views/example/edit'), |
| 261 | // name: 'EditArticle', | 231 | // name: 'EditArticle', |
| 262 | // meta: { title: 'editArticle', noCache: true, activeMenu: '/example/list' }, | 232 | // meta: { title: 'editArticle', noCache: true, activeMenu: '/example/list' }, |
| 263 | // hidden: true | 233 | // hidden: true |
| 264 | // }, | 234 | // }, |
| 265 | // { | 235 | // { |
| 266 | // path: 'list', | 236 | // path: 'list', |
| 267 | // component: () => import('@/views/example/list'), | 237 | // component: () => import('@/views/example/list'), |
| 268 | // name: 'ArticleList', | 238 | // name: 'ArticleList', |
| 269 | // meta: { title: 'articleList', icon: 'list' } | 239 | // meta: { title: 'articleList', icon: 'list' } |
| 270 | // } | 240 | // } |
| 271 | // ] | 241 | // ] |
| 272 | // }, | 242 | // }, |
| 273 | 243 | ||
| 274 | // { | 244 | // { |
| 275 | // path: '/tab', | 245 | // path: '/tab', |
| 276 | // component: Layout, | 246 | // component: Layout, |
| 277 | // children: [ | 247 | // children: [ |
| 278 | // { | 248 | // { |
| 279 | // path: 'index', | 249 | // path: 'index', |
| 280 | // component: () => import('@/views/tab/index'), | 250 | // component: () => import('@/views/tab/index'), |
| 281 | // name: 'Tab', | 251 | // name: 'Tab', |
| 282 | // meta: { title: 'tab', icon: 'tab' } | 252 | // meta: { title: 'tab', icon: 'tab' } |
| 283 | // } | 253 | // } |
| 284 | // ] | 254 | // ] |
| 285 | // }, | 255 | // }, |
| 286 | 256 | ||
| 287 | // { | 257 | // { |
| 288 | // path: '/error', | 258 | // path: '/error', |
| 289 | // component: Layout, | 259 | // component: Layout, |
| 290 | // redirect: 'noRedirect', | 260 | // redirect: 'noRedirect', |
| 291 | // name: 'ErrorPages', | 261 | // name: 'ErrorPages', |
| 292 | // meta: { | 262 | // meta: { |
| 293 | // title: 'errorPages', | 263 | // title: 'errorPages', |
| 294 | // icon: '404' | 264 | // icon: '404' |
| 295 | // }, | 265 | // }, |
| 296 | // children: [ | 266 | // children: [ |
| 297 | // { | 267 | // { |
| 298 | // path: '401', | 268 | // path: '401', |
| 299 | // component: () => import('@/views/error-page/401'), | 269 | // component: () => import('@/views/error-page/401'), |
| 300 | // name: 'Page401', | 270 | // name: 'Page401', |
| 301 | // meta: { title: 'page401', noCache: true } | 271 | // meta: { title: 'page401', noCache: true } |
| 302 | // }, | 272 | // }, |
| 303 | // { | 273 | // { |
| 304 | // path: '404', | 274 | // path: '404', |
| 305 | // component: () => import('@/views/error-page/404'), | 275 | // component: () => import('@/views/error-page/404'), |
| 306 | // name: 'Page404', | 276 | // name: 'Page404', |
| 307 | // meta: { title: 'page404', noCache: true } | 277 | // meta: { title: 'page404', noCache: true } |
| 308 | // } | 278 | // } |
| 309 | // ] | 279 | // ] |
| 310 | // }, | 280 | // }, |
| 311 | 281 | ||
| 312 | // { | 282 | // { |
| 313 | // path: '/error-log', | 283 | // path: '/error-log', |
| 314 | // component: Layout, | 284 | // component: Layout, |
| 315 | // children: [ | 285 | // children: [ |
| 316 | // { | 286 | // { |
| 317 | // path: 'log', | 287 | // path: 'log', |
| 318 | // component: () => import('@/views/error-log/index'), | 288 | // component: () => import('@/views/error-log/index'), |
| 319 | // name: 'ErrorLog', | 289 | // name: 'ErrorLog', |
| 320 | // meta: { title: 'errorLog', icon: 'bug' } | 290 | // meta: { title: 'errorLog', icon: 'bug' } |
| 321 | // } | 291 | // } |
| 322 | // ] | 292 | // ] |
| 323 | // }, | 293 | // }, |
| 324 | 294 | ||
| 325 | // { | 295 | // { |
| 326 | // path: '/excel', | 296 | // path: '/excel', |
| 327 | // component: Layout, | 297 | // component: Layout, |
| 328 | // redirect: '/excel/export-excel', | 298 | // redirect: '/excel/export-excel', |
| 329 | // name: 'Excel', | 299 | // name: 'Excel', |
| 330 | // meta: { | 300 | // meta: { |
| 331 | // title: 'excel', | 301 | // title: 'excel', |
| 332 | // icon: 'excel' | 302 | // icon: 'excel' |
| 333 | // }, | 303 | // }, |
| 334 | // children: [ | 304 | // children: [ |
| 335 | // { | 305 | // { |
| 336 | // path: 'export-excel', | 306 | // path: 'export-excel', |
| 337 | // component: () => import('@/views/excel/export-excel'), | 307 | // component: () => import('@/views/excel/export-excel'), |
| 338 | // name: 'ExportExcel', | 308 | // name: 'ExportExcel', |
| 339 | // meta: { title: 'exportExcel' } | 309 | // meta: { title: 'exportExcel' } |
| 340 | // }, | 310 | // }, |
| 341 | // { | 311 | // { |
| 342 | // path: 'export-selected-excel', | 312 | // path: 'export-selected-excel', |
| 343 | // component: () => import('@/views/excel/select-excel'), | 313 | // component: () => import('@/views/excel/select-excel'), |
| 344 | // name: 'SelectExcel', | 314 | // name: 'SelectExcel', |
| 345 | // meta: { title: 'selectExcel' } | 315 | // meta: { title: 'selectExcel' } |
| 346 | // }, | 316 | // }, |
| 347 | // { | 317 | // { |
| 348 | // path: 'export-merge-header', | 318 | // path: 'export-merge-header', |
| 349 | // component: () => import('@/views/excel/merge-header'), | 319 | // component: () => import('@/views/excel/merge-header'), |
| 350 | // name: 'MergeHeader', | 320 | // name: 'MergeHeader', |
| 351 | // meta: { title: 'mergeHeader' } | 321 | // meta: { title: 'mergeHeader' } |
| 352 | // }, | 322 | // }, |
| 353 | // { | 323 | // { |
| 354 | // path: 'upload-excel', | 324 | // path: 'upload-excel', |
| 355 | // component: () => import('@/views/excel/upload-excel'), | 325 | // component: () => import('@/views/excel/upload-excel'), |
| 356 | // name: 'UploadExcel', | 326 | // name: 'UploadExcel', |
| 357 | // meta: { title: 'uploadExcel' } | 327 | // meta: { title: 'uploadExcel' } |
| 358 | // } | 328 | // } |
| 359 | // ] | 329 | // ] |
| 360 | // }, | 330 | // }, |
| 361 | 331 | ||
| 362 | // { | 332 | // { |
| 363 | // path: '/zip', | 333 | // path: '/zip', |
| 364 | // component: Layout, | 334 | // component: Layout, |
| 365 | // redirect: '/zip/download', | 335 | // redirect: '/zip/download', |
| 366 | // alwaysShow: true, | 336 | // alwaysShow: true, |
| 367 | // name: 'Zip', | 337 | // name: 'Zip', |
| 368 | // meta: { title: 'zip', icon: 'zip' }, | 338 | // meta: { title: 'zip', icon: 'zip' }, |
| 369 | // children: [ | 339 | // children: [ |
| 370 | // { | 340 | // { |
| 371 | // path: 'download', | 341 | // path: 'download', |
| 372 | // component: () => import('@/views/zip/index'), | 342 | // component: () => import('@/views/zip/index'), |
| 373 | // name: 'ExportZip', | 343 | // name: 'ExportZip', |
| 374 | // meta: { title: 'exportZip' } | 344 | // meta: { title: 'exportZip' } |
| 375 | // } | 345 | // } |
| 376 | // ] | 346 | // ] |
| 377 | // }, | 347 | // }, |
| 378 | 348 | ||
| 379 | // { | 349 | // { |
| 380 | // path: '/pdf', | 350 | // path: '/pdf', |
| 381 | // component: Layout, | 351 | // component: Layout, |
| 382 | // redirect: '/pdf/index', | 352 | // redirect: '/pdf/index', |
| 383 | // children: [ | 353 | // children: [ |
| 384 | // { | 354 | // { |
| 385 | // path: 'index', | 355 | // path: 'index', |
| 386 | // component: () => import('@/views/pdf/index'), | 356 | // component: () => import('@/views/pdf/index'), |
| 387 | // name: 'PDF', | 357 | // name: 'PDF', |
| 388 | // meta: { title: 'pdf', icon: 'pdf' } | 358 | // meta: { title: 'pdf', icon: 'pdf' } |
| 389 | // } | 359 | // } |
| 390 | // ] | 360 | // ] |
| 391 | // }, | 361 | // }, |
| 392 | // { | 362 | // { |
| 393 | // path: '/pdf/download', | 363 | // path: '/pdf/download', |
| 394 | // component: () => import('@/views/pdf/download'), | 364 | // component: () => import('@/views/pdf/download'), |
| 395 | // hidden: true | 365 | // hidden: true |
| 396 | // }, | 366 | // }, |
| 397 | 367 | ||
| 398 | // { | 368 | // { |
| 399 | // path: '/theme', | 369 | // path: '/theme', |
| 400 | // component: Layout, | 370 | // component: Layout, |
| 401 | // children: [ | 371 | // children: [ |
| 402 | // { | 372 | // { |
| 403 | // path: 'index', | 373 | // path: 'index', |
| 404 | // component: () => import('@/views/theme/index'), | 374 | // component: () => import('@/views/theme/index'), |
| 405 | // name: 'Theme', | 375 | // name: 'Theme', |
| 406 | // meta: { title: 'theme', icon: 'theme' } | 376 | // meta: { title: 'theme', icon: 'theme' } |
| 407 | // } | 377 | // } |
| 408 | // ] | 378 | // ] |
| 409 | // }, | 379 | // }, |
| 410 | 380 | ||
| 411 | // { | 381 | // { |
| 412 | // path: '/clipboard', | 382 | // path: '/clipboard', |
| 413 | // component: Layout, | 383 | // component: Layout, |
| 414 | // children: [ | 384 | // children: [ |
| 415 | // { | 385 | // { |
| 416 | // path: 'index', | 386 | // path: 'index', |
| 417 | // component: () => import('@/views/clipboard/index'), | 387 | // component: () => import('@/views/clipboard/index'), |
| 418 | // name: 'ClipboardDemo', | 388 | // name: 'ClipboardDemo', |
| 419 | // meta: { title: 'clipboardDemo', icon: 'clipboard' } | 389 | // meta: { title: 'clipboardDemo', icon: 'clipboard' } |
| 420 | // } | 390 | // } |
| 421 | // ] | 391 | // ] |
| 422 | // }, | 392 | // }, |
| 423 | 393 | ||
| 424 | // { | 394 | // { |
| 425 | // path: '/i18n', | 395 | // path: '/i18n', |
| 426 | // component: Layout, | 396 | // component: Layout, |
| 427 | // children: [ | 397 | // children: [ |
| 428 | // { | 398 | // { |
| 429 | // path: 'index', | 399 | // path: 'index', |
| 430 | // component: () => import('@/views/i18n-demo/index'), | 400 | // component: () => import('@/views/i18n-demo/index'), |
| 431 | // name: 'I18n', | 401 | // name: 'I18n', |
| 432 | // meta: { title: 'i18n', icon: 'international' } | 402 | // meta: { title: 'i18n', icon: 'international' } |
| 433 | // } | 403 | // } |
| 434 | // ] | 404 | // ] |
| 435 | // }, | 405 | // }, |
| 436 | 406 | ||
| 437 | // { | 407 | // { |
| 438 | // path: 'external-link', | 408 | // path: 'external-link', |
| 439 | // component: Layout, | 409 | // component: Layout, |
| 440 | // children: [ | 410 | // children: [ |
| 441 | // { | 411 | // { |
| 442 | // path: 'https://github.com/PanJiaChen/vue-element-admin', | 412 | // path: 'https://github.com/PanJiaChen/vue-element-admin', |
| 443 | // meta: { title: 'externalLink', icon: 'link' } | 413 | // meta: { title: 'externalLink', icon: 'link' } |
| 444 | // } | 414 | // } |
| 445 | // ] | 415 | // ] |
| 446 | // }, | 416 | // }, |
| 447 | 417 | ||
| 448 | // 404 page must be placed at the end !!! | 418 | // 404 page must be placed at the end !!! |
| 449 | { | 419 | { |
| 450 | path: '*', | 420 | path: '*', |
| 451 | redirect: '/404', | 421 | redirect: '/404', |
| 452 | hidden: true | 422 | hidden: true |
| 453 | } | 423 | } |
| 454 | ] | 424 | ] |
| 455 | 425 | ||
| 456 | const createRouter = () => new Router({ | 426 | const createRouter = () => new Router({ |
| 457 | // mode: 'history', // require service support | 427 | // mode: 'history', // require service support |
| 458 | scrollBehavior: () => ({ | 428 | scrollBehavior: () => ({ |
| 459 | y: 0 | 429 | y: 0 |
| 460 | }), | 430 | }), |
| 461 | routes: constantRoutes | 431 | routes: constantRoutes |
| 462 | }) | 432 | }) |
| 463 | 433 | ||
| 464 | const router = createRouter() | 434 | const router = createRouter() |
| 465 | 435 | ||
| 466 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 | 436 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 |
| 467 | export function resetRouter() { | 437 | export function resetRouter() { |
| 468 | const newRouter = createRouter() | 438 | const newRouter = createRouter() |
| 469 | router.matcher = newRouter.matcher // reset router | 439 | router.matcher = newRouter.matcher // reset router |
| 470 | } | 440 | } |
| 471 | 441 | ||
| 472 | export default router | 442 | export default router |
src/router/modules/prod.js
| 1 | /** When your routing table is too long, you can split it into small modules**/ | 1 | /** When your routing table is too long, you can split it into small modules**/ |
| 2 | 2 | ||
| 3 | import Layout from '@/layout' | 3 | import Layout from '@/layout' |
| 4 | 4 | ||
| 5 | const prodRouter = { | 5 | const prodRouter = { |
| 6 | path: '/prods', | 6 | path: '/prods', |
| 7 | component: Layout, | 7 | component: Layout, |
| 8 | redirect: '/prod/page', | 8 | redirect: '/prod/page', |
| 9 | alwaysShow: true, // will always show the root menu | 9 | alwaysShow: true, // will always show the root menu |
| 10 | name: 'Prod', | 10 | name: 'Prod', |
| 11 | meta: { | 11 | meta: { |
| 12 | title: 'prods', // 会自动被i18n替换 | 12 | title: 'prods', // 会自动被i18n替换 |
| 13 | icon: 'star', | 13 | icon: 'star', |
| 14 | roles: ['admin', 'assistant', 'runner', 'shoper'] // you can set roles in root nav | 14 | roles: ['admin', 'assistant', 'runner', 'shoper'] // you can set roles in root nav |
| 15 | }, | 15 | }, |
| 16 | children: [{ | 16 | children: [{ |
| 17 | path: 'page', | 17 | path: 'page', |
| 18 | component: () => import('@/views/permission/page'), | 18 | component: () => import('@/views/permission/page'), |
| 19 | name: 'ProdList', | 19 | name: 'ProdList', |
| 20 | meta: { | 20 | meta: { |
| 21 | title: 'ProdList', | 21 | title: 'ProdList', |
| 22 | roles: ['admin', 'assistant', 'runner', 'shoper'] // or you can only set roles in sub nav | 22 | roles: ['admin', 'assistant', 'runner', 'shoper'] |
| 23 | } | 23 | } |
| 24 | }, | 24 | }, |
| 25 | { | 25 | { |
| 26 | path: 'defined', | 26 | path: 'defined', |
| 27 | component: () => import('@/views/permission/directive'), | 27 | component: () => import('@/views/permission/directive'), |
| 28 | name: 'ProdDefiend', | 28 | name: 'ProdDefiend', |
| 29 | meta: { | 29 | meta: { |
| 30 | title: 'ProdDefiend', | 30 | title: 'ProdDefiend', |
| 31 | roles: ['admin', 'assistant', 'shoper'] | 31 | roles: ['admin', 'assistant', 'shoper'] |
| 32 | // if do not set roles, means: this page does not require permission | 32 | } |
| 33 | }, { | ||
| 34 | path: 'defined2', | ||
| 35 | component: () => import('@/views/prod/list'), | ||
| 36 | name: 'prod...', | ||
| 37 | meta: { | ||
| 38 | title: '产品列表', | ||
| 39 | roles: ['admin', 'assistant', 'shoper'] | ||
| 33 | } | 40 | } |
| 34 | } | 41 | } |
| 35 | ] | 42 | ] |
| 36 | } | 43 | } |
| 37 | 44 | ||
| 38 | export default prodRouter | 45 | export default prodRouter |
| 39 | 46 |
src/views/order/complex-table.vue
| 1 | <template> | File was deleted | |
| 2 | <div class="app-container"> | ||
| 3 | <div class="filter-container"> | ||
| 4 | <el-input v-model="listQuery.title" :placeholder="$t('table.title')" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" /> | ||
| 5 | <el-select v-model="listQuery.importance" :placeholder="$t('table.importance')" clearable style="width: 90px" class="filter-item"> | ||
| 6 | <el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item" /> | ||
| 7 | </el-select> | ||
| 8 | <el-select v-model="listQuery.type" :placeholder="$t('table.type')" clearable class="filter-item" style="width: 130px"> | ||
| 9 | <el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name+'('+item.key+')'" :value="item.key" /> | ||
| 10 | </el-select> | ||
| 11 | <el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> | ||
| 12 | <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> | ||
| 13 | </el-select> | ||
| 14 | <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> | ||
| 15 | {{ $t('table.search') }} | ||
| 16 | </el-button> | ||
| 17 | <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate"> | ||
| 18 | {{ $t('table.add') }} | ||
| 19 | </el-button> | ||
| 20 | <el-button v-waves :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload"> | ||
| 21 | {{ $t('table.export') }} | ||
| 22 | </el-button> | ||
| 23 | <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1"> | ||
| 24 | {{ $t('table.reviewer') }} | ||
| 25 | </el-checkbox> | ||
| 26 | </div> | ||
| 27 | |||
| 28 | <el-table | ||
| 29 | :key="tableKey" | ||
| 30 | v-loading="listLoading" | ||
| 31 | :data="list" | ||
| 32 | border | ||
| 33 | fit | ||
| 34 | highlight-current-row | ||
| 35 | style="width: 100%;" | ||
| 36 | @sort-change="sortChange" | ||
| 37 | > | ||
| 38 | <el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="80" :class-name="getSortClass('id')"> | ||
| 39 | <template slot-scope="{row}"> | ||
| 40 | <span>{{ row.id }}</span> | ||
| 41 | </template> | ||
| 42 | </el-table-column> | ||
| 43 | <el-table-column :label="$t('table.date')" width="150px" align="center"> | ||
| 44 | <template slot-scope="{row}"> | ||
| 45 | <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span> | ||
| 46 | </template> | ||
| 47 | </el-table-column> | ||
| 48 | <el-table-column :label="$t('table.title')" min-width="150px"> | ||
| 49 | <template slot-scope="{row}"> | ||
| 50 | <span class="link-type" @click="handleUpdate(row)">{{ row.title }}</span> | ||
| 51 | <el-tag>{{ row.type | typeFilter }}</el-tag> | ||
| 52 | </template> | ||
| 53 | </el-table-column> | ||
| 54 | <el-table-column :label="$t('table.author')" width="110px" align="center"> | ||
| 55 | <template slot-scope="{row}"> | ||
| 56 | <span>{{ row.author }}</span> | ||
| 57 | </template> | ||
| 58 | </el-table-column> | ||
| 59 | <el-table-column v-if="showReviewer" :label="$t('table.reviewer')" width="110px" align="center"> | ||
| 60 | <template slot-scope="{row}"> | ||
| 61 | <span style="color:red;">{{ row.reviewer }}</span> | ||
| 62 | </template> | ||
| 63 | </el-table-column> | ||
| 64 | <el-table-column :label="$t('table.importance')" width="80px"> | ||
| 65 | <template slot-scope="{row}"> | ||
| 66 | <svg-icon v-for="n in +row.importance" :key="n" icon-class="star" class="meta-item__icon" /> | ||
| 67 | </template> | ||
| 68 | </el-table-column> | ||
| 69 | <el-table-column :label="$t('table.readings')" align="center" width="95"> | ||
| 70 | <template slot-scope="{row}"> | ||
| 71 | <span v-if="row.pageviews" class="link-type" @click="handleFetchPv(row.pageviews)">{{ row.pageviews }}</span> | ||
| 72 | <span v-else>0</span> | ||
| 73 | </template> | ||
| 74 | </el-table-column> | ||
| 75 | <el-table-column :label="$t('table.status')" class-name="status-col" width="100"> | ||
| 76 | <template slot-scope="{row}"> | ||
| 77 | <el-tag :type="row.status | statusFilter"> | ||
| 78 | {{ row.status }} | ||
| 79 | </el-tag> | ||
| 80 | </template> | ||
| 81 | </el-table-column> | ||
| 82 | <el-table-column :label="$t('table.actions')" align="center" width="230" class-name="small-padding fixed-width"> | ||
| 83 | <template slot-scope="{row,$index}"> | ||
| 84 | <el-button type="primary" size="mini" @click="handleUpdate(row)"> | ||
| 85 | {{ $t('table.edit') }} | ||
| 86 | </el-button> | ||
| 87 | <el-button v-if="row.status!='published'" size="mini" type="success" @click="handleModifyStatus(row,'published')"> | ||
| 88 | {{ $t('table.publish') }} | ||
| 89 | </el-button> | ||
| 90 | <el-button v-if="row.status!='draft'" size="mini" @click="handleModifyStatus(row,'draft')"> | ||
| 91 | {{ $t('table.draft') }} | ||
| 92 | </el-button> | ||
| 93 | <el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)"> | ||
| 94 | {{ $t('table.delete') }} | ||
| 95 | </el-button> | ||
| 96 | </template> | ||
| 97 | </el-table-column> | ||
| 98 | </el-table> | ||
| 99 | |||
| 100 | <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" /> | ||
| 101 | |||
| 102 | <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible"> | ||
| 103 | <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;"> | ||
| 104 | <el-form-item :label="$t('table.type')" prop="type"> | ||
| 105 | <el-select v-model="temp.type" class="filter-item" placeholder="Please select"> | ||
| 106 | <el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" /> | ||
| 107 | </el-select> | ||
| 108 | </el-form-item> | ||
| 109 | <el-form-item :label="$t('table.date')" prop="timestamp"> | ||
| 110 | <el-date-picker v-model="temp.timestamp" type="datetime" placeholder="Please pick a date" /> | ||
| 111 | </el-form-item> | ||
| 112 | <el-form-item :label="$t('table.title')" prop="title"> | ||
| 113 | <el-input v-model="temp.title" /> | ||
| 114 | </el-form-item> | ||
| 115 | <el-form-item :label="$t('table.status')"> | ||
| 116 | <el-select v-model="temp.status" class="filter-item" placeholder="Please select"> | ||
| 117 | <el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" /> | ||
| 118 | </el-select> | ||
| 119 | </el-form-item> | ||
| 120 | <el-form-item :label="$t('table.importance')"> | ||
| 121 | <el-rate v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max="3" style="margin-top:8px;" /> | ||
| 122 | </el-form-item> | ||
| 123 | <el-form-item :label="$t('table.remark')"> | ||
| 124 | <el-input v-model="temp.remark" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="Please input" /> | ||
| 125 | </el-form-item> | ||
| 126 | </el-form> | ||
| 127 | <div slot="footer" class="dialog-footer"> | ||
| 128 | <el-button @click="dialogFormVisible = false"> | ||
| 129 | {{ $t('table.cancel') }} | ||
| 130 | </el-button> | ||
| 131 | <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()"> | ||
| 132 | {{ $t('table.confirm') }} | ||
| 133 | </el-button> | ||
| 134 | </div> | ||
| 135 | </el-dialog> | ||
| 136 | |||
| 137 | <el-dialog :visible.sync="dialogPvVisible" title="Reading statistics"> | ||
| 138 | <el-table :data="pvData" border fit highlight-current-row style="width: 100%"> | ||
| 139 | <el-table-column prop="key" label="Channel" /> | ||
| 140 | <el-table-column prop="pv" label="Pv" /> | ||
| 141 | </el-table> | ||
| 142 | <span slot="footer" class="dialog-footer"> | ||
| 143 | <el-button type="primary" @click="dialogPvVisible = false">{{ $t('table.confirm') }}</el-button> | ||
| 144 | </span> | ||
| 145 | </el-dialog> | ||
| 146 | </div> | ||
| 147 | </template> | ||
| 148 | |||
| 149 | <script> | ||
| 150 | import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article' | ||
| 151 | import waves from '@/directive/waves' // waves directive | ||
| 152 | import { parseTime } from '@/utils' | ||
| 153 | import Pagination from '@/components/Pagination' // secondary package based on el-pagination | ||
| 154 | |||
| 155 | const calendarTypeOptions = [ | ||
| 156 | { key: 'CN', display_name: 'China' }, | ||
| 157 | { key: 'US', display_name: 'USA' }, | ||
| 158 | { key: 'JP', display_name: 'Japan' }, | ||
| 159 | { key: 'EU', display_name: 'Eurozone' } | ||
| 160 | ] | ||
| 161 | |||
| 162 | // arr to obj, such as { CN : "China", US : "USA" } | ||
| 163 | const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => { | ||
| 164 | acc[cur.key] = cur.display_name | ||
| 165 | return acc | ||
| 166 | }, {}) | ||
| 167 | |||
| 168 | export default { | ||
| 169 | name: 'ComplexTable', | ||
| 170 | components: { Pagination }, | ||
| 171 | directives: { waves }, | ||
| 172 | filters: { | ||
| 173 | statusFilter(status) { | ||
| 174 | const statusMap = { | ||
| 175 | published: 'success', | ||
| 176 | draft: 'info', | ||
| 177 | deleted: 'danger' | ||
| 178 | } | ||
| 179 | return statusMap[status] | ||
| 180 | }, | ||
| 181 | typeFilter(type) { | ||
| 182 | return calendarTypeKeyValue[type] | ||
| 183 | } | ||
| 184 | }, | ||
| 185 | data() { | ||
| 186 | return { | ||
| 187 | tableKey: 0, | ||
| 188 | list: null, | ||
| 189 | total: 0, | ||
| 190 | listLoading: true, | ||
| 191 | listQuery: { | ||
| 192 | page: 1, | ||
| 193 | limit: 20, | ||
| 194 | importance: undefined, | ||
| 195 | title: undefined, | ||
| 196 | type: undefined, | ||
| 197 | sort: '+id' | ||
| 198 | }, | ||
| 199 | importanceOptions: [1, 2, 3], | ||
| 200 | calendarTypeOptions, | ||
| 201 | sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }], | ||
| 202 | statusOptions: ['published', 'draft', 'deleted'], | ||
| 203 | showReviewer: false, | ||
| 204 | temp: { | ||
| 205 | id: undefined, | ||
| 206 | importance: 1, | ||
| 207 | remark: '', | ||
| 208 | timestamp: new Date(), | ||
| 209 | title: '', | ||
| 210 | type: '', | ||
| 211 | status: 'published' | ||
| 212 | }, | ||
| 213 | dialogFormVisible: false, | ||
| 214 | dialogStatus: '', | ||
| 215 | textMap: { | ||
| 216 | update: 'Edit', | ||
| 217 | create: 'Create' | ||
| 218 | }, | ||
| 219 | dialogPvVisible: false, | ||
| 220 | pvData: [], | ||
| 221 | rules: { | ||
| 222 | type: [{ required: true, message: 'type is required', trigger: 'change' }], | ||
| 223 | timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }], | ||
| 224 | title: [{ required: true, message: 'title is required', trigger: 'blur' }] | ||
| 225 | }, | ||
| 226 | downloadLoading: false | ||
| 227 | } | ||
| 228 | }, | ||
| 229 | created() { | ||
| 230 | this.getList() | ||
| 231 | }, | ||
| 232 | methods: { | ||
| 233 | getList() { | ||
| 234 | this.listLoading = true | ||
| 235 | fetchList(this.listQuery).then(response => { | ||
| 236 | this.list = response.data.items | ||
| 237 | this.total = response.data.total | ||
| 238 | |||
| 239 | // Just to simulate the time of the request | ||
| 240 | setTimeout(() => { | ||
| 241 | this.listLoading = false | ||
| 242 | }, 1.5 * 1000) | ||
| 243 | }) | ||
| 244 | }, | ||
| 245 | handleFilter() { | ||
| 246 | this.listQuery.page = 1 | ||
| 247 | this.getList() | ||
| 248 | }, | ||
| 249 | handleModifyStatus(row, status) { | ||
| 250 | this.$message({ | ||
| 251 | message: '操作成功', | ||
| 252 | type: 'success' | ||
| 253 | }) | ||
| 254 | row.status = status | ||
| 255 | }, | ||
| 256 | sortChange(data) { | ||
| 257 | const { prop, order } = data | ||
| 258 | if (prop === 'id') { | ||
| 259 | this.sortByID(order) | ||
| 260 | } | ||
| 261 | }, | ||
| 262 | sortByID(order) { | ||
| 263 | if (order === 'ascending') { | ||
| 264 | this.listQuery.sort = '+id' | ||
| 265 | } else { | ||
| 266 | this.listQuery.sort = '-id' | ||
| 267 | } | ||
| 268 | this.handleFilter() | ||
| 269 | }, | ||
| 270 | resetTemp() { | ||
| 271 | this.temp = { | ||
| 272 | id: undefined, | ||
| 273 | importance: 1, | ||
| 274 | remark: '', | ||
| 275 | timestamp: new Date(), | ||
| 276 | title: '', | ||
| 277 | status: 'published', | ||
| 278 | type: '' | ||
| 279 | } | ||
| 280 | }, | ||
| 281 | handleCreate() { | ||
| 282 | this.resetTemp() | ||
| 283 | this.dialogStatus = 'create' | ||
| 284 | this.dialogFormVisible = true | ||
| 285 | this.$nextTick(() => { | ||
| 286 | this.$refs['dataForm'].clearValidate() | ||
| 287 | }) | ||
| 288 | }, | ||
| 289 | createData() { | ||
| 290 | this.$refs['dataForm'].validate((valid) => { | ||
| 291 | if (valid) { | ||
| 292 | this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id | ||
| 293 | this.temp.author = '秀野堂主' | ||
| 294 | createArticle(this.temp).then(() => { | ||
| 295 | this.list.unshift(this.temp) | ||
| 296 | this.dialogFormVisible = false | ||
| 297 | this.$notify({ | ||
| 298 | title: '成功', | ||
| 299 | message: '创建成功', | ||
| 300 | type: 'success', | ||
| 301 | duration: 2000 | ||
| 302 | }) | ||
| 303 | }) | ||
| 304 | } | ||
| 305 | }) | ||
| 306 | }, | ||
| 307 | handleUpdate(row) { | ||
| 308 | this.temp = Object.assign({}, row) // copy obj | ||
| 309 | this.temp.timestamp = new Date(this.temp.timestamp) | ||
| 310 | this.dialogStatus = 'update' | ||
| 311 | this.dialogFormVisible = true | ||
| 312 | this.$nextTick(() => { | ||
| 313 | this.$refs['dataForm'].clearValidate() | ||
| 314 | }) | ||
| 315 | }, | ||
| 316 | updateData() { | ||
| 317 | this.$refs['dataForm'].validate((valid) => { | ||
| 318 | if (valid) { | ||
| 319 | const tempData = Object.assign({}, this.temp) | ||
| 320 | tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464 | ||
| 321 | updateArticle(tempData).then(() => { | ||
| 322 | const index = this.list.findIndex(v => v.id === this.temp.id) | ||
| 323 | this.list.splice(index, 1, this.temp) | ||
| 324 | this.dialogFormVisible = false | ||
| 325 | this.$notify({ | ||
| 326 | title: '成功', | ||
| 327 | message: '更新成功', | ||
| 328 | type: 'success', | ||
| 329 | duration: 2000 | ||
| 330 | }) | ||
| 331 | }) | ||
| 332 | } | ||
| 333 | }) | ||
| 334 | }, | ||
| 335 | handleDelete(row, index) { | ||
| 336 | this.$notify({ | ||
| 337 | title: '成功', | ||
| 338 | message: '删除成功', | ||
| 339 | type: 'success', | ||
| 340 | duration: 2000 | ||
| 341 | }) | ||
| 342 | this.list.splice(index, 1) | ||
| 343 | }, | ||
| 344 | handleFetchPv(pv) { | ||
| 345 | fetchPv(pv).then(response => { | ||
| 346 | this.pvData = response.data.pvData | ||
| 347 | this.dialogPvVisible = true | ||
| 348 | }) | ||
| 349 | }, | ||
| 350 | handleDownload() { | ||
| 351 | this.downloadLoading = true | ||
| 352 | import('@/vendor/Export2Excel').then(excel => { | ||
| 353 | const tHeader = ['timestamp', 'title', 'type', 'importance', 'status'] | ||
| 354 | const filterVal = ['timestamp', 'title', 'type', 'importance', 'status'] | ||
| 355 | const data = this.formatJson(filterVal) | ||
| 356 | excel.export_json_to_excel({ | ||
| 357 | header: tHeader, | ||
| 358 | data, | ||
| 359 | filename: 'table-list' | ||
| 360 | }) | ||
| 361 | this.downloadLoading = false | ||
| 362 | }) | ||
| 363 | }, | ||
| 364 | formatJson(filterVal) { | ||
| 365 | return this.list.map(v => filterVal.map(j => { | ||
| 366 | if (j === 'timestamp') { | ||
| 367 | return parseTime(v[j]) | ||
| 368 | } else { | ||
| 369 | return v[j] | ||
| 370 | } | ||
| 371 | })) | ||
| 372 | }, | ||
| 373 | getSortClass: function(key) { | ||
| 374 | const sort = this.listQuery.sort | ||
| 375 | return sort === `+${key}` ? 'ascending' : 'descending' | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | </script> | ||
| 380 | 1 | <template> |
src/views/prod/complex-table.vue
| 1 | <template> | File was deleted | |
| 2 | <div class="app-container"> | ||
| 3 | <div class="filter-container"> | ||
| 4 | <el-input v-model="listQuery.title" :placeholder="$t('table.title')" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" /> | ||
| 5 | <el-select v-model="listQuery.importance" :placeholder="$t('table.importance')" clearable style="width: 90px" class="filter-item"> | ||
| 6 | <el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item" /> | ||
| 7 | </el-select> | ||
| 8 | <el-select v-model="listQuery.type" :placeholder="$t('table.type')" clearable class="filter-item" style="width: 130px"> | ||
| 9 | <el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name+'('+item.key+')'" :value="item.key" /> | ||
| 10 | </el-select> | ||
| 11 | <el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> | ||
| 12 | <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> | ||
| 13 | </el-select> | ||
| 14 | <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> | ||
| 15 | {{ $t('table.search') }} | ||
| 16 | </el-button> | ||
| 17 | <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate"> | ||
| 18 | {{ $t('table.add') }} | ||
| 19 | </el-button> | ||
| 20 | <el-button v-waves :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload"> | ||
| 21 | {{ $t('table.export') }} | ||
| 22 | </el-button> | ||
| 23 | <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1"> | ||
| 24 | {{ $t('table.reviewer') }} | ||
| 25 | </el-checkbox> | ||
| 26 | </div> | ||
| 27 | |||
| 28 | <el-table | ||
| 29 | :key="tableKey" | ||
| 30 | v-loading="listLoading" | ||
| 31 | :data="list" | ||
| 32 | border | ||
| 33 | fit | ||
| 34 | highlight-current-row | ||
| 35 | style="width: 100%;" | ||
| 36 | @sort-change="sortChange" | ||
| 37 | > | ||
| 38 | <el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="80" :class-name="getSortClass('id')"> | ||
| 39 | <template slot-scope="{row}"> | ||
| 40 | <span>{{ row.id }}</span> | ||
| 41 | </template> | ||
| 42 | </el-table-column> | ||
| 43 | <el-table-column :label="$t('table.date')" width="150px" align="center"> | ||
| 44 | <template slot-scope="{row}"> | ||
| 45 | <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span> | ||
| 46 | </template> | ||
| 47 | </el-table-column> | ||
| 48 | <el-table-column :label="$t('table.title')" min-width="150px"> | ||
| 49 | <template slot-scope="{row}"> | ||
| 50 | <span class="link-type" @click="handleUpdate(row)">{{ row.title }}</span> | ||
| 51 | <el-tag>{{ row.type | typeFilter }}</el-tag> | ||
| 52 | </template> | ||
| 53 | </el-table-column> | ||
| 54 | <el-table-column :label="$t('table.author')" width="110px" align="center"> | ||
| 55 | <template slot-scope="{row}"> | ||
| 56 | <span>{{ row.author }}</span> | ||
| 57 | </template> | ||
| 58 | </el-table-column> | ||
| 59 | <el-table-column v-if="showReviewer" :label="$t('table.reviewer')" width="110px" align="center"> | ||
| 60 | <template slot-scope="{row}"> | ||
| 61 | <span style="color:red;">{{ row.reviewer }}</span> | ||
| 62 | </template> | ||
| 63 | </el-table-column> | ||
| 64 | <el-table-column :label="$t('table.importance')" width="80px"> | ||
| 65 | <template slot-scope="{row}"> | ||
| 66 | <svg-icon v-for="n in +row.importance" :key="n" icon-class="star" class="meta-item__icon" /> | ||
| 67 | </template> | ||
| 68 | </el-table-column> | ||
| 69 | <el-table-column :label="$t('table.readings')" align="center" width="95"> | ||
| 70 | <template slot-scope="{row}"> | ||
| 71 | <span v-if="row.pageviews" class="link-type" @click="handleFetchPv(row.pageviews)">{{ row.pageviews }}</span> | ||
| 72 | <span v-else>0</span> | ||
| 73 | </template> | ||
| 74 | </el-table-column> | ||
| 75 | <el-table-column :label="$t('table.status')" class-name="status-col" width="100"> | ||
| 76 | <template slot-scope="{row}"> | ||
| 77 | <el-tag :type="row.status | statusFilter"> | ||
| 78 | {{ row.status }} | ||
| 79 | </el-tag> | ||
| 80 | </template> | ||
| 81 | </el-table-column> | ||
| 82 | <el-table-column :label="$t('table.actions')" align="center" width="230" class-name="small-padding fixed-width"> | ||
| 83 | <template slot-scope="{row,$index}"> | ||
| 84 | <el-button type="primary" size="mini" @click="handleUpdate(row)"> | ||
| 85 | {{ $t('table.edit') }} | ||
| 86 | </el-button> | ||
| 87 | <el-button v-if="row.status!='published'" size="mini" type="success" @click="handleModifyStatus(row,'published')"> | ||
| 88 | {{ $t('table.publish') }} | ||
| 89 | </el-button> | ||
| 90 | <el-button v-if="row.status!='draft'" size="mini" @click="handleModifyStatus(row,'draft')"> | ||
| 91 | {{ $t('table.draft') }} | ||
| 92 | </el-button> | ||
| 93 | <el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)"> | ||
| 94 | {{ $t('table.delete') }} | ||
| 95 | </el-button> | ||
| 96 | </template> | ||
| 97 | </el-table-column> | ||
| 98 | </el-table> | ||
| 99 | |||
| 100 | <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" /> | ||
| 101 | |||
| 102 | <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible"> | ||
| 103 | <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;"> | ||
| 104 | <el-form-item :label="$t('table.type')" prop="type"> | ||
| 105 | <el-select v-model="temp.type" class="filter-item" placeholder="Please select"> | ||
| 106 | <el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" /> | ||
| 107 | </el-select> | ||
| 108 | </el-form-item> | ||
| 109 | <el-form-item :label="$t('table.date')" prop="timestamp"> | ||
| 110 | <el-date-picker v-model="temp.timestamp" type="datetime" placeholder="Please pick a date" /> | ||
| 111 | </el-form-item> | ||
| 112 | <el-form-item :label="$t('table.title')" prop="title"> | ||
| 113 | <el-input v-model="temp.title" /> | ||
| 114 | </el-form-item> | ||
| 115 | <el-form-item :label="$t('table.status')"> | ||
| 116 | <el-select v-model="temp.status" class="filter-item" placeholder="Please select"> | ||
| 117 | <el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" /> | ||
| 118 | </el-select> | ||
| 119 | </el-form-item> | ||
| 120 | <el-form-item :label="$t('table.importance')"> | ||
| 121 | <el-rate v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max="3" style="margin-top:8px;" /> | ||
| 122 | </el-form-item> | ||
| 123 | <el-form-item :label="$t('table.remark')"> | ||
| 124 | <el-input v-model="temp.remark" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="Please input" /> | ||
| 125 | </el-form-item> | ||
| 126 | </el-form> | ||
| 127 | <div slot="footer" class="dialog-footer"> | ||
| 128 | <el-button @click="dialogFormVisible = false"> | ||
| 129 | {{ $t('table.cancel') }} | ||
| 130 | </el-button> | ||
| 131 | <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()"> | ||
| 132 | {{ $t('table.confirm') }} | ||
| 133 | </el-button> | ||
| 134 | </div> | ||
| 135 | </el-dialog> | ||
| 136 | |||
| 137 | <el-dialog :visible.sync="dialogPvVisible" title="Reading statistics"> | ||
| 138 | <el-table :data="pvData" border fit highlight-current-row style="width: 100%"> | ||
| 139 | <el-table-column prop="key" label="Channel" /> | ||
| 140 | <el-table-column prop="pv" label="Pv" /> | ||
| 141 | </el-table> | ||
| 142 | <span slot="footer" class="dialog-footer"> | ||
| 143 | <el-button type="primary" @click="dialogPvVisible = false">{{ $t('table.confirm') }}</el-button> | ||
| 144 | </span> | ||
| 145 | </el-dialog> | ||
| 146 | </div> | ||
| 147 | </template> | ||
| 148 | |||
| 149 | <script> | ||
| 150 | import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article' | ||
| 151 | import waves from '@/directive/waves' // waves directive | ||
| 152 | import { parseTime } from '@/utils' | ||
| 153 | import Pagination from '@/components/Pagination' // secondary package based on el-pagination | ||
| 154 | |||
| 155 | const calendarTypeOptions = [ | ||
| 156 | { key: 'CN', display_name: 'China' }, | ||
| 157 | { key: 'US', display_name: 'USA' }, | ||
| 158 | { key: 'JP', display_name: 'Japan' }, | ||
| 159 | { key: 'EU', display_name: 'Eurozone' } | ||
| 160 | ] | ||
| 161 | |||
| 162 | // arr to obj, such as { CN : "China", US : "USA" } | ||
| 163 | const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => { | ||
| 164 | acc[cur.key] = cur.display_name | ||
| 165 | return acc | ||
| 166 | }, {}) | ||
| 167 | |||
| 168 | export default { | ||
| 169 | name: 'ComplexTable', | ||
| 170 | components: { Pagination }, | ||
| 171 | directives: { waves }, | ||
| 172 | filters: { | ||
| 173 | statusFilter(status) { | ||
| 174 | const statusMap = { | ||
| 175 | published: 'success', | ||
| 176 | draft: 'info', | ||
| 177 | deleted: 'danger' | ||
| 178 | } | ||
| 179 | return statusMap[status] | ||
| 180 | }, | ||
| 181 | typeFilter(type) { | ||
| 182 | return calendarTypeKeyValue[type] | ||
| 183 | } | ||
| 184 | }, | ||
| 185 | data() { | ||
| 186 | return { | ||
| 187 | tableKey: 0, | ||
| 188 | list: null, | ||
| 189 | total: 0, | ||
| 190 | listLoading: true, | ||
| 191 | listQuery: { | ||
| 192 | page: 1, | ||
| 193 | limit: 20, | ||
| 194 | importance: undefined, | ||
| 195 | title: undefined, | ||
| 196 | type: undefined, | ||
| 197 | sort: '+id' | ||
| 198 | }, | ||
| 199 | importanceOptions: [1, 2, 3], | ||
| 200 | calendarTypeOptions, | ||
| 201 | sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }], | ||
| 202 | statusOptions: ['published', 'draft', 'deleted'], | ||
| 203 | showReviewer: false, | ||
| 204 | temp: { | ||
| 205 | id: undefined, | ||
| 206 | importance: 1, | ||
| 207 | remark: '', | ||
| 208 | timestamp: new Date(), | ||
| 209 | title: '', | ||
| 210 | type: '', | ||
| 211 | status: 'published' | ||
| 212 | }, | ||
| 213 | dialogFormVisible: false, | ||
| 214 | dialogStatus: '', | ||
| 215 | textMap: { | ||
| 216 | update: 'Edit', | ||
| 217 | create: 'Create' | ||
| 218 | }, | ||
| 219 | dialogPvVisible: false, | ||
| 220 | pvData: [], | ||
| 221 | rules: { | ||
| 222 | type: [{ required: true, message: 'type is required', trigger: 'change' }], | ||
| 223 | timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }], | ||
| 224 | title: [{ required: true, message: 'title is required', trigger: 'blur' }] | ||
| 225 | }, | ||
| 226 | downloadLoading: false | ||
| 227 | } | ||
| 228 | }, | ||
| 229 | created() { | ||
| 230 | this.getList() | ||
| 231 | }, | ||
| 232 | methods: { | ||
| 233 | getList() { | ||
| 234 | this.listLoading = true | ||
| 235 | fetchList(this.listQuery).then(response => { | ||
| 236 | this.list = response.data.items | ||
| 237 | this.total = response.data.total | ||
| 238 | |||
| 239 | // Just to simulate the time of the request | ||
| 240 | setTimeout(() => { | ||
| 241 | this.listLoading = false | ||
| 242 | }, 1.5 * 1000) | ||
| 243 | }) | ||
| 244 | }, | ||
| 245 | handleFilter() { | ||
| 246 | this.listQuery.page = 1 | ||
| 247 | this.getList() | ||
| 248 | }, | ||
| 249 | handleModifyStatus(row, status) { | ||
| 250 | this.$message({ | ||
| 251 | message: '操作成功', | ||
| 252 | type: 'success' | ||
| 253 | }) | ||
| 254 | row.status = status | ||
| 255 | }, | ||
| 256 | sortChange(data) { | ||
| 257 | const { prop, order } = data | ||
| 258 | if (prop === 'id') { | ||
| 259 | this.sortByID(order) | ||
| 260 | } | ||
| 261 | }, | ||
| 262 | sortByID(order) { | ||
| 263 | if (order === 'ascending') { | ||
| 264 | this.listQuery.sort = '+id' | ||
| 265 | } else { | ||
| 266 | this.listQuery.sort = '-id' | ||
| 267 | } | ||
| 268 | this.handleFilter() | ||
| 269 | }, | ||
| 270 | resetTemp() { | ||
| 271 | this.temp = { | ||
| 272 | id: undefined, | ||
| 273 | importance: 1, | ||
| 274 | remark: '', | ||
| 275 | timestamp: new Date(), | ||
| 276 | title: '', | ||
| 277 | status: 'published', | ||
| 278 | type: '' | ||
| 279 | } | ||
| 280 | }, | ||
| 281 | handleCreate() { | ||
| 282 | this.resetTemp() | ||
| 283 | this.dialogStatus = 'create' | ||
| 284 | this.dialogFormVisible = true | ||
| 285 | this.$nextTick(() => { | ||
| 286 | this.$refs['dataForm'].clearValidate() | ||
| 287 | }) | ||
| 288 | }, | ||
| 289 | createData() { | ||
| 290 | this.$refs['dataForm'].validate((valid) => { | ||
| 291 | if (valid) { | ||
| 292 | this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id | ||
| 293 | this.temp.author = '秀野堂主' | ||
| 294 | createArticle(this.temp).then(() => { | ||
| 295 | this.list.unshift(this.temp) | ||
| 296 | this.dialogFormVisible = false | ||
| 297 | this.$notify({ | ||
| 298 | title: '成功', | ||
| 299 | message: '创建成功', | ||
| 300 | type: 'success', | ||
| 301 | duration: 2000 | ||
| 302 | }) | ||
| 303 | }) | ||
| 304 | } | ||
| 305 | }) | ||
| 306 | }, | ||
| 307 | handleUpdate(row) { | ||
| 308 | this.temp = Object.assign({}, row) // copy obj | ||
| 309 | this.temp.timestamp = new Date(this.temp.timestamp) | ||
| 310 | this.dialogStatus = 'update' | ||
| 311 | this.dialogFormVisible = true | ||
| 312 | this.$nextTick(() => { | ||
| 313 | this.$refs['dataForm'].clearValidate() | ||
| 314 | }) | ||
| 315 | }, | ||
| 316 | updateData() { | ||
| 317 | this.$refs['dataForm'].validate((valid) => { | ||
| 318 | if (valid) { | ||
| 319 | const tempData = Object.assign({}, this.temp) | ||
| 320 | tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464 | ||
| 321 | updateArticle(tempData).then(() => { | ||
| 322 | const index = this.list.findIndex(v => v.id === this.temp.id) | ||
| 323 | this.list.splice(index, 1, this.temp) | ||
| 324 | this.dialogFormVisible = false | ||
| 325 | this.$notify({ | ||
| 326 | title: '成功', | ||
| 327 | message: '更新成功', | ||
| 328 | type: 'success', | ||
| 329 | duration: 2000 | ||
| 330 | }) | ||
| 331 | }) | ||
| 332 | } | ||
| 333 | }) | ||
| 334 | }, | ||
| 335 | handleDelete(row, index) { | ||
| 336 | this.$notify({ | ||
| 337 | title: '成功', | ||
| 338 | message: '删除成功', | ||
| 339 | type: 'success', | ||
| 340 | duration: 2000 | ||
| 341 | }) | ||
| 342 | this.list.splice(index, 1) | ||
| 343 | }, | ||
| 344 | handleFetchPv(pv) { | ||
| 345 | fetchPv(pv).then(response => { | ||
| 346 | this.pvData = response.data.pvData | ||
| 347 | this.dialogPvVisible = true | ||
| 348 | }) | ||
| 349 | }, | ||
| 350 | handleDownload() { | ||
| 351 | this.downloadLoading = true | ||
| 352 | import('@/vendor/Export2Excel').then(excel => { | ||
| 353 | const tHeader = ['timestamp', 'title', 'type', 'importance', 'status'] | ||
| 354 | const filterVal = ['timestamp', 'title', 'type', 'importance', 'status'] | ||
| 355 | const data = this.formatJson(filterVal) | ||
| 356 | excel.export_json_to_excel({ | ||
| 357 | header: tHeader, | ||
| 358 | data, | ||
| 359 | filename: 'table-list' | ||
| 360 | }) | ||
| 361 | this.downloadLoading = false | ||
| 362 | }) | ||
| 363 | }, | ||
| 364 | formatJson(filterVal) { | ||
| 365 | return this.list.map(v => filterVal.map(j => { | ||
| 366 | if (j === 'timestamp') { | ||
| 367 | return parseTime(v[j]) | ||
| 368 | } else { | ||
| 369 | return v[j] | ||
| 370 | } | ||
| 371 | })) | ||
| 372 | }, | ||
| 373 | getSortClass: function(key) { | ||
| 374 | const sort = this.listQuery.sort | ||
| 375 | return sort === `+${key}` ? 'ascending' : 'descending' | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | </script> | ||
| 380 | 1 | <template> |