feat: 新增磨砂玻璃透明度设置功能

This commit is contained in:
Yuhang Wu
2026-10-16 11:21:52 +08:00
parent aba606a570
commit dbde020d7e
7 changed files with 128 additions and 13 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "frpilot",
"private": true,
"version": "1.1.0",
"version": "1.2.0",
"type": "module",
"scripts": {
"dev": "vite",
+19 -10
View File
@@ -13,6 +13,12 @@ body,
/* 窗口背景色与渐变(供 html / #app 复用,避免重复定义) */
:root {
/* 全局文字颜色:普通 HTML 文本继承使用(Antd 组件由 CSS-in-JS 自行着色,不受影响) */
--app-text-color: rgba(15, 23, 42, 0.88);
/* 磨砂玻璃透明度:由「外观设置」实时写入 <html>,默认值与初始外观保持一致 */
--app-card-alpha: 0.45;
--app-card-alpha-hover: 0.62;
--app-modal-alpha: 0.62;
--app-bg-color: #eef2ff;
--app-bg-gradient:
radial-gradient(1200px 600px at 10% -10%, rgba(99, 102, 241, 0.28), transparent 60%),
@@ -21,6 +27,7 @@ body,
}
html.dark {
--app-text-color: rgba(226, 232, 240, 0.92);
--app-bg-color: #0b1220;
--app-bg-gradient:
radial-gradient(1200px 600px at 10% -10%, rgba(99, 102, 241, 0.35), transparent 60%),
@@ -85,6 +92,8 @@ body {
overflow: hidden;
font-family: 'Inter', 'HarmonyOS Sans SC', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
-webkit-font-smoothing: antialiased;
/* 文字颜色跟随明暗主题,避免暗色下普通文本仍是浏览器默认黑色 */
color: var(--app-text-color);
/* 背景交由 <html>(或 macOS 下的 #app)绘制,body 保持透明避免覆盖画布背景 */
background-color: transparent;
}
@@ -112,7 +121,7 @@ body {
.glass-panel {
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.55);
background: rgba(255, 255, 255, 0.45);
background: rgba(255, 255, 255, var(--app-card-alpha));
backdrop-filter: blur(18px) saturate(160%);
-webkit-backdrop-filter: blur(18px) saturate(160%);
box-shadow: 0 8px 32px rgba(15, 23, 42, 0.12);
@@ -120,7 +129,7 @@ body {
.dark .glass-panel {
border-color: rgba(255, 255, 255, 0.08);
background: rgba(15, 23, 42, 0.45);
background: rgba(15, 23, 42, var(--app-card-alpha));
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
@@ -131,7 +140,7 @@ body {
*/
.glass-card.ant-card {
border: 1px solid rgba(255, 255, 255, 0.5) !important;
background: rgba(255, 255, 255, 0.45) !important;
background: rgba(255, 255, 255, var(--app-card-alpha)) !important;
backdrop-filter: blur(16px) saturate(160%);
-webkit-backdrop-filter: blur(16px) saturate(160%);
box-shadow: 0 6px 22px rgba(15, 23, 42, 0.08) !important;
@@ -140,19 +149,19 @@ body {
.glass-card.ant-card:hover {
border-color: rgba(99, 102, 241, 0.45) !important;
background: rgba(255, 255, 255, 0.62) !important;
background: rgba(255, 255, 255, var(--app-card-alpha-hover)) !important;
box-shadow: 0 10px 28px rgba(15, 23, 42, 0.14) !important;
}
.dark .glass-card.ant-card {
border-color: rgba(255, 255, 255, 0.1) !important;
background: rgba(30, 41, 59, 0.5) !important;
background: rgba(30, 41, 59, var(--app-card-alpha)) !important;
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.35) !important;
}
.dark .glass-card.ant-card:hover {
border-color: rgba(129, 140, 248, 0.5) !important;
background: rgba(30, 41, 59, 0.62) !important;
background: rgba(30, 41, 59, var(--app-card-alpha-hover)) !important;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45) !important;
}
@@ -172,7 +181,7 @@ body {
max-height: 80vh;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.55);
background: rgba(255, 255, 255, 0.62) !important;
background: rgba(255, 255, 255, var(--app-modal-alpha)) !important;
backdrop-filter: blur(24px) saturate(170%);
-webkit-backdrop-filter: blur(24px) saturate(170%);
box-shadow: 0 16px 48px rgba(15, 23, 42, 0.18) !important;
@@ -187,7 +196,7 @@ body {
.dark .form-modal-wrap .ant-modal-content {
border-color: rgba(255, 255, 255, 0.1);
background: rgba(30, 41, 59, 0.62) !important;
background: rgba(30, 41, 59, var(--app-modal-alpha)) !important;
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5) !important;
}
@@ -217,7 +226,7 @@ body {
max-height: 60vh;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.55);
background: rgba(255, 255, 255, 0.72) !important;
background: rgba(255, 255, 255, var(--app-modal-alpha)) !important;
backdrop-filter: blur(24px) saturate(170%);
-webkit-backdrop-filter: blur(24px) saturate(170%);
box-shadow: 0 16px 48px rgba(15, 23, 42, 0.18) !important;
@@ -232,7 +241,7 @@ body {
.dark .update-modal-wrap .ant-modal-content {
border-color: rgba(255, 255, 255, 0.1);
background: rgba(30, 41, 59, 0.72) !important;
background: rgba(30, 41, 59, var(--app-modal-alpha)) !important;
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5) !important;
}
+6 -1
View File
@@ -94,7 +94,12 @@
"bg6": "Background 6",
"bg7": "Background 7",
"bg8": "Background 8",
"bg9": "Background 9"
"bg9": "Background 9",
"glass": "Glass transparency",
"glassTip": "Drag the sliders to adjust how transparent the cards and dialogs are. Higher values are more transparent.",
"cardTransparency": "Card transparency",
"modalTransparency": "Dialog transparency",
"previewTip": "Top preview shows cards, bottom shows dialogs — drag a slider to preview live."
}
},
"server": {
+6 -1
View File
@@ -94,7 +94,12 @@
"bg6": "背景六",
"bg7": "背景七",
"bg8": "背景八",
"bg9": "背景九"
"bg9": "背景九",
"glass": "磨砂玻璃透明度",
"glassTip": "拖动滑块调整卡片与弹窗背景的透明程度,数值越大越透明",
"cardTransparency": "卡片透明度",
"modalTransparency": "弹窗透明度",
"previewTip": "上方为卡片效果,下方为弹窗效果,可拖动滑块实时预览"
}
},
"server": {
+23
View File
@@ -45,6 +45,27 @@ export const useAppStore = defineStore("app", () => {
}
})
// 磨砂玻璃透明度(0 - 100,数值越大越透明,持久化)
const cardTransparency = useStorage<number>("frpilot:card-transparency", 55)
const modalTransparency = useStorage<number>("frpilot:modal-transparency", 38)
/** 透明度百分比 → 背景 alpha(越大越透明,alpha 越小;并做范围兜底) */
function alphaFromTransparency(value: number) {
const percent = Math.min(100, Math.max(0, Number(value) || 0))
return (100 - percent) / 100
}
const cardAlpha = computed(() => alphaFromTransparency(cardTransparency.value))
const modalAlpha = computed(() => alphaFromTransparency(modalTransparency.value))
// 把透明度实时写入 <html> 的 CSS 变量,供磨砂玻璃样式(卡片 / 弹窗)即时生效
watchEffect(() => {
const root = document.documentElement
root.style.setProperty("--app-card-alpha", String(cardAlpha.value))
root.style.setProperty("--app-card-alpha-hover", String(Math.min(cardAlpha.value + 0.15, 0.95)))
root.style.setProperty("--app-modal-alpha", String(modalAlpha.value))
})
// Frpc 运行状态(后续由后端驱动)
const runStatus = ref<RunStatus>("stopped")
@@ -76,6 +97,8 @@ export const useAppStore = defineStore("app", () => {
toggleDark,
background,
backgroundUrl,
cardTransparency,
modalTransparency,
runStatus,
antdLocale,
setLanguage,
+1
View File
@@ -36,6 +36,7 @@ declare module 'vue' {
ARow: typeof import('ant-design-vue/es')['Row']
ASegmented: typeof import('ant-design-vue/es')['Segmented']
ASelect: typeof import('ant-design-vue/es')['Select']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
AStatistic: typeof import('ant-design-vue/es')['Statistic']
+72
View File
@@ -33,6 +33,37 @@
</a-card>
</div>
</section>
<a-divider />
<!-- 磨砂玻璃透明度(拖动即时生效) -->
<section>
<div class="mb-1 text-sm font-medium">{{ t("settings.appearance.glass") }}</div>
<p class="mb-3 text-xs opacity-60">{{ t("settings.appearance.glassTip") }}</p>
<div class="mb-4">
<div class="mb-1 flex items-center justify-between text-xs">
<span>{{ t("settings.appearance.cardTransparency") }}</span>
<span class="font-medium tabular-nums">{{ appStore.cardTransparency }}%</span>
</div>
<a-slider v-model:value="appStore.cardTransparency" :min="0" :max="100" />
</div>
<div class="mb-4">
<div class="mb-1 flex items-center justify-between text-xs">
<span>{{ t("settings.appearance.modalTransparency") }}</span>
<span class="font-medium tabular-nums">{{ appStore.modalTransparency }}%</span>
</div>
<a-slider v-model:value="appStore.modalTransparency" :min="0" :max="100" />
</div>
<!-- 实时预览:两种磨砂效果叠加在彩色底图上,拖动滑块即时可见 -->
<div class="glass-preview" aria-hidden="true">
<div class="glass-preview-card"></div>
<div class="glass-preview-modal"></div>
</div>
<p class="mt-2 text-xs opacity-60">{{ t("settings.appearance.previewTip") }}</p>
</section>
</div>
</template>
@@ -105,4 +136,45 @@ function selectBackground(key: BackgroundKey) {
rgba(148, 163, 184, 0.25) 0 8px,
transparent 8px 16px);
}
/* 透明度实时预览:纯色底图(暖调中性色,不用渐变)+ 卡片 / 弹窗两种磨砂效果 */
.glass-preview {
display: flex;
flex-direction: column;
gap: 12px;
padding: 14px;
border-radius: 12px;
background-color: #a09adf;
}
.dark .glass-preview {
background-color: #a09adf;
}
.glass-preview-card,
.glass-preview-modal {
border: 1px solid rgba(255, 255, 255, 0.5);
backdrop-filter: blur(10px) saturate(160%);
-webkit-backdrop-filter: blur(10px) saturate(160%);
}
.glass-preview-card {
height: 48px;
border-radius: 12px;
background: rgba(255, 255, 255, var(--app-card-alpha));
}
.glass-preview-modal {
height: 48px;
border-radius: 10px;
background: rgba(255, 255, 255, var(--app-modal-alpha));
}
.dark .glass-preview-card {
background: rgba(30, 41, 59, var(--app-card-alpha));
}
.dark .glass-preview-modal {
background: rgba(30, 41, 59, var(--app-modal-alpha));
}
</style>