Commit 1cd75fcf5ba7a3114399db8f22cf8eb6f2e4d783
1 parent
21d0ed92
feat(workbench): add workbench page
Showing
10 changed files
with
609 additions
and
3 deletions
src/hooks/web/useApexCharts.ts
src/router/menus/modules/demo/dashboard.ts
src/router/routes/modules/demo/dashboard.ts
... | ... | @@ -7,7 +7,7 @@ export default { |
7 | 7 | path: '/dashboard', |
8 | 8 | name: 'Dashboard', |
9 | 9 | component: PAGE_LAYOUT_COMPONENT, |
10 | - redirect: '/dashboard/welcome', | |
10 | + redirect: '/dashboard/workbench', | |
11 | 11 | meta: { |
12 | 12 | icon: 'ant-design:home-outlined', |
13 | 13 | title: 'Dashboard', |
... | ... | @@ -20,7 +20,15 @@ export default { |
20 | 20 | name: 'Welcome', |
21 | 21 | component: () => import('/@/views/dashboard/welcome/index.vue'), |
22 | 22 | meta: { |
23 | - title: '欢迎页', | |
23 | + title: '首页', | |
24 | + }, | |
25 | + }, | |
26 | + { | |
27 | + path: '/workbench', | |
28 | + name: 'Workbench', | |
29 | + component: () => import('/@/views/dashboard/workbench/index.vue'), | |
30 | + meta: { | |
31 | + title: '工作台', | |
24 | 32 | affix: true, |
25 | 33 | }, |
26 | 34 | }, | ... | ... |
src/views/dashboard/workbench/components/NewsList.vue
0 → 100644
1 | +<template> | |
2 | + <CollapseContainer class="news-list" title="动态" :canExpan="false"> | |
3 | + <ScrollContainer> | |
4 | + <List> | |
5 | + <template v-for="item in newList" :key="item.id"> | |
6 | + <ListItem class="news-list__item"> | |
7 | + <ListItemMeta> | |
8 | + <template #avatar> | |
9 | + <img src="/@/assets/images/header.jpg" class="news-list__item-avatar" /> | |
10 | + </template> | |
11 | + <template #description> | |
12 | + <div class="news-list__item-desc"> | |
13 | + <div class="news-list__item-time mb-1"> {{ item.sendTime }}</div> | |
14 | + <div class="news-list__item-title mb-1"> | |
15 | + <span class="news-list__item-light">{{ item.sender }} </span>申请迭代 | |
16 | + <span class="news-list__item-light"> {{ item.title }} </span>发布 | |
17 | + </div> | |
18 | + <div class="news-list__item-cnte p-2"> | |
19 | + <span class="news-list__item-cnte__title"> {{ item.cnteId }}</span> | |
20 | + <br /> | |
21 | + Status: {{ item.cnteStas }} | |
22 | + <br /> | |
23 | + Repository: {{ item.cnteRepo }} | |
24 | + <br /> | |
25 | + </div> | |
26 | + </div> | |
27 | + </template> | |
28 | + </ListItemMeta> | |
29 | + </ListItem> | |
30 | + </template> | |
31 | + </List> | |
32 | + </ScrollContainer> | |
33 | + </CollapseContainer> | |
34 | +</template> | |
35 | +<script lang="ts"> | |
36 | + import { defineComponent } from 'vue'; | |
37 | + import { List } from 'ant-design-vue'; | |
38 | + import { CollapseContainer, ScrollContainer } from '/@/components/Container/index'; | |
39 | + | |
40 | + import { newList } from '../data'; | |
41 | + export default defineComponent({ | |
42 | + components: { | |
43 | + List, | |
44 | + ListItem: List.Item, | |
45 | + ListItemMeta: List.Item.Meta, | |
46 | + CollapseContainer, | |
47 | + ScrollContainer, | |
48 | + }, | |
49 | + setup() { | |
50 | + return { newList }; | |
51 | + }, | |
52 | + }); | |
53 | +</script> | |
54 | +<style lang="less" scoped> | |
55 | + .news-list { | |
56 | + &__item { | |
57 | + &-avatar { | |
58 | + width: 35px; | |
59 | + height: 35px; | |
60 | + border-radius: 50%; | |
61 | + } | |
62 | + | |
63 | + &-title { | |
64 | + font-size: 14px; | |
65 | + line-height: 22px; | |
66 | + color: #000; | |
67 | + opacity: 0.65; | |
68 | + } | |
69 | + | |
70 | + &-time { | |
71 | + font-size: 14px; | |
72 | + line-height: 22px; | |
73 | + color: #000; | |
74 | + opacity: 0.45; | |
75 | + } | |
76 | + | |
77 | + &-light { | |
78 | + font-size: 14px; | |
79 | + line-height: 22px; | |
80 | + color: #000; | |
81 | + opacity: 0.85; | |
82 | + } | |
83 | + | |
84 | + &-cnte { | |
85 | + background: #eef3fb; | |
86 | + border-radius: 2px; | |
87 | + opacity: 0.6; | |
88 | + | |
89 | + &__title { | |
90 | + font-size: 14px; | |
91 | + line-height: 22px; | |
92 | + color: #2c3a61; | |
93 | + } | |
94 | + } | |
95 | + } | |
96 | + } | |
97 | +</style> | ... | ... |
src/views/dashboard/workbench/components/ProdTotal.vue
0 → 100644
1 | +<template> | |
2 | + <Row class="prod-total"> | |
3 | + <template v-for="(item, index) in wokbProd" :key="item.type"> | |
4 | + <Col :xs="12" :sm="6" class="prod-total__item" :class="`prod-total__item-${index}`"> | |
5 | + <div class="img" :class="`prod-total__item-${index}-img`" /> | |
6 | + <div>{{ item.amount }}</div> | |
7 | + <span>{{ item.type }}</span> | |
8 | + </Col> | |
9 | + </template> | |
10 | + </Row> | |
11 | +</template> | |
12 | +<script lang="tsx"> | |
13 | + import { defineComponent } from 'vue'; | |
14 | + import { Row, Col } from 'ant-design-vue'; | |
15 | + | |
16 | + import { wokbProd } from '../data'; | |
17 | + // import {ProdTypeEnum} from '@/api/dashboard/model/wokbModel' | |
18 | + export default defineComponent({ | |
19 | + components: { Row, Col }, | |
20 | + setup() { | |
21 | + return { wokbProd }; | |
22 | + }, | |
23 | + }); | |
24 | +</script> | |
25 | +<style lang="less" scoped> | |
26 | + .prod-total { | |
27 | + padding: 12px 4px 12px 12px; | |
28 | + background: #fff; | |
29 | + | |
30 | + &__item { | |
31 | + display: inline-block; | |
32 | + width: calc(25% - 8px); | |
33 | + padding: 20px 10px; | |
34 | + margin-right: 8px; | |
35 | + border-radius: 4px; | |
36 | + | |
37 | + span { | |
38 | + font-size: 14px; | |
39 | + line-height: 28px; | |
40 | + } | |
41 | + | |
42 | + div { | |
43 | + font-size: 26px; | |
44 | + } | |
45 | + | |
46 | + .img { | |
47 | + float: left; | |
48 | + width: 62px; | |
49 | + height: 62px; | |
50 | + } | |
51 | + | |
52 | + &-0 { | |
53 | + background: rgba(254, 97, 178, 0.1); | |
54 | + | |
55 | + &-img { | |
56 | + background: url(../../../../assets/images/dashboard/wokb/datashow1.png) no-repeat; | |
57 | + } | |
58 | + | |
59 | + div { | |
60 | + color: #fe61b2; | |
61 | + } | |
62 | + } | |
63 | + | |
64 | + &-1 { | |
65 | + background: rgba(254, 163, 64, 0.1); | |
66 | + | |
67 | + &-img { | |
68 | + background: url(../../../..//assets/images/dashboard/wokb/datashow2.png) no-repeat; | |
69 | + } | |
70 | + | |
71 | + div { | |
72 | + color: #fea340; | |
73 | + } | |
74 | + } | |
75 | + | |
76 | + &-2 { | |
77 | + background: rgba(172, 70, 255, 0.1); | |
78 | + | |
79 | + &-img { | |
80 | + background: url(../../../..//assets/images/dashboard/wokb/datashow3.png) no-repeat; | |
81 | + } | |
82 | + | |
83 | + div { | |
84 | + color: #9e55ff; | |
85 | + } | |
86 | + } | |
87 | + | |
88 | + &-3 { | |
89 | + background: rgba(0, 196, 186, 0.1); | |
90 | + | |
91 | + &-img { | |
92 | + background: url(../../../..//assets/images/dashboard/wokb/datashow4.png) no-repeat; | |
93 | + } | |
94 | + | |
95 | + div { | |
96 | + color: #00c4ba; | |
97 | + } | |
98 | + } | |
99 | + } | |
100 | + } | |
101 | +</style> | ... | ... |
src/views/dashboard/workbench/components/ShortCuts.vue
0 → 100644
1 | +<template> | |
2 | + <CollapseContainer class="shortcuts" title="快捷入口" :canExpan="false"> | |
3 | + <template #action> | |
4 | + <a-button size="small" type="link"> 新建 </a-button> | |
5 | + </template> | |
6 | + <Row> | |
7 | + <template v-for="item in shortCuts" :key="item.img"> | |
8 | + <Col :span="8" class="shortcuts__item p-3"> | |
9 | + <img :src="item.img" class="shortcuts__item-img mb-2" /> | |
10 | + <br /> | |
11 | + <span>{{ item.name }}</span> | |
12 | + </Col> | |
13 | + </template> | |
14 | + | |
15 | + <Col :span="8" class="shortcuts__item p-3"> | |
16 | + <span class="shortcuts__item-all mb-2"> | |
17 | + <RightOutlined /> | |
18 | + </span> | |
19 | + <br /> | |
20 | + <span>查看全部</span> | |
21 | + </Col> | |
22 | + </Row> | |
23 | + </CollapseContainer> | |
24 | +</template> | |
25 | +<script lang="ts"> | |
26 | + import { defineComponent } from 'vue'; | |
27 | + import { Row, Col } from 'ant-design-vue'; | |
28 | + import { CollapseContainer } from '/@/components/Container/index'; | |
29 | + | |
30 | + import { RightOutlined } from '@ant-design/icons-vue'; | |
31 | + import wokbImg1 from '/@/assets/images/dashboard/wokb/attendance.png'; | |
32 | + import wokbImg2 from '/@/assets/images/dashboard/wokb/overtime.png'; | |
33 | + import wokbImg3 from '/@/assets/images/dashboard/wokb/meal.png'; | |
34 | + import wokbImg4 from '/@/assets/images/dashboard/wokb/leave.png'; | |
35 | + import wokbImg5 from '/@/assets/images/dashboard/wokb/stamp.png'; | |
36 | + import wokbImg6 from '/@/assets/images/dashboard/wokb/travel.png'; | |
37 | + import wokbImg7 from '/@/assets/images/dashboard/wokb/performance.png'; | |
38 | + import wokbImg8 from '/@/assets/images/dashboard/wokb/approve.png'; | |
39 | + const shortCuts = [ | |
40 | + { | |
41 | + img: wokbImg1, | |
42 | + name: '考勤记录', | |
43 | + }, | |
44 | + { | |
45 | + img: wokbImg2, | |
46 | + name: '加班申请', | |
47 | + }, | |
48 | + { | |
49 | + img: wokbImg3, | |
50 | + name: '餐补申请', | |
51 | + }, | |
52 | + { | |
53 | + img: wokbImg4, | |
54 | + name: '请假', | |
55 | + }, | |
56 | + { | |
57 | + img: wokbImg5, | |
58 | + name: '用章申请', | |
59 | + }, | |
60 | + { | |
61 | + img: wokbImg6, | |
62 | + name: '差旅报销', | |
63 | + }, | |
64 | + { | |
65 | + img: wokbImg7, | |
66 | + name: '绩效申请', | |
67 | + }, | |
68 | + { | |
69 | + img: wokbImg8, | |
70 | + name: '审批', | |
71 | + }, | |
72 | + ]; | |
73 | + export default defineComponent({ | |
74 | + components: { Row, Col, CollapseContainer, RightOutlined }, | |
75 | + setup() { | |
76 | + return { shortCuts }; | |
77 | + }, | |
78 | + }); | |
79 | +</script> | |
80 | +<style lang="less" scoped> | |
81 | + .shortcuts { | |
82 | + &__item { | |
83 | + text-align: center; | |
84 | + | |
85 | + &-img { | |
86 | + width: 36px; | |
87 | + } | |
88 | + | |
89 | + &-all { | |
90 | + display: inline-block; | |
91 | + width: 36px; | |
92 | + height: 36px; | |
93 | + line-height: 36px; | |
94 | + color: #000; | |
95 | + cursor: pointer; | |
96 | + background: lightgrey; | |
97 | + border-radius: 50%; | |
98 | + } | |
99 | + } | |
100 | + } | |
101 | +</style> | ... | ... |
src/views/dashboard/workbench/components/TodoList.vue
0 → 100644
1 | +<template> | |
2 | + <CollapseContainer class="todo-list" title="待办事项" :canExpan="false"> | |
3 | + <template #title> | |
4 | + <span> 待办事项 <span class="todo-list__total">30</span> </span> | |
5 | + </template> | |
6 | + | |
7 | + <List> | |
8 | + <template v-for="item in todoList" :key="item.id"> | |
9 | + <ListItem class="todo-list__item"> | |
10 | + <ListItemMeta> | |
11 | + <template #title> | |
12 | + <div> | |
13 | + <span class="todo-list__item-title">{{ item.title }}</span> | |
14 | + <span class="todo-list__item-memo">{{ item.memo }}</span> | |
15 | + </div> | |
16 | + </template> | |
17 | + <template #description> | |
18 | + <div class="todo-list__item-desc"> | |
19 | + 提交人:{{ item.sbmter }} | |
20 | + <br /> | |
21 | + 提交时间:{{ item.sbmtTime }} | |
22 | + </div> | |
23 | + </template> | |
24 | + </ListItemMeta> | |
25 | + <a-button type="link"> | |
26 | + <Tag color="blue">待审批</Tag> | |
27 | + </a-button> | |
28 | + </ListItem> | |
29 | + </template> | |
30 | + </List> | |
31 | + <div class="todo-list__all"> | |
32 | + <Tooltip placement="topRight"> | |
33 | + <template #title>查看更多</template> | |
34 | + <EllipsisOutlined /> | |
35 | + </Tooltip> | |
36 | + </div> | |
37 | + </CollapseContainer> | |
38 | +</template> | |
39 | +<script lang="ts"> | |
40 | + import { defineComponent } from 'vue'; | |
41 | + import { List, Tag, Tooltip } from 'ant-design-vue'; | |
42 | + import { CollapseContainer } from '/@/components/Container/index'; | |
43 | + | |
44 | + import { EllipsisOutlined } from '@ant-design/icons-vue'; | |
45 | + import { todoList } from '../data'; | |
46 | + | |
47 | + export default defineComponent({ | |
48 | + name: 'TodoList', | |
49 | + components: { | |
50 | + CollapseContainer, | |
51 | + List, | |
52 | + ListItem: List.Item, | |
53 | + ListItemMeta: List.Item.Meta, | |
54 | + Tag, | |
55 | + Tooltip, | |
56 | + EllipsisOutlined, | |
57 | + }, | |
58 | + setup() { | |
59 | + return { todoList }; | |
60 | + }, | |
61 | + }); | |
62 | +</script> | |
63 | +<style lang="less" scoped> | |
64 | + .todo-list { | |
65 | + position: relative; | |
66 | + | |
67 | + &__total { | |
68 | + display: inline-block; | |
69 | + width: 20px; | |
70 | + height: 20px; | |
71 | + font-size: 12px; | |
72 | + line-height: 20px; | |
73 | + color: #fff; | |
74 | + text-align: center; | |
75 | + background: rgba(255, 0, 0, 0.7); | |
76 | + border-radius: 50%; | |
77 | + } | |
78 | + | |
79 | + &__all { | |
80 | + position: absolute; | |
81 | + top: 0; | |
82 | + right: 10px; | |
83 | + height: 56px; | |
84 | + font-size: 24px; | |
85 | + line-height: 56px; | |
86 | + text-align: center; | |
87 | + cursor: pointer; | |
88 | + } | |
89 | + | |
90 | + &__item { | |
91 | + padding: 8px; | |
92 | + | |
93 | + &-title { | |
94 | + font-size: 14px; | |
95 | + font-weight: normal; | |
96 | + line-height: 22px; | |
97 | + color: #1c1d21; | |
98 | + } | |
99 | + | |
100 | + &-memo { | |
101 | + font-size: 12px; | |
102 | + font-weight: normal; | |
103 | + line-height: 22px; | |
104 | + color: #7c8087; | |
105 | + } | |
106 | + | |
107 | + &-desc { | |
108 | + font-size: 12px; | |
109 | + line-height: 22px; | |
110 | + color: #7c8087; | |
111 | + } | |
112 | + } | |
113 | + } | |
114 | +</style> | ... | ... |
src/views/dashboard/workbench/components/Week.vue
0 → 100644
1 | +<template> | |
2 | + <CollapseContainer title="任务安排" :canExpan="false"> | |
3 | + <div ref="chartRef" :style="{ width: '100%' }" /> | |
4 | + </CollapseContainer> | |
5 | +</template> | |
6 | +<script lang="ts"> | |
7 | + import { defineComponent, Ref, ref, onMounted } from 'vue'; | |
8 | + | |
9 | + import { CollapseContainer } from '/@/components/Container/index'; | |
10 | + import { useApexCharts } from '/@/hooks/web/useApexCharts'; | |
11 | + | |
12 | + import moment from 'moment'; | |
13 | + export default defineComponent({ | |
14 | + components: { CollapseContainer }, | |
15 | + setup() { | |
16 | + const chartRef = ref<HTMLDivElement | null>(null); | |
17 | + const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>); | |
18 | + onMounted(() => { | |
19 | + setOptions({ | |
20 | + series: [ | |
21 | + { | |
22 | + data: [ | |
23 | + { | |
24 | + x: 'Analysis', | |
25 | + y: [new Date('2019-02-27').getTime(), new Date('2019-03-04').getTime()], | |
26 | + fillColor: '#008FFB', | |
27 | + }, | |
28 | + { | |
29 | + x: 'Design', | |
30 | + y: [new Date('2019-03-04').getTime(), new Date('2019-03-08').getTime()], | |
31 | + fillColor: '#00E396', | |
32 | + }, | |
33 | + { | |
34 | + x: 'Coding', | |
35 | + y: [new Date('2019-03-07').getTime(), new Date('2019-03-10').getTime()], | |
36 | + fillColor: '#775DD0', | |
37 | + }, | |
38 | + { | |
39 | + x: 'Testing', | |
40 | + y: [new Date('2019-03-08').getTime(), new Date('2019-03-12').getTime()], | |
41 | + fillColor: '#FEB019', | |
42 | + }, | |
43 | + { | |
44 | + x: 'Deployment', | |
45 | + y: [new Date('2019-03-12').getTime(), new Date('2019-03-17').getTime()], | |
46 | + fillColor: '#FF4560', | |
47 | + }, | |
48 | + ], | |
49 | + }, | |
50 | + ], | |
51 | + chart: { | |
52 | + height: 350, | |
53 | + type: 'rangeBar', | |
54 | + }, | |
55 | + plotOptions: { | |
56 | + bar: { | |
57 | + horizontal: true, | |
58 | + distributed: true, | |
59 | + dataLabels: { | |
60 | + hideOverflowingLabels: false, | |
61 | + }, | |
62 | + }, | |
63 | + }, | |
64 | + dataLabels: { | |
65 | + enabled: true, | |
66 | + formatter: function (val: any, opts: any) { | |
67 | + var label = opts.w.globals.labels[opts.dataPointIndex]; | |
68 | + var a = moment(val[0]); | |
69 | + var b = moment(val[1]); | |
70 | + var diff = b.diff(a, 'days'); | |
71 | + return label + ': ' + diff + (diff > 1 ? ' days' : ' day'); | |
72 | + }, | |
73 | + style: { | |
74 | + colors: ['#f3f4f5', '#fff'], | |
75 | + }, | |
76 | + }, | |
77 | + xaxis: { | |
78 | + type: 'datetime', | |
79 | + }, | |
80 | + yaxis: { | |
81 | + show: false, | |
82 | + }, | |
83 | + grid: { | |
84 | + row: { | |
85 | + colors: ['#f3f4f5', '#fff'], | |
86 | + opacity: 1, | |
87 | + }, | |
88 | + }, | |
89 | + }); | |
90 | + }); | |
91 | + return { chartRef }; | |
92 | + }, | |
93 | + }); | |
94 | +</script> | ... | ... |
src/views/dashboard/workbench/data.ts
0 → 100644
1 | +export const wokbProd = [ | |
2 | + { | |
3 | + amount: '20', | |
4 | + type: '成品总数', | |
5 | + }, | |
6 | + { | |
7 | + amount: '50', | |
8 | + type: '未发布', | |
9 | + }, | |
10 | + { | |
11 | + amount: '80', | |
12 | + type: '发布中', | |
13 | + }, | |
14 | + { | |
15 | + amount: '100', | |
16 | + type: '异常', | |
17 | + }, | |
18 | +]; | |
19 | + | |
20 | +export const todoList = (() => { | |
21 | + const ret: any[] = []; | |
22 | + for (let index = 0; index < 3; index++) { | |
23 | + ret.push({ | |
24 | + id: index, | |
25 | + sbmter: '张三', | |
26 | + sbmtTime: new Date().toLocaleString(), | |
27 | + title: '主要', | |
28 | + memo: '工作任务', | |
29 | + }); | |
30 | + } | |
31 | + return ret; | |
32 | +})(); | |
33 | +export const newList = (() => { | |
34 | + const ret: any[] = []; | |
35 | + for (let index = 0; index < 3; index++) { | |
36 | + ret.push({ | |
37 | + id: index, | |
38 | + sender: '李四', | |
39 | + sendTime: new Date().toLocaleString(), | |
40 | + title: '代码', | |
41 | + memo: '工作任务', | |
42 | + cnteId: `c${index}`, | |
43 | + cnteStas: 'opened', | |
44 | + cnteRepo: index, | |
45 | + }); | |
46 | + } | |
47 | + return ret; | |
48 | +})(); | ... | ... |
src/views/dashboard/workbench/index.vue
0 → 100644
1 | +<template> | |
2 | + <Row class="workbench p-4" :gutter="12"> | |
3 | + <Col :md="24" :lg="17"> | |
4 | + <ProdTotal class="mb-3" /> | |
5 | + <TodoList class="mb-3" /> | |
6 | + <NewsList class="mb-3" /> | |
7 | + </Col> | |
8 | + <Col :md="24" :lg="7"> | |
9 | + <img src="/@/assets/images/dashboard/wokb/wokb.png" class="workbench__wokb-img mb-3" /> | |
10 | + <ShortCuts class="mb-3" /> | |
11 | + <Week class="mb-3" /> | |
12 | + </Col> | |
13 | + </Row> | |
14 | +</template> | |
15 | +<script lang="ts"> | |
16 | + import { defineComponent } from 'vue'; | |
17 | + import { Row, Col } from 'ant-design-vue'; | |
18 | + import ProdTotal from './components/ProdTotal.vue'; | |
19 | + import TodoList from './components/TodoList.vue'; | |
20 | + import Week from './components/Week.vue'; | |
21 | + import NewsList from './components/NewsList.vue'; | |
22 | + import ShortCuts from './components/ShortCuts.vue'; | |
23 | + | |
24 | + export default defineComponent({ | |
25 | + components: { Row, Col, ProdTotal, TodoList, Week, ShortCuts, NewsList }, | |
26 | + setup() { | |
27 | + return {}; | |
28 | + }, | |
29 | + }); | |
30 | +</script> | |
31 | +<style lang="less" scoped> | |
32 | + .workbench { | |
33 | + &__wokb-img { | |
34 | + width: 100%; | |
35 | + height: 240px; | |
36 | + } | |
37 | + } | |
38 | +</style> | ... | ... |