修复桌面端界面显示与稳定性问题
@@ -4,7 +4,11 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" href="data:," />
|
<meta name="theme-color" content="#13243a" />
|
||||||
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||||
|
<link rel="icon" type="image/png" href="/icons/ctms-icon-192.png" />
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<title>CTMS</title>
|
<title>CTMS</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -41,11 +45,16 @@
|
|||||||
width: 42px;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background: #183b63;
|
background: #ffffff;
|
||||||
color: #ffffff;
|
box-shadow: 0 10px 22px rgba(15, 35, 58, 0.16);
|
||||||
font-size: 12px;
|
overflow: hidden;
|
||||||
font-weight: 900;
|
}
|
||||||
letter-spacing: 0.08em;
|
|
||||||
|
.ctms-boot-mark img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctms-boot-title {
|
.ctms-boot-title {
|
||||||
@@ -62,7 +71,9 @@
|
|||||||
</style>
|
</style>
|
||||||
<div class="ctms-boot-splash">
|
<div class="ctms-boot-splash">
|
||||||
<div class="ctms-boot-card">
|
<div class="ctms-boot-card">
|
||||||
<div class="ctms-boot-mark">CTMS</div>
|
<div class="ctms-boot-mark" aria-hidden="true">
|
||||||
|
<img src="/icons/ctms-icon-192.png" alt="" />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="ctms-boot-title">正在启动 CTMS</p>
|
<p class="ctms-boot-title">正在启动 CTMS</p>
|
||||||
<p class="ctms-boot-subtitle">正在加载桌面客户端...</p>
|
<p class="ctms-boot-subtitle">正在加载桌面客户端...</p>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 387 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "CTMS",
|
||||||
|
"short_name": "CTMS",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/ctms-icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/ctms-icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#13243a",
|
||||||
|
"background_color": "#f4f7fb",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
@@ -75,6 +75,11 @@ const verifyTauriConfig = async () => {
|
|||||||
assert(mainWindow?.decorations === true, "Main desktop window must keep native window decorations enabled.");
|
assert(mainWindow?.decorations === true, "Main desktop window must keep native window decorations enabled.");
|
||||||
assert(mainWindow?.titleBarStyle === "Overlay", "Main desktop window must use the macOS overlay title bar.");
|
assert(mainWindow?.titleBarStyle === "Overlay", "Main desktop window must use the macOS overlay title bar.");
|
||||||
assert(mainWindow?.hiddenTitle === true, "Main desktop window must hide the native title text.");
|
assert(mainWindow?.hiddenTitle === true, "Main desktop window must hide the native title text.");
|
||||||
|
assert(mainWindow?.backgroundColor === "#f9fafb", "Main desktop window must use a non-black WebView background.");
|
||||||
|
assert(
|
||||||
|
mainWindow?.backgroundThrottling === "disabled",
|
||||||
|
"Main desktop window must disable background suspend to avoid macOS resume black flashes.",
|
||||||
|
);
|
||||||
assert(
|
assert(
|
||||||
mainWindow?.trafficLightPosition?.x === 16 && mainWindow?.trafficLightPosition?.y === 18,
|
mainWindow?.trafficLightPosition?.x === 16 && mainWindow?.trafficLightPosition?.y === 18,
|
||||||
"Main desktop window must keep traffic light controls at x=16 y=18.",
|
"Main desktop window must keep traffic light controls at x=16 y=18.",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 659 B After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 99 B After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 154 B After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 123 B After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 374 KiB |
@@ -21,6 +21,8 @@
|
|||||||
"decorations": true,
|
"decorations": true,
|
||||||
"titleBarStyle": "Overlay",
|
"titleBarStyle": "Overlay",
|
||||||
"hiddenTitle": true,
|
"hiddenTitle": true,
|
||||||
|
"backgroundColor": "#f9fafb",
|
||||||
|
"backgroundThrottling": "disabled",
|
||||||
"trafficLightPosition": { "x": 16, "y": 18 }
|
"trafficLightPosition": { "x": 16, "y": 18 }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -31,7 +33,14 @@
|
|||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"targets": ["app", "dmg"],
|
"targets": ["app", "dmg"],
|
||||||
"createUpdaterArtifacts": true
|
"createUpdaterArtifacts": true,
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"updater": {
|
"updater": {
|
||||||
|
|||||||
@@ -30,5 +30,6 @@ initDesktopUpdateManager();
|
|||||||
.app-shell {
|
.app-shell {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
|
background: var(--ctms-bg-base);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
<aside class="desktop-sidebar">
|
<aside class="desktop-sidebar">
|
||||||
<header class="sidebar-head">
|
<header class="sidebar-head">
|
||||||
<div class="sidebar-title-row">
|
<div class="sidebar-title-row">
|
||||||
<h1>{{ TEXT.common.appName }}</h1>
|
<div class="sidebar-app-brand">
|
||||||
|
<img class="sidebar-app-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
|
<h1>{{ TEXT.common.appName }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button v-if="showSidebarProjectChooser" class="study-switcher-trigger empty" type="button" @click="openDesktopProjectEntry">
|
<button v-if="showSidebarProjectChooser" class="study-switcher-trigger empty" type="button" @click="openDesktopProjectEntry">
|
||||||
选择项目
|
选择项目
|
||||||
@@ -1383,6 +1386,23 @@ useDesktopShortcuts(
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-app-brand {
|
||||||
|
display: inline-grid;
|
||||||
|
min-width: 0;
|
||||||
|
grid-template-columns: 30px minmax(0, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
gap: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-app-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.14);
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@@ -2357,13 +2377,6 @@ useDesktopShortcuts(
|
|||||||
:global(.desktop-preferences-dialog .el-dialog__body) {
|
:global(.desktop-preferences-dialog .el-dialog__body) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.desktop-preferences-dialog .el-dialog__body > *) {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:global([data-ctms-theme="dark"] .desktop-workbench) {
|
:global([data-ctms-theme="dark"] .desktop-workbench) {
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ describe("desktop layout shell", () => {
|
|||||||
expect(tauriConfig).toContain('"decorations": true');
|
expect(tauriConfig).toContain('"decorations": true');
|
||||||
expect(tauriConfig).toContain('"titleBarStyle": "Overlay"');
|
expect(tauriConfig).toContain('"titleBarStyle": "Overlay"');
|
||||||
expect(tauriConfig).toContain('"hiddenTitle": true');
|
expect(tauriConfig).toContain('"hiddenTitle": true');
|
||||||
|
expect(tauriConfig).toContain('"backgroundColor": "#f9fafb"');
|
||||||
|
expect(tauriConfig).toContain('"backgroundThrottling": "disabled"');
|
||||||
expect(tauriConfig).toContain('"trafficLightPosition": { "x": 16, "y": 18 }');
|
expect(tauriConfig).toContain('"trafficLightPosition": { "x": 16, "y": 18 }');
|
||||||
expect(layout).toContain("padding: 44px 10px 10px;");
|
expect(layout).toContain("padding: 44px 10px 10px;");
|
||||||
expect(layout).toContain("-webkit-app-region: drag;");
|
expect(layout).toContain("-webkit-app-region: drag;");
|
||||||
@@ -159,7 +161,8 @@ describe("desktop layout shell", () => {
|
|||||||
expect(preferences).toContain('class="preferences-pane"');
|
expect(preferences).toContain('class="preferences-pane"');
|
||||||
expect(preferences).toContain("const activeSectionId = ref<PreferenceSectionId>");
|
expect(preferences).toContain("const activeSectionId = ref<PreferenceSectionId>");
|
||||||
expect(preferences).toContain('grid-template-columns: 232px minmax(0, 1fr);');
|
expect(preferences).toContain('grid-template-columns: 232px minmax(0, 1fr);');
|
||||||
expect(preferences).toContain("height: min(700px, calc(100vh - 96px));");
|
expect(preferences).toContain("height: 700px;");
|
||||||
|
expect(preferences).toContain("max-height: calc(100vh - 96px);");
|
||||||
expect(preferences).toContain("scrollbar-gutter: stable;");
|
expect(preferences).toContain("scrollbar-gutter: stable;");
|
||||||
expect(preferences).toContain('{ id: "connection", label: "连接"');
|
expect(preferences).toContain('{ id: "connection", label: "连接"');
|
||||||
expect(preferences).toContain('{ id: "appearance", label: "外观"');
|
expect(preferences).toContain('{ id: "appearance", label: "外观"');
|
||||||
@@ -193,6 +196,7 @@ describe("desktop layout shell", () => {
|
|||||||
expect(desktopLayout).not.toContain(':global([data-ctms-theme="dark"]) .desktop-workbench');
|
expect(desktopLayout).not.toContain(':global([data-ctms-theme="dark"]) .desktop-workbench');
|
||||||
expect(desktopLayout).toContain('width="940px"');
|
expect(desktopLayout).toContain('width="940px"');
|
||||||
expect(webLayout).toContain('width="940px"');
|
expect(webLayout).toContain('width="940px"');
|
||||||
|
expect(desktopLayout).not.toContain(":global(.desktop-preferences-dialog .el-dialog__body > *)");
|
||||||
expect(desktopLayout).toContain('title: "连接设置"');
|
expect(desktopLayout).toContain('title: "连接设置"');
|
||||||
expect(webLayout).toContain('title: "连接设置"');
|
expect(webLayout).toContain('title: "连接设置"');
|
||||||
expect(desktopLayout).not.toContain('router.push("/desktop/server-settings")');
|
expect(desktopLayout).not.toContain('router.push("/desktop/server-settings")');
|
||||||
@@ -484,7 +488,9 @@ describe("desktop layout shell", () => {
|
|||||||
expect(styles).toContain("body.is-desktop-runtime .el-message-box__header");
|
expect(styles).toContain("body.is-desktop-runtime .el-message-box__header");
|
||||||
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog");
|
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog");
|
||||||
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body");
|
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body");
|
||||||
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body {\n padding: 0;");
|
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body {\n height: 700px;");
|
||||||
|
expect(styles).toContain("max-height: calc(100vh - 96px);");
|
||||||
|
expect(styles).toContain("padding: 0;");
|
||||||
expect(styles).toContain("body.is-desktop-runtime .el-message-box__btns");
|
expect(styles).toContain("body.is-desktop-runtime .el-message-box__btns");
|
||||||
expect(styles).toContain("body.is-desktop-runtime .dialog-fade-enter-from .el-dialog");
|
expect(styles).toContain("body.is-desktop-runtime .dialog-fade-enter-from .el-dialog");
|
||||||
expect(styles).toContain("body.is-desktop-runtime .msgbox-fade-enter-from .el-message-box");
|
expect(styles).toContain("body.is-desktop-runtime .msgbox-fade-enter-from .el-message-box");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<el-container class="layout-container web-layout-container">
|
<el-container class="layout-container web-layout-container">
|
||||||
<el-aside :width="isCollapsed ? '68px' : '200px'" class="layout-aside" :class="{ collapsed: isCollapsed }">
|
<el-aside :width="isCollapsed ? '68px' : '200px'" class="layout-aside" :class="{ collapsed: isCollapsed }">
|
||||||
<div class="aside-logo">
|
<div class="aside-logo">
|
||||||
<div class="logo-icon"></div>
|
<img class="logo-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<span class="logo-text">{{ TEXT.common.appName }}</span>
|
<span class="logo-text">{{ TEXT.common.appName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<el-menu
|
<el-menu
|
||||||
@@ -1309,6 +1309,7 @@ useDesktopShortcuts(
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.26);
|
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.26);
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-text {
|
.logo-text {
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ html,
|
|||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
background: var(--ctms-bg-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -1114,6 +1115,8 @@ body.is-desktop-runtime .desktop-preferences-dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body {
|
body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body {
|
||||||
|
height: 700px;
|
||||||
|
max-height: calc(100vh - 96px);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -807,9 +807,8 @@ onBeforeUnmount(() => {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 232px minmax(0, 1fr);
|
grid-template-columns: 232px minmax(0, 1fr);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/* height 让组件有确定高度,配合 dialog body 的 flex:1 撑满容器
|
height: 700px;
|
||||||
* 若 dialog 比 700px 高,组件会通过 flex:1 继续撑大 */
|
max-height: calc(100vh - 96px);
|
||||||
height: min(700px, calc(100vh - 96px));
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--pref-bg-pane);
|
background: var(--pref-bg-pane);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<aside class="entry-sidebar" data-tauri-drag-region>
|
<aside class="entry-sidebar" data-tauri-drag-region>
|
||||||
<div class="sidebar-top">
|
<div class="sidebar-top">
|
||||||
<div class="sidebar-brand">
|
<div class="sidebar-brand">
|
||||||
<div class="brand-mark"><span>CTMS</span></div>
|
<img class="brand-mark" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<div class="brand-text">
|
<div class="brand-text">
|
||||||
<span class="brand-kicker">Desktop Workbench</span>
|
<span class="brand-kicker">Desktop Workbench</span>
|
||||||
<h1>工作台总控</h1>
|
<h1>工作台总控</h1>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 空状态 -->
|
||||||
<div v-else class="empty-project-workspace">
|
<div v-else class="empty-project-workspace">
|
||||||
<div class="empty-icon-capsule"><span>CTMS</span></div>
|
<img class="empty-icon-capsule" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<h3>未检测到可用项目</h3>
|
<h3>未检测到可用项目</h3>
|
||||||
<p>您的账号尚未关联至任何研究项目,请联系系统管理员进行项目授权与分配。</p>
|
<p>您的账号尚未关联至任何研究项目,请联系系统管理员进行项目授权与分配。</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
<div class="transition-overlay" :class="{ active: isTransitioning }" aria-hidden="true">
|
<div class="transition-overlay" :class="{ active: isTransitioning }" aria-hidden="true">
|
||||||
<div class="overlay-glow"></div>
|
<div class="overlay-glow"></div>
|
||||||
<div class="overlay-spinner">
|
<div class="overlay-spinner">
|
||||||
<span class="spinner-brand">CTMS</span>
|
<img class="spinner-brand" src="/icons/ctms-icon-192.png" alt="" />
|
||||||
<div class="spinner-ring"></div>
|
<div class="spinner-ring"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -293,19 +293,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brand-mark {
|
.brand-mark {
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
border: 1px solid rgba(59, 130, 246, 0.1);
|
border: 1px solid rgba(59, 130, 246, 0.1);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
background: #ffffff;
|
||||||
color: #ffffff;
|
|
||||||
font-size: 13.5px;
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
box-shadow: 0 8px 20px rgba(37, 99, 235, 0.18);
|
box-shadow: 0 8px 20px rgba(37, 99, 235, 0.18);
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-text h1 {
|
.brand-text h1 {
|
||||||
@@ -809,18 +803,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon-capsule {
|
.empty-icon-capsule {
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 42px;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #eff6ff;
|
background: #ffffff;
|
||||||
color: #2563eb;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
object-fit: contain;
|
||||||
|
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-project-workspace h3 {
|
.empty-project-workspace h3 {
|
||||||
@@ -1064,11 +1053,11 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.spinner-brand {
|
.spinner-brand {
|
||||||
color: #ffffff;
|
width: 42px;
|
||||||
font-size: 15px;
|
height: 42px;
|
||||||
font-weight: 900;
|
border-radius: 10px;
|
||||||
letter-spacing: 0.1em;
|
object-fit: contain;
|
||||||
text-shadow: 0 0 12px rgba(59, 130, 246, 0.4);
|
box-shadow: 0 0 20px rgba(59, 130, 246, 0.24);
|
||||||
animation: pulseBrand 1.4s ease-in-out infinite alternate;
|
animation: pulseBrand 1.4s ease-in-out infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="desktop-session-restore" data-tauri-drag-region>
|
<div class="desktop-session-restore" data-tauri-drag-region>
|
||||||
<section class="restore-panel">
|
<section class="restore-panel">
|
||||||
<div class="restore-mark">CTMS</div>
|
<img class="restore-mark" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<div class="restore-copy">
|
<div class="restore-copy">
|
||||||
<span class="restore-eyebrow">Desktop Session</span>
|
<span class="restore-eyebrow">Desktop Session</span>
|
||||||
<h1>正在恢复登录状态</h1>
|
<h1>正在恢复登录状态</h1>
|
||||||
@@ -134,14 +134,10 @@ onBeforeUnmount(() => {
|
|||||||
.restore-mark {
|
.restore-mark {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
background: #15344f;
|
background: #ffffff;
|
||||||
color: #ffffff;
|
box-shadow: 0 14px 30px rgba(21, 52, 79, 0.18);
|
||||||
font-size: 15px;
|
object-fit: contain;
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.restore-copy {
|
.restore-copy {
|
||||||
|
|||||||
@@ -11,13 +11,7 @@
|
|||||||
<div class="brand-content-wrapper">
|
<div class="brand-content-wrapper">
|
||||||
<!-- 顶部系统微标 -->
|
<!-- 顶部系统微标 -->
|
||||||
<div class="brand-mini-logo">
|
<div class="brand-mini-logo">
|
||||||
<div class="mini-logo-icon">
|
<img class="mini-logo-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
||||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
|
||||||
<path d="M2 17l10 5 10-5"/>
|
|
||||||
<path d="M2 12l10 5 10-5"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<span class="mini-logo-text">华邦制药 · CTMS</span>
|
<span class="mini-logo-text">华邦制药 · CTMS</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -84,13 +78,7 @@
|
|||||||
<div class="login-card-container">
|
<div class="login-card-container">
|
||||||
<!-- 顶部 Logo 组合 -->
|
<!-- 顶部 Logo 组合 -->
|
||||||
<div class="login-brand-header">
|
<div class="login-brand-header">
|
||||||
<div class="brand-logo-icon">
|
<img class="brand-logo-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
||||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
|
||||||
<path d="M2 17l10 5 10-5"/>
|
|
||||||
<path d="M2 12l10 5 10-5"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="brand-divider"></div>
|
<div class="brand-divider"></div>
|
||||||
<span class="brand-system-text">CTMS</span>
|
<span class="brand-system-text">CTMS</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -605,14 +593,12 @@ const onSubmit = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mini-logo-icon {
|
.mini-logo-icon {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 55px;
|
width: 55px;
|
||||||
height: 55px;
|
height: 55px;
|
||||||
background: #2563eb;
|
background: #ffffff;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
color: white;
|
box-shadow: 0 12px 26px rgba(37, 99, 235, 0.18);
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-logo-text {
|
.mini-logo-text {
|
||||||
@@ -818,14 +804,12 @@ const onSubmit = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brand-logo-icon {
|
.brand-logo-icon {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 34px;
|
width: 34px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
background: #2563eb;
|
background: #ffffff;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
color: white;
|
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.16);
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-divider {
|
.brand-divider {
|
||||||
|
|||||||
@@ -8,13 +8,7 @@
|
|||||||
<div class="brand-content-wrapper">
|
<div class="brand-content-wrapper">
|
||||||
<!-- 顶部系统微标 -->
|
<!-- 顶部系统微标 -->
|
||||||
<div class="brand-mini-logo">
|
<div class="brand-mini-logo">
|
||||||
<div class="mini-logo-icon">
|
<img class="mini-logo-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
||||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
|
||||||
<path d="M2 17l10 5 10-5"/>
|
|
||||||
<path d="M2 12l10 5 10-5"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<span class="mini-logo-text">华邦制药 · CTMS</span>
|
<span class="mini-logo-text">华邦制药 · CTMS</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -77,13 +71,7 @@
|
|||||||
<div class="reg-card-container">
|
<div class="reg-card-container">
|
||||||
<!-- 顶部 Logo -->
|
<!-- 顶部 Logo -->
|
||||||
<div class="reg-brand-header">
|
<div class="reg-brand-header">
|
||||||
<div class="brand-logo-icon">
|
<img class="brand-logo-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
|
||||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
||||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
|
||||||
<path d="M2 17l10 5 10-5"/>
|
|
||||||
<path d="M2 12l10 5 10-5"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="brand-divider"></div>
|
<div class="brand-divider"></div>
|
||||||
<span class="brand-system-text">新用户注册</span>
|
<span class="brand-system-text">新用户注册</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -821,14 +809,12 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mini-logo-icon {
|
.mini-logo-icon {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 55px;
|
width: 55px;
|
||||||
height: 55px;
|
height: 55px;
|
||||||
background: #2563eb;
|
background: #ffffff;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
color: white;
|
box-shadow: 0 12px 26px rgba(37, 99, 235, 0.18);
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-logo-text {
|
.mini-logo-text {
|
||||||
@@ -989,14 +975,12 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brand-logo-icon {
|
.brand-logo-icon {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 34px;
|
width: 34px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
background: #2563eb;
|
background: #ffffff;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
color: white;
|
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.16);
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-divider {
|
.brand-divider {
|
||||||
|
|||||||
@@ -73,11 +73,19 @@ describe("project management access", () => {
|
|||||||
expect(source).toContain(`:label="TEXT.common.fields.protocolNo || '方案号'" show-overflow-tooltip`);
|
expect(source).toContain(`:label="TEXT.common.fields.protocolNo || '方案号'" show-overflow-tooltip`);
|
||||||
expect(source).toContain(':label="TEXT.common.fields.status" align="center"');
|
expect(source).toContain(':label="TEXT.common.fields.status" align="center"');
|
||||||
expect(source).toContain('label="锁定状态" align="center"');
|
expect(source).toContain('label="锁定状态" align="center"');
|
||||||
expect(source).toContain(':label="TEXT.common.labels.actions" align="center"');
|
expect(source).toContain(':label="TEXT.common.labels.actions" align="center" width="320" class-name="project-actions-column"');
|
||||||
expect(source).not.toContain('projectColumnWidth');
|
expect(source).not.toContain('projectColumnWidth');
|
||||||
expect(source).not.toContain('fixed="right"');
|
expect(source).not.toContain('fixed="right"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reserves enough width for the densest project action set", () => {
|
||||||
|
const source = readProjects();
|
||||||
|
|
||||||
|
expect(source).toContain('width="320" class-name="project-actions-column"');
|
||||||
|
expect(source).toContain(".project-table :deep(.project-actions-column .cell)");
|
||||||
|
expect(source).toContain("overflow: visible;");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps the project summary header compact", () => {
|
it("keeps the project summary header compact", () => {
|
||||||
const source = readProjects();
|
const source = readProjects();
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="TEXT.common.labels.actions" align="center">
|
<el-table-column :label="TEXT.common.labels.actions" align="center" width="320" class-name="project-actions-column">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="action-row">
|
<div class="action-row">
|
||||||
<el-tooltip v-if="canProject(scope.row, 'project_members', 'read')" :content="TEXT.modules.adminProjects.members" placement="top">
|
<el-tooltip v-if="canProject(scope.row, 'project_members', 'read')" :content="TEXT.modules.adminProjects.members" placement="top">
|
||||||
@@ -580,6 +580,10 @@ onMounted(() => {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-table :deep(.project-actions-column .cell) {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.stats-row {
|
.stats-row {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|||||||