feat(analysis): add analysis page

This commit is contained in:
vben
2020-10-11 22:59:44 +08:00
parent 1cd75fcf5b
commit 52ee35c4be
36 changed files with 1520 additions and 193 deletions

View File

@@ -0,0 +1,91 @@
<template>
<div ref="chartRef" :style="{ height, width }" />
</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',
backgroundColor: 'rgba(0, 0, 0, .6)',
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>

View File

@@ -0,0 +1,101 @@
<template>
<div ref="chartRef" :style="{ height, width }" />
</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,
backgroundColor: 'rgba(0, 0, 0, .6)',
borderColor: '#777',
borderWidth: 1,
},
legend: {
icon: 'rect',
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: {
normal: {
color: '#5B8FF9',
},
},
// areaStyle: {},
data: [330, 132, 101, 134, 90, 230, 210, 150, 232, 234, 230, 400],
animationDuration: 4000,
},
{
name: '产品二',
type: 'line',
itemStyle: {
normal: {
color: '#55D187',
},
},
data: [220, 182, 191, 234, 290, 330, 310, 330, 232, 201, 330, 190],
animationDuration: 4000,
},
],
});
});
return { chartRef };
},
});
</script>

View File

@@ -0,0 +1,79 @@
<template>
<div ref="chartRef" :style="{ height, width }" />
</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({
name: 'AnalysisLine',
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',
// @ts-ignore
x: '34.5%',
y: '40%',
},
],
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, .6)',
},
legend: {
icon: 'circle',
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>

View File

@@ -0,0 +1,82 @@
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 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>
</>
)}
</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>
);
},
});

View File

@@ -0,0 +1,116 @@
<template>
<div class="grow-card">
<div class="grow-card-header">
<div class="grow-card__info">
<p class="grow-card__title">{{ info.title }}</p>
<CountTo prefix="$" :startVal="1" :endVal="info.price" />
</div>
<img :src="info.icon" />
</div>
<div class="grow-card-footer" :class="{ 'is-up': info.up }">
<Statistic :value="info.percent">
<template #prefix> <img :src="info.up ? riseSvg : downSvg" /> </template>
</Statistic>
<span class="grow-card__mom">{{ info.mom }}</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { Statistic } from 'ant-design-vue';
import { CountTo } from '/@/components/CountTo/index';
import riseSvg from '/@/assets/svg/dashboard/analysis-rise.svg';
import downSvg from '/@/assets/svg/dashboard/analysis-down.svg';
import { GrowCardItem } from '../types';
export default defineComponent({
components: { Statistic, CountTo },
props: {
info: {
type: Object as PropType<GrowCardItem>,
default: null,
},
},
setup() {
return {
riseSvg,
downSvg,
};
},
});
</script>
<style lang="less">
@import (reference) '../../../../design/index.less';
.grow-card {
display: flex;
width: calc(100% - 12px);
height: 158px;
padding: 16px 16px 12px 16px;
// margin: 0 12px 12px 12px;
cursor: pointer;
background: @white;
border-radius: 4px;
box-shadow: 6px 6px 54px 0 rgba(0, 0, 0, 0.05);
flex-direction: column;
&:hover {
box-shadow: 6px 6px 54px 0 rgba(0, 0, 0, 0.1);
}
&-header {
display: flex;
width: 100%;
justify-content: space-between;
}
&__title {
font-family: PingFangSC-Regular;
font-size: 16px;
letter-spacing: 0;
color: #2c3a61;
opacity: 0.7;
}
&__info {
span {
font-family: NeoSans;
font-size: 26px;
line-height: 38px;
}
}
&-footer {
display: flex;
width: 100%;
margin-top: 24px;
align-items: center;
.ant-statistic-content-value {
color: @error-color;
}
.ant-statistic-content-prefix svg {
width: 0.98rem !important;
height: 0.98rem !important;
}
&.is-up {
.ant-statistic-content-value {
color: @success-color;
}
}
}
&__mom {
display: inline-block;
padding-left: 10px;
font-family: PingFangSC-Regular;
font-size: 12px;
line-height: 22px;
letter-spacing: 0;
color: #606060;
}
}
</style>

View File

@@ -0,0 +1,154 @@
<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: #2c3a61;
}
&__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>

View File

@@ -0,0 +1,105 @@
<template>
<div ref="chartRef" :style="{ height, width }" />
</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, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
onMounted(() => {
setOptions({
// title: {
// text: '产品成交额',
// },
tooltip: {
trigger: 'axis',
padding: 3,
backgroundColor: 'rgba(0, 0, 0, .6)',
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: {
// 线性渐变前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是true则该四个值是绝对像素位置。
// @ts-ignore
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, // shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
},
// areaStyle: {},
data: [134, 330, 132, 101, 90, 230, 210, 150, 230, 400, 232, 234],
animationDuration: 3000,
},
],
});
});
return { chartRef };
},
});
</script>

View File

@@ -0,0 +1,56 @@
.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;
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
import { PropType } from 'vue';
export interface BasicProps {
width: string;
height: string;
}
export const basicProps = {
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '280px',
},
};

View File

@@ -0,0 +1,107 @@
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,
];

View File

@@ -0,0 +1,83 @@
<template>
<div class="analysis p-4">
<Row class="pl-2">
<template v-for="item in growCardList" :key="item.title">
<ACol :sm="24" :md="12" :lg="6">
<GrowCard :info="item" />
</ACol>
</template>
</Row>
<Row>
<ACol :md="24" :lg="17" class="my-3">
<CollapseContainer class="mr-3" title="产品成交额" :canExpan="false">
<AnalysisLine />
</CollapseContainer>
<Row class="mt-3">
<ACol :md="24" :lg="12" class="product-total">
<CollapseContainer class="mr-3" title="产品成交额" :canExpan="false">
<AnalysisPie />
</CollapseContainer>
</ACol>
<ACol :md="24" :lg="12">
<CollapseContainer class="mr-3" title="用户来源" :canExpan="false">
<AnalysisBar />
</CollapseContainer>
</ACol>
</Row>
</ACol>
<ACol :md="24" :lg="7">
<CollapseContainer class="mt-3" title="项目进度" :canExpan="false">
<template v-for="item in taskList" :key="item.title">
<TaskCard :info="item" />
</template>
</CollapseContainer>
</ACol>
</Row>
<Row>
<FlowAnalysis />
</Row>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import GrowCard from './components/GrowCard.vue';
import TrendLine from './components/TrendLine.vue';
import AnalysisLine from './components/AnalysisLine.vue';
import AnalysisPie from './components/AnalysisPie.vue';
import AnalysisBar from './components/AnalysisBar.vue';
import TaskCard from './components/TaskCard.vue';
import FlowAnalysis from './components/FlowAnalysis';
import { Row, Col } from 'ant-design-vue';
import { CollapseContainer } from '/@/components/Container/index';
import { growCardList, taskList } from './data';
export default defineComponent({
components: {
Row,
ACol: Col,
GrowCard,
CollapseContainer,
TrendLine,
AnalysisLine,
AnalysisPie,
AnalysisBar,
TaskCard,
FlowAnalysis,
},
setup() {
return { growCardList, taskList };
},
});
</script>
<style lang="less" scoped>
@import (reference) '../../../design/index.less';
.analysis {
width: 100%;
.product-total {
.respond-to(small-and-medium, {padding-right: 0;margin-bottom: 24px;});
}
}
</style>

View File

@@ -0,0 +1,16 @@
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;
}

View File

@@ -1,5 +1,5 @@
<template>
<CollapseContainer title="任务安排" :canExpan="false">
<CollapseContainer title="销售统计" :canExpan="false">
<div ref="chartRef" :style="{ width: '100%' }" />
</CollapseContainer>
</template>
@@ -9,7 +9,6 @@
import { CollapseContainer } from '/@/components/Container/index';
import { useApexCharts } from '/@/hooks/web/useApexCharts';
import moment from 'moment';
export default defineComponent({
components: { CollapseContainer },
setup() {
@@ -18,72 +17,72 @@
onMounted(() => {
setOptions({
series: [
{
data: [
{
x: 'Analysis',
y: [new Date('2019-02-27').getTime(), new Date('2019-03-04').getTime()],
fillColor: '#008FFB',
},
{
x: 'Design',
y: [new Date('2019-03-04').getTime(), new Date('2019-03-08').getTime()],
fillColor: '#00E396',
},
{
x: 'Coding',
y: [new Date('2019-03-07').getTime(), new Date('2019-03-10').getTime()],
fillColor: '#775DD0',
},
{
x: 'Testing',
y: [new Date('2019-03-08').getTime(), new Date('2019-03-12').getTime()],
fillColor: '#FEB019',
},
{
x: 'Deployment',
y: [new Date('2019-03-12').getTime(), new Date('2019-03-17').getTime()],
fillColor: '#FF4560',
},
],
},
{ name: 'Visits', data: [90, 50, 86, 40, 100, 20] },
{ name: 'Sales', data: [70, 75, 70, 76, 20, 85] },
],
dataLabels: {
style: {
colors: ['#b9c3cd', '#b9c3cd', '#b9c3cd', '#b9c3cd', '#b9c3cd', '#b9c3cd'],
},
},
chart: {
height: 350,
type: 'rangeBar',
},
plotOptions: {
bar: {
horizontal: true,
distributed: true,
dataLabels: {
hideOverflowingLabels: false,
},
type: 'radar',
dropShadow: {
enabled: true,
blur: 1,
left: 1,
top: 1,
},
},
dataLabels: {
enabled: true,
formatter: function (val: any, opts: any) {
var label = opts.w.globals.labels[opts.dataPointIndex];
var a = moment(val[0]);
var b = moment(val[1]);
var diff = b.diff(a, 'days');
return label + ': ' + diff + (diff > 1 ? ' days' : ' day');
},
style: {
colors: ['#f3f4f5', '#fff'],
},
},
xaxis: {
type: 'datetime',
},
yaxis: {
show: false,
},
grid: {
row: {
colors: ['#f3f4f5', '#fff'],
opacity: 1,
show: false,
},
legend: { show: false },
title: {
show: false,
},
tooltip: {
x: { show: false },
},
markers: {
size: 0,
},
xaxis: {
categories: ['2011', '2012', '2013', '2014', '2015', '2016'],
},
stroke: {
width: 0,
},
colors: ['#9f8ed7', '#1edec5'],
plotOptions: {
radar: {
polygons: {
strokeColors: [
'#e8e8e8',
'transparent',
'transparent',
'transparent',
'transparent',
'transparent',
],
connectorColors: 'transparent',
},
},
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
gradientToColors: ['#8e9ad6', '#1fcadb'],
shadeIntensity: 1,
type: 'horizontal',
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100, 100, 100],
},
},
});