代码修改

This commit is contained in:
qiuyuan 2025-11-21 16:34:12 +08:00
parent 1b6239b4bc
commit 0f5690fadb
6 changed files with 524 additions and 680 deletions

View File

@ -1,8 +1,9 @@
<!-- src/App.vue -->
<template>
<div id="app">
<Header />
<router-view />
<main class="main-content">
<router-view />
</main>
</div>
</template>
@ -19,12 +20,26 @@ import Header from '@/components/Header.vue'
html, body {
height: 100%;
overflow-x: hidden; /* 可选:防止水平滚动 */
}
#app {
min-height: 100vh;
margin-top: 66px;
display: flex;
flex-direction: column;
}
/* 假设 Header 高度是 66px */
header {
height: 66px;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.main-content {
padding-top: 30px;
}
</style>

View File

@ -76,7 +76,8 @@ const menuItems: MenuItem[] = [
name: '账号',
icon: TeamOutlined,
children: [
{ path: '/controlPanel/account/profile', name: '个人资料' },
{ path: '/accountSecurity', name: '账号安全' },
{ path: '/accountHistory', name: '访问记录' },
{ path: '/controlPanel/account/security', name: '安全设置' }
]
}
@ -102,7 +103,8 @@ const handleMenuClick = ({ key }: { key: string }) => {
height: 100vh;
background: #fff;
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
overflow-y: auto;
padding-top:30px;
/* overflow-y: auto; */
}
.menu-item-content {

View File

@ -4,6 +4,10 @@ import Layout from '@/components/Layout.vue'
const HomeView = () => import('@/views/home/index.vue')
const FileStore = () => import('@/views/controlPanel/fileStore/index.vue')
// 账号安全
const AccountSecurity = () => import('@/views/controlPanel/account/security/index.vue')
// 访问记录
const AccountHistory = () => import('@/views/controlPanel/account/history/index.vue')
const routes: RouteRecordRaw[] = [
{
@ -20,6 +24,16 @@ const routes: RouteRecordRaw[] = [
path: '', // 空路径,访问 /fileStore 时显示FileStore组件
name: 'FileStoreContent',
component: FileStore
},
{
path: '/accountSecurity',
name: 'AccountSecurity',
component: AccountSecurity
},
{
path: '/accountHistory',
name: 'AccountHistory',
component: AccountHistory
}
]
},

View File

@ -0,0 +1,116 @@
<!-- src/components/AccessLog.vue -->
<template>
<div class="access-log">
<div class="title-wrapper">
<h3>访问记录</h3>
<span class="desc">以下显示内容为账号及其子账号近3个月的登录记录</span>
</div>
<a-table
:columns="columns"
:data-source="loginRecords"
:pagination="false"
size="middle"
:bordered="true"
class="login-table"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
//
interface LoginRecord {
id: number;
time: string;
ip: string;
city: string;
type: string;
operator: string;
}
//
const loginRecords = ref<LoginRecord[]>([
{
id: 1,
time: '2025-11-21 09:58:38',
ip: '183.209.69.36',
city: '中国 江苏省',
type: 'Web微信登录',
operator: '炼丹师5075',
},
{
id: 2,
time: '2025-11-19 17:07:13',
ip: '183.209.69.36',
city: '中国 江苏省',
type: 'Web微信登录',
operator: '炼丹师5075',
},
]);
//
const columns = [
{
title: '访问时间',
dataIndex: 'time',
key: 'time',
width: 200,
},
{
title: '访问IP地址',
dataIndex: 'ip',
key: 'ip',
width: 180,
},
{
title: '访问城市',
dataIndex: 'city',
key: 'city',
width: 180,
},
{
title: '登录类型',
dataIndex: 'type',
key: 'type',
width: 200,
},
{
title: '操作人',
dataIndex: 'operator',
key: 'operator',
width: 180,
},
];
</script>
<style scoped>
.access-log {
padding: 20px;
background-color: #f9f9f9;
}
.title-wrapper {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 16px;
}
.title-wrapper h3 {
margin: 0;
color: #333;
font-size: 1.2em;
}
.desc {
color: #faad14;
font-size: 14px;
font-weight: normal;
}
.login-table {
border-radius: 6px;
}
</style>

View File

@ -0,0 +1,203 @@
<!-- src/components/AccountSecurity.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import { CheckCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
//
interface SecurityItem {
title: string;
description: string;
status: 'unset' | 'bound' | 'unverified' | 'verified';
actionText: string;
actionHandler: () => void;
}
const securityItems = ref<SecurityItem[]>([
{
title: '登录密码',
description: '安全性高的密码可以使账号更安全。建议您定期更换密码设置一个包含字母和数字且长度超过8位的密码',
status: 'unset',
actionText: '设置',
actionHandler: () => alert('跳转到设置密码页面'),
},
{
title: '手机绑定',
description: '您已绑定了手机178****5075您的手机号可以直接用于登录、找回密码等',
status: 'bound',
actionText: '修改',
actionHandler: () => alert('跳转到修改手机号页面'),
},
{
title: '实名认证',
description: '实名认证后可以使用AutoDL更完整的功能如打开实例的自定义服务等',
status: 'unverified',
actionText: '立即认证',
actionHandler: () => alert('跳转到实名认证页面'),
},
{
title: '微信绑定',
description: '您已绑定微信,可快速扫码登录',
status: 'bound',
actionText: '解绑',
actionHandler: () => alert('确认解绑微信?'),
},
{
title: '邮箱绑定',
description: '绑定邮箱后可接收系统消息,如余额不足、实例即将到期、实例即将释放等消息',
status: 'unset',
actionText: '绑定',
actionHandler: () => alert('跳转到绑定邮箱页面'),
},
]);
//
const getStatusIcon = (status: SecurityItem['status']) => {
switch (status) {
case 'bound':
return CheckCircleOutlined;
case 'unverified':
case 'unset':
return ExclamationCircleOutlined;
default:
return null;
}
};
const getStatusColor = (status: SecurityItem['status']) => {
switch (status) {
case 'bound':
return 'green';
case 'unverified':
case 'unset':
return 'red';
default:
return 'gray';
}
};
const getActionColor = (status: SecurityItem['status']) => {
if (status === 'bound') {
return 'blue';
}
return 'blue'; //
};
</script>
<template>
<div class="account-security">
<h2>账号安全</h2>
<div v-for="(item, index) in securityItems" :key="index" class="security-item">
<div class="item-header">
<span class="title">{{ item.title }}</span>
<div class="status">
<component
:is="getStatusIcon(item.status)"
v-if="getStatusIcon(item.status)"
:class="['icon', `color-${getStatusColor(item.status)}`]"
/>
<span :class="['text', `color-${getStatusColor(item.status)}`]">
{{ item.status === 'bound' ? '已绑定' : item.status === 'unverified' ? '未认证' : '未设置' }}
</span>
<span class="divider">|</span>
<button
class="action-btn"
:class="`color-${getActionColor(item.status)}`"
@click="item.actionHandler"
>
{{ item.actionText }}
</button>
</div>
</div>
<p class="description">{{ item.description }}</p>
</div>
</div>
</template>
<style scoped>
.account-security {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background-color: #f9f9f9;
}
.account-security h2 {
margin-top: 0;
color: #333;
font-size: 1.4em;
}
.security-item {
border: 1px solid #ddd;
border-radius: 6px;
margin-bottom: 16px;
padding: 16px;
background: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.title {
font-weight: bold;
color: #333;
}
.status {
display: flex;
align-items: center;
gap: 8px;
}
.icon {
font-size: 16px;
line-height: 1;
}
.text {
font-size: 14px;
font-weight: normal;
}
.divider {
color: #ccc;
font-size: 14px;
}
.action-btn {
background: none;
border: none;
color: #0066cc;
cursor: pointer;
font-size: 14px;
padding: 0;
margin-left: 8px;
text-decoration: underline;
}
.action-btn:hover {
text-decoration: none;
}
.color-green {
color: #52c41a !important;
}
.color-red {
color: #ff4d4f !important;
}
.color-blue {
color: #0066cc !important;
}
.description {
color: #666;
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -1,761 +1,255 @@
<template>
<div class="container">
<header>
<!-- 标题 -->
<div class="header">
<h1>文件存储</h1>
</header>
<div class="storage-grid">
<div
v-for="storage in storageOptions"
:key="storage.id"
:class="['storage-card', storage.class, { selected: selectedStorage === storage.id }]"
@click="selectStorage(storage.id)"
>
<div class="card-header">
<div class="card-icon">
<i :class="storage.icon"></i>
</div>
<div class="card-title">{{ storage.name }}</div>
</div>
<div class="card-description">
{{ storage.description }}
</div>
<div class="card-stats">
<div class="stat">
<div class="stat-value">{{ storage.capacity }}</div>
<div class="stat-label">存储容量</div>
</div>
<div class="stat">
<div class="stat-value">{{ storage.speed }}</div>
<div class="stat-label">传输速度</div>
</div>
<div class="stat">
<div class="stat-value">{{ storage.price }}</div>
<div class="stat-label">价格/</div>
</div>
</div>
</div>
<!-- 区域选择 -->
<div class="region-section">
<h2 class="section-title">选择存储区域</h2>
<div class="region-tabs">
<button
v-for="region in regions"
:key="region.id"
:class="['region-tab', { active: selectedRegion === region.id }]"
@click="selectRegion(region.id)"
>
{{ region.name }}
</button>
</div>
</div>
<div class="divider"></div>
<section class="init-section">
<h2>初始化文件存储</h2>
<div class="init-content">
<div class="init-form" v-if="!isInitialized">
<div class="form-group">
<label for="storageName">存储名称</label>
<input
id="storageName"
v-model="initForm.name"
type="text"
placeholder="请输入存储名称"
:class="{ error: formErrors.name }"
>
<span class="error-message" v-if="formErrors.name">{{ formErrors.name }}</span>
</div>
<div class="form-group">
<label for="storageSize">存储容量</label>
<div class="size-input">
<input
id="storageSize"
v-model.number="initForm.size"
type="number"
placeholder="请输入存储容量"
:class="{ error: formErrors.size }"
>
<select v-model="initForm.sizeUnit">
<option value="GB">GB</option>
<option value="TB">TB</option>
<option value="PB">PB</option>
</select>
</div>
<span class="error-message" v-if="formErrors.size">{{ formErrors.size }}</span>
</div>
<div class="form-group">
<label>存储类型</label>
<div class="radio-group">
<label class="radio-option">
<input
type="radio"
v-model="initForm.storageType"
value="standard"
>
<span class="radio-label">标准存储</span>
</label>
<label class="radio-option">
<input
type="radio"
v-model="initForm.storageType"
value="archive"
>
<span class="radio-label">归档存储</span>
</label>
<label class="radio-option">
<input
type="radio"
v-model="initForm.storageType"
value="performance"
>
<span class="radio-label">性能存储</span>
</label>
</div>
</div>
<div class="form-group">
<label class="checkbox-option">
<input
type="checkbox"
v-model="initForm.enableEncryption"
>
<span class="checkmark"></span>
启用数据加密
</label>
</div>
<div class="form-actions">
<button class="btn btn-outline" @click="resetForm">重置</button>
<button
class="btn btn-primary"
@click="initializeStorage"
:disabled="!canInitialize"
>
{{ isInitializing ? '初始化中...' : '开始初始化' }}
</button>
</div>
</div>
<div class="init-success" v-else>
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<h3>初始化成功</h3>
<p>文件存储已成功创建您现在可以开始使用了</p>
<div class="storage-info">
<div class="info-item">
<span class="info-label">存储名称</span>
<span class="info-value">{{ initializedStorage.name }}</span>
</div>
<div class="info-item">
<span class="info-label">存储区域</span>
<span class="info-value">{{ getSelectedStorageName() }}</span>
</div>
<div class="info-item">
<span class="info-label">存储容量</span>
<span class="info-value">{{ initializedStorage.size }} {{ initializedStorage.sizeUnit }}</span>
</div>
<div class="info-item">
<span class="info-label">存储类型</span>
<span class="info-value">{{ getStorageTypeText(initializedStorage.storageType) }}</span>
</div>
</div>
<div class="success-actions">
<button class="btn btn-outline" @click="createNewStorage">创建新的存储</button>
<button class="btn btn-primary" @click="goToStorage">进入文件存储</button>
</div>
</div>
<div class="init-help">
<h3>文件存储使用介绍</h3>
<p>选择适合您需求的存储区域配置存储参数并开始使用</p>
<div v-if="selectedStorage" class="selected-info">
当前选择: <strong>{{ getSelectedStorageName() }}</strong>
</div>
<div class="help-actions">
<button class="btn btn-outline">查看文档</button>
<button class="btn btn-primary" @click="viewPricingRules">查看计费规则</button>
</div>
</div>
<!-- 中心图标 -->
<div class="center-section">
<div class="center-icon">
<i class="icon-book"></i>
</div>
</section>
</div>
<!-- 主要操作按钮 -->
<div class="action-section">
<button class="btn-primary" @click="initializeStorage">
初始化文件存储
</button>
<p class="action-hint">将在 {{ getSelectedRegionName() }} 创建存储空间</p>
</div>
<!-- 底部帮助链接 -->
<div class="help-section">
<div class="help-links">
<a href="#" class="link" @click.prevent="viewHelp">文件存储使用介绍</a>
<a href="#" class="link" @click.prevent="viewPricing">查看计费规则</a>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { ref } from 'vue'
//
interface StorageOption {
id: number
//
interface Region {
id: string
name: string
class: string
icon: string
description: string
capacity: string
speed: string
price: string
}
//
interface InitForm {
name: string
size: number | null
sizeUnit: string
storageType: string
enableEncryption: boolean
const regions = [
{ id: 'a100', name: 'A100专区' },
{ id: 'v100', name: 'V100专区' },
{ id: 'foshan', name: '佛山区' },
{ id: 'beijing', name: '北京B区' }
]
const selectedRegion = ref('a100') // A100
//
const getSelectedRegionName = () => {
const region = regions.find(r => r.id === selectedRegion.value)
return region ? region.name : ''
}
interface FormErrors {
name?: string
size?: string
}
interface InitializedStorage {
name: string
size: number
sizeUnit: string
storageType: string
enableEncryption: boolean
}
//
const selectedStorage = ref<number | null>(null)
const isInitializing = ref(false)
const isInitialized = ref(false)
//
const initForm = reactive<InitForm>({
name: '',
size: null,
sizeUnit: 'GB',
storageType: 'standard',
enableEncryption: true
})
const formErrors = reactive<FormErrors>({})
const initializedStorage = ref<InitializedStorage | null>(null)
//
const storageOptions = ref<StorageOption[]>([
{
id: 1,
name: 'A100专区',
class: 'a100',
icon: 'fas fa-microchip',
description: '高性能计算存储适用于AI训练和大数据分析',
capacity: '10TB',
speed: '10Gbps',
price: '¥299'
},
{
id: 2,
name: 'V100专区',
class: 'v100',
icon: 'fas fa-server',
description: '企业级存储解决方案,提供高可靠性和稳定性',
capacity: '20TB',
speed: '8Gbps',
price: '¥499'
},
{
id: 3,
name: '佛山区',
class: 'foshan',
icon: 'fas fa-map-marker-alt',
description: '华南地区存储节点,低延迟访问',
capacity: '15TB',
speed: '6Gbps',
price: '¥199'
},
{
id: 4,
name: '北京B区',
class: 'beijing',
icon: 'fas fa-cloud',
description: '华北地区存储节点,高可用性架构',
capacity: '25TB',
speed: '12Gbps',
price: '¥399'
}
])
//
const canInitialize = computed(() => {
return selectedStorage.value !== null &&
initForm.name.trim() !== '' &&
initForm.size !== null &&
initForm.size > 0
})
//
const selectStorage = (id: number): void => {
selectedStorage.value = id
}
//
const getSelectedStorageName = (): string => {
const selected = storageOptions.value.find(storage => storage.id === selectedStorage.value)
return selected ? selected.name : ''
}
//
const validateForm = (): boolean => {
//
formErrors.name = ''
formErrors.size = ''
let isValid = true
if (!initForm.name.trim()) {
formErrors.name = '请输入存储名称'
isValid = false
}
if (initForm.size === null || initForm.size <= 0) {
formErrors.size = '请输入有效的存储容量'
isValid = false
}
return isValid
//
const selectRegion = (id: string) => {
selectedRegion.value = id
}
//
const initializeStorage = async (): Promise<void> => {
if (!validateForm()) {
return
}
if (!selectedStorage.value) {
alert('请先选择存储区域')
return
}
isInitializing.value = true
try {
// API
await new Promise(resolve => setTimeout(resolve, 2000))
//
initializedStorage.value = {
name: initForm.name,
size: initForm.size!,
sizeUnit: initForm.sizeUnit,
storageType: initForm.storageType,
enableEncryption: initForm.enableEncryption
}
isInitialized.value = true
} catch (error) {
console.error('初始化失败:', error)
alert('初始化失败,请重试')
} finally {
isInitializing.value = false
}
const initializeStorage = () => {
alert(`正在初始化 ${selectedRegion.value} 的文件存储...`)
//
}
//
const resetForm = (): void => {
initForm.name = ''
initForm.size = null
initForm.sizeUnit = 'GB'
initForm.storageType = 'standard'
initForm.enableEncryption = true
//
formErrors.name = ''
formErrors.size = ''
}
//
const createNewStorage = (): void => {
isInitialized.value = false
resetForm()
}
//
const goToStorage = (): void => {
alert('正在跳转到文件存储管理页面...')
//
}
//
const getStorageTypeText = (type: string): string => {
const typeMap: { [key: string]: string } = {
standard: '标准存储',
archive: '归档存储',
performance: '性能存储'
}
return typeMap[type] || type
//
const viewHelp = () => {
alert('文件存储使用介绍')
}
//
const viewPricingRules = (): void => {
alert('计费规则页面即将打开')
const viewPricing = () => {
alert('计费规则详情')
}
</script>
<style scoped>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
/* max-width: 800px;
margin: 0 auto; */
padding: 40px 30px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: #333;
}
header {
margin-bottom: 30px;
}
h1 {
font-size: 2.2rem;
color: #2c3e50;
margin-bottom: 10px;
}
.divider {
height: 1px;
background: linear-gradient(to right, transparent, #ddd, transparent);
margin: 25px 0;
}
h2 {
font-size: 1.6rem;
color: #34495e;
margin-bottom: 15px;
}
.storage-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
/* 标题区域 */
.header {
margin-bottom: 40px;
}
.storage-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
cursor: pointer;
border: 2px solid transparent;
h1 {
font-size: 2rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 8px;
}
.storage-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
/* 区域选择 */
.region-section {
margin-bottom: 40px;
}
.storage-card.selected {
border-color: #3498db;
background-color: #f8fafd;
.section-title {
font-size: 1.1rem;
font-weight: 500;
color: #555;
margin-bottom: 16px;
}
.card-header {
.region-tabs {
display: flex;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 12px;
}
.card-icon {
width: 50px;
height: 50px;
border-radius: 10px;
.region-tab {
padding: 12px 24px;
border: 1px solid #ddd;
background: white;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
min-width: 140px;
}
.region-tab:hover {
border-color: #999;
}
.region-tab.active {
background-color: #3498db;
color: white;
border-color: #3498db;
}
/* 中心图标区域 */
.center-section {
margin: 40px 0;
display: flex;
justify-content: center;
}
.center-icon {
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-size: 1.5rem;
background: #f5f5f5;
border-radius: 8px;
}
.a100 .card-icon {
background-color: #e8f4fd;
.icon-book {
font-size: 36px;
color: #3498db;
}
.v100 .card-icon {
background-color: #e8f6f3;
color: #1abc9c;
.icon-book::before {
content: "📁";
}
.foshan .card-icon {
background-color: #fef9e7;
color: #f39c12;
}
.beijing .card-icon {
background-color: #fdedec;
color: #e74c3c;
}
.card-title {
font-size: 1.2rem;
font-weight: 600;
}
.card-description {
color: #7f8c8d;
font-size: 0.9rem;
margin-bottom: 15px;
}
.card-stats {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.stat {
/* 操作区域 */
.action-section {
margin: 40px 0;
text-align: center;
}
.stat-value {
font-weight: 700;
font-size: 1.1rem;
}
.stat-label {
font-size: 0.8rem;
color: #7f8c8d;
}
.init-section {
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.init-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
}
.init-form {
grid-column: 1;
}
.init-help {
grid-column: 2;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
}
.form-group input[type="text"],
.form-group input[type="number"] {
width: 100%;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-group input:focus {
outline: none;
border-color: #3498db;
}
.form-group input.error {
border-color: #e74c3c;
}
.error-message {
color: #e74c3c;
font-size: 12px;
margin-top: 5px;
display: block;
}
.size-input {
display: flex;
gap: 10px;
}
.size-input input {
flex: 1;
}
.size-input select {
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.radio-option {
display: flex;
align-items: center;
cursor: pointer;
}
.radio-option input {
margin-right: 8px;
}
.checkbox-option {
display: flex;
align-items: center;
cursor: pointer;
}
.checkbox-option input {
margin-right: 8px;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 30px;
}
.btn {
padding: 10px 20px;
border-radius: 6px;
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-primary {
background-color: #3498db;
color: white;
border: none;
padding: 14px 32px;
border-radius: 6px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.btn-primary:hover:not(:disabled) {
.btn-primary:hover {
background-color: #2980b9;
}
.btn-outline {
background-color: transparent;
border: 1px solid #3498db;
color: #3498db;
.action-hint {
margin-top: 12px;
font-size: 0.9rem;
color: #666;
}
.btn-outline:hover {
background-color: #e8f4fd;
/* 帮助区域 */
.help-section {
margin-top: 50px;
}
.init-success {
grid-column: 1 / -1;
text-align: center;
padding: 40px 20px;
}
.success-icon {
font-size: 4rem;
color: #27ae60;
margin-bottom: 20px;
}
.success-icon i {
animation: scaleIn 0.5s ease;
}
@keyframes scaleIn {
0% { transform: scale(0); }
70% { transform: scale(1.2); }
100% { transform: scale(1); }
}
.storage-info {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin: 30px 0;
text-align: left;
max-width: 400px;
margin-left: auto;
margin-right: auto;
}
.info-item {
.help-links {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #e9ecef;
}
.info-item:last-child {
margin-bottom: 0;
border-bottom: none;
}
.info-label {
font-weight: 600;
color: #495057;
}
.info-value {
color: #6c757d;
}
.success-actions {
display: flex;
gap: 10px;
gap: 30px;
justify-content: center;
margin-top: 30px;
}
.selected-info {
margin-top: 20px;
padding: 15px;
background-color: #e8f4fd;
border-radius: 8px;
border-left: 4px solid #3498db;
.link {
text-decoration: none;
color: #3498db;
font-size: 14px;
transition: color 0.2s;
}
.help-actions {
margin-top: 20px;
}
.init-help h3 {
font-size: 1.4rem;
margin-bottom: 8px;
color: #2c3e50;
}
.init-help p {
color: #7f8c8d;
margin-bottom: 15px;
.link:hover {
color: #2980b9;
text-decoration: underline;
}
/* 响应式设计 */
@media (max-width: 768px) {
.storage-grid {
grid-template-columns: 1fr;
.container {
padding: 30px 20px;
}
.init-content {
grid-template-columns: 1fr;
gap: 20px;
}
.init-form,
.init-help {
grid-column: 1;
}
.success-actions {
.region-tabs {
flex-direction: column;
}
.form-actions {
.region-tab {
width: 100%;
max-width: 100%;
}
.help-links {
flex-direction: column;
gap: 15px;
}
.center-section {
justify-content: center;
}
}
</style>