chore: 调整工作台页面样式-ui稿一致

This commit is contained in:
lzh
2025-12-22 17:15:55 +08:00
parent 6bb7219fe5
commit 2f848f3ac2
5 changed files with 482 additions and 301 deletions

View File

@@ -0,0 +1,66 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
interface Props {
/** 图表配置选项 */
options: any;
/** 图表透明度 */
opacity?: number;
/** 图表容器底部内边距 */
paddingBottom?: string;
}
const props = withDefaults(defineProps<Props>(), {
opacity: 0.6,
paddingBottom: '1.5rem',
});
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
// 初始化图表
function initChart() {
if (props.options) {
renderEcharts(props.options);
}
}
// 监听配置变化
watch(
() => props.options,
() => {
initChart();
},
{ deep: true },
);
// 窗口大小变化时调整图表
const handleResize = () => {
if (chartRef.value) {
chartRef.value.resize?.();
}
};
onMounted(() => {
initChart();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<template>
<div
class="absolute inset-x-0 bottom-0 top-0 z-0 overflow-visible rounded-lg"
:style="{ paddingBottom: paddingBottom }"
>
<EchartsUI ref="chartRef" :style="{ opacity: opacity }" class="h-full w-full" />
</div>
</template>

View File

@@ -0,0 +1,2 @@
export { default as BackgroundChart } from './BackgroundChart.vue';

View File

@@ -1,14 +1,23 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import { computed, onMounted, ref } from 'vue';
import { computed, ref } from 'vue';
import { GlassCard } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { Button, Dropdown, message, Modal, Tag } from 'ant-design-vue';
import { BackgroundChart } from '../../../components/background-chart';
import { createBackgroundChartOptions } from './utils/chart-options';
import {
aiCardClasses,
buttonClasses,
cardContentClasses,
modalClasses,
statCardClasses,
taskListClasses,
textShadowClasses,
} from './utils/styles';
// 机器人图片路径
const robotImage = '/images/Image_robot.png';
@@ -172,121 +181,40 @@ const filteredTasks = computed(() => {
return tasks;
});
// --- 图表引用 ---
const flowChartRef = ref<EchartsUIType>();
const workOrderChartRef = ref<EchartsUIType>();
const { renderEcharts: renderFlowChart } = useEcharts(flowChartRef);
const { renderEcharts: renderWorkOrderChart } = useEcharts(workOrderChartRef);
// --- 初始化图表 ---
onMounted(() => {
initFlowChart();
initWorkOrderChart();
});
// 初始化客流图表
function initFlowChart() {
const options: any = {
grid: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
xAxis: {
type: 'category' as const,
data: flowData.map((item) => item.time),
show: false,
boundaryGap: false,
},
yAxis: {
type: 'value' as const,
show: false,
},
series: [
{
type: 'line' as const,
data: flowData.map((item) => item.value),
smooth: true,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(255, 160, 10, 0.4)' },
{ offset: 1, color: 'rgba(255, 160, 10, 0)' },
],
},
},
lineStyle: {
color: '#FFA00A',
width: 4,
},
symbol: 'none',
symbolSize: 0,
},
// --- 图表配置 ---
const flowChartOptions = computed(() =>
createBackgroundChartOptions({
xAxisData: flowData.map((item) => item.time),
yAxisData: flowData.map((item) => item.value),
seriesName: '客流',
lineColor: '#FFA00A',
areaColor: [
'rgba(255, 160, 10, 0.25)',
'rgba(255, 160, 10, 0.15)',
'rgba(255, 160, 10, 0)',
],
tooltip: {
show: false,
yAxisFormatter: (value: number) => {
if (value >= 1000) {
return `${(value / 1000).toFixed(1)}k`;
}
return value.toString();
},
};
renderFlowChart(options);
}
}),
);
// 初始化工单趋势图表
function initWorkOrderChart() {
const options: any = {
grid: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
xAxis: {
type: 'category' as const,
data: workOrderTrend.map((item) => item.time),
show: false,
boundaryGap: false,
},
yAxis: {
type: 'value' as const,
show: false,
},
series: [
{
type: 'line' as const,
data: workOrderTrend.map((item) => item.value),
smooth: true,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(59, 130, 246, 0.4)' },
{ offset: 1, color: 'rgba(59, 130, 246, 0)' },
],
},
},
lineStyle: {
color: '#3B82F6',
width: 4,
},
symbol: 'none',
symbolSize: 0,
},
const workOrderChartOptions = computed(() =>
createBackgroundChartOptions({
xAxisData: workOrderTrend.map((item) => item.time),
yAxisData: workOrderTrend.map((item) => item.value),
seriesName: '工单',
lineColor: '#3B82F6',
areaColor: [
'rgba(59, 130, 246, 0.25)',
'rgba(59, 130, 246, 0.15)',
'rgba(59, 130, 246, 0)',
],
tooltip: {
show: false,
},
};
renderWorkOrderChart(options);
}
}),
);
// --- 交互方法 ---
function handleTaskComplete(taskId: number) {
@@ -375,24 +303,15 @@ function getColorClass(color: string, type: 'bg' | 'text') {
<!-- 卡片1: 实时客流监测 -->
<GlassCard class="relative flex h-[35%] flex-col overflow-hidden p-6">
<div class="relative z-10 flex items-start justify-between">
<div>
<h3
class="text-sm font-semibold uppercase tracking-wide text-slate-500"
>
实时客流监测
</h3>
<div class="mt-1 flex items-baseline gap-2">
<span class="text-4xl font-bold text-slate-800">2,450</span>
<Tag color="success" class="text-xs font-bold">+12%</Tag>
<div :class="cardContentClasses.container">
<h3 :class="cardContentClasses.title">实时客流监测</h3>
<div :class="cardContentClasses.numberContainer">
<span :class="cardContentClasses.largeNumber">2,450</span>
<Tag color="success" :class="`text-xs font-bold ${textShadowClasses.tag}`">+12%</Tag>
</div>
<p class="mt-1 text-xs text-slate-400">预计高峰时间: 14:00</p>
<p :class="cardContentClasses.description">预计高峰时间: 14:00</p>
</div>
<Button
type="text"
shape="circle"
class="text-slate-400 hover:text-orange-600"
@click="handleRefreshStats"
>
<Button type="text" shape="circle" :class="buttonClasses.refresh" @click="handleRefreshStats">
<template #icon>
<IconifyIcon icon="ant-design:more-outlined" class="text-base" />
</template>
@@ -400,36 +319,23 @@ function getColorClass(color: string, type: 'bg' | 'text') {
</div>
<!-- 背景图表 -->
<div
class="pointer-events-none absolute inset-x-0 bottom-0 z-0 h-[70%] w-full opacity-80"
>
<EchartsUI ref="flowChartRef" class="h-full w-full" />
</div>
<BackgroundChart :options="flowChartOptions" :opacity="0.6" />
</GlassCard>
<!-- 卡片2: 紧急待办事项 -->
<GlassCard class="flex min-h-0 flex-1 flex-col overflow-hidden p-0">
<div
class="flex shrink-0 items-center justify-between border-b border-white/40 p-5"
>
<h3 class="flex items-center gap-2 font-bold text-slate-800">
<span class="h-2 w-2 animate-pulse rounded-full bg-red-500"></span>
<GlassCard :class="taskListClasses.card">
<div :class="taskListClasses.header">
<h3 :class="taskListClasses.title">
<span :class="taskListClasses.titleDot"></span>
紧急待办事项
</h3>
<Dropdown>
<Button
type="text"
size="small"
class="text-xs font-medium text-slate-400 hover:text-orange-600"
>
<Button type="text" size="small" :class="taskListClasses.filterButton">
筛选
<IconifyIcon
icon="ant-design:filter-outlined"
class="ml-1 text-xs"
/>
<IconifyIcon icon="ant-design:filter-outlined" class="ml-1 text-xs" />
</Button>
<template #overlay>
<div class="min-w-[120px] rounded-lg bg-white p-2 shadow-lg">
<div :class="taskListClasses.filterMenu">
<div
v-for="option in [
{ label: '全部', value: 'all' },
@@ -438,11 +344,10 @@ function getColorClass(color: string, type: 'bg' | 'text') {
{ label: 'P2 普通', value: 'P2' },
]"
:key="option.value"
class="cursor-pointer rounded px-3 py-2 text-sm hover:bg-gray-50"
:class="{
'bg-orange-50 text-orange-600':
priorityFilter === option.value,
}"
:class="[
taskListClasses.filterMenuItem,
priorityFilter === option.value && taskListClasses.filterMenuItemActive,
]"
@click="handleFilterChange(option.value as any)"
>
{{ option.label }}
@@ -452,16 +357,10 @@ function getColorClass(color: string, type: 'bg' | 'text') {
</Dropdown>
</div>
<div class="flex-1 overflow-y-auto px-2">
<div
v-if="filteredTasks.length === 0"
class="flex h-full items-center justify-center text-slate-400"
>
<div class="text-center">
<IconifyIcon
icon="ant-design:check-circle-outlined"
class="mb-2 text-4xl"
/>
<div :class="taskListClasses.listContainer">
<div v-if="filteredTasks.length === 0" :class="taskListClasses.emptyState">
<div :class="taskListClasses.emptyContent">
<IconifyIcon icon="ant-design:check-circle-outlined" :class="taskListClasses.emptyIcon" />
<p>暂无待办任务</p>
</div>
</div>
@@ -469,14 +368,13 @@ function getColorClass(color: string, type: 'bg' | 'text') {
<div
v-for="task in filteredTasks"
:key="task.id"
class="group cursor-pointer rounded-lg border-b border-slate-50 p-3 transition-colors last:border-0 hover:bg-orange-50/40"
:class="taskListClasses.taskItem"
@click="handleTaskDetail(task)"
>
<div class="flex items-center justify-between">
<div class="flex min-w-0 flex-1 items-center gap-3">
<div :class="taskListClasses.taskContent">
<div :class="taskListClasses.taskLeft">
<div
class="flex h-7 w-7 items-center justify-center rounded-lg shadow-sm transition-transform group-hover:scale-110"
:class="getPriorityConfig(task.priority).bg"
:class="[taskListClasses.taskIcon, getPriorityConfig(task.priority).bg]"
>
<IconifyIcon
icon="ant-design:clock-circle-outlined"
@@ -484,69 +382,53 @@ function getColorClass(color: string, type: 'bg' | 'text') {
:class="[getPriorityConfig(task.priority).text]"
/>
</div>
<div class="min-w-0 flex-1">
<div class="truncate font-semibold text-slate-700">
{{ task.title }}
</div>
<div
class="mt-1 flex items-center gap-3 text-xs text-slate-500"
>
<div :class="taskListClasses.taskInfo">
<div :class="taskListClasses.taskTitle">{{ task.title }}</div>
<div :class="taskListClasses.taskMeta">
<span>{{ task.location }}</span>
<span v-if="task.createTime"
>• {{ task.createTime }}</span
>
<span v-if="task.createTime">• {{ task.createTime }}</span>
<span v-if="task.assignee">• {{ task.assignee }}</span>
</div>
</div>
<Tag
:color="getPriorityConfig(task.priority).color"
class="border px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide"
:class="[getPriorityConfig(task.priority).border]"
:class="[taskListClasses.taskPriorityTag, getPriorityConfig(task.priority).border]"
>
{{ task.priority }}
</Tag>
</div>
<div class="ml-2 flex items-center gap-1">
<div :class="taskListClasses.taskActions">
<Button
type="text"
size="small"
shape="circle"
class="h-7 w-7 bg-slate-50 text-slate-400 hover:bg-green-500 hover:text-white"
:class="[buttonClasses.circleIcon, buttonClasses.complete]"
@click.stop="handleTaskComplete(task.id)"
>
<template #icon>
<IconifyIcon
icon="ant-design:check-circle-outlined"
class="text-xs"
/>
<IconifyIcon icon="ant-design:check-circle-outlined" class="text-xs" />
</template>
</Button>
<Button
type="text"
size="small"
shape="circle"
class="h-7 w-7 bg-slate-50 text-slate-400 hover:bg-orange-500 hover:text-white"
:class="[buttonClasses.circleIcon, buttonClasses.view]"
@click.stop="handleTaskDetail(task)"
>
<template #icon>
<IconifyIcon
icon="ant-design:eye-outlined"
class="text-xs"
/>
<IconifyIcon icon="ant-design:eye-outlined" class="text-xs" />
</template>
</Button>
<Button
type="text"
size="small"
shape="circle"
class="h-7 w-7 bg-slate-50 text-slate-400 hover:bg-red-500 hover:text-white"
:class="[buttonClasses.circleIcon, buttonClasses.delete]"
@click.stop="handleTaskDelete(task.id)"
>
<template #icon>
<IconifyIcon
icon="ant-design:delete-outlined"
class="text-xs"
/>
<IconifyIcon icon="ant-design:delete-outlined" class="text-xs" />
</template>
</Button>
</div>
@@ -558,86 +440,58 @@ function getColorClass(color: string, type: 'bg' | 'text') {
</div>
<!-- --- 右侧列 (40%) --- -->
<div class="flex h-full w-[40%] flex-none flex-col gap-5">
<div class="flex h-full w-[40%] flex-none flex-col gap-5" style="padding-right: 1.25rem;">
<!-- 卡片3: 工单趋势分析 -->
<GlassCard class="relative flex h-[35%] flex-col overflow-hidden p-6">
<div class="relative z-10 mb-2 flex items-start justify-between">
<div>
<h3
class="text-sm font-semibold uppercase tracking-wide text-slate-500"
>
工单趋势分析
</h3>
<div class="mt-1 flex items-baseline gap-2">
<span class="text-4xl font-bold text-slate-800">89</span>
<Tag color="processing" class="text-xs font-bold">+5 新增</Tag>
<div :class="cardContentClasses.container">
<h3 :class="cardContentClasses.title">工单趋势分析</h3>
<div :class="cardContentClasses.numberContainer">
<span :class="cardContentClasses.largeNumber">89</span>
<Tag color="processing" :class="`text-xs font-bold ${textShadowClasses.tag}`">+5 新增</Tag>
</div>
</div>
<Button
type="text"
size="small"
class="rounded-lg bg-white/50 px-2 py-1 text-xs font-medium hover:text-blue-600"
@click="message.info('查看全部工单')"
>
<Button type="text" size="small" :class="buttonClasses.viewAll" @click="message.info('查看全部工单')">
查看全部
</Button>
</div>
<!-- 背景图表 -->
<div
class="pointer-events-none absolute inset-x-0 bottom-0 z-0 h-[65%] w-full opacity-80"
>
<EchartsUI ref="workOrderChartRef" class="h-full w-full" />
</div>
<BackgroundChart :options="workOrderChartOptions" :opacity="0.6" />
</GlassCard>
<!-- 卡片4: 统计网格 + AI卡片 -->
<div class="flex min-h-0 flex-1 flex-col gap-4">
<!-- 2x2 统计网格 -->
<div class="grid shrink-0 grid-cols-2 gap-3">
<div :class="statCardClasses.container">
<GlassCard
v-for="(stat, idx) in stats"
:key="idx"
class="glass-card glass-border glass-shadow glass-highlight flex cursor-pointer items-center gap-3 rounded-[2rem] p-3 transition-colors hover:bg-white/60"
:class="statCardClasses.card"
@click="handleStatCardClick(stat)"
>
<div
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-lg"
:class="[
statCardClasses.iconContainer,
getColorClass(stat.color, 'bg'),
getColorClass(stat.color, 'text'),
]"
>
<IconifyIcon :icon="stat.icon" class="text-lg" />
</div>
<div class="min-w-0">
<p
class="truncate text-[10px] font-bold uppercase text-slate-400"
>
{{ stat.label }}
</p>
<p class="mt-0.5 text-lg font-bold leading-none text-slate-800">
{{ stat.value }}
</p>
<div :class="statCardClasses.content">
<p :class="statCardClasses.label">{{ stat.label }}</p>
<p :class="statCardClasses.value">{{ stat.value }}</p>
</div>
</GlassCard>
</div>
<!-- AI助手卡片 -->
<GlassCard
class="group relative flex min-h-[140px] flex-1 items-center overflow-hidden border-0 !bg-gradient-to-br from-orange-400 to-amber-300 p-0 shadow-lg"
>
<GlassCard :class="aiCardClasses.container">
<!-- 神经网络背景图案 -->
<div class="pointer-events-none absolute inset-0 opacity-20">
<div :class="aiCardClasses.background">
<svg width="100%" height="100%">
<pattern
id="grid"
x="0"
y="0"
width="20"
height="20"
patternUnits="userSpaceOnUse"
>
<pattern id="grid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="1" fill="white" />
</pattern>
<rect width="100%" height="100%" fill="url(#grid)" />
@@ -652,47 +506,23 @@ function getColorClass(color: string, type: 'bg' | 'text') {
</div>
<!-- 左侧内容 -->
<div
class="relative z-10 flex h-full w-[60%] flex-col justify-center py-4 pl-8 pr-2"
>
<div :class="aiCardClasses.leftContent">
<div class="mb-2 flex items-center gap-2">
<span
class="flex h-6 w-6 items-center justify-center rounded-lg bg-white/20 shadow-sm backdrop-blur-sm"
>
<IconifyIcon
icon="ant-design:robot-outlined"
class="text-xs text-white"
/>
<span :class="aiCardClasses.iconContainer">
<IconifyIcon icon="ant-design:robot-outlined" class="text-xs text-white" />
</span>
<span class="text-[10px] font-bold uppercase tracking-widest text-white/90">AI Copilot</span>
<span :class="aiCardClasses.label">AI Copilot</span>
</div>
<h3
class="mb-4 whitespace-nowrap text-lg font-bold leading-tight text-white drop-shadow-sm"
>
需要我帮你做什么吗?
</h3>
<Button
type="primary"
class="flex w-fit items-center gap-2 rounded-full bg-white px-6 py-2.5 text-sm font-bold text-amber-600 shadow-md shadow-orange-900/10 transition-all hover:scale-105 hover:shadow-lg"
@click="message.info('打开AI助手')"
>
<h3 :class="aiCardClasses.title">需要我帮你做什么吗?</h3>
<Button type="primary" :class="aiCardClasses.button" @click="message.info('打开AI助手')">
使用AI助手
<IconifyIcon
icon="ant-design:robot-outlined"
class="text-base text-amber-400 transition-transform group-hover:rotate-12"
/>
<IconifyIcon icon="ant-design:robot-outlined" :class="aiCardClasses.buttonIcon" />
</Button>
</div>
<!-- 右侧图片 -->
<div
class="pointer-events-none absolute bottom-0 right-[-10px] top-0 z-20 flex w-[45%] items-center justify-center"
>
<img
:src="robotImage"
alt="AI Robot"
class="h-full w-full object-contain"
/>
<div :class="aiCardClasses.imageContainer">
<img :src="robotImage" alt="AI Robot" :class="aiCardClasses.image" />
</div>
</GlassCard>
</div>
@@ -705,41 +535,30 @@ function getColorClass(color: string, type: 'bg' | 'text') {
:footer="null"
width="600px"
>
<div v-if="selectedTask" class="space-y-4">
<div v-if="selectedTask" :class="modalClasses.field">
<div>
<div class="mb-1 text-sm text-slate-400">任务标题</div>
<div class="text-lg font-bold text-slate-800">
{{ selectedTask.title }}
</div>
<div :class="modalClasses.label">任务标题</div>
<div :class="modalClasses.titleValue">{{ selectedTask.title }}</div>
</div>
<div>
<div class="mb-1 text-sm text-slate-400">位置</div>
<div class="text-base text-slate-700">
{{ selectedTask.location }}
</div>
<div :class="modalClasses.label">位置</div>
<div :class="modalClasses.value">{{ selectedTask.location }}</div>
</div>
<div>
<div class="mb-1 text-sm text-slate-400">优先级</div>
<Tag
:color="getPriorityConfig(selectedTask.priority).color"
class="px-2 py-1 text-xs font-bold uppercase"
>
<div :class="modalClasses.label">优先级</div>
<Tag :color="getPriorityConfig(selectedTask.priority).color" class="px-2 py-1 text-xs font-bold uppercase">
{{ selectedTask.priority }}
</Tag>
</div>
<div v-if="selectedTask.assignee">
<div class="mb-1 text-sm text-slate-400">负责人</div>
<div class="text-base text-slate-700">
{{ selectedTask.assignee }}
</div>
<div :class="modalClasses.label">负责人</div>
<div :class="modalClasses.value">{{ selectedTask.assignee }}</div>
</div>
<div v-if="selectedTask.createTime">
<div class="mb-1 text-sm text-slate-400">创建时间</div>
<div class="text-base text-slate-700">
{{ selectedTask.createTime }}
</div>
<div :class="modalClasses.label">创建时间</div>
<div :class="modalClasses.value">{{ selectedTask.createTime }}</div>
</div>
<div class="flex gap-2 pt-4">
<div :class="modalClasses.actions">
<Button
type="primary"
@click="

View File

@@ -0,0 +1,138 @@
/**
* 创建背景图表的通用配置
*/
export function createBackgroundChartOptions(config: {
xAxisData: string[];
yAxisData: number[];
seriesName: string;
lineColor: string;
areaColor: string[];
yAxisFormatter?: (value: number) => string;
}) {
const {
xAxisData,
yAxisData,
seriesName,
lineColor,
areaColor,
yAxisFormatter,
} = config;
return {
grid: {
left: '4%',
right: '2%',
top: '5%',
bottom: '15%',
containLabel: true,
},
xAxis: {
type: 'category' as const,
data: xAxisData,
show: true,
boundaryGap: false,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: true,
color: 'rgba(148, 163, 184, 0.6)',
fontSize: 10,
fontWeight: 400,
margin: 6,
interval: 0,
rotate: 0,
},
splitLine: {
show: false,
},
},
yAxis: {
type: 'value' as const,
show: true,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: true,
color: 'rgba(148, 163, 184, 0.6)',
fontSize: 10,
fontWeight: 400,
margin: 6,
formatter: yAxisFormatter,
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(148, 163, 184, 0.1)',
type: 'dashed',
width: 1,
},
},
},
series: [
{
name: seriesName,
type: 'line' as const,
data: yAxisData,
smooth: true,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: areaColor[0] },
{ offset: 0.5, color: areaColor[1] },
{ offset: 1, color: areaColor[2] },
],
},
},
lineStyle: {
color: lineColor,
width: 4,
},
symbol: 'none',
symbolSize: 0,
},
],
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'line',
lineStyle: {
color: 'rgba(148, 163, 184, 0.3)',
width: 1,
},
},
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: 'rgba(148, 163, 184, 0.2)',
borderWidth: 1,
textStyle: {
color: '#475569',
fontSize: 12,
},
padding: [8, 12],
formatter: (params: any) => {
const param = params[0];
return `
<div style="margin-bottom: 4px; font-weight: 600;">${param.name}</div>
<div style="display: flex; align-items: center; gap: 8px;">
<span style="display: inline-block; width: 10px; height: 10px; background-color: ${param.color}; border-radius: 50%;"></span>
<span>${param.seriesName || '数值'}: <strong>${param.value}</strong></span>
</div>
`;
},
},
};
}

View File

@@ -0,0 +1,156 @@
/**
* 文字阴影样式类名
*/
export const textShadowClasses = {
/** 标题阴影 */
title: 'drop-shadow-[0_2px_16px_rgba(255,255,255,0.95),0_0_8px_rgba(255,255,255,0.8)]',
/** 大数字阴影 */
largeNumber: 'drop-shadow-[0_2px_20px_rgba(255,255,255,0.98),0_0_12px_rgba(255,255,255,0.9)]',
/** 标签阴影 */
tag: 'drop-shadow-[0_1px_8px_rgba(255,255,255,0.9),0_0_4px_rgba(255,255,255,0.8)]',
/** 小文字阴影 */
smallText: 'drop-shadow-[0_1px_8px_rgba(255,255,255,0.85),0_0_4px_rgba(255,255,255,0.7)]',
};
/**
* 卡片内容样式类名
*/
export const cardContentClasses = {
/** 内容容器 */
container: 'relative',
/** 标题 */
title: `text-sm font-semibold uppercase tracking-wide text-slate-500 ${textShadowClasses.title}`,
/** 数字容器 */
numberContainer: 'mt-1 flex items-baseline gap-2',
/** 大数字 */
largeNumber: `text-4xl font-bold text-slate-800 ${textShadowClasses.largeNumber}`,
/** 描述文字 */
description: `mt-1 text-xs text-slate-400 ${textShadowClasses.smallText}`,
};
/**
* 任务列表样式类名
*/
export const taskListClasses = {
/** 任务卡片容器 */
card: 'flex min-h-0 flex-1 flex-col overflow-hidden p-0',
/** 任务列表头部 */
header: 'flex shrink-0 items-center justify-between border-b border-white/40 p-5',
/** 任务列表标题 */
title: 'flex items-center gap-2 font-bold text-slate-800',
/** 任务列表标题指示点 */
titleDot: 'h-2 w-2 animate-pulse rounded-full bg-red-500',
/** 筛选按钮 */
filterButton: 'text-xs font-medium text-slate-400 hover:text-orange-600',
/** 筛选下拉菜单 */
filterMenu: 'min-w-[120px] rounded-lg bg-white p-2 shadow-lg',
/** 筛选菜单项 */
filterMenuItem: 'cursor-pointer rounded px-3 py-2 text-sm hover:bg-gray-50',
/** 筛选菜单项激活状态 */
filterMenuItemActive: 'bg-orange-50 text-orange-600',
/** 任务列表容器 */
listContainer: 'flex-1 overflow-y-auto px-2',
/** 空状态容器 */
emptyState: 'flex h-full items-center justify-center text-slate-400',
/** 空状态内容 */
emptyContent: 'text-center',
/** 空状态图标 */
emptyIcon: 'mb-2 text-4xl',
/** 任务项容器 */
taskItem: 'group cursor-pointer rounded-lg border-b border-slate-50 p-3 transition-colors last:border-0 hover:bg-orange-50/40',
/** 任务项内容 */
taskContent: 'flex items-center justify-between',
/** 任务左侧内容 */
taskLeft: 'flex min-w-0 flex-1 items-center gap-3',
/** 任务图标容器 */
taskIcon: 'flex h-7 w-7 items-center justify-center rounded-lg shadow-sm transition-transform group-hover:scale-110',
/** 任务信息容器 */
taskInfo: 'min-w-0 flex-1',
/** 任务标题 */
taskTitle: 'truncate font-semibold text-slate-700',
/** 任务元信息 */
taskMeta: 'mt-1 flex items-center gap-3 text-xs text-slate-500',
/** 任务操作按钮组 */
taskActions: 'ml-2 flex items-center gap-1',
/** 任务优先级标签 */
taskPriorityTag: 'border px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide',
};
/**
* 按钮样式类名
*/
export const buttonClasses = {
/** 圆形图标按钮基础样式 */
circleIcon: 'h-7 w-7 bg-slate-50 text-slate-400',
/** 完成按钮 */
complete: 'hover:bg-green-500 hover:text-white',
/** 查看按钮 */
view: 'hover:bg-orange-500 hover:text-white',
/** 删除按钮 */
delete: 'hover:bg-red-500 hover:text-white',
/** 刷新按钮 */
refresh: 'text-slate-400 hover:text-orange-600',
/** 查看全部按钮 */
viewAll: 'rounded-lg bg-white/50 px-2 py-1 text-xs font-medium hover:text-blue-600',
};
/**
* 统计卡片样式类名
*/
export const statCardClasses = {
/** 统计卡片容器 */
container: 'grid shrink-0 grid-cols-2 gap-3',
/** 统计卡片 */
card: 'glass-card glass-border glass-shadow glass-highlight flex cursor-pointer items-center gap-3 rounded-[2rem] p-3 transition-colors hover:bg-white/60',
/** 图标容器 */
iconContainer: 'flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-lg',
/** 内容容器 */
content: 'min-w-0',
/** 标签文字 */
label: 'truncate text-[10px] font-bold uppercase text-slate-400',
/** 数值 */
value: 'mt-0.5 text-lg font-bold leading-none text-slate-800',
};
/**
* AI助手卡片样式类名
*/
export const aiCardClasses = {
/** AI卡片容器 */
container: 'group relative flex min-h-[140px] flex-1 items-center overflow-hidden border-0 !bg-gradient-to-br from-orange-400 to-amber-300 p-0 shadow-lg',
/** 背景图案容器 */
background: 'pointer-events-none absolute inset-0 opacity-20',
/** 左侧内容容器 */
leftContent: 'relative z-10 flex h-full w-[60%] flex-col justify-center py-4 pl-8 pr-2',
/** 图标容器 */
iconContainer: 'flex h-6 w-6 items-center justify-center rounded-lg bg-white/20 shadow-sm backdrop-blur-sm',
/** 标签文字 */
label: 'text-[10px] font-bold uppercase tracking-widest text-white/90',
/** 标题 */
title: 'mb-4 whitespace-nowrap text-lg font-bold leading-tight text-white drop-shadow-sm',
/** 按钮 */
button: 'flex w-fit items-center gap-2 rounded-full bg-white px-6 py-2.5 text-sm font-bold text-amber-600 shadow-md shadow-orange-900/10 transition-all hover:scale-105 hover:shadow-lg',
/** 按钮图标 */
buttonIcon: 'text-base text-amber-400 transition-transform group-hover:rotate-12',
/** 右侧图片容器 */
imageContainer: 'pointer-events-none absolute bottom-0 right-[-10px] top-0 z-20 flex w-[45%] items-center justify-center',
/** 图片 */
image: 'h-full w-full object-contain',
};
/**
* 模态框样式类名
*/
export const modalClasses = {
/** 字段容器 */
field: 'space-y-4',
/** 字段标签 */
label: 'mb-1 text-sm text-slate-400',
/** 字段值 */
value: 'text-base text-slate-700',
/** 标题值 */
titleValue: 'text-lg font-bold text-slate-800',
/** 操作按钮组 */
actions: 'flex gap-2 pt-4',
};