dashboard (#6)
* feat(dashboard): 增加dashboard示例 * feat(chart-ui): 增加图表UI组件库 * feat(dashboard): 完善dashboard示例
This commit is contained in:
@@ -48,6 +48,7 @@
|
||||
"@vben/locales": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"@vueuse/integrations": "^10.11.0",
|
||||
"@vben/chart-ui": "workspace:*",
|
||||
"qrcode": "^1.5.3",
|
||||
"vue": "^3.4.30",
|
||||
"vue-router": "^4.4.0"
|
||||
|
45
packages/business/universal-ui/src/dashboard/card.vue
Normal file
45
packages/business/universal-ui/src/dashboard/card.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<script lang="ts" setup>
|
||||
import { VbenIcon, Badge } from '@vben-core/shadcn-ui';
|
||||
defineOptions({ name: 'DashboardCard' });
|
||||
import type { CardItem } from './typings';
|
||||
interface Props {
|
||||
item: CardItem;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rounded-lg border-2 border-solid">
|
||||
<div class="flex justify-between p-2">
|
||||
<div class="">
|
||||
<slot name="title">{{ item.title }}</slot>
|
||||
</div>
|
||||
<div class="text-xs" :class="`bg-${item.color}-500`">
|
||||
<slot name="extra"
|
||||
><Badge>{{ item.extra }}</Badge></slot
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-2 mr-2">
|
||||
<div class="m-2 flex justify-between">
|
||||
<div class="text-4xl">
|
||||
<slot name="leftContent">{{ item.leftContent }}</slot>
|
||||
</div>
|
||||
<div>
|
||||
<slot name="rightContent"
|
||||
><VbenIcon :icon="item.rightContent" class="size-10"
|
||||
/></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2 flex justify-between">
|
||||
<div>
|
||||
<slot name="leftFooter">{{ item.leftFooter }}</slot>
|
||||
</div>
|
||||
<div>
|
||||
<slot name="rightFooter">{{ item.rightFooter }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
24
packages/business/universal-ui/src/dashboard/chartCard.vue
Normal file
24
packages/business/universal-ui/src/dashboard/chartCard.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { chart } from '@vben/chart-ui';
|
||||
defineOptions({ name: 'ChartCard' });
|
||||
import type { ChartItem } from './typings';
|
||||
import { onMounted, ref } from 'vue';
|
||||
interface Props {
|
||||
item: ChartItem;
|
||||
}
|
||||
const chartRef = ref();
|
||||
onMounted(() => {
|
||||
chartRef.value.setChart(props.item.option);
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rounded-lg border-2 border-solid">
|
||||
<div class="">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<chart ref="chartRef" />
|
||||
</div>
|
||||
</template>
|
41
packages/business/universal-ui/src/dashboard/chartTab.vue
Normal file
41
packages/business/universal-ui/src/dashboard/chartTab.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang="ts" setup>
|
||||
import { Tabs, TabsList, TabsTrigger } from '@vben-core/shadcn-ui';
|
||||
import { chart } from '@vben/chart-ui';
|
||||
defineOptions({ name: 'ChartTab' });
|
||||
import type { ChartItem } from './typings';
|
||||
import { onMounted, ref } from 'vue';
|
||||
interface Props {
|
||||
items: ChartItem[];
|
||||
}
|
||||
const chartRef = ref();
|
||||
onMounted(() => {
|
||||
change(0);
|
||||
});
|
||||
const change = (i) => {
|
||||
const item = props.items[i];
|
||||
if (!item) return;
|
||||
item.option && chartRef.value.setChart(item.option);
|
||||
};
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rounded-lg border-2 border-solid">
|
||||
<Tabs
|
||||
:defaultValue="items[0].name"
|
||||
className="w-[400px]"
|
||||
@update:modelValue="change"
|
||||
>
|
||||
<TabsList className="flex w-full ">
|
||||
<TabsTrigger
|
||||
:value="index"
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
>{{ item.title }}</TabsTrigger
|
||||
>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
|
||||
<chart ref="chartRef" />
|
||||
</div>
|
||||
</template>
|
152
packages/business/universal-ui/src/dashboard/dashboard.vue
Normal file
152
packages/business/universal-ui/src/dashboard/dashboard.vue
Normal file
@@ -0,0 +1,152 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CardItem, ChartItem } from './typings';
|
||||
defineOptions({ name: 'Dashboard' });
|
||||
import Card from './card.vue';
|
||||
import ChartTab from './chartTab.vue';
|
||||
import ChartCard from './chartCard.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface Props {
|
||||
cardList: CardItem[];
|
||||
chartTabs: ChartItem[];
|
||||
}
|
||||
const itemA = ref({
|
||||
title: '玫瑰图',
|
||||
option: {
|
||||
legend: {
|
||||
top: 'bottom',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true },
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Nightingale Chart',
|
||||
type: 'pie',
|
||||
radius: [50, 200],
|
||||
center: ['50%', '50%'],
|
||||
roseType: 'area',
|
||||
itemStyle: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
data: [
|
||||
{ value: 40, name: 'rose 1' },
|
||||
{ value: 38, name: 'rose 2' },
|
||||
{ value: 32, name: 'rose 3' },
|
||||
{ value: 30, name: 'rose 4' },
|
||||
{ value: 28, name: 'rose 5' },
|
||||
{ value: 26, name: 'rose 6' },
|
||||
{ value: 22, name: 'rose 7' },
|
||||
{ value: 18, name: 'rose 8' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const itemB = ref({
|
||||
title: '雷达图',
|
||||
option: {
|
||||
legend: {
|
||||
data: ['Allocated Budget', 'Actual Spending'],
|
||||
},
|
||||
radar: {
|
||||
// shape: 'circle',
|
||||
indicator: [
|
||||
{ name: 'Sales', max: 6500 },
|
||||
{ name: 'Administration', max: 16000 },
|
||||
{ name: 'Information Technology', max: 30000 },
|
||||
{ name: 'Customer Support', max: 38000 },
|
||||
{ name: 'Development', max: 52000 },
|
||||
{ name: 'Marketing', max: 25000 },
|
||||
],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Budget vs spending',
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: [4200, 3000, 20000, 35000, 50000, 18000],
|
||||
name: 'Allocated Budget',
|
||||
},
|
||||
{
|
||||
value: [5000, 14000, 28000, 26000, 42000, 21000],
|
||||
name: 'Actual Spending',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const itemC = ref({
|
||||
title: '饼图',
|
||||
option: {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
top: '5%',
|
||||
left: 'center',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [
|
||||
{ value: 1048, name: 'Search Engine' },
|
||||
{ value: 735, name: 'Direct' },
|
||||
{ value: 580, name: 'Email' },
|
||||
{ value: 484, name: 'Union Ads' },
|
||||
{ value: 300, name: 'Video Ads' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
cardList: () => [],
|
||||
chartTabs: () => [],
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="grid grid-cols-4 gap-4 p-2">
|
||||
<Card v-for="item in cardList" :key="item.title" :item="item" />
|
||||
</div>
|
||||
<div class="p-2"><ChartTab :items="chartTabs" /></div>
|
||||
<div class="grid grid-cols-3 gap-2 p-2">
|
||||
<ChartCard :item="itemA" />
|
||||
<ChartCard :item="itemB" />
|
||||
<ChartCard :item="itemC" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
3
packages/business/universal-ui/src/dashboard/index.ts
Normal file
3
packages/business/universal-ui/src/dashboard/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as DashboardLayout } from './layout.vue';
|
||||
export { default as Dashboard } from './dashboard.vue';
|
||||
export { default as chartCard } from './chartCard.vue';
|
7
packages/business/universal-ui/src/dashboard/layout.vue
Normal file
7
packages/business/universal-ui/src/dashboard/layout.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'DashboardLayout' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>dashboardLayout</div>
|
||||
</template>
|
17
packages/business/universal-ui/src/dashboard/typings.ts
Normal file
17
packages/business/universal-ui/src/dashboard/typings.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
interface CardItem {
|
||||
title: string;
|
||||
extra: string;
|
||||
leftContent: string;
|
||||
rightContent: string;
|
||||
color?: string;
|
||||
leftFooter: string;
|
||||
rightFooter: string;
|
||||
}
|
||||
|
||||
interface ChartItem {
|
||||
name: string;
|
||||
title: string;
|
||||
options: any;
|
||||
}
|
||||
|
||||
export type { CardItem, ChartItem };
|
@@ -8,3 +8,4 @@ export * from './notification';
|
||||
export * from './preferences';
|
||||
export * from './theme-toggle';
|
||||
export * from './user-dropdown';
|
||||
export * from './dashboard';
|
||||
|
Reference in New Issue
Block a user