2ND
This commit is contained in:
325
frontend/src/style/components.scss
Normal file
325
frontend/src/style/components.scss
Normal file
@@ -0,0 +1,325 @@
|
||||
// 組件樣式
|
||||
|
||||
// 狀態標籤樣式
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
border-radius: $border-radius-base;
|
||||
font-size: $font-size-small;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
&.pending {
|
||||
background-color: map-get($status-colors, 'PENDING');
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.processing {
|
||||
background-color: map-get($status-colors, 'PROCESSING');
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.completed {
|
||||
background-color: map-get($status-colors, 'COMPLETED');
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.failed {
|
||||
background-color: map-get($status-colors, 'FAILED');
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.retry {
|
||||
background-color: map-get($status-colors, 'RETRY');
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
// 檔案圖示樣式
|
||||
.file-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: $border-radius-base;
|
||||
color: white;
|
||||
font-size: $font-size-small;
|
||||
font-weight: bold;
|
||||
|
||||
&.docx, &.doc {
|
||||
background-color: map-get($file-type-colors, 'docx');
|
||||
}
|
||||
|
||||
&.pptx, &.ppt {
|
||||
background-color: map-get($file-type-colors, 'pptx');
|
||||
}
|
||||
|
||||
&.xlsx, &.xls {
|
||||
background-color: map-get($file-type-colors, 'xlsx');
|
||||
}
|
||||
|
||||
&.pdf {
|
||||
background-color: map-get($file-type-colors, 'pdf');
|
||||
}
|
||||
}
|
||||
|
||||
// 進度條樣式
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background-color: $border-color-lighter;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, $primary-color, lighten($primary-color, 10%));
|
||||
border-radius: 3px;
|
||||
transition: width 0.3s ease;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(
|
||||
-45deg,
|
||||
rgba(255, 255, 255, 0.2) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.2) 50%,
|
||||
rgba(255, 255, 255, 0.2) 75%,
|
||||
transparent 75%,
|
||||
transparent
|
||||
);
|
||||
background-size: 20px 20px;
|
||||
animation: progress-stripes 1s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes progress-stripes {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 20px 0; }
|
||||
}
|
||||
|
||||
// 上傳區域樣式
|
||||
.upload-area {
|
||||
border: 2px dashed $border-color;
|
||||
border-radius: $border-radius-base;
|
||||
background-color: $bg-color-light;
|
||||
transition: all $transition-duration-base;
|
||||
|
||||
&:hover, &.dragover {
|
||||
border-color: $primary-color;
|
||||
background-color: rgba($primary-color, 0.05);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
border-color: $border-color-lighter;
|
||||
background-color: $border-color-extra-light;
|
||||
cursor: not-allowed;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 任務卡片樣式
|
||||
.job-card {
|
||||
@include card-style;
|
||||
margin-bottom: $spacing-md;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
border-color: $primary-color;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.job-header {
|
||||
@include flex-between;
|
||||
margin-bottom: $spacing-sm;
|
||||
|
||||
.job-title {
|
||||
font-weight: 600;
|
||||
color: $text-color-primary;
|
||||
@include text-ellipsis;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.job-actions {
|
||||
display: flex;
|
||||
gap: $spacing-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.job-info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: $spacing-sm;
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-secondary;
|
||||
|
||||
@include respond-to(sm) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.job-progress {
|
||||
margin-top: $spacing-sm;
|
||||
|
||||
.progress-text {
|
||||
@include flex-between;
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-secondary;
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.job-footer {
|
||||
@include flex-between;
|
||||
margin-top: $spacing-sm;
|
||||
padding-top: $spacing-sm;
|
||||
border-top: 1px solid $border-color-lighter;
|
||||
|
||||
.job-time {
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 統計卡片樣式
|
||||
.stat-card {
|
||||
@include card-style($spacing-lg);
|
||||
text-align: center;
|
||||
|
||||
.stat-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0 auto $spacing-sm;
|
||||
border-radius: 50%;
|
||||
@include flex-center;
|
||||
|
||||
&.primary { background-color: rgba($primary-color, 0.1); color: $primary-color; }
|
||||
&.success { background-color: rgba($success-color, 0.1); color: $success-color; }
|
||||
&.warning { background-color: rgba($warning-color, 0.1); color: $warning-color; }
|
||||
&.danger { background-color: rgba($danger-color, 0.1); color: $danger-color; }
|
||||
&.info { background-color: rgba($info-color, 0.1); color: $info-color; }
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: $font-size-extra-large;
|
||||
font-weight: bold;
|
||||
color: $text-color-primary;
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-secondary;
|
||||
margin-bottom: $spacing-sm;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
font-size: $font-size-small;
|
||||
|
||||
&.positive { color: $success-color; }
|
||||
&.negative { color: $danger-color; }
|
||||
}
|
||||
}
|
||||
|
||||
// 空狀態樣式
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: $spacing-xxl * 2;
|
||||
color: $text-color-secondary;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64px;
|
||||
color: $border-color;
|
||||
margin-bottom: $spacing-lg;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: $font-size-large;
|
||||
color: $text-color-primary;
|
||||
margin-bottom: $spacing-sm;
|
||||
}
|
||||
|
||||
.empty-description {
|
||||
font-size: $font-size-base;
|
||||
line-height: 1.6;
|
||||
margin-bottom: $spacing-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// 語言標籤樣式
|
||||
.language-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
margin: 2px;
|
||||
background-color: $primary-color;
|
||||
color: white;
|
||||
border-radius: $border-radius-small;
|
||||
font-size: $font-size-small;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 載入覆蓋層
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(2px);
|
||||
@include flex-center;
|
||||
z-index: $z-index-modal;
|
||||
|
||||
.loading-content {
|
||||
text-align: center;
|
||||
|
||||
.loading-spinner {
|
||||
@include loading-spinner(32px);
|
||||
margin: 0 auto $spacing-md;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: $text-color-secondary;
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 工具提示樣式覆蓋
|
||||
.custom-tooltip {
|
||||
&.el-popper {
|
||||
max-width: 300px;
|
||||
|
||||
.el-popper__arrow::before {
|
||||
border-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.el-tooltip__content {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
border-radius: $border-radius-base;
|
||||
padding: $spacing-sm $spacing-md;
|
||||
font-size: $font-size-small;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
458
frontend/src/style/layouts.scss
Normal file
458
frontend/src/style/layouts.scss
Normal file
@@ -0,0 +1,458 @@
|
||||
// 布局樣式
|
||||
|
||||
// 主要布局容器
|
||||
.app-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
// 側邊欄
|
||||
.layout-sidebar {
|
||||
width: 240px;
|
||||
background-color: $sidebar-bg;
|
||||
color: $sidebar-text-color;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: width $transition-duration-base;
|
||||
z-index: $z-index-top;
|
||||
|
||||
&.collapsed {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
@include respond-to(md) {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
transform: translateX(-100%);
|
||||
|
||||
&.mobile-show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: $spacing-lg;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
@include flex-center;
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: white;
|
||||
font-size: $font-size-large;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
|
||||
.logo-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: $spacing-sm;
|
||||
background: linear-gradient(45deg, $primary-color, lighten($primary-color, 10%));
|
||||
border-radius: $border-radius-base;
|
||||
@include flex-center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
transition: opacity $transition-duration-base;
|
||||
|
||||
.collapsed & {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
flex: 1;
|
||||
padding: $spacing-lg 0;
|
||||
overflow-y: auto;
|
||||
@include custom-scrollbar(rgba(255, 255, 255, 0.3), transparent, 4px);
|
||||
|
||||
.menu-item {
|
||||
display: block;
|
||||
padding: $spacing-md $spacing-lg;
|
||||
color: $sidebar-text-color;
|
||||
text-decoration: none;
|
||||
transition: all $transition-duration-fast;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: rgba($primary-color, 0.2);
|
||||
color: $primary-color;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
background-color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 20px;
|
||||
margin-right: $spacing-sm;
|
||||
text-align: center;
|
||||
transition: margin-right $transition-duration-base;
|
||||
|
||||
.collapsed & {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
transition: opacity $transition-duration-base;
|
||||
|
||||
.collapsed & {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
padding: $spacing-lg;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
.collapse-toggle {
|
||||
width: 100%;
|
||||
padding: $spacing-sm;
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: $border-radius-base;
|
||||
color: $sidebar-text-color;
|
||||
cursor: pointer;
|
||||
transition: all $transition-duration-fast;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主要內容區
|
||||
.layout-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background-color: $bg-color-page;
|
||||
|
||||
// 頂部導航欄
|
||||
.layout-header {
|
||||
height: 60px;
|
||||
background-color: $header-bg;
|
||||
border-bottom: 1px solid $border-color-lighter;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
@include flex-between;
|
||||
padding: 0 $spacing-lg;
|
||||
z-index: $z-index-normal;
|
||||
|
||||
@include respond-to(md) {
|
||||
padding: 0 $spacing-md;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
padding: $spacing-sm;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin-right: $spacing-md;
|
||||
|
||||
@include respond-to(md) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: $font-size-base;
|
||||
color: $text-color-secondary;
|
||||
|
||||
.breadcrumb-item {
|
||||
&:not(:last-child)::after {
|
||||
content: '/';
|
||||
margin: 0 $spacing-sm;
|
||||
color: $text-color-placeholder;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
color: $text-color-primary;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-md;
|
||||
|
||||
.notification-bell {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: $spacing-sm;
|
||||
border-radius: $border-radius-base;
|
||||
transition: background-color $transition-duration-fast;
|
||||
|
||||
&:hover {
|
||||
background-color: $bg-color-light;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: $danger-color;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
|
||||
.avatar-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $spacing-sm;
|
||||
border-radius: $border-radius-base;
|
||||
transition: background-color $transition-duration-fast;
|
||||
|
||||
&:hover {
|
||||
background-color: $bg-color-light;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(45deg, $primary-color, lighten($primary-color, 10%));
|
||||
@include flex-center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
margin-right: $spacing-sm;
|
||||
|
||||
@include respond-to(sm) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
@include respond-to(sm) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: $font-size-base;
|
||||
font-weight: 500;
|
||||
color: $text-color-primary;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-secondary;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 內容區域
|
||||
.layout-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.content-wrapper {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: $spacing-lg;
|
||||
|
||||
@include respond-to(md) {
|
||||
padding: $spacing-md;
|
||||
}
|
||||
|
||||
@include respond-to(sm) {
|
||||
padding: $spacing-sm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 移動設備遮罩
|
||||
.mobile-mask {
|
||||
display: none;
|
||||
|
||||
@include respond-to(md) {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: $z-index-top - 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all $transition-duration-base;
|
||||
|
||||
&.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 登入頁面布局
|
||||
.login-layout {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
@include flex-center;
|
||||
padding: $spacing-lg;
|
||||
|
||||
.login-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
background: white;
|
||||
border-radius: $border-radius-base * 2;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
.login-header {
|
||||
background: linear-gradient(45deg, $primary-color, lighten($primary-color, 10%));
|
||||
padding: $spacing-xxl;
|
||||
text-align: center;
|
||||
color: white;
|
||||
|
||||
.login-logo {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 auto $spacing-lg;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
@include flex-center;
|
||||
font-size: $font-size-extra-large;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: $font-size-extra-large;
|
||||
font-weight: bold;
|
||||
margin-bottom: $spacing-sm;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
font-size: $font-size-base;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.login-body {
|
||||
padding: $spacing-xxl;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
padding: $spacing-lg $spacing-xxl;
|
||||
background-color: $bg-color-light;
|
||||
text-align: center;
|
||||
color: $text-color-secondary;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 頁面標題區域
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: $spacing-lg;
|
||||
padding-bottom: $spacing-md;
|
||||
border-bottom: 1px solid $border-color-lighter;
|
||||
|
||||
@include respond-to(sm) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: $spacing-md;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: $font-size-extra-large;
|
||||
font-weight: bold;
|
||||
color: $text-color-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
display: flex;
|
||||
gap: $spacing-sm;
|
||||
}
|
||||
}
|
||||
|
||||
// 內容卡片
|
||||
.content-card {
|
||||
@include card-style;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: $spacing-lg;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
@include flex-between;
|
||||
margin-bottom: $spacing-lg;
|
||||
padding-bottom: $spacing-md;
|
||||
border-bottom: 1px solid $border-color-lighter;
|
||||
|
||||
.card-title {
|
||||
font-size: $font-size-large;
|
||||
font-weight: 600;
|
||||
color: $text-color-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: $spacing-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
// 內容樣式由具體組件定義
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
margin-top: $spacing-lg;
|
||||
padding-top: $spacing-md;
|
||||
border-top: 1px solid $border-color-lighter;
|
||||
@include flex-between;
|
||||
}
|
||||
}
|
187
frontend/src/style/main.scss
Normal file
187
frontend/src/style/main.scss
Normal file
@@ -0,0 +1,187 @@
|
||||
// 主要樣式文件
|
||||
@import './variables.scss';
|
||||
@import './mixins.scss';
|
||||
@import './components.scss';
|
||||
@import './layouts.scss';
|
||||
|
||||
// 全局重置樣式
|
||||
*, *::before, *::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
font-family: $font-family;
|
||||
background-color: var(--el-bg-color-page);
|
||||
color: var(--el-text-color-primary);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 滾動條樣式
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--el-fill-color-lighter);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-border-color-darker);
|
||||
}
|
||||
}
|
||||
|
||||
// Firefox 滾動條
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--el-border-color) var(--el-fill-color-lighter);
|
||||
}
|
||||
|
||||
// 文字選擇顏色
|
||||
::selection {
|
||||
background: var(--el-color-primary-light-8);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: var(--el-color-primary-light-8);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
// 通用輔助類別
|
||||
.text-center { text-align: center; }
|
||||
.text-left { text-align: left; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.flex { display: flex; }
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.flex-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
// 間距輔助類別
|
||||
@for $i from 1 through 10 {
|
||||
.m-#{$i} { margin: #{$i * 4}px; }
|
||||
.mt-#{$i} { margin-top: #{$i * 4}px; }
|
||||
.mr-#{$i} { margin-right: #{$i * 4}px; }
|
||||
.mb-#{$i} { margin-bottom: #{$i * 4}px; }
|
||||
.ml-#{$i} { margin-left: #{$i * 4}px; }
|
||||
.mx-#{$i} {
|
||||
margin-left: #{$i * 4}px;
|
||||
margin-right: #{$i * 4}px;
|
||||
}
|
||||
.my-#{$i} {
|
||||
margin-top: #{$i * 4}px;
|
||||
margin-bottom: #{$i * 4}px;
|
||||
}
|
||||
|
||||
.p-#{$i} { padding: #{$i * 4}px; }
|
||||
.pt-#{$i} { padding-top: #{$i * 4}px; }
|
||||
.pr-#{$i} { padding-right: #{$i * 4}px; }
|
||||
.pb-#{$i} { padding-bottom: #{$i * 4}px; }
|
||||
.pl-#{$i} { padding-left: #{$i * 4}px; }
|
||||
.px-#{$i} {
|
||||
padding-left: #{$i * 4}px;
|
||||
padding-right: #{$i * 4}px;
|
||||
}
|
||||
.py-#{$i} {
|
||||
padding-top: #{$i * 4}px;
|
||||
padding-bottom: #{$i * 4}px;
|
||||
}
|
||||
}
|
||||
|
||||
// 響應式斷點
|
||||
.hidden-xs {
|
||||
@include respond-to(xs) { display: none !important; }
|
||||
}
|
||||
.hidden-sm {
|
||||
@include respond-to(sm) { display: none !important; }
|
||||
}
|
||||
.hidden-md {
|
||||
@include respond-to(md) { display: none !important; }
|
||||
}
|
||||
.hidden-lg {
|
||||
@include respond-to(lg) { display: none !important; }
|
||||
}
|
||||
|
||||
// 動畫類別
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
|
||||
}
|
||||
|
||||
.slide-enter-from {
|
||||
transform: translateX(-20px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-leave-to {
|
||||
transform: translateX(20px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// 卡片陰影
|
||||
.card-shadow {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-hover-shadow {
|
||||
transition: box-shadow 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
// 載入狀態
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 999;
|
||||
}
|
272
frontend/src/style/mixins.scss
Normal file
272
frontend/src/style/mixins.scss
Normal file
@@ -0,0 +1,272 @@
|
||||
// SCSS Mixins 混合器
|
||||
|
||||
// 響應式斷點混合器
|
||||
@mixin respond-to($breakpoint) {
|
||||
@if $breakpoint == xs {
|
||||
@media (max-width: #{$breakpoint-xs - 1px}) { @content; }
|
||||
}
|
||||
@if $breakpoint == sm {
|
||||
@media (max-width: #{$breakpoint-sm - 1px}) { @content; }
|
||||
}
|
||||
@if $breakpoint == md {
|
||||
@media (max-width: #{$breakpoint-md - 1px}) { @content; }
|
||||
}
|
||||
@if $breakpoint == lg {
|
||||
@media (max-width: #{$breakpoint-lg - 1px}) { @content; }
|
||||
}
|
||||
@if $breakpoint == xl {
|
||||
@media (min-width: $breakpoint-xl) { @content; }
|
||||
}
|
||||
}
|
||||
|
||||
// 最小寬度斷點
|
||||
@mixin respond-above($breakpoint) {
|
||||
@if $breakpoint == xs {
|
||||
@media (min-width: $breakpoint-xs) { @content; }
|
||||
}
|
||||
@if $breakpoint == sm {
|
||||
@media (min-width: $breakpoint-sm) { @content; }
|
||||
}
|
||||
@if $breakpoint == md {
|
||||
@media (min-width: $breakpoint-md) { @content; }
|
||||
}
|
||||
@if $breakpoint == lg {
|
||||
@media (min-width: $breakpoint-lg) { @content; }
|
||||
}
|
||||
}
|
||||
|
||||
// Flexbox 輔助混合器
|
||||
@mixin flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@mixin flex-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@mixin flex-start {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
@mixin flex-end {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@mixin flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@mixin flex-column-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 文字省略號
|
||||
@mixin text-ellipsis {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@mixin multi-line-ellipsis($lines: 2) {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: $lines;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// 清除浮動
|
||||
@mixin clearfix {
|
||||
&::after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
// 隱藏滾動條
|
||||
@mixin hide-scrollbar {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none; /* Chrome Safari */
|
||||
}
|
||||
}
|
||||
|
||||
// 自定義滾動條
|
||||
@mixin custom-scrollbar($thumb-color: $border-color, $track-color: transparent, $size: 6px) {
|
||||
&::-webkit-scrollbar {
|
||||
width: $size;
|
||||
height: $size;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $track-color;
|
||||
border-radius: $size / 2;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $thumb-color;
|
||||
border-radius: $size / 2;
|
||||
|
||||
&:hover {
|
||||
background: darken($thumb-color, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 絕對定位置中
|
||||
@mixin absolute-center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@mixin absolute-center-x {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@mixin absolute-center-y {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
// 固定比例容器
|
||||
@mixin aspect-ratio($width: 16, $height: 9) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-top: ($height / $width) * 100%;
|
||||
}
|
||||
|
||||
> * {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 過渡動畫
|
||||
@mixin transition($property: all, $duration: $transition-duration-base, $timing-function: ease-in-out) {
|
||||
transition: $property $duration $timing-function;
|
||||
}
|
||||
|
||||
@mixin hover-lift {
|
||||
transition: transform $transition-duration-fast ease-out, box-shadow $transition-duration-fast ease-out;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: $box-shadow-dark;
|
||||
}
|
||||
}
|
||||
|
||||
// 按鈕樣式混合器
|
||||
@mixin button-variant($color, $background, $border: $background) {
|
||||
color: $color;
|
||||
background-color: $background;
|
||||
border-color: $border;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $color;
|
||||
background-color: lighten($background, 5%);
|
||||
border-color: lighten($border, 5%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $color;
|
||||
background-color: darken($background, 5%);
|
||||
border-color: darken($border, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
// 狀態標籤樣式
|
||||
@mixin status-badge($color) {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
font-size: $font-size-small;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
background-color: $color;
|
||||
border-radius: $border-radius-base;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
// 卡片樣式
|
||||
@mixin card-style($padding: $spacing-lg, $border-radius: $border-radius-base) {
|
||||
background: $bg-color;
|
||||
border: 1px solid $border-color-lighter;
|
||||
border-radius: $border-radius;
|
||||
box-shadow: $box-shadow-light;
|
||||
padding: $padding;
|
||||
transition: box-shadow $transition-duration-base;
|
||||
|
||||
&:hover {
|
||||
box-shadow: $box-shadow-dark;
|
||||
}
|
||||
}
|
||||
|
||||
// 表單輸入樣式
|
||||
@mixin form-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
font-size: $font-size-base;
|
||||
line-height: $line-height-base;
|
||||
color: $text-color-primary;
|
||||
background-color: $bg-color;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: $border-radius-base;
|
||||
transition: border-color $transition-duration-fast, box-shadow $transition-duration-fast;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $primary-color;
|
||||
box-shadow: 0 0 0 2px rgba($primary-color, 0.2);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: $bg-color-light;
|
||||
color: $text-color-placeholder;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading 動畫
|
||||
@mixin loading-spinner($size: 20px, $color: $primary-color) {
|
||||
width: $size;
|
||||
height: $size;
|
||||
border: 2px solid transparent;
|
||||
border-top-color: $color;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
106
frontend/src/style/variables.scss
Normal file
106
frontend/src/style/variables.scss
Normal file
@@ -0,0 +1,106 @@
|
||||
// SCSS 變數定義
|
||||
|
||||
// 顏色系統
|
||||
$primary-color: #409eff;
|
||||
$success-color: #67c23a;
|
||||
$warning-color: #e6a23c;
|
||||
$danger-color: #f56c6c;
|
||||
$info-color: #909399;
|
||||
|
||||
// 文字顏色
|
||||
$text-color-primary: #303133;
|
||||
$text-color-regular: #606266;
|
||||
$text-color-secondary: #909399;
|
||||
$text-color-placeholder: #c0c4cc;
|
||||
|
||||
// 背景顏色
|
||||
$bg-color-page: #f2f3f5;
|
||||
$bg-color: #ffffff;
|
||||
$bg-color-light: #fafafa;
|
||||
|
||||
// 邊框顏色
|
||||
$border-color: #dcdfe6;
|
||||
$border-color-light: #e4e7ed;
|
||||
$border-color-lighter: #ebeef5;
|
||||
$border-color-extra-light: #f2f6fc;
|
||||
|
||||
// 字體
|
||||
$font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
$font-size-extra-large: 20px;
|
||||
$font-size-large: 18px;
|
||||
$font-size-medium: 16px;
|
||||
$font-size-base: 14px;
|
||||
$font-size-small: 13px;
|
||||
$font-size-extra-small: 12px;
|
||||
|
||||
// 行高
|
||||
$line-height-base: 1.5;
|
||||
|
||||
// 間距
|
||||
$spacing-base: 4px;
|
||||
$spacing-xs: 4px;
|
||||
$spacing-sm: 8px;
|
||||
$spacing-md: 12px;
|
||||
$spacing-lg: 16px;
|
||||
$spacing-xl: 20px;
|
||||
$spacing-xxl: 24px;
|
||||
|
||||
// 邊框半徑
|
||||
$border-radius-base: 4px;
|
||||
$border-radius-small: 2px;
|
||||
$border-radius-round: 20px;
|
||||
$border-radius-circle: 50%;
|
||||
|
||||
// 陰影
|
||||
$box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
|
||||
$box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
$box-shadow-dark: 0 4px 20px 0 rgba(0, 0, 0, .15);
|
||||
|
||||
// z-index 層級
|
||||
$z-index-normal: 1;
|
||||
$z-index-top: 1000;
|
||||
$z-index-popper: 2000;
|
||||
$z-index-modal: 3000;
|
||||
|
||||
// 斷點
|
||||
$breakpoint-xs: 480px;
|
||||
$breakpoint-sm: 768px;
|
||||
$breakpoint-md: 992px;
|
||||
$breakpoint-lg: 1200px;
|
||||
$breakpoint-xl: 1920px;
|
||||
|
||||
// 動畫持續時間
|
||||
$transition-duration-fast: 0.2s;
|
||||
$transition-duration-base: 0.3s;
|
||||
$transition-duration-slow: 0.5s;
|
||||
|
||||
// 動畫緩動函數
|
||||
$ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
$ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46);
|
||||
$ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46);
|
||||
|
||||
// 組件特定顏色
|
||||
$header-bg: #fff;
|
||||
$sidebar-bg: #304156;
|
||||
$sidebar-text-color: #bfcbd9;
|
||||
$sidebar-active-color: #409eff;
|
||||
|
||||
// 狀態顏色映射
|
||||
$status-colors: (
|
||||
'PENDING': #909399,
|
||||
'PROCESSING': #409eff,
|
||||
'COMPLETED': #67c23a,
|
||||
'FAILED': #f56c6c,
|
||||
'RETRY': #e6a23c
|
||||
);
|
||||
|
||||
// 檔案類型圖示顏色
|
||||
$file-type-colors: (
|
||||
'docx': #2b579a,
|
||||
'doc': #2b579a,
|
||||
'pptx': #d24726,
|
||||
'ppt': #d24726,
|
||||
'xlsx': #207245,
|
||||
'xls': #207245,
|
||||
'pdf': #ff0000
|
||||
);
|
Reference in New Issue
Block a user