|
|
@@ -8,18 +8,28 @@
|
|
|
今天是 {{ new Date().toLocaleDateString() }}。您有 5 条流程待处理。
|
|
|
</p>
|
|
|
</div>
|
|
|
- <div class="hero-accent" aria-hidden="true"></div>
|
|
|
+ <div class="opration">
|
|
|
+ <el-button type="primary" size="default" color="#02409b"
|
|
|
+ ><Icon icon="mdi:plus-thick" class="icon pr-1" />快速发起</el-button
|
|
|
+ >
|
|
|
+ <el-button type="default" size="default"
|
|
|
+ ><Icon
|
|
|
+ icon="mdi:chart-bar"
|
|
|
+ class="icon pr-1"
|
|
|
+ />查看效率报表</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
</section>
|
|
|
|
|
|
<!-- 任务统计 -->
|
|
|
<section class="total">
|
|
|
<div class="total-card" v-for="(item, index) in stats" :key="index">
|
|
|
<div class="card-icon" :style="{ backgroundColor: item.bgcolor }">
|
|
|
- <Icon :icon="item.icon" />
|
|
|
+ <Icon :icon="item.icon" :color="item.color" />
|
|
|
</div>
|
|
|
<div class="card-content">
|
|
|
<p class="card-title">{{ item.title }}</p>
|
|
|
- <p class="card-number">{{ item.number }}</p>
|
|
|
+ <p class="card-number">{{ oaTasks.todoCount }}</p>
|
|
|
</div>
|
|
|
<div v-if="item.extra" class="card-extra">
|
|
|
{{ item.extra }}
|
|
|
@@ -28,46 +38,32 @@
|
|
|
</section>
|
|
|
|
|
|
<div class="content">
|
|
|
- <div class="tabs" role="tablist" aria-label="EHR模块">
|
|
|
+ <div class="tabs-container" role="tablist" aria-label="EHR模块">
|
|
|
<button
|
|
|
- class="tab"
|
|
|
+ class="el-tab-item"
|
|
|
type="button"
|
|
|
role="tab"
|
|
|
- :class="{ active: activeKey === 'all' }"
|
|
|
+ :class="{ 'is-active': activeKey === 'all' }"
|
|
|
:aria-selected="activeKey === 'all'"
|
|
|
@click="setAll"
|
|
|
>
|
|
|
- 全部
|
|
|
+ <span class="tab-label">全部</span>
|
|
|
</button>
|
|
|
<button
|
|
|
v-for="tab in tabs"
|
|
|
:key="tab.groupName"
|
|
|
- class="tab"
|
|
|
- :class="{ active: tab.groupName === activeKey }"
|
|
|
+ class="el-tab-item"
|
|
|
+ :class="{ 'is-active': tab.groupName === activeKey }"
|
|
|
type="button"
|
|
|
role="tab"
|
|
|
:aria-selected="tab.groupName === activeKey"
|
|
|
@click="getById(tab)"
|
|
|
>
|
|
|
- <span class="tab-title">{{ tab.groupName }}</span>
|
|
|
- <span class="tab-sub">{{ tab.remark }}</span>
|
|
|
+ <span class="tab-label">{{ tab.groupName }}</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
- <div class="panel" role="tabpanel">
|
|
|
- <div class="panel-head">
|
|
|
- <div>
|
|
|
- <p class="panel-title">{{ activeTab.groupName }}</p>
|
|
|
- <p class="panel-subtitle">{{ activeTab.remark }}</p>
|
|
|
- </div>
|
|
|
- <div class="panel-meta">
|
|
|
- <span class="panel-count"
|
|
|
- >{{ activeTab.flowRespVOS.length }} 项</span
|
|
|
- >
|
|
|
- <span class="panel-note">流程与表单</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
+ <div role="tabpanel">
|
|
|
<div class="items-grid">
|
|
|
<div
|
|
|
v-for="item in activeTab.flowRespVOS"
|
|
|
@@ -89,7 +85,12 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 修复宽度:自适应包裹按钮 -->
|
|
|
+ <div class="charts-container">
|
|
|
+ <!-- 折线图 -->
|
|
|
+ <div class="chart-item" ref="lineChartRef"></div>
|
|
|
+ <!-- 环形图 -->
|
|
|
+ <div class="chart-item" ref="pieChartRef"></div>
|
|
|
+ </div>
|
|
|
|
|
|
<Footer />
|
|
|
</div>
|
|
|
@@ -98,14 +99,173 @@
|
|
|
<script setup>
|
|
|
import Header from "@components/home/header.vue";
|
|
|
import Footer from "@components/home/Footer.vue";
|
|
|
-import { computed, ref, onMounted } from "vue";
|
|
|
+import { computed, ref, onMounted, onBeforeUnmount, nextTick } from "vue";
|
|
|
import { Icon } from "@iconify/vue";
|
|
|
-import { getFlows, ssoLogin } from "@/api/user";
|
|
|
+import { getFlows, ssoLogin, getOATasks } from "@/api/user";
|
|
|
import { useUserStore } from "@/stores/useUserStore";
|
|
|
import { getAccessToken } from "@/utils/auth";
|
|
|
+import * as echarts from "echarts";
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
+const lineChartInstance = ref(null);
|
|
|
+let lineChartRef = ref(null);
|
|
|
+let pieChartRef = ref(null);
|
|
|
+const pieChartInstance = ref(null);
|
|
|
+let chartResizeObserver = null;
|
|
|
+let chartInitTimer = null;
|
|
|
+
|
|
|
+const initChartsSafe = (attempt = 0) => {
|
|
|
+ const lineDom = lineChartRef.value;
|
|
|
+ const pieDom = pieChartRef.value;
|
|
|
+
|
|
|
+ if (!lineDom || !pieDom) return;
|
|
|
+
|
|
|
+ const lineRect = lineDom.getBoundingClientRect();
|
|
|
+ const pieRect = pieDom.getBoundingClientRect();
|
|
|
+ const isLineReady = lineRect.width > 0 && lineRect.height > 0;
|
|
|
+ const isPieReady = pieRect.width > 0 && pieRect.height > 0;
|
|
|
+
|
|
|
+ if (isLineReady && isPieReady) {
|
|
|
+ initLineChart();
|
|
|
+ initPieChart();
|
|
|
+ handleResize();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (attempt < 8) {
|
|
|
+ chartInitTimer = window.setTimeout(() => {
|
|
|
+ initChartsSafe(attempt + 1);
|
|
|
+ }, 120);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 模拟数据 - 请根据实际 API 返回的数据调整
|
|
|
+const lineChartData = {
|
|
|
+ title: "流程处理趋势 (30天)",
|
|
|
+ xAxis: ["03-27", "03-28", "03-29", "03-30", "03-31", "04-01", "04-02"],
|
|
|
+ yAxis: [12, 18, 15, 20, 27, 24, 31],
|
|
|
+};
|
|
|
+
|
|
|
+const pieChartData = {
|
|
|
+ title: "流程类型占比",
|
|
|
+ seriesData: [
|
|
|
+ { name: "财务报销", value: 35, itemStyle: { color: "#409eff" } },
|
|
|
+ { name: "行政办公", value: 25, itemStyle: { color: "#f56c6c" } },
|
|
|
+ { name: "IT技术", value: 20, itemStyle: { color: "#9a66ff" } },
|
|
|
+ { name: "人力资源", value: 15, itemStyle: { color: "#e6a23c" } },
|
|
|
+ { name: "业务申请", value: 5, itemStyle: { color: "#50c878" } },
|
|
|
+ ],
|
|
|
+};
|
|
|
+
|
|
|
+// 初始化图表
|
|
|
+const initLineChart = () => {
|
|
|
+ const chartDom = lineChartRef.value;
|
|
|
+ if (!chartDom) return;
|
|
|
+
|
|
|
+ const chart = echarts.init(chartDom);
|
|
|
+ lineChartInstance.value = chart; // 保存实例
|
|
|
+
|
|
|
+ chart.setOption({
|
|
|
+ title: {
|
|
|
+ text: lineChartData.title,
|
|
|
+ left: "10",
|
|
|
+ top: 20,
|
|
|
+
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: "bold",
|
|
|
+ color: "#333",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // ... 原有的 option 配置保持不变 ...
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ axisPointer: { type: "shadow" },
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: "3%",
|
|
|
+ right: "4%",
|
|
|
+ bottom: "10%",
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ data: lineChartData.xAxis,
|
|
|
+ axisLabel: { formatter: (value) => value },
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ splitLine: { show: true, lineStyle: { color: "#eee" } },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "处理数量",
|
|
|
+ type: "line",
|
|
|
+ smooth: true,
|
|
|
+ areaStyle: {
|
|
|
+ opacity: 0.3,
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: "#409eff" },
|
|
|
+ { offset: 1, color: "#b3d8ff" },
|
|
|
+ ]),
|
|
|
+ },
|
|
|
+ lineStyle: { width: 3, color: "#2563eb" },
|
|
|
+ symbol: "circle",
|
|
|
+ symbolSize: 8,
|
|
|
+ data: lineChartData.yAxis,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const initPieChart = () => {
|
|
|
+ const chartDom = pieChartRef.value;
|
|
|
+ if (!chartDom) return;
|
|
|
+
|
|
|
+ const chart = echarts.init(chartDom);
|
|
|
+ pieChartInstance.value = chart; // 保存实例
|
|
|
+
|
|
|
+ chart.setOption({
|
|
|
+ title: {
|
|
|
+ text: pieChartData.title,
|
|
|
+ left: "10",
|
|
|
+ top: 20,
|
|
|
+
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: "bold",
|
|
|
+ color: "#333",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // ... 原有的 option 配置保持不变 ...
|
|
|
+ tooltip: {
|
|
|
+ trigger: "item",
|
|
|
+ formatter: "{a} <br/>{b}: {c} ({d}%)",
|
|
|
+ },
|
|
|
+ legend: { show: false },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "流程类型",
|
|
|
+ type: "pie",
|
|
|
+ radius: ["50%", "70%"],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ label: { show: false, position: "center" },
|
|
|
+ emphasis: {
|
|
|
+ label: { show: true, fontSize: "14", fontWeight: "bold" },
|
|
|
+ },
|
|
|
+ labelLine: { show: false },
|
|
|
+ data: pieChartData.seriesData,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const handleResize = () => {
|
|
|
+ lineChartInstance.value?.resize();
|
|
|
+ pieChartInstance.value?.resize();
|
|
|
+};
|
|
|
+
|
|
|
const tabs = ref([]);
|
|
|
|
|
|
const activeKey = ref("all");
|
|
|
@@ -136,12 +296,14 @@ const stats = [
|
|
|
number: "05",
|
|
|
extra: "+2 今日",
|
|
|
bgcolor: "#fff7ed",
|
|
|
+ color: "#f59e0b",
|
|
|
},
|
|
|
{
|
|
|
icon: "mdi:check-circle-outline",
|
|
|
title: "已办事项",
|
|
|
number: "128",
|
|
|
bgcolor: "#eff6ff",
|
|
|
+ color: "#2563eb",
|
|
|
},
|
|
|
{
|
|
|
icon: "mdi:arrow-right-bold-box-outline",
|
|
|
@@ -149,12 +311,14 @@ const stats = [
|
|
|
number: "42",
|
|
|
extra: "85% 准时",
|
|
|
bgcolor: "#eff6ff",
|
|
|
+ color: "#2563eb",
|
|
|
},
|
|
|
{
|
|
|
icon: "mdi:file-document-outline",
|
|
|
title: "草箱箱",
|
|
|
number: "03",
|
|
|
- bgcolor: "#fef3c7",
|
|
|
+ bgcolor: "#f8fafc",
|
|
|
+ color: "#475569",
|
|
|
},
|
|
|
];
|
|
|
|
|
|
@@ -176,47 +340,95 @@ const getById = (tab) => {
|
|
|
};
|
|
|
|
|
|
const go = async (item) => {
|
|
|
- console.log("跳转", item);
|
|
|
if (userStore.getUser.username && getAccessToken()) {
|
|
|
- const res = await ssoLogin({
|
|
|
- username: userStore.getUser.username,
|
|
|
- });
|
|
|
+ if (item.type === "OA") {
|
|
|
+ const res = await ssoLogin({
|
|
|
+ username: userStore.getUser.username,
|
|
|
+ });
|
|
|
|
|
|
- if (res) {
|
|
|
+ if (res) {
|
|
|
+ const newTab = window.open("", "_blank");
|
|
|
+
|
|
|
+ newTab.location.href = item.indexUrl + "?ssoToken=" + res + "#/main";
|
|
|
+
|
|
|
+ setTimeout(function () {
|
|
|
+ newTab.location.href = item.flowUrl;
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.type === "CRM") {
|
|
|
const newTab = window.open("", "_blank");
|
|
|
|
|
|
- newTab.location.href = item.indexUrl + "?ssoToken=" + res + "#/main";
|
|
|
+ newTab.location.href =
|
|
|
+ item.indexUrl +
|
|
|
+ "/global/sso/callback/00APEB9EEEA9B2E338B686B7ECFA8585808C.action?token=" +
|
|
|
+ getAccessToken();
|
|
|
|
|
|
setTimeout(function () {
|
|
|
newTab.location.href = item.flowUrl;
|
|
|
- }, 1000);
|
|
|
+ }, 0);
|
|
|
}
|
|
|
} else {
|
|
|
router.push({ path: "/login" });
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+let oaTasks = ref([]);
|
|
|
onMounted(async () => {
|
|
|
getAll();
|
|
|
+ // 等待 DOM 与样式生效,避免移动端首屏尺寸为 0
|
|
|
+ await nextTick();
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ initChartsSafe();
|
|
|
+ // 添加监听
|
|
|
+ window.addEventListener("resize", handleResize);
|
|
|
+ // 使用 ResizeObserver 监听容器尺寸变化(移动端更稳定)
|
|
|
+ if (typeof ResizeObserver !== "undefined") {
|
|
|
+ chartResizeObserver = new ResizeObserver(() => {
|
|
|
+ handleResize();
|
|
|
+ });
|
|
|
+ if (lineChartRef.value) chartResizeObserver.observe(lineChartRef.value);
|
|
|
+ if (pieChartRef.value) chartResizeObserver.observe(pieChartRef.value);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (userStore.getUser.username) {
|
|
|
+ const res = await getOATasks(userStore.getUser.username);
|
|
|
+ oaTasks.value = res;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 组件卸载时移除监听,防止内存泄漏
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener("resize", handleResize);
|
|
|
+ chartResizeObserver?.disconnect();
|
|
|
+ if (chartInitTimer) window.clearTimeout(chartInitTimer);
|
|
|
+ // 可选:销毁 echarts 实例
|
|
|
+ lineChartInstance.value?.dispose();
|
|
|
+ pieChartInstance.value?.dispose();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-:global(body) {
|
|
|
- background-color: #f4f4f2;
|
|
|
-}
|
|
|
-
|
|
|
/* .ehr-page {
|
|
|
color: #1f2a37;
|
|
|
background: linear-gradient(180deg, #f4f4f2 0%, #f7f6f3 50%, #f2f1ef 100%);
|
|
|
min-height: 100vh;
|
|
|
} */
|
|
|
|
|
|
+:global(body) {
|
|
|
+ background-color: #f8fafc;
|
|
|
+}
|
|
|
+
|
|
|
.hero {
|
|
|
position: relative;
|
|
|
padding: 72px 6vw 48px;
|
|
|
overflow: hidden;
|
|
|
margin-top: 20px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
}
|
|
|
|
|
|
.hero-inner {
|
|
|
@@ -244,11 +456,7 @@ onMounted(async () => {
|
|
|
right: -140px;
|
|
|
width: 360px;
|
|
|
height: 360px;
|
|
|
- background: radial-gradient(
|
|
|
- circle at 30% 30%,
|
|
|
- rgba(2, 64, 155, 0.25),
|
|
|
- transparent 65%
|
|
|
- );
|
|
|
+
|
|
|
border-radius: 50%;
|
|
|
opacity: 0.9;
|
|
|
pointer-events: none;
|
|
|
@@ -259,48 +467,54 @@ onMounted(async () => {
|
|
|
/* height: 80vh; */
|
|
|
}
|
|
|
|
|
|
-.tabs {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
|
|
|
- gap: 16px;
|
|
|
- margin-bottom: 28px;
|
|
|
+.tabs-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ border-bottom: 1px solid #e4e7ed; /* Element Plus 标准的分割线颜色 */
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding-left: 0;
|
|
|
+ overflow-x: auto; /* 防止Tab过多时溢出 */
|
|
|
}
|
|
|
|
|
|
-.tab {
|
|
|
- padding: 18px 20px;
|
|
|
- border-radius: 14px;
|
|
|
- /* border: 1px solid #e5e7eb; */
|
|
|
- background: #ffffff;
|
|
|
- text-align: left;
|
|
|
- transition: all 0.25s ease;
|
|
|
+.el-tab-item {
|
|
|
+ position: relative;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 0 20px;
|
|
|
+ height: 40px; /* 标准高度 */
|
|
|
+ font-size: 14px;
|
|
|
+ color: #64748b; /* Element Plus 主要文字颜色 */
|
|
|
+ background-color: transparent;
|
|
|
+ border: none;
|
|
|
+ border-bottom: 2px solid transparent; /* 用于激活态的下划线 */
|
|
|
cursor: pointer;
|
|
|
- box-shadow: 0 12px 24px rgba(15, 23, 42, 0.05);
|
|
|
+ transition: all 0.3s;
|
|
|
+ margin-right: 0;
|
|
|
+ outline: none;
|
|
|
+ flex-shrink: 0; /* 防止压缩 */
|
|
|
}
|
|
|
|
|
|
-.tab:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- border-color: #02409b;
|
|
|
+.el-tab-item:hover {
|
|
|
+ color: #02409b; /* Element Plus 主题蓝 */
|
|
|
}
|
|
|
|
|
|
-.tab.active {
|
|
|
- background: linear-gradient(135deg, #02409b 0%, #0b2f6d 60%, #0b1f45 100%);
|
|
|
- border-color: transparent;
|
|
|
- color: #f9fafb;
|
|
|
- box-shadow: 0 18px 36px rgba(2, 64, 155, 0.25);
|
|
|
+.el-tab-item.is-active {
|
|
|
+ color: #02409b; /* 激活态文字颜色 */
|
|
|
+ font-weight: 500;
|
|
|
+ border-bottom-color: #02409b; /* 激活态下划线 */
|
|
|
}
|
|
|
|
|
|
-.tab-title {
|
|
|
- display: block;
|
|
|
- font-weight: 600;
|
|
|
- font-size: 16px;
|
|
|
- margin-bottom: 6px;
|
|
|
+.tab-label {
|
|
|
+ line-height: 1;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
|
|
|
.tab-sub {
|
|
|
- display: block;
|
|
|
+ margin-left: 8px;
|
|
|
font-size: 12px;
|
|
|
- color: inherit;
|
|
|
- opacity: 0.75;
|
|
|
+ color: #909399; /* 次要文字颜色 */
|
|
|
+ transform: scale(0.9);
|
|
|
}
|
|
|
|
|
|
.panel {
|
|
|
@@ -358,18 +572,22 @@ onMounted(async () => {
|
|
|
padding: 22px 22px 18px;
|
|
|
border-radius: 22px;
|
|
|
background: #ffffff;
|
|
|
- border: 1px solid #edf0f5;
|
|
|
- box-shadow: 0 14px 30px rgba(15, 23, 42, 0.06);
|
|
|
+ border: 1px solid rgb(241, 245, 249);
|
|
|
+ box-shadow:
|
|
|
+ rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
|
|
+ rgba(0, 0, 0, 0) 0px 0px 0px 0px,
|
|
|
+ rgba(0, 0, 0, 0.05) 0px 1px 2px 0px;
|
|
|
transition:
|
|
|
transform 0.2s ease,
|
|
|
border-color 0.2s ease,
|
|
|
box-shadow 0.2s ease;
|
|
|
cursor: pointer;
|
|
|
+ border-bottom: 1px solid rgb(241, 245, 249);
|
|
|
}
|
|
|
|
|
|
.item-card:hover {
|
|
|
transform: translateY(-3px);
|
|
|
- border-color: rgba(2, 64, 155, 0.25);
|
|
|
+
|
|
|
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.08);
|
|
|
color: #02409b !important;
|
|
|
}
|
|
|
@@ -470,7 +688,7 @@ onMounted(async () => {
|
|
|
.total {
|
|
|
display: flex;
|
|
|
gap: 16px;
|
|
|
- padding: 0 6vw 80px;
|
|
|
+ padding: 0 6vw 50px;
|
|
|
/* margin-bottom: 24px; */
|
|
|
}
|
|
|
|
|
|
@@ -539,4 +757,27 @@ onMounted(async () => {
|
|
|
margin-top: 8px;
|
|
|
text-align: right;
|
|
|
}
|
|
|
+
|
|
|
+.charts-container {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 24px;
|
|
|
+ padding: 0 6vw;
|
|
|
+ margin-bottom: 80px;
|
|
|
+ width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-item {
|
|
|
+ flex: 1;
|
|
|
+ /* 桌面端最小宽度,防止过度挤压 */
|
|
|
+ min-width: 300px;
|
|
|
+ /* 必须设置固定高度,ECharts 需要明确的高度才能渲染 */
|
|
|
+ height: 350px;
|
|
|
+ border-radius: 16px;
|
|
|
+ background-color: #ffffff;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
</style>
|