|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 54 KiB |
@ -1,90 +0,0 @@ |
|||
<template> |
|||
<div ref="chartRef" :style="{ height, width }"></div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, onMounted, ref, Ref } from 'vue'; |
|||
|
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
import { basicProps } from './props'; |
|||
export default defineComponent({ |
|||
name: 'AnalysisLine', |
|||
props: basicProps, |
|||
setup() { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
|
|||
onMounted(() => { |
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
// 坐标轴指示器,坐标轴触发有效 |
|||
type: 'shadow', // 默认为直线,可选为:'line' | 'shadow' |
|||
}, |
|||
}, |
|||
legend: { |
|||
itemWidth: 15, |
|||
right: 10, |
|||
data: ['产品一', '产品二', '产品三'], |
|||
}, |
|||
grid: { |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '3%', |
|||
containLabel: true, |
|||
}, |
|||
xAxis: [ |
|||
{ |
|||
type: 'category', |
|||
axisTick: { |
|||
inside: true, // 刻度朝内 |
|||
}, |
|||
data: ['付费用户', '免费用户', '自主'], |
|||
}, |
|||
], |
|||
yAxis: [ |
|||
{ |
|||
type: 'value', |
|||
axisTick: { |
|||
inside: true, // 刻度朝内 |
|||
}, |
|||
}, |
|||
], |
|||
series: [ |
|||
{ |
|||
name: '产品一', |
|||
type: 'bar', |
|||
itemStyle: { |
|||
color: '#3ca0f6', |
|||
}, |
|||
data: [3200, 3320, 3010], |
|||
animationDuration: 4000, |
|||
}, |
|||
{ |
|||
name: '产品二', |
|||
type: 'bar', |
|||
itemStyle: { |
|||
color: '#7dd9b9', |
|||
}, |
|||
data: [1200, 2600, 1010], |
|||
animationDuration: 4000, |
|||
}, |
|||
|
|||
{ |
|||
name: '产品三', |
|||
type: 'bar', |
|||
itemStyle: { |
|||
color: '#e6a23c', |
|||
}, |
|||
data: [862, 2500, 964], |
|||
animationDuration: 4000, |
|||
}, |
|||
], |
|||
}); |
|||
}); |
|||
|
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,94 +0,0 @@ |
|||
<template> |
|||
<div ref="chartRef" :style="{ height, width }"></div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, onMounted, ref, Ref } from 'vue'; |
|||
|
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
import { basicProps } from './props'; |
|||
export default defineComponent({ |
|||
name: 'AnalysisLine', |
|||
props: basicProps, |
|||
setup() { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
|
|||
onMounted(() => { |
|||
setOptions({ |
|||
// title: { |
|||
// text: '产品成交额', |
|||
// }, |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
padding: 3, |
|||
borderColor: '#777', |
|||
borderWidth: 1, |
|||
}, |
|||
legend: { |
|||
itemWidth: 15, |
|||
itemHeight: 4, |
|||
left: 80, |
|||
top: 0, |
|||
orient: 'horizontal', |
|||
data: ['产品一', '产品二'], |
|||
}, |
|||
grid: { |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '3%', |
|||
containLabel: true, |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
boundaryGap: false, |
|||
axisTick: { |
|||
inside: true, // 刻度朝内 |
|||
}, |
|||
data: [ |
|||
'一月', |
|||
'二月', |
|||
'三月', |
|||
'四月', |
|||
'五月', |
|||
'六月', |
|||
'七月', |
|||
'八月', |
|||
'九月', |
|||
'十月', |
|||
'十一月', |
|||
'十二月', |
|||
], |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
axisTick: { |
|||
inside: true, // 刻度朝内 |
|||
}, |
|||
}, |
|||
series: [ |
|||
{ |
|||
name: '产品一', |
|||
type: 'line', |
|||
itemStyle: { |
|||
color: '#5B8FF9', |
|||
}, |
|||
data: [330, 132, 101, 134, 90, 230, 210, 150, 232, 234, 230, 400], |
|||
animationDuration: 4000, |
|||
}, |
|||
{ |
|||
name: '产品二', |
|||
type: 'line', |
|||
itemStyle: { |
|||
color: '#55D187', |
|||
}, |
|||
data: [220, 182, 191, 234, 290, 330, 310, 330, 232, 201, 330, 190], |
|||
animationDuration: 4000, |
|||
}, |
|||
], |
|||
}); |
|||
}); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,75 +0,0 @@ |
|||
<template> |
|||
<div ref="chartRef" :style="{ height, width }"></div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, onMounted, ref, Ref } from 'vue'; |
|||
|
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
import { basicProps } from './props'; |
|||
|
|||
const m2R2Data = [ |
|||
{ value: 335, name: '移动设备', itemStyle: { color: '#1b65b9' } }, |
|||
{ value: 310, name: '网页端', itemStyle: { color: '#3ca0f6' } }, |
|||
{ value: 234, name: '手表', itemStyle: { color: '#2dc0c0' } }, |
|||
{ value: 234, name: '其他', itemStyle: { color: '#7dd9b9' } }, |
|||
]; |
|||
export default defineComponent({ |
|||
props: basicProps, |
|||
setup() { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
|
|||
onMounted(() => { |
|||
setOptions({ |
|||
title: [ |
|||
{ |
|||
text: '总设备', |
|||
subtext: '1,430', |
|||
textStyle: { |
|||
fontSize: 12, |
|||
color: '#4B535E85', |
|||
}, |
|||
subtextStyle: { |
|||
fontSize: 24, |
|||
color: 'black', |
|||
}, |
|||
textAlign: 'center', |
|||
left: '34.5%', |
|||
top: '40%', |
|||
}, |
|||
], |
|||
tooltip: { |
|||
trigger: 'item', |
|||
}, |
|||
legend: { |
|||
itemHeight: 10, |
|||
type: 'scroll', |
|||
orient: 'vertical', |
|||
left: '70%', |
|||
align: 'left', |
|||
top: 'middle', |
|||
textStyle: { |
|||
color: '#8C8C8C', |
|||
}, |
|||
height: 250, |
|||
}, |
|||
series: [ |
|||
{ |
|||
name: '成交额', |
|||
type: 'pie', |
|||
center: ['35%', '50%'], |
|||
radius: ['45%', '65%'], |
|||
label: { |
|||
show: false, |
|||
}, |
|||
data: m2R2Data, |
|||
animationDuration: 3000, |
|||
}, |
|||
], |
|||
}); |
|||
}); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,80 +0,0 @@ |
|||
import { defineComponent } from 'vue'; |
|||
import { Tabs, Row, Col, Progress, Divider } from 'ant-design-vue'; |
|||
import { CollapseContainer } from '/@/components/Container/index'; |
|||
import TrendLine from './TrendLine.vue'; |
|||
import './flow-ana.less'; |
|||
const prefixCls = 'flow-analysis'; |
|||
export default defineComponent({ |
|||
name: 'AnalysisFLow', |
|||
setup() { |
|||
const renderContent = () => { |
|||
return ( |
|||
<Row> |
|||
{() => ( |
|||
<> |
|||
<Col md={24} lg={8}> |
|||
{() => ( |
|||
<CollapseContainer |
|||
title="整体流量评分" |
|||
canExpan={false} |
|||
class={`${prefixCls}__left`} |
|||
> |
|||
{() => ( |
|||
<div> |
|||
<div class={`${prefixCls}__score`}> |
|||
86.2<span>分</span> |
|||
</div> |
|||
<div class={`${prefixCls}__rank`}> |
|||
排名<span>前20%</span> |
|||
</div> |
|||
<Progress percent={70} showInfo={false} status="active" /> |
|||
<Divider /> |
|||
|
|||
<ul class={`${prefixCls}__rs`}> |
|||
<li> |
|||
<span>平均分</span> |
|||
<span>77.5</span> |
|||
</li> |
|||
<li> |
|||
<span>最高分</span> |
|||
<span>99.5</span> |
|||
</li> |
|||
<li> |
|||
<span>最低分</span> |
|||
<span>56.5</span> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
)} |
|||
</CollapseContainer> |
|||
)} |
|||
</Col> |
|||
<Col md={24} lg={16}> |
|||
{() => ( |
|||
<CollapseContainer title="整体流量趋势" canExpan={false}> |
|||
{() => <TrendLine />} |
|||
</CollapseContainer> |
|||
)} |
|||
</Col> |
|||
</> |
|||
)} |
|||
</Row> |
|||
); |
|||
}; |
|||
return () => ( |
|||
<Tabs class={prefixCls} default-active-key="1"> |
|||
{() => [ |
|||
<Tabs.TabPane key="1" tab="产品一"> |
|||
{() => renderContent()} |
|||
</Tabs.TabPane>, |
|||
<Tabs.TabPane key="2" tab="产品二"> |
|||
{() => renderContent()} |
|||
</Tabs.TabPane>, |
|||
<Tabs.TabPane key="3" tab="产品三"> |
|||
{() => renderContent()} |
|||
</Tabs.TabPane>, |
|||
]} |
|||
</Tabs> |
|||
); |
|||
}, |
|||
}); |
|||
@ -0,0 +1,69 @@ |
|||
<template> |
|||
<Card title="成交占比" :loading="loading"> |
|||
<div ref="chartRef" :style="{ width, height }"></div> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, Ref, ref, watch } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card }, |
|||
props: { |
|||
loading: Boolean, |
|||
width: { |
|||
type: String as PropType<string>, |
|||
default: '100%', |
|||
}, |
|||
height: { |
|||
type: String as PropType<string>, |
|||
default: '300px', |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
watch( |
|||
() => props.loading, |
|||
() => { |
|||
if (props.loading) { |
|||
return; |
|||
} |
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'item', |
|||
}, |
|||
|
|||
series: [ |
|||
{ |
|||
name: '访问来源', |
|||
type: 'pie', |
|||
radius: '80%', |
|||
center: ['50%', '50%'], |
|||
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'], |
|||
data: [ |
|||
{ value: 500, name: '电子产品' }, |
|||
{ value: 310, name: '服装' }, |
|||
{ value: 274, name: '化妆品' }, |
|||
{ value: 400, name: '家居' }, |
|||
].sort(function (a, b) { |
|||
return a.value - b.value; |
|||
}), |
|||
roseType: 'radius', |
|||
animationType: 'scale', |
|||
animationEasing: 'exponentialInOut', |
|||
animationDelay: function () { |
|||
return Math.random() * 400; |
|||
}, |
|||
}, |
|||
], |
|||
}); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,46 @@ |
|||
<template> |
|||
<Card |
|||
:tab-list="tabListTitle" |
|||
v-bind="$attrs" |
|||
:active-tab-key="activeKey" |
|||
@tabChange="onTabChange" |
|||
> |
|||
<p v-if="activeKey === 'tab1'"> |
|||
<VisitAnalysis /> |
|||
</p> |
|||
<p v-if="activeKey === 'tab2'"> |
|||
<VisitAnalysisBar /> |
|||
</p> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, ref } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
|
|||
import VisitAnalysis from './VisitAnalysis.vue'; |
|||
import VisitAnalysisBar from './VisitAnalysisBar.vue'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card, VisitAnalysis, VisitAnalysisBar }, |
|||
setup() { |
|||
const activeKey = ref('tab1'); |
|||
|
|||
const tabListTitle = [ |
|||
{ |
|||
key: 'tab1', |
|||
tab: '流量趋势', |
|||
}, |
|||
{ |
|||
key: 'tab2', |
|||
tab: '访问量', |
|||
}, |
|||
]; |
|||
|
|||
function onTabChange(key) { |
|||
activeKey.value = key; |
|||
} |
|||
return { tabListTitle, activeKey, onTabChange }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,154 +0,0 @@ |
|||
<template> |
|||
<div :class="prefixCls"> |
|||
<div :class="`${prefixCls}-header`"> |
|||
<div :class="`${prefixCls}__info`"> |
|||
<span :class="`${prefixCls}__title`">{{ info.title }}</span> |
|||
<span :class="`${prefixCls}__desc`">{{ info.desc }}</span> |
|||
</div> |
|||
<span :class="`${prefixCls}__tag ${info.status}`">{{ info.text }}</span> |
|||
</div> |
|||
|
|||
<div :class="`${prefixCls}-body mt-5`"> |
|||
<div :class="`${prefixCls}__process-nfo`"> |
|||
<span>进度</span> |
|||
<span>{{ info.percent }}%</span> |
|||
</div> |
|||
<Progress :percent="info.percent" :showInfo="false" :status="info.status" /> |
|||
</div> |
|||
<div :class="`${prefixCls}-footer`"> |
|||
<span :class="`${prefixCls}__date`"> |
|||
更新日期: <span>{{ info.updateTime }}</span> |
|||
</span> |
|||
<div :class="`${prefixCls}__avatar`"> |
|||
<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" /> |
|||
<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" /> |
|||
<Avatar>+3</Avatar> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { computed, defineComponent, PropType } from 'vue'; |
|||
import { Progress, Avatar } from 'ant-design-vue'; |
|||
|
|||
import { TaskItem } from '../types'; |
|||
|
|||
export default defineComponent({ |
|||
name: 'GrowCard', |
|||
components: { Progress, Avatar }, |
|||
props: { |
|||
info: { |
|||
type: Object as PropType<TaskItem>, |
|||
default: null, |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
return { |
|||
prefixCls: 'task-card', |
|||
text: computed(() => { |
|||
const { status } = props.info || {}; |
|||
return status === 'active' |
|||
? '进度正常' |
|||
: status === 'exception' |
|||
? '进度滞后' |
|||
: '项目完成'; |
|||
}), |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.task-card { |
|||
display: flex; |
|||
width: calc(100% - 24px); |
|||
height: 199px; |
|||
padding: 24px 20px 12px 16px; |
|||
margin: 0 12px 12px 12px; |
|||
background: #fff; |
|||
border: 1px solid #ececf2; |
|||
border-radius: 12px; |
|||
flex-direction: column; |
|||
|
|||
&-header { |
|||
display: flex; |
|||
width: 100%; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
&__tag { |
|||
display: inline-block; |
|||
padding: 4px 6px; |
|||
font-family: PingFangSC-Regular; |
|||
font-size: 12px; |
|||
border-radius: 6px; |
|||
|
|||
&.success { |
|||
color: #55d187; |
|||
background: rgba(85, 209, 135, 0.16); |
|||
} |
|||
|
|||
&.warn { |
|||
color: #ffa07d; |
|||
background: #ffd16416; |
|||
} |
|||
|
|||
&.done { |
|||
color: #0593ff; |
|||
background: #0593ff16; |
|||
} |
|||
} |
|||
|
|||
&__info { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
&__title { |
|||
font-family: PingFangSC-Medium; |
|||
font-size: 16px; |
|||
line-height: 24px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
} |
|||
|
|||
&__desc { |
|||
font-family: PingFangSC-Regular; |
|||
font-size: 12px; |
|||
line-height: 21px; |
|||
color: #8181a5; |
|||
} |
|||
|
|||
&__process-nfo { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
span { |
|||
font-size: 14px; |
|||
line-height: 21px; |
|||
color: #8181a5; |
|||
} |
|||
} |
|||
|
|||
&-footer { |
|||
display: flex; |
|||
width: 100%; |
|||
margin-top: 16px; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
&__date { |
|||
font-size: 12px; |
|||
line-height: 21px; |
|||
color: #2c3a61; |
|||
|
|||
span { |
|||
color: #7c8087; |
|||
} |
|||
} |
|||
|
|||
&__avatar { |
|||
display: flex; |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,97 +0,0 @@ |
|||
<template> |
|||
<div ref="chartRef" :style="{ height, width }"></div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, onMounted, ref, Ref } from 'vue'; |
|||
|
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
import { basicProps } from './props'; |
|||
export default defineComponent({ |
|||
props: basicProps, |
|||
setup() { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
|
|||
onMounted(() => { |
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
padding: 3, |
|||
borderColor: '#777', |
|||
borderWidth: 1, |
|||
}, |
|||
legend: { |
|||
show: false, |
|||
}, |
|||
grid: { |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '3%', |
|||
containLabel: true, |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
boundaryGap: false, |
|||
axisTick: { |
|||
inside: true, |
|||
}, |
|||
data: [ |
|||
'一月', |
|||
'二月', |
|||
'三月', |
|||
'四月', |
|||
'五月', |
|||
'六月', |
|||
'七月', |
|||
'八月', |
|||
'九月', |
|||
'十月', |
|||
'十一月', |
|||
'十二月', |
|||
], |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
axisTick: { |
|||
inside: true, |
|||
}, |
|||
}, |
|||
series: [ |
|||
{ |
|||
name: '产品一', |
|||
type: 'line', |
|||
itemStyle: { |
|||
color: '#5B8FF9', |
|||
}, |
|||
areaStyle: { |
|||
color: new echarts.graphic.LinearGradient( |
|||
0, |
|||
0, |
|||
0, |
|||
1, |
|||
[ |
|||
{ |
|||
offset: 0, |
|||
color: '#5B8FF9', |
|||
}, |
|||
{ |
|||
offset: 1, |
|||
color: 'rgba(118,168,248, 0)', |
|||
}, |
|||
], |
|||
false |
|||
), |
|||
shadowColor: 'rgba(118,168,248, 0.9)', |
|||
shadowBlur: 20, |
|||
}, |
|||
data: [134, 330, 132, 101, 90, 230, 210, 150, 230, 400, 232, 234], |
|||
animationDuration: 3000, |
|||
}, |
|||
], |
|||
}); |
|||
}); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,146 @@ |
|||
<template> |
|||
<div ref="chartRef" :style="{ height, width }"></div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, onMounted, ref, Ref } from 'vue'; |
|||
|
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
import { basicProps } from './props'; |
|||
export default defineComponent({ |
|||
props: basicProps, |
|||
setup() { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
|
|||
onMounted(() => { |
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
lineStyle: { |
|||
width: 1, |
|||
color: '#019680', |
|||
}, |
|||
}, |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
boundaryGap: false, |
|||
data: [ |
|||
'6:00', |
|||
'7:00', |
|||
'8:00', |
|||
'9:00', |
|||
'10:00', |
|||
'11:00', |
|||
'12:00', |
|||
'13:00', |
|||
'14:00', |
|||
'15:00', |
|||
'16:00', |
|||
'17:00', |
|||
'18:00', |
|||
'19:00', |
|||
'20:00', |
|||
'21:00', |
|||
'22:00', |
|||
'23:00', |
|||
], |
|||
splitLine: { |
|||
show: true, |
|||
lineStyle: { |
|||
width: 1, |
|||
type: 'solid', |
|||
color: 'rgba(226,226,226,0.5)', |
|||
}, |
|||
}, |
|||
axisTick: { |
|||
show: false, |
|||
}, |
|||
}, |
|||
yAxis: [ |
|||
{ |
|||
type: 'value', |
|||
max: 80000, |
|||
splitNumber: 4, |
|||
axisTick: { |
|||
show: false, |
|||
}, |
|||
splitArea: { |
|||
show: true, |
|||
areaStyle: { |
|||
color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'], |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true }, |
|||
series: [ |
|||
{ |
|||
smooth: true, |
|||
data: [ |
|||
111, |
|||
222, |
|||
4000, |
|||
18000, |
|||
33333, |
|||
55555, |
|||
66666, |
|||
33333, |
|||
14000, |
|||
36000, |
|||
66666, |
|||
44444, |
|||
22222, |
|||
11111, |
|||
4000, |
|||
2000, |
|||
500, |
|||
333, |
|||
222, |
|||
111, |
|||
], |
|||
type: 'line', |
|||
areaStyle: {}, |
|||
itemStyle: { |
|||
color: '#5ab1ef', |
|||
}, |
|||
}, |
|||
{ |
|||
smooth: true, |
|||
data: [ |
|||
33, |
|||
66, |
|||
88, |
|||
333, |
|||
3333, |
|||
5000, |
|||
18000, |
|||
3000, |
|||
1200, |
|||
13000, |
|||
22000, |
|||
11000, |
|||
2221, |
|||
1201, |
|||
390, |
|||
198, |
|||
60, |
|||
30, |
|||
22, |
|||
11, |
|||
], |
|||
type: 'line', |
|||
areaStyle: {}, |
|||
itemStyle: { |
|||
color: '#019680', |
|||
}, |
|||
}, |
|||
], |
|||
}); |
|||
}); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,62 @@ |
|||
<template> |
|||
<div ref="chartRef" :style="{ height, width }"></div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, onMounted, ref, Ref } from 'vue'; |
|||
|
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
import { basicProps } from './props'; |
|||
export default defineComponent({ |
|||
props: basicProps, |
|||
setup() { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
|
|||
onMounted(() => { |
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
lineStyle: { |
|||
width: 1, |
|||
color: '#019680', |
|||
}, |
|||
}, |
|||
}, |
|||
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true }, |
|||
xAxis: { |
|||
type: 'category', |
|||
data: [ |
|||
'1月', |
|||
'2月', |
|||
'3月', |
|||
'4月', |
|||
'5月', |
|||
'6月', |
|||
'7月', |
|||
'8月', |
|||
'9月', |
|||
'10月', |
|||
'11月', |
|||
'12月', |
|||
], |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
max: 8000, |
|||
splitNumber: 4, |
|||
}, |
|||
series: [ |
|||
{ |
|||
data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800], |
|||
type: 'bar', |
|||
barMaxWidth: 80, |
|||
}, |
|||
], |
|||
}); |
|||
}); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,107 @@ |
|||
<template> |
|||
<Card title="转化率" :loading="loading"> |
|||
<div ref="chartRef" :style="{ width, height }"></div> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, Ref, ref, watch } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card }, |
|||
props: { |
|||
loading: Boolean, |
|||
width: { |
|||
type: String as PropType<string>, |
|||
default: '100%', |
|||
}, |
|||
height: { |
|||
type: String as PropType<string>, |
|||
default: '300px', |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
watch( |
|||
() => props.loading, |
|||
() => { |
|||
if (props.loading) { |
|||
return; |
|||
} |
|||
setOptions({ |
|||
backgroundColor: '#fff', |
|||
legend: { |
|||
bottom: 0, |
|||
data: ['访问', '购买'], |
|||
}, |
|||
tooltip: {}, |
|||
radar: { |
|||
radius: '60%', |
|||
splitNumber: 8, |
|||
indicator: [ |
|||
{ |
|||
text: '电脑', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '充电器', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '耳机', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '手机', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: 'Ipad', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '耳机', |
|||
max: 100, |
|||
}, |
|||
], |
|||
}, |
|||
series: [ |
|||
{ |
|||
type: 'radar', |
|||
symbolSize: 0, |
|||
areaStyle: { |
|||
shadowBlur: 0, |
|||
shadowColor: 'rgba(0,0,0,.2)', |
|||
shadowOffsetX: 0, |
|||
shadowOffsetY: 10, |
|||
opacity: 1, |
|||
}, |
|||
data: [ |
|||
{ |
|||
value: [90, 50, 86, 40, 50, 20], |
|||
name: '访问', |
|||
itemStyle: { |
|||
color: '#b6a2de', |
|||
}, |
|||
}, |
|||
{ |
|||
value: [70, 75, 70, 76, 20, 85], |
|||
name: '购买', |
|||
itemStyle: { |
|||
color: '#5ab1ef', |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,88 @@ |
|||
<template> |
|||
<Card title="访问来源" :loading="loading"> |
|||
<div ref="chartRef" :style="{ width, height }"></div> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, Ref, ref, watch } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card }, |
|||
props: { |
|||
loading: Boolean, |
|||
width: { |
|||
type: String as PropType<string>, |
|||
default: '100%', |
|||
}, |
|||
height: { |
|||
type: String as PropType<string>, |
|||
default: '300px', |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
watch( |
|||
() => props.loading, |
|||
() => { |
|||
if (props.loading) { |
|||
return; |
|||
} |
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'item', |
|||
}, |
|||
legend: { |
|||
bottom: '1%', |
|||
left: 'center', |
|||
}, |
|||
series: [ |
|||
{ |
|||
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'], |
|||
name: '访问来源', |
|||
type: 'pie', |
|||
radius: ['40%', '70%'], |
|||
avoidLabelOverlap: false, |
|||
itemStyle: { |
|||
borderRadius: 10, |
|||
borderColor: '#fff', |
|||
borderWidth: 2, |
|||
}, |
|||
label: { |
|||
show: false, |
|||
position: 'center', |
|||
}, |
|||
emphasis: { |
|||
label: { |
|||
show: true, |
|||
fontSize: '12', |
|||
fontWeight: 'bold', |
|||
}, |
|||
}, |
|||
labelLine: { |
|||
show: false, |
|||
}, |
|||
data: [ |
|||
{ value: 1048, name: '搜索引擎' }, |
|||
{ value: 735, name: '直接访问' }, |
|||
{ value: 580, name: '邮件营销' }, |
|||
{ value: 484, name: '联盟广告' }, |
|||
], |
|||
animationType: 'scale', |
|||
animationEasing: 'exponentialInOut', |
|||
animationDelay: function () { |
|||
return Math.random() * 100; |
|||
}, |
|||
}, |
|||
], |
|||
}); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,56 +0,0 @@ |
|||
.flow-analysis { |
|||
width: 100%; |
|||
background: #fff; |
|||
|
|||
&__left { |
|||
padding: 10px 20px !important; |
|||
border-right: 1px solid rgba(0, 0, 0, 0.06); |
|||
border-radius: 0; |
|||
} |
|||
|
|||
&__score { |
|||
margin-top: 20px; |
|||
font-size: 30px; |
|||
line-height: 38px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
|
|||
span { |
|||
font-size: 20px; |
|||
line-height: 28px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
} |
|||
} |
|||
|
|||
&__rank { |
|||
margin: 16px 0; |
|||
font-size: 12px; |
|||
line-height: 20px; |
|||
color: #7c8087; |
|||
|
|||
span { |
|||
display: inline-block; |
|||
margin-left: 10px; |
|||
color: #1c1d21; |
|||
} |
|||
} |
|||
|
|||
&__rs { |
|||
li { |
|||
display: flex; |
|||
line-height: 28px; |
|||
justify-content: space-between; |
|||
|
|||
span { |
|||
&:nth-child(1) { |
|||
font-size: 14px; |
|||
color: #1c1d21; |
|||
} |
|||
|
|||
&:nth-child(2) { |
|||
font-size: 16px; |
|||
color: #1c1d21; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
export interface GrowCardItem { |
|||
icon: string; |
|||
title: string; |
|||
value: number; |
|||
total: number; |
|||
color: string; |
|||
action: string; |
|||
} |
|||
|
|||
export const growCardList: GrowCardItem[] = [ |
|||
{ |
|||
title: '访问数', |
|||
icon: 'visit-count|svg', |
|||
value: 2000, |
|||
total: 120000, |
|||
color: 'green', |
|||
action: '月', |
|||
}, |
|||
{ |
|||
title: '成交额', |
|||
icon: 'total-sales|svg', |
|||
value: 20000, |
|||
total: 500000, |
|||
color: 'blue', |
|||
action: '月', |
|||
}, |
|||
{ |
|||
title: '下载数', |
|||
icon: 'download-count|svg', |
|||
value: 8000, |
|||
total: 120000, |
|||
color: 'orange', |
|||
action: '周', |
|||
}, |
|||
{ |
|||
title: '成交数', |
|||
icon: 'transaction|svg', |
|||
value: 5000, |
|||
total: 50000, |
|||
color: 'purple', |
|||
action: '年', |
|||
}, |
|||
]; |
|||
@ -1,107 +0,0 @@ |
|||
import { GrowCardItem, TaskItem } from './types'; |
|||
import iconSvg1 from '/@/assets/svg/dashboard/analysis-icon1.svg'; |
|||
import iconSvg2 from '/@/assets/svg/dashboard/analysis-icon2.svg'; |
|||
import iconSvg3 from '/@/assets/svg/dashboard/analysis-icon3.svg'; |
|||
import iconSvg4 from '/@/assets/svg/dashboard/analysis-icon4.svg'; |
|||
export const taskList: TaskItem[] = [ |
|||
{ |
|||
percent: 50, |
|||
title: '开发任务一', |
|||
updateTime: '2020.7.12', |
|||
desc: '开发任务一简介', |
|||
status: 'active', |
|||
}, |
|||
{ |
|||
percent: 67, |
|||
title: '开发任务二', |
|||
updateTime: '2020.3.12', |
|||
desc: '开发任务二简介', |
|||
status: 'exception', |
|||
}, |
|||
{ |
|||
percent: 100, |
|||
title: '开发任务三', |
|||
updateTime: '2020.4.12', |
|||
desc: '开发任务三简介', |
|||
|
|||
status: 'success', |
|||
}, |
|||
]; |
|||
export const growCardList: GrowCardItem[] = [ |
|||
{ |
|||
title: '总用户数', |
|||
icon: iconSvg1, |
|||
price: 80000, |
|||
up: true, |
|||
mom: '环比增长', |
|||
percent: 2.5, |
|||
}, |
|||
{ |
|||
title: '产品数量', |
|||
icon: iconSvg2, |
|||
price: 4000, |
|||
up: true, |
|||
mom: '同比增长', |
|||
percent: 3, |
|||
}, |
|||
{ |
|||
title: '总营业额', |
|||
icon: iconSvg3, |
|||
price: 3000000, |
|||
up: false, |
|||
mom: '环比降低', |
|||
percent: 2, |
|||
}, |
|||
{ |
|||
title: '总任务数', |
|||
icon: iconSvg4, |
|||
price: 10000, |
|||
up: false, |
|||
mom: '同比降低', |
|||
percent: 1, |
|||
}, |
|||
]; |
|||
export const randomizeArray = function (arg: any) { |
|||
const array = arg.slice(); |
|||
let currentIndex = array.length, |
|||
temporaryValue, |
|||
randomIndex; |
|||
|
|||
while (0 !== currentIndex) { |
|||
randomIndex = Math.floor(Math.random() * currentIndex); |
|||
currentIndex -= 1; |
|||
|
|||
temporaryValue = array[currentIndex]; |
|||
array[currentIndex] = array[randomIndex]; |
|||
array[randomIndex] = temporaryValue; |
|||
} |
|||
|
|||
return array; |
|||
}; |
|||
|
|||
export const sparklineData = [ |
|||
47, |
|||
45, |
|||
54, |
|||
38, |
|||
56, |
|||
24, |
|||
65, |
|||
31, |
|||
37, |
|||
39, |
|||
62, |
|||
51, |
|||
35, |
|||
41, |
|||
35, |
|||
27, |
|||
93, |
|||
53, |
|||
61, |
|||
27, |
|||
54, |
|||
43, |
|||
19, |
|||
46, |
|||
]; |
|||
@ -1,16 +0,0 @@ |
|||
export interface GrowCardItem { |
|||
icon: string; |
|||
title: string; |
|||
price: number; |
|||
up: boolean; |
|||
mom: string; |
|||
percent: number; |
|||
} |
|||
|
|||
export interface TaskItem { |
|||
percent: number; |
|||
status: 'success' | 'exception' | 'active'; |
|||
updateTime: string; |
|||
title: string; |
|||
desc: string; |
|||
} |
|||
@ -1,494 +0,0 @@ |
|||
.house-wrap { |
|||
position: relative; |
|||
width: 600px; |
|||
height: 600px; |
|||
transform: scale(0.5); |
|||
|
|||
.house { |
|||
position: absolute; |
|||
position: relative; |
|||
top: 50%; |
|||
left: 50%; |
|||
display: flex; |
|||
width: 400px; |
|||
height: 300px; |
|||
transform: translateX(-50%) translateY(-13%); |
|||
justify-content: center; |
|||
perspective: 200px; |
|||
} |
|||
|
|||
.floor { |
|||
position: absolute; |
|||
bottom: 0; |
|||
display: flex; |
|||
width: 95%; |
|||
height: 30px; |
|||
background-color: #e1f6fd; |
|||
border: 4px solid #314b70; |
|||
border-top-right-radius: 4px; |
|||
border-top-left-radius: 4px; |
|||
box-shadow: inset 4px 4px 0 #fffdff; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.floor::before, |
|||
.floor::after { |
|||
position: absolute; |
|||
bottom: 0; |
|||
width: 32%; |
|||
height: 60%; |
|||
background-image: linear-gradient(to bottom, #e0f5fc 50%, #aac4d0 50%); |
|||
border-top: 4px solid #314b70; |
|||
border-right: 4px solid #314b70; |
|||
border-left: 4px solid #314b70; |
|||
border-top-right-radius: 4px; |
|||
border-top-left-radius: 4px; |
|||
content: ''; |
|||
box-shadow: 4px 0 0 #aac4d0; |
|||
} |
|||
|
|||
.floor::after { |
|||
top: 0; |
|||
width: 25%; |
|||
height: 40%; |
|||
border-top: none; |
|||
border-top-right-radius: 0; |
|||
border-top-left-radius: 0; |
|||
} |
|||
|
|||
.wall { |
|||
position: absolute; |
|||
bottom: 30px; |
|||
display: flex; |
|||
width: 91%; |
|||
height: 175px; |
|||
overflow: hidden; |
|||
background: #c3e0e7; |
|||
border-right: 4px solid #314b70; |
|||
border-left: 4px solid #314b70; |
|||
justify-content: space-between; |
|||
align-items: flex-end; |
|||
} |
|||
|
|||
.window { |
|||
position: relative; |
|||
width: 34%; |
|||
height: 125px; |
|||
background: #aac4d0; |
|||
border-top: 4px solid #314b70; |
|||
border-right: 4px solid #314b70; |
|||
border-bottom: none; |
|||
border-left: none; |
|||
border-top-right-radius: 8px; |
|||
box-shadow: inset 0 4px 2px #e0f5fc; |
|||
} |
|||
|
|||
.window::before { |
|||
position: absolute; |
|||
top: 6%; |
|||
left: 0; |
|||
width: 94%; |
|||
height: 88%; |
|||
background-image: linear-gradient(to top, #f3f6fa 47%, #9ab2d3 47%, #9ab2d3 50%, #f3f6fa 50%); |
|||
border-top: 4px solid #314b70; |
|||
border-right: 4px solid #314b70; |
|||
border-bottom: 4px solid #314b70; |
|||
border-left: none; |
|||
border-top-right-radius: 4px; |
|||
border-bottom-right-radius: 4px; |
|||
content: ''; |
|||
} |
|||
|
|||
.window::after { |
|||
position: absolute; |
|||
top: 19%; |
|||
left: 20%; |
|||
width: 30px; |
|||
height: 40px; |
|||
background-color: #f9aabe; |
|||
border: 4px solid #9ab2d3; |
|||
content: ''; |
|||
} |
|||
|
|||
.window:nth-of-type(3) { |
|||
border-top: none; |
|||
border-right: 4px solid #314b70; |
|||
border-bottom: 4px solid #314b70; |
|||
border-left: none; |
|||
border-top-right-radius: 0; |
|||
border-bottom-right-radius: 8px; |
|||
transform: rotateZ(180deg); |
|||
box-shadow: inset 0 -4px 2px #e0f5fc; |
|||
} |
|||
|
|||
.window:nth-of-type(3)::after { |
|||
content: none; |
|||
} |
|||
|
|||
.door { |
|||
display: flex; |
|||
width: 20%; |
|||
height: 130px; |
|||
padding-left: 8px; |
|||
background-color: #ffc26b; |
|||
border: 4px solid #314b70; |
|||
border-bottom: none; |
|||
border-top-right-radius: 10px; |
|||
border-top-left-radius: 10px; |
|||
box-shadow: inset 3px 3px #ffe0ad, inset -10px -8px #ffad61, 4px 0 #aac4d0; |
|||
flex-direction: column; |
|||
justify-content: space-evenly; |
|||
align-items: flex-start; |
|||
} |
|||
|
|||
.door__square { |
|||
width: 85%; |
|||
height: 47px; |
|||
border: 4px solid #314b70; |
|||
border-radius: 4px; |
|||
box-shadow: inset 3px 3px #ffe0ad; |
|||
} |
|||
|
|||
.door__line { |
|||
width: 25%; |
|||
height: 4px; |
|||
background: #314b70; |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
.top { |
|||
position: absolute; |
|||
width: 82%; |
|||
height: 30px; |
|||
background-color: #aac4d0; |
|||
border: 4px solid #314b70; |
|||
border-top-right-radius: 4px; |
|||
border-top-left-radius: 4px; |
|||
box-shadow: inset 4px 4px 0 #e1f6fd; |
|||
} |
|||
|
|||
.circle { |
|||
position: absolute; |
|||
top: -10%; |
|||
display: flex; |
|||
width: 115px; |
|||
height: 115px; |
|||
background-color: #e0f5fc; |
|||
border: 4px solid #314b70; |
|||
border-radius: 50%; |
|||
content: ''; |
|||
box-shadow: inset 4px 4px 0 #fffdff, inset 4px -4px 0 #fffdff, inset -4px 4px 0 #fffdff, |
|||
inset -4px -4px 0 #fffdff; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.circle::before, |
|||
.circle::after { |
|||
position: absolute; |
|||
top: 35%; |
|||
width: 70%; |
|||
height: 4px; |
|||
background-color: #314b70; |
|||
content: ''; |
|||
} |
|||
|
|||
.circle::after { |
|||
top: 20%; |
|||
width: 35%; |
|||
} |
|||
|
|||
.plastic { |
|||
position: absolute; |
|||
top: 30%; |
|||
z-index: 100; |
|||
width: 100%; |
|||
height: 30px; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.plastic__g { |
|||
display: flex; |
|||
justify-content: center; |
|||
width: 100%; |
|||
overflow: hidden; |
|||
transform: translateY(-22px); |
|||
} |
|||
|
|||
.plastic__item { |
|||
width: 43px; |
|||
height: 43px; |
|||
margin-bottom: 4px; |
|||
border: 3px solid #314b70; |
|||
border-radius: 50%; |
|||
box-shadow: 0 4px 0 #aac4d0; |
|||
} |
|||
|
|||
.plastic__item:nth-child(odd) { |
|||
background: #0792d9; |
|||
box-shadow: 0 4px 0 #aac4d0, inset 4px 4px 0 #66c8fa; |
|||
} |
|||
|
|||
.plastic__item:nth-child(even) { |
|||
background: #fffdff; |
|||
} |
|||
|
|||
.plastic__item:nth-of-type(1), |
|||
.plastic__item:nth-last-of-type(1) { |
|||
width: 45px; |
|||
height: 45px; |
|||
box-shadow: none; |
|||
box-shadow: inset 4px 4px 0 #66c8fa; |
|||
} |
|||
|
|||
.plastic__item:nth-of-type(5) { |
|||
width: 45px; |
|||
height: 45px; |
|||
} |
|||
|
|||
.line { |
|||
position: absolute; |
|||
top: 15px; |
|||
display: flex; |
|||
width: 90%; |
|||
height: 85px; |
|||
background-color: #e1f6fd; |
|||
border-right: 4px solid #314b70; |
|||
border-bottom: 4px solid #314b70; |
|||
border-left: 4px solid #314b70; |
|||
border-radius: 4px; |
|||
transform: rotateX(25deg); |
|||
transform-style: preserve-3d; |
|||
} |
|||
|
|||
.line__item { |
|||
height: 100%; |
|||
flex-grow: 1; |
|||
border-right: 4px solid #314b70; |
|||
} |
|||
|
|||
.line__item:nth-child(odd) { |
|||
background: #00affa; |
|||
box-shadow: inset 4px 4px 0 #66c8fa; |
|||
} |
|||
|
|||
.line__item:nth-child(even) { |
|||
background: #fffdff; |
|||
} |
|||
|
|||
.line__item:nth-last-of-type(1) { |
|||
border-right: none; |
|||
} |
|||
|
|||
.line__item:nth-child(4), |
|||
.line__item:nth-child(5), |
|||
.line__item:nth-child(6) { |
|||
border-top: 6px solid #314b70; |
|||
} |
|||
|
|||
.tree { |
|||
position: absolute; |
|||
bottom: 19%; |
|||
left: 10%; |
|||
display: flex; |
|||
width: 100px; |
|||
height: 165px; |
|||
background-color: #00d398; |
|||
border: 4px solid #314b70; |
|||
border-radius: 50px; |
|||
box-shadow: inset 4px 0 0 #77e4c6, inset -4px 0 0 #00a073; |
|||
animation: tree 1s linear alternate infinite; |
|||
justify-content: center; |
|||
transform-origin: 0% 100%; |
|||
} |
|||
|
|||
.tree__item { |
|||
position: absolute; |
|||
bottom: -80px; |
|||
width: 4px; |
|||
height: 140px; |
|||
background: #314b70; |
|||
} |
|||
|
|||
.tree__item:nth-of-type(2) { |
|||
bottom: 80px; |
|||
height: 40px; |
|||
border-radius: 20px; |
|||
box-shadow: 0 0 0 8px #77e4c6; |
|||
} |
|||
|
|||
.tree__item:nth-of-type(2)::before { |
|||
position: absolute; |
|||
bottom: -45px; |
|||
left: -30px; |
|||
width: 20px; |
|||
height: 35px; |
|||
background-color: #77e4c6; |
|||
border-radius: 15px; |
|||
content: ''; |
|||
} |
|||
|
|||
.tree__item:nth-of-type(3) { |
|||
bottom: 20px; |
|||
left: 36%; |
|||
width: 4px; |
|||
height: 30px; |
|||
background-color: #314b70; |
|||
transform: rotateZ(-45deg); |
|||
} |
|||
|
|||
.dot { |
|||
position: absolute; |
|||
bottom: 38px; |
|||
width: 100%; |
|||
height: 4px; |
|||
background-image: linear-gradient( |
|||
to right, |
|||
#314b70 10%, |
|||
transparent 10%, |
|||
transparent 11%, |
|||
#314b70 11%, |
|||
#314b70 85%, |
|||
transparent 85%, |
|||
transparent 86%, |
|||
#314b70 86% |
|||
); |
|||
} |
|||
|
|||
.bush__item { |
|||
position: absolute; |
|||
bottom: 40px; |
|||
left: 18%; |
|||
width: 80px; |
|||
height: 60px; |
|||
background-color: #00d398; |
|||
border: 1px solid red; |
|||
border: 4px solid #314b70; |
|||
border-bottom: none; |
|||
border-top-right-radius: 100px; |
|||
border-top-left-radius: 50px; |
|||
box-shadow: inset 4px 0 0 #77e4c6, inset -4px 0 0 #00a073; |
|||
animation: bush 2s alternate infinite; |
|||
transform-origin: bottom center; |
|||
} |
|||
|
|||
.bush__item:nth-of-type(2) { |
|||
left: 13%; |
|||
width: 50px; |
|||
height: 40px; |
|||
border-top-right-radius: 10px; |
|||
border-top-left-radius: 50px; |
|||
animation: tree 2s alternate reverse infinite 0.5s; |
|||
} |
|||
|
|||
.bush__item::before { |
|||
position: absolute; |
|||
top: 10px; |
|||
left: 10px; |
|||
width: 20px; |
|||
height: 20px; |
|||
background: #77e4c6; |
|||
border-radius: 50%; |
|||
content: ''; |
|||
} |
|||
|
|||
.cloud { |
|||
position: absolute; |
|||
top: 200px; |
|||
left: 60px; |
|||
display: flex; |
|||
justify-content: center; |
|||
width: 85px; |
|||
height: 20px; |
|||
border-bottom: 4px solid #e1e8f2; |
|||
animation: cloud 4s infinite alternate; |
|||
} |
|||
|
|||
.cloud:nth-of-type(2) { |
|||
top: 150px; |
|||
left: 50%; |
|||
animation: cloud 4s infinite reverse alternate 0.5s; |
|||
} |
|||
|
|||
.cloud:nth-of-type(3) { |
|||
top: 250px; |
|||
left: 80%; |
|||
animation: cloud 4s ease infinite alternate 0.75s; |
|||
} |
|||
|
|||
.cloud__item { |
|||
position: relative; |
|||
border-top: 20px solid #e1e8f2; |
|||
border-right: 20px solid transparent; |
|||
border-bottom: 20px solid transparent; |
|||
border-left: 20px solid #e1e8f2; |
|||
border-radius: 50%; |
|||
transform: rotateZ(45deg); |
|||
} |
|||
|
|||
.cloud__item:nth-of-type(2) { |
|||
margin-top: 5px; |
|||
margin-left: -7px; |
|||
border-top: 15px solid #e1e8f2; |
|||
border-right: 15px solid transparent; |
|||
border-bottom: 15px solid transparent; |
|||
border-left: 15px solid #e1e8f2; |
|||
} |
|||
|
|||
.bird { |
|||
position: absolute; |
|||
right: 10%; |
|||
bottom: 40%; |
|||
z-index: -1; |
|||
width: 20px; |
|||
height: 20px; |
|||
border-top: 4px solid #becde2; |
|||
border-left: 4px solid #becde2; |
|||
transform: rotateZ(-135deg); |
|||
animation: bird 1s ease alternate infinite; |
|||
} |
|||
|
|||
.bird:nth-of-type(2) { |
|||
right: 20%; |
|||
bottom: 30%; |
|||
width: 15px; |
|||
height: 15px; |
|||
} |
|||
|
|||
@keyframes bird { |
|||
0% { |
|||
transform: scaleY(0.7) rotateZ(-135deg) translateX(0) translateY(0) skew(-10deg, -10deg); |
|||
} |
|||
|
|||
100% { |
|||
transform: scaleY(1) rotateZ(-135deg) translateX(50%) translateY(50%) skew(-10deg, -10deg); |
|||
} |
|||
} |
|||
@keyframes tree { |
|||
0% { |
|||
transform: scaleY(1); |
|||
} |
|||
|
|||
100% { |
|||
transform: scaleY(0.975); |
|||
} |
|||
} |
|||
@keyframes bush { |
|||
0% { |
|||
transform: skewX(-2deg); |
|||
} |
|||
|
|||
100% { |
|||
transform: skewX(5deg); |
|||
} |
|||
} |
|||
@keyframes cloud { |
|||
0% { |
|||
transform: translateX(-10%); |
|||
} |
|||
|
|||
100% { |
|||
transform: translateX(20%); |
|||
} |
|||
} |
|||
} |
|||
@ -1,89 +0,0 @@ |
|||
<template> |
|||
<div class="house-wrap"> |
|||
<div class="house"> |
|||
<div class="floor"></div> |
|||
<div class="wall"> |
|||
<div class="window"></div> |
|||
<div class="door"> |
|||
<div class="door__square"></div> |
|||
<div class="door__line"></div> |
|||
<div class="door__square"></div> |
|||
</div> |
|||
<div class="window"></div> |
|||
</div> |
|||
<div class="top"></div> |
|||
<div class="circle"></div> |
|||
<div class="plastic"> |
|||
<div class="plastic__g"> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
<div class="plastic__item"></div> |
|||
</div> |
|||
</div> |
|||
<div class="line"> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
<div class="line__item"></div> |
|||
</div> |
|||
</div> |
|||
<div class="clouds"> |
|||
<div class="cloud"> |
|||
<div class="cloud__item"></div> |
|||
<div class="cloud__item"></div> |
|||
</div> |
|||
<div class="cloud"> |
|||
<div class="cloud__item"></div> |
|||
<div class="cloud__item"></div> |
|||
</div> |
|||
<div class="cloud"> |
|||
<div class="cloud__item"></div> |
|||
<div class="cloud__item"></div> |
|||
</div> |
|||
<div class="bird"></div> |
|||
</div> |
|||
<div class="birds"> |
|||
<div class="bird"></div> |
|||
<div class="bird"></div> |
|||
</div> |
|||
<div class="tree"> |
|||
<div class="tree__item"></div> |
|||
<div class="tree__item"></div> |
|||
<div class="tree__item"></div> |
|||
</div> |
|||
<div class="bush"> |
|||
<div class="bush__item"></div> |
|||
<div class="bush__item"></div> |
|||
</div> |
|||
<div class="dot"></div> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import type { PropType } from 'vue'; |
|||
import { defineComponent } from 'vue'; |
|||
export default defineComponent({ |
|||
name: 'House', |
|||
props: { |
|||
size: { |
|||
type: Number as PropType<number>, |
|||
default: 600, |
|||
}, |
|||
}, |
|||
setup() { |
|||
return {}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
@import './index.less'; |
|||
</style> |
|||
@ -1,22 +0,0 @@ |
|||
<template> |
|||
<div class="welcome"> |
|||
<House /> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import House from '../house/index.vue'; |
|||
export default defineComponent({ |
|||
name: 'Welcome', |
|||
components: { House }, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.welcome { |
|||
display: flex; |
|||
width: 100%; |
|||
height: 100%; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,37 @@ |
|||
<template> |
|||
<Card title="最新动态" v-bind="$attrs"> |
|||
<template #extra> |
|||
<a-button type="link" size="small">更多</a-button> |
|||
</template> |
|||
<List item-layout="horizontal" :data-source="items"> |
|||
<template #renderItem="{ item }"> |
|||
<ListItem> |
|||
<ListItemMeta> |
|||
<template #description> |
|||
{{ item.date }} |
|||
</template> |
|||
<template #title> {{ item.name }} <span v-html="item.desc"> </span> </template> |
|||
<template #avatar> |
|||
<Icon :icon="item.avatar" :size="30" /> |
|||
</template> |
|||
</ListItemMeta> |
|||
</ListItem> |
|||
</template> |
|||
</List> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
|
|||
import { Card, List } from 'ant-design-vue'; |
|||
import { dynamicInfoItems } from './data'; |
|||
import headerImg from '/@/assets/images/header.jpg'; |
|||
import { Icon } from '/@/components/Icon'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card, List, ListItem: List.Item, ListItemMeta: List.Item.Meta, Icon }, |
|||
setup() { |
|||
return { items: dynamicInfoItems, headerImg }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,100 +0,0 @@ |
|||
<template> |
|||
<CollapseContainer class="news-list" title="动态" :canExpan="false"> |
|||
<ScrollContainer> |
|||
<List> |
|||
<template v-for="item in newList" :key="item.id"> |
|||
<ListItem class="news-list__item"> |
|||
<ListItemMeta> |
|||
<template #avatar> |
|||
<img :src="headerImg" class="news-list__item-avatar" /> |
|||
</template> |
|||
<template #description> |
|||
<div class="news-list__item-desc"> |
|||
<div class="news-list__item-time mb-1"> |
|||
{{ item.sendTime }} |
|||
</div> |
|||
<div class="news-list__item-title mb-1"> |
|||
<span class="news-list__item-light">{{ item.sender }} </span>申请迭代 |
|||
<span class="news-list__item-light"> {{ item.title }} </span>发布 |
|||
</div> |
|||
<div class="news-list__item-cnte p-2"> |
|||
<span class="news-list__item-cnte__title"> {{ item.cnteId }}</span> |
|||
<br /> |
|||
Status: {{ item.cnteStas }} |
|||
<br /> |
|||
Repository: {{ item.cnteRepo }} |
|||
<br /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
</ListItemMeta> |
|||
</ListItem> |
|||
</template> |
|||
</List> |
|||
</ScrollContainer> |
|||
</CollapseContainer> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { List } from 'ant-design-vue'; |
|||
import { CollapseContainer, ScrollContainer } from '/@/components/Container/index'; |
|||
import headerImg from '/@/assets/images/header.jpg'; |
|||
|
|||
import { newList } from '../data'; |
|||
export default defineComponent({ |
|||
components: { |
|||
List, |
|||
ListItem: List.Item, |
|||
ListItemMeta: List.Item.Meta, |
|||
CollapseContainer, |
|||
ScrollContainer, |
|||
}, |
|||
setup() { |
|||
return { newList, headerImg }; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.news-list { |
|||
&__item { |
|||
&-avatar { |
|||
width: 35px; |
|||
height: 35px; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
&-title { |
|||
font-size: 14px; |
|||
line-height: 22px; |
|||
color: #000; |
|||
opacity: 0.65; |
|||
} |
|||
|
|||
&-time { |
|||
font-size: 14px; |
|||
line-height: 22px; |
|||
color: #000; |
|||
opacity: 0.45; |
|||
} |
|||
|
|||
&-light { |
|||
font-size: 14px; |
|||
line-height: 22px; |
|||
color: #000; |
|||
opacity: 0.85; |
|||
} |
|||
|
|||
&-cnte { |
|||
background: #eef3fb; |
|||
border-radius: 2px; |
|||
opacity: 0.6; |
|||
|
|||
&__title { |
|||
font-size: 14px; |
|||
line-height: 22px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,101 +0,0 @@ |
|||
<template> |
|||
<a-row class="prod-total"> |
|||
<template v-for="(item, index) in wokbProd" :key="item.type"> |
|||
<a-col :xs="12" :sm="6" class="prod-total__item" :class="`prod-total__item-${index}`"> |
|||
<div class="img" :class="`prod-total__item-${index}-img`"></div> |
|||
<div>{{ item.amount }}</div> |
|||
<span>{{ item.type }}</span> |
|||
</a-col> |
|||
</template> |
|||
</a-row> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { Row, Col } from 'ant-design-vue'; |
|||
|
|||
import { wokbProd } from '../data'; |
|||
// import {ProdTypeEnum} from '@/api/dashboard/model/wokbModel' |
|||
export default defineComponent({ |
|||
components: { [Row.name]: Row, [Col.name]: Col }, |
|||
setup() { |
|||
return { wokbProd }; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.prod-total { |
|||
padding: 12px 4px 12px 12px; |
|||
background: #fff; |
|||
|
|||
&__item { |
|||
display: inline-block; |
|||
flex: 0 0 calc(25% - 8px); |
|||
padding: 20px 10px; |
|||
margin-right: 8px; |
|||
border-radius: 4px; |
|||
|
|||
span { |
|||
font-size: 14px; |
|||
line-height: 28px; |
|||
} |
|||
|
|||
div { |
|||
font-size: 26px; |
|||
} |
|||
|
|||
.img { |
|||
float: left; |
|||
width: 62px; |
|||
height: 62px; |
|||
} |
|||
|
|||
&-0 { |
|||
background: rgba(254, 97, 178, 0.1); |
|||
|
|||
&-img { |
|||
background: url(../../../../assets/images/dashboard/wokb/datashow1.png) no-repeat; |
|||
} |
|||
|
|||
div { |
|||
color: #fe61b2; |
|||
} |
|||
} |
|||
|
|||
&-1 { |
|||
background: rgba(254, 163, 64, 0.1); |
|||
|
|||
&-img { |
|||
background: url(../../../..//assets/images/dashboard/wokb/datashow2.png) no-repeat; |
|||
} |
|||
|
|||
div { |
|||
color: #fea340; |
|||
} |
|||
} |
|||
|
|||
&-2 { |
|||
background: rgba(172, 70, 255, 0.1); |
|||
|
|||
&-img { |
|||
background: url(../../../..//assets/images/dashboard/wokb/datashow3.png) no-repeat; |
|||
} |
|||
|
|||
div { |
|||
color: #9e55ff; |
|||
} |
|||
} |
|||
|
|||
&-3 { |
|||
background: rgba(0, 196, 186, 0.1); |
|||
|
|||
&-img { |
|||
background: url(../../../..//assets/images/dashboard/wokb/datashow4.png) no-repeat; |
|||
} |
|||
|
|||
div { |
|||
color: #00c4ba; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,35 @@ |
|||
<template> |
|||
<Card title="项目" v-bind="$attrs"> |
|||
<template #extra> |
|||
<a-button type="link" size="small">更多</a-button> |
|||
</template> |
|||
|
|||
<template v-for="item in items" :key="item"> |
|||
<CardGrid class="!md:w-1/3 !w-full"> |
|||
<span class="flex"> |
|||
<Icon :icon="item.icon" :color="item.color" size="30" /> |
|||
<span class="text-lg ml-4">{{ item.title }}</span> |
|||
</span> |
|||
<div class="flex mt-2 h-10 text-secondary"> {{ item.desc }} </div> |
|||
<div class="flex justify-between text-secondary"> |
|||
<span>{{ item.group }}</span> |
|||
<span>{{ item.date }}</span> |
|||
</div> |
|||
</CardGrid> |
|||
</template> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { Icon } from '/@/components/Icon'; |
|||
import { groupItems } from './data'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card, CardGrid: Card.Grid, Icon }, |
|||
setup() { |
|||
return { items: groupItems }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,26 @@ |
|||
<template> |
|||
<Card title="快捷导航" v-bind="$attrs"> |
|||
<template v-for="item in items" :key="item"> |
|||
<CardGrid> |
|||
<span class="flex flex-col items-center"> |
|||
<Icon :icon="item.icon" :color="item.color" size="20" /> |
|||
<span class="text-md mt-2">{{ item.title }}</span> |
|||
</span> |
|||
</CardGrid> |
|||
</template> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { Icon } from '/@/components/Icon'; |
|||
import { navItems } from './data'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card, CardGrid: Card.Grid, Icon }, |
|||
setup() { |
|||
return { items: navItems }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,107 @@ |
|||
<template> |
|||
<Card title="销售统计" :loading="loading"> |
|||
<div ref="chartRef" :style="{ width, height }"></div> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, Ref, ref, watch } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card }, |
|||
props: { |
|||
loading: Boolean, |
|||
width: { |
|||
type: String as PropType<string>, |
|||
default: '100%', |
|||
}, |
|||
height: { |
|||
type: String as PropType<string>, |
|||
default: '400px', |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
watch( |
|||
() => props.loading, |
|||
() => { |
|||
if (props.loading) { |
|||
return; |
|||
} |
|||
setOptions({ |
|||
backgroundColor: '#fff', |
|||
legend: { |
|||
bottom: 0, |
|||
data: ['Visits', 'Sales'], |
|||
}, |
|||
tooltip: {}, |
|||
radar: { |
|||
radius: '60%', |
|||
splitNumber: 8, |
|||
indicator: [ |
|||
{ |
|||
text: '2017', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2017', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2018', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2019', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2020', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2021', |
|||
max: 100, |
|||
}, |
|||
], |
|||
}, |
|||
series: [ |
|||
{ |
|||
type: 'radar', |
|||
symbolSize: 0, |
|||
areaStyle: { |
|||
shadowBlur: 0, |
|||
shadowColor: 'rgba(0,0,0,.2)', |
|||
shadowOffsetX: 0, |
|||
shadowOffsetY: 10, |
|||
opacity: 1, |
|||
}, |
|||
data: [ |
|||
{ |
|||
value: [90, 50, 86, 40, 50, 20], |
|||
name: 'Visits', |
|||
itemStyle: { |
|||
color: '#b6a2de', |
|||
}, |
|||
}, |
|||
{ |
|||
value: [70, 75, 70, 76, 20, 85], |
|||
name: 'Sales', |
|||
itemStyle: { |
|||
color: '#67e0e3', |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -1,102 +0,0 @@ |
|||
<template> |
|||
<CollapseContainer class="shortcuts" title="快捷入口" :canExpan="false"> |
|||
<template #action> |
|||
<a-button size="small" type="link"> 新建 </a-button> |
|||
</template> |
|||
<a-row> |
|||
<template v-for="item in shortCuts" :key="item.img"> |
|||
<a-col :span="8" class="p-3 shortcuts__item"> |
|||
<img :src="item.img" class="mb-2 shortcuts__item-img" /> |
|||
<span>{{ item.name }}</span> |
|||
</a-col> |
|||
</template> |
|||
|
|||
<a-col :span="8" class="p-3 shortcuts__item"> |
|||
<span class="mb-2 shortcuts__item-all"> |
|||
<RightOutlined /> |
|||
</span> |
|||
<br /> |
|||
<span>查看全部</span> |
|||
</a-col> |
|||
</a-row> |
|||
</CollapseContainer> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { Row, Col } from 'ant-design-vue'; |
|||
import { CollapseContainer } from '/@/components/Container/index'; |
|||
|
|||
import { RightOutlined } from '@ant-design/icons-vue'; |
|||
import wokbImg1 from '/@/assets/images/dashboard/wokb/attendance.png'; |
|||
import wokbImg2 from '/@/assets/images/dashboard/wokb/overtime.png'; |
|||
import wokbImg3 from '/@/assets/images/dashboard/wokb/meal.png'; |
|||
import wokbImg4 from '/@/assets/images/dashboard/wokb/leave.png'; |
|||
import wokbImg5 from '/@/assets/images/dashboard/wokb/stamp.png'; |
|||
import wokbImg6 from '/@/assets/images/dashboard/wokb/travel.png'; |
|||
import wokbImg7 from '/@/assets/images/dashboard/wokb/performance.png'; |
|||
import wokbImg8 from '/@/assets/images/dashboard/wokb/approve.png'; |
|||
const shortCuts = [ |
|||
{ |
|||
img: wokbImg1, |
|||
name: '考勤记录', |
|||
}, |
|||
{ |
|||
img: wokbImg2, |
|||
name: '加班申请', |
|||
}, |
|||
{ |
|||
img: wokbImg3, |
|||
name: '餐补申请', |
|||
}, |
|||
{ |
|||
img: wokbImg4, |
|||
name: '请假', |
|||
}, |
|||
{ |
|||
img: wokbImg5, |
|||
name: '用章申请', |
|||
}, |
|||
{ |
|||
img: wokbImg6, |
|||
name: '差旅报销', |
|||
}, |
|||
{ |
|||
img: wokbImg7, |
|||
name: '绩效申请', |
|||
}, |
|||
{ |
|||
img: wokbImg8, |
|||
name: '审批', |
|||
}, |
|||
]; |
|||
export default defineComponent({ |
|||
components: { [Row.name]: Row, [Col.name]: Col, CollapseContainer, RightOutlined }, |
|||
setup() { |
|||
return { shortCuts }; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.shortcuts { |
|||
&__item { |
|||
text-align: center; |
|||
|
|||
&-img { |
|||
width: 36px; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
&-all { |
|||
display: inline-block; |
|||
width: 36px; |
|||
height: 36px; |
|||
line-height: 36px; |
|||
color: #000; |
|||
cursor: pointer; |
|||
background: lightgrey; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,114 +0,0 @@ |
|||
<template> |
|||
<CollapseContainer class="todo-list" title="待办事项" :canExpan="false"> |
|||
<template #title> |
|||
<span> 待办事项 <span class="todo-list__total">30</span> </span> |
|||
</template> |
|||
|
|||
<List> |
|||
<template v-for="item in todoList" :key="item.id"> |
|||
<ListItem class="todo-list__item"> |
|||
<ListItemMeta> |
|||
<template #title> |
|||
<div> |
|||
<span class="todo-list__item-title">{{ item.title }}</span> |
|||
<span class="todo-list__item-memo">{{ item.memo }}</span> |
|||
</div> |
|||
</template> |
|||
<template #description> |
|||
<div class="todo-list__item-desc"> |
|||
提交人:{{ item.sbmter }} |
|||
<br /> |
|||
提交时间:{{ item.sbmtTime }} |
|||
</div> |
|||
</template> |
|||
</ListItemMeta> |
|||
<a-button type="link"> |
|||
<Tag color="blue"> 待审批 </Tag> |
|||
</a-button> |
|||
</ListItem> |
|||
</template> |
|||
</List> |
|||
<div class="todo-list__all"> |
|||
<Tooltip placement="topRight"> |
|||
<template #title> 查看更多 </template> |
|||
<EllipsisOutlined /> |
|||
</Tooltip> |
|||
</div> |
|||
</CollapseContainer> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { List, Tag, Tooltip } from 'ant-design-vue'; |
|||
import { CollapseContainer } from '/@/components/Container/index'; |
|||
|
|||
import { EllipsisOutlined } from '@ant-design/icons-vue'; |
|||
import { todoList } from '../data'; |
|||
|
|||
export default defineComponent({ |
|||
name: 'TodoList', |
|||
components: { |
|||
CollapseContainer, |
|||
List, |
|||
ListItem: List.Item, |
|||
ListItemMeta: List.Item.Meta, |
|||
Tag, |
|||
Tooltip, |
|||
EllipsisOutlined, |
|||
}, |
|||
setup() { |
|||
return { todoList }; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.todo-list { |
|||
position: relative; |
|||
|
|||
&__total { |
|||
display: inline-block; |
|||
width: 20px; |
|||
height: 20px; |
|||
font-size: 12px; |
|||
line-height: 20px; |
|||
color: #fff; |
|||
text-align: center; |
|||
background: rgba(255, 0, 0, 0.7); |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
&__all { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 10px; |
|||
height: 56px; |
|||
font-size: 24px; |
|||
line-height: 56px; |
|||
text-align: center; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
&__item { |
|||
padding: 8px; |
|||
|
|||
&-title { |
|||
font-size: 14px; |
|||
font-weight: normal; |
|||
line-height: 22px; |
|||
color: #1c1d21; |
|||
} |
|||
|
|||
&-memo { |
|||
font-size: 12px; |
|||
font-weight: normal; |
|||
line-height: 22px; |
|||
color: #7c8087; |
|||
} |
|||
|
|||
&-desc { |
|||
font-size: 12px; |
|||
line-height: 22px; |
|||
color: #7c8087; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,37 @@ |
|||
<template> |
|||
<div class="lg:flex"> |
|||
<Avatar :src="headerImg" :size="72" class="!mx-auto !block" /> |
|||
<div class="md:ml-6 flex flex-col justify-center md:mt-0 mt-2"> |
|||
<h1 class="md:text-lg text-md">早安, Vben, 开始您一天的工作吧!</h1> |
|||
<span class="text-secondary"> 今日晴,20℃ - 32℃! </span> |
|||
</div> |
|||
<div class="flex flex-1 justify-end md:mt-0 mt-4"> |
|||
<div class="flex flex-col justify-center text-right"> |
|||
<span class="text-secondary"> 待办 </span> |
|||
<span class="text-2xl">2/10</span> |
|||
</div> |
|||
|
|||
<div class="flex flex-col justify-center text-right md:mx-16 mx-12"> |
|||
<span class="text-secondary"> 项目 </span> |
|||
<span class="text-2xl">8</span> |
|||
</div> |
|||
<div class="flex flex-col justify-center text-right md:mr-10 mr-4"> |
|||
<span class="text-secondary"> 团队 </span> |
|||
<span class="text-2xl">300</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
|
|||
import { Avatar } from 'ant-design-vue'; |
|||
|
|||
import headerImg from '/@/assets/images/header.jpg'; |
|||
export default defineComponent({ |
|||
components: { Avatar }, |
|||
setup() { |
|||
return { headerImg }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,156 @@ |
|||
interface GroupItem { |
|||
title: string; |
|||
icon: string; |
|||
color: string; |
|||
desc: string; |
|||
date: string; |
|||
group: string; |
|||
} |
|||
|
|||
interface NavItem { |
|||
title: string; |
|||
icon: string; |
|||
color: string; |
|||
} |
|||
|
|||
interface DynamicInfoItem { |
|||
avatar: string; |
|||
name: string; |
|||
date: string; |
|||
desc: string; |
|||
} |
|||
|
|||
export const navItems: NavItem[] = [ |
|||
{ |
|||
title: '首页', |
|||
icon: 'ion:home-outline', |
|||
color: '#1fdaca', |
|||
}, |
|||
{ |
|||
title: '仪表盘', |
|||
icon: 'ion:grid-outline', |
|||
color: '#bf0c2c', |
|||
}, |
|||
{ |
|||
title: '组件', |
|||
icon: 'ion:layers-outline', |
|||
color: '#e18525', |
|||
}, |
|||
{ |
|||
title: '系统管理', |
|||
icon: 'ion:settings-outline', |
|||
color: '#3fb27f', |
|||
}, |
|||
{ |
|||
title: '权限管理', |
|||
icon: 'ion:key-outline', |
|||
color: '#4daf1bc9', |
|||
}, |
|||
{ |
|||
title: '图表', |
|||
icon: 'ion:bar-chart-outline', |
|||
color: '#00d8ff', |
|||
}, |
|||
]; |
|||
|
|||
export const dynamicInfoItems: DynamicInfoItem[] = [ |
|||
{ |
|||
avatar: 'dynamic-avatar-1|svg', |
|||
name: '威廉', |
|||
date: '刚刚', |
|||
desc: `在 <a>开源组</a> 创建了项目 <a>Vue</a>`, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-2|svg', |
|||
name: '艾文', |
|||
date: '1个小时前', |
|||
desc: `关注了 <a>威廉</a> `, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-3|svg', |
|||
name: '克里斯', |
|||
date: '1天前', |
|||
desc: `发布了 <a>个人动态</a> `, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-4|svg', |
|||
name: 'Vben', |
|||
date: '2天前', |
|||
desc: `发表文章 <a>如何编写一个Vite插件</a> `, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-5|svg', |
|||
name: '皮特', |
|||
date: '3天前', |
|||
desc: `回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>`, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-6|svg', |
|||
name: '杰克', |
|||
date: '1周前', |
|||
desc: `关闭了问题 <a>如何运行项目</a> `, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-1|svg', |
|||
name: '威廉', |
|||
date: '1周前', |
|||
desc: `发布了 <a>个人动态</a> `, |
|||
}, |
|||
{ |
|||
avatar: 'dynamic-avatar-1|svg', |
|||
name: '威廉', |
|||
date: '2021-04-01 20:00', |
|||
desc: `推送了代码到 <a>Github</a>`, |
|||
}, |
|||
]; |
|||
|
|||
export const groupItems: GroupItem[] = [ |
|||
{ |
|||
title: 'Github', |
|||
icon: 'carbon:logo-github', |
|||
color: '', |
|||
desc: '不要等待机会,而要创造机会。', |
|||
group: '开源组', |
|||
date: '2021-04-01', |
|||
}, |
|||
{ |
|||
title: 'Vue', |
|||
icon: 'ion:logo-vue', |
|||
color: '#3fb27f', |
|||
desc: '现在的你决定将来的你。', |
|||
group: '算法组', |
|||
date: '2021-04-01', |
|||
}, |
|||
{ |
|||
title: 'Html5', |
|||
icon: 'ion:logo-html5', |
|||
color: '#e18525', |
|||
desc: '没有什么才能比努力更重要。', |
|||
group: '上班摸鱼', |
|||
date: '2021-04-01', |
|||
}, |
|||
{ |
|||
title: 'Angular', |
|||
icon: 'ion:logo-angular', |
|||
color: '#bf0c2c', |
|||
desc: '热情和欲望可以突破一切难关。', |
|||
group: 'UI', |
|||
date: '2021-04-01', |
|||
}, |
|||
{ |
|||
title: 'React', |
|||
icon: 'bx:bxl-react', |
|||
color: '#00d8ff', |
|||
desc: '健康的身体是实目标的基石。', |
|||
group: '技术牛', |
|||
date: '2021-04-01', |
|||
}, |
|||
{ |
|||
title: 'Js', |
|||
icon: 'ion:logo-javascript', |
|||
color: '#4daf1bc9', |
|||
desc: '路是走出来的,而不是空想出来的。', |
|||
group: '架构组', |
|||
date: '2021-04-01', |
|||
}, |
|||
]; |
|||
@ -1,48 +0,0 @@ |
|||
export const wokbProd = [ |
|||
{ |
|||
amount: '20', |
|||
type: '成品总数', |
|||
}, |
|||
{ |
|||
amount: '50', |
|||
type: '未发布', |
|||
}, |
|||
{ |
|||
amount: '80', |
|||
type: '发布中', |
|||
}, |
|||
{ |
|||
amount: '100', |
|||
type: '异常', |
|||
}, |
|||
]; |
|||
|
|||
export const todoList = (() => { |
|||
const ret: any[] = []; |
|||
for (let index = 0; index < 3; index++) { |
|||
ret.push({ |
|||
id: index, |
|||
sbmter: '张三', |
|||
sbmtTime: new Date().toLocaleString(), |
|||
title: '主要', |
|||
memo: '工作任务', |
|||
}); |
|||
} |
|||
return ret; |
|||
})(); |
|||
export const newList = (() => { |
|||
const ret: any[] = []; |
|||
for (let index = 0; index < 3; index++) { |
|||
ret.push({ |
|||
id: index, |
|||
sender: '李四', |
|||
sendTime: new Date().toLocaleString(), |
|||
title: '代码', |
|||
memo: '工作任务', |
|||
cnteId: `c${index}`, |
|||
cnteStas: 'opened', |
|||
cnteRepo: index, |
|||
}); |
|||
} |
|||
return ret; |
|||
})(); |
|||
@ -0,0 +1,107 @@ |
|||
<template> |
|||
<Card title="销售统计" :loading="loading"> |
|||
<div ref="chartRef" :style="{ width, height }"></div> |
|||
</Card> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, Ref, ref, watch } from 'vue'; |
|||
|
|||
import { Card } from 'ant-design-vue'; |
|||
import { useECharts } from '/@/hooks/web/useECharts'; |
|||
|
|||
export default defineComponent({ |
|||
components: { Card }, |
|||
props: { |
|||
loading: Boolean, |
|||
width: { |
|||
type: String as PropType<string>, |
|||
default: '100%', |
|||
}, |
|||
height: { |
|||
type: String as PropType<string>, |
|||
default: '400px', |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
const chartRef = ref<HTMLDivElement | null>(null); |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
|||
watch( |
|||
() => props.loading, |
|||
() => { |
|||
if (props.loading) { |
|||
return; |
|||
} |
|||
setOptions({ |
|||
backgroundColor: '#fff', |
|||
legend: { |
|||
bottom: 0, |
|||
data: ['Visits', 'Sales'], |
|||
}, |
|||
tooltip: {}, |
|||
radar: { |
|||
radius: '60%', |
|||
splitNumber: 8, |
|||
indicator: [ |
|||
{ |
|||
text: '2017', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2017', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2018', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2019', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2020', |
|||
max: 100, |
|||
}, |
|||
{ |
|||
text: '2021', |
|||
max: 100, |
|||
}, |
|||
], |
|||
}, |
|||
series: [ |
|||
{ |
|||
type: 'radar', |
|||
symbolSize: 0, |
|||
areaStyle: { |
|||
shadowBlur: 0, |
|||
shadowColor: 'rgba(0,0,0,.2)', |
|||
shadowOffsetX: 0, |
|||
shadowOffsetY: 10, |
|||
opacity: 1, |
|||
}, |
|||
data: [ |
|||
{ |
|||
value: [90, 50, 86, 40, 50, 20], |
|||
name: 'Visits', |
|||
itemStyle: { |
|||
color: '#9f8ed7', |
|||
}, |
|||
}, |
|||
{ |
|||
value: [70, 75, 70, 76, 20, 85], |
|||
name: 'Sales', |
|||
itemStyle: { |
|||
color: '#1edec5', |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
return { chartRef }; |
|||
}, |
|||
}); |
|||
</script> |
|||