新增前端vue
This commit is contained in:
@@ -29,13 +29,6 @@
|
||||
<MenuItem value="accountCenter" :text="t('sys.account.center')" icon="i-ion:person-outline" />
|
||||
<MenuItem value="modifyPwd" :text="t('sys.account.modifyPwd')" icon="i-ant-design:key-outlined" />
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
value="doc"
|
||||
:text="t('layout.header.dropdownItemDoc')"
|
||||
icon="i-ion:document-text-outline"
|
||||
v-if="getShowDoc"
|
||||
/>
|
||||
<MenuDivider v-if="getShowDoc" />
|
||||
<MenuItem
|
||||
v-if="getUseLockPage"
|
||||
value="lock"
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<template>
|
||||
<div class="md:flex">
|
||||
<template v-for="(item, index) in growCardList" :key="item.title">
|
||||
<Card
|
||||
size="small"
|
||||
:loading="loading"
|
||||
:title="item.title"
|
||||
class="w-full cursor-pointer !mt-4 md:w-1/4 !md:mt-0"
|
||||
:class="[index + 1 < 4 && '!md:mr-4']"
|
||||
:canExpan="false"
|
||||
@click="navPage(item.url)"
|
||||
>
|
||||
<template #extra>
|
||||
<Tag :color="item.color">{{ item.action }}</Tag>
|
||||
</template>
|
||||
|
||||
<div class="flex justify-between px-4 py-4">
|
||||
<CountTo prefix="" :startVal="1" :endVal="item.value" class="text-2xl" />
|
||||
<Icon :icon="item.icon" :size="40" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between p-2 px-4">
|
||||
<span>点击我</span>
|
||||
<CountTo prefix="共" :startVal="1" :endVal="item.total" />
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { CountTo } from '@jeesite/core/components/CountTo';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import { Tag, Card } from 'ant-design-vue';
|
||||
import { useGo } from '@jeesite/core/hooks/web/usePage';
|
||||
|
||||
const loading = ref(true);
|
||||
const growCardList = ref<GrowCardItem[]>();
|
||||
const go = useGo();
|
||||
|
||||
interface GrowCardItem {
|
||||
icon: string;
|
||||
title: string;
|
||||
value: number;
|
||||
total: number;
|
||||
color: string;
|
||||
action: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const list: GrowCardItem[] = [
|
||||
{
|
||||
title: '工作台',
|
||||
icon: 'icons/visit-count.svg',
|
||||
value: 1999,
|
||||
total: 120000,
|
||||
color: 'green',
|
||||
action: '时',
|
||||
url: '/desktop/workbench',
|
||||
},
|
||||
{
|
||||
title: '关于我们',
|
||||
icon: 'icons/total-sales.svg',
|
||||
value: 2999,
|
||||
total: 500000,
|
||||
color: 'blue',
|
||||
action: '日',
|
||||
url: '/desktop/about',
|
||||
},
|
||||
{
|
||||
title: '源码下载',
|
||||
icon: 'icons/download-count.svg',
|
||||
value: 3999,
|
||||
total: 120000,
|
||||
color: 'orange',
|
||||
action: '周',
|
||||
url: 'https://gitee.com/thinkgem/jeesite-vue',
|
||||
},
|
||||
{
|
||||
title: '官方网站',
|
||||
icon: 'icons/transaction.svg',
|
||||
value: 9999,
|
||||
total: 99999,
|
||||
color: 'purple',
|
||||
action: '月',
|
||||
url: 'http://jeesite.com',
|
||||
},
|
||||
];
|
||||
// 此处写后端 API 获取 list 数据
|
||||
growCardList.value = list;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
});
|
||||
|
||||
function navPage(url: string) {
|
||||
if (!url || url === '') {
|
||||
return;
|
||||
}
|
||||
if (url.indexOf('://') != -1) {
|
||||
window.open(url);
|
||||
} else {
|
||||
go(url);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div class="quick-login-container">
|
||||
<!-- 标题区域(保留内置图标) -->
|
||||
<div class="title-bar">
|
||||
<LoginOutlined class="title-icon" />
|
||||
<span class="title-text">应用系统</span>
|
||||
</div>
|
||||
|
||||
<!-- 搜索框(保留内置图标) -->
|
||||
<a-input
|
||||
v-model:value="searchKey"
|
||||
placeholder="搜索应用系统..."
|
||||
class="search-input"
|
||||
size="middle"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined class="search-icon" />
|
||||
</template>
|
||||
</a-input>
|
||||
|
||||
<!-- 应用卡片滚动容器 -->
|
||||
<div class="app-scroll-container">
|
||||
<div class="app-card-grid">
|
||||
<!-- 应用卡片(图标为图片地址) -->
|
||||
<div
|
||||
v-for="(app, index) in filteredAppList"
|
||||
:key="index"
|
||||
class="app-card"
|
||||
@click="openAppUrl(app.url)"
|
||||
>
|
||||
<!-- 图片图标容器 -->
|
||||
<div class="app-icon-wrapper" :style="{ backgroundColor: app.bgColor }">
|
||||
<img
|
||||
:src="app.icon"
|
||||
:alt="app.name"
|
||||
class="app-icon"
|
||||
@error="handleImgError($event)"
|
||||
/>
|
||||
</div>
|
||||
<!-- 应用名称 -->
|
||||
<div class="app-name">{{ app.name }}</div>
|
||||
<!-- 悬浮遮罩层 -->
|
||||
<div class="app-hover-mask" :style="{ background: app.maskColor }">
|
||||
<span class="hover-text">打开 {{ app.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态提示(保留内置图标) -->
|
||||
<div v-if="filteredAppList.length === 0" class="empty-tip">
|
||||
<SearchOutlined class="empty-icon" />
|
||||
<p class="empty-text">未找到匹配的应用</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { Input } from 'ant-design-vue';
|
||||
import { LoginOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
// 声明应用列表TS类型(已删除fallbackIcon字段)
|
||||
interface AppItem {
|
||||
name: string;
|
||||
icon: string; // 应用图标为图片地址
|
||||
url: string;
|
||||
bgColor: string; // 图标背景色
|
||||
maskColor: string; // 悬浮遮罩色
|
||||
}
|
||||
|
||||
const appList = ref<AppItem[]>([]);
|
||||
|
||||
// 搜索关键词
|
||||
const searchKey = ref('');
|
||||
|
||||
// 过滤后的应用列表
|
||||
const filteredAppList = computed(() => {
|
||||
if (!searchKey.value) return appList.value;
|
||||
return appList.value.filter(app =>
|
||||
app.name.toLowerCase().includes(searchKey.value.toLowerCase())
|
||||
);
|
||||
});
|
||||
|
||||
// 打开应用URL(新窗口)
|
||||
const openAppUrl = (url: string) => {
|
||||
if (!url) return;
|
||||
window.open(
|
||||
url,
|
||||
'_blank'
|
||||
);
|
||||
};
|
||||
|
||||
// 图片加载失败处理(直接使用通用默认图标,已删除app参数)
|
||||
const handleImgError = (e: Event) => {
|
||||
const img = e.target as HTMLImageElement;
|
||||
// 终极备用图标(通用默认)
|
||||
img.src = 'http://crontab.club:30012/cApi/images/login-brand.png';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 全局容器 */
|
||||
.quick-login-container {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8f0 100%);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
width: 360px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 标题区域 */
|
||||
.title-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 20px;
|
||||
color: #1890ff;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
/* 搜索框 */
|
||||
.search-input {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.search-input :deep(.ant-input) {
|
||||
border-radius: 8px;
|
||||
padding: 10px 16px;
|
||||
border: 1px solid #e5e6eb;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-input :deep(.ant-input:focus) {
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
/* 搜索图标样式 */
|
||||
.search-icon {
|
||||
font-size: 16px;
|
||||
color: #86909c;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 核心:滚动容器 */
|
||||
.app-scroll-container {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* 自定义滚动条 */
|
||||
.app-scroll-container::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.app-scroll-container::-webkit-scrollbar-track {
|
||||
background: #f1f3f5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.app-scroll-container::-webkit-scrollbar-thumb {
|
||||
background: #c9cdd4;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.app-scroll-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #86909c;
|
||||
}
|
||||
|
||||
/* 卡片网格布局 */
|
||||
.app-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* 应用卡片 */
|
||||
.app-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 120px;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 悬浮动效 */
|
||||
.app-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* 图标容器 */
|
||||
.app-icon-wrapper {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.app-card:hover .app-icon-wrapper {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 图片图标样式 */
|
||||
.app-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
object-fit: contain; /* 保持图片比例 */
|
||||
}
|
||||
|
||||
/* 应用名称 */
|
||||
.app-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.app-card:hover .app-name {
|
||||
color: #1890ff; /* 悬浮时名称变色 */
|
||||
}
|
||||
|
||||
/* 悬浮遮罩层 */
|
||||
.app-hover-mask {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.app-card:hover .app-hover-mask {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hover-text {
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease 0.1s;
|
||||
}
|
||||
|
||||
.app-card:hover .hover-text {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 空状态样式 */
|
||||
.empty-tip {
|
||||
grid-column: span 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 120px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 28px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,51 +0,0 @@
|
||||
<template>
|
||||
<Card title="成交占比" :loading="loading">
|
||||
<div ref="chartRef" class="h-75 w-full"></div>
|
||||
</Card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, Ref, ref } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { useECharts } from '@jeesite/core/hooks/web/useECharts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
const loading = ref(true);
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
const options: EChartsOption = {
|
||||
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;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
// 此处写后端 API 获取 options 数据
|
||||
setTimeout(() => {
|
||||
setOptions(options);
|
||||
loading.value = false;
|
||||
}, 900);
|
||||
});
|
||||
</script>
|
||||
@@ -1,44 +0,0 @@
|
||||
<template>
|
||||
<Card
|
||||
v-bind="$attrs"
|
||||
:loading="loading"
|
||||
:tab-list="tabListTitle"
|
||||
:active-tab-key="activeKey"
|
||||
@tab-change="onTabChange"
|
||||
>
|
||||
<p v-if="activeKey === 'tab1'">
|
||||
<VisitAnalysis />
|
||||
</p>
|
||||
<p v-if="activeKey === 'tab2'">
|
||||
<VisitAnalysisBar />
|
||||
</p>
|
||||
</Card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import VisitAnalysis from './VisitAnalysis.vue';
|
||||
import VisitAnalysisBar from './VisitAnalysisBar.vue';
|
||||
|
||||
const loading = ref(true);
|
||||
const activeKey = ref('tab1');
|
||||
|
||||
const tabListTitle = [
|
||||
{
|
||||
key: 'tab1',
|
||||
tab: '流量趋势',
|
||||
},
|
||||
{
|
||||
key: 'tab2',
|
||||
tab: '访问量',
|
||||
},
|
||||
];
|
||||
|
||||
function onTabChange(key) {
|
||||
activeKey.value = key;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 700);
|
||||
</script>
|
||||
@@ -1,109 +0,0 @@
|
||||
<template>
|
||||
<Skeleton active :paragraph="{ rows: 5 }" :loading="loading">
|
||||
<div ref="chartRef" class="h-70 w-full"></div>
|
||||
</Skeleton>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, Ref } from 'vue';
|
||||
import { Skeleton } from 'ant-design-vue';
|
||||
import { useECharts } from '@jeesite/core/hooks/web/useECharts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
const loading = ref(true);
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
const options: EChartsOption = {
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
// 此处写后端 API 获取 options 数据
|
||||
setOptions(options);
|
||||
loading.value = false;
|
||||
});
|
||||
</script>
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<Skeleton active :paragraph="{ rows: 5 }" :loading="loading">
|
||||
<div ref="chartRef" class="h-70 w-full"></div>
|
||||
</Skeleton>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, Ref } from 'vue';
|
||||
import { Skeleton } from 'ant-design-vue';
|
||||
import { useECharts } from '@jeesite/core/hooks/web/useECharts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
const loading = ref(true);
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
const options: EChartsOption = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
// 此处写后端 API 获取 options 数据
|
||||
setOptions(options);
|
||||
loading.value = false;
|
||||
});
|
||||
</script>
|
||||
@@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<Card title="转化率" :loading="loading">
|
||||
<div ref="chartRef" class="h-75 w-full"></div>
|
||||
</Card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, Ref, ref } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { useECharts } from '@jeesite/core/hooks/web/useECharts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
const loading = ref(true);
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
const options: EChartsOption = {
|
||||
legend: {
|
||||
bottom: 0,
|
||||
data: ['访问', '购买'],
|
||||
},
|
||||
tooltip: {},
|
||||
radar: {
|
||||
radius: '60%',
|
||||
splitNumber: 8,
|
||||
indicator: [
|
||||
{
|
||||
name: '电脑',
|
||||
max: 100,
|
||||
},
|
||||
{
|
||||
name: '充电器',
|
||||
max: 100,
|
||||
},
|
||||
{
|
||||
name: '耳机',
|
||||
max: 100,
|
||||
},
|
||||
{
|
||||
name: '手机',
|
||||
max: 100,
|
||||
},
|
||||
{
|
||||
name: 'Ipad',
|
||||
max: 100,
|
||||
},
|
||||
{
|
||||
name: '耳机',
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
// 此处写后端 API 获取 options 数据
|
||||
setTimeout(() => {
|
||||
setOptions(options);
|
||||
loading.value = false;
|
||||
}, 900);
|
||||
});
|
||||
</script>
|
||||
@@ -1,71 +0,0 @@
|
||||
<template>
|
||||
<Card title="访问来源" :loading="loading">
|
||||
<div ref="chartRef" class="h-75 w-full"></div>
|
||||
</Card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, Ref, ref } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { useECharts } from '@jeesite/core/hooks/web/useECharts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
const loading = ref(true);
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
const options: EChartsOption = {
|
||||
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;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
// 此处写后端 API 获取 options 数据
|
||||
setTimeout(() => {
|
||||
setOptions(options);
|
||||
loading.value = false;
|
||||
}, 900);
|
||||
});
|
||||
</script>
|
||||
@@ -1,18 +1,8 @@
|
||||
<template>
|
||||
<div class="mb-4">
|
||||
<GrowCard class="enter-y" />
|
||||
<SiteAnalysis class="enter-y !my-4" />
|
||||
<div class="enter-y md:flex">
|
||||
<VisitRadar class="w-full md:w-1/3" />
|
||||
<VisitSource class="w-full !my-4 md:w-1/3 !md:mx-4 !md:my-0" />
|
||||
<SalesProductPie class="w-full md:w-1/3" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<Home class="enter-y" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="Analysis">
|
||||
import GrowCard from './components/GrowCard.vue';
|
||||
import SiteAnalysis from './components/SiteAnalysis.vue';
|
||||
import VisitSource from './components/VisitSource.vue';
|
||||
import VisitRadar from './components/VisitRadar.vue';
|
||||
import SalesProductPie from './components/SalesProductPie.vue';
|
||||
import Home from './components/Home.vue';
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user