yangg 3 өдөр өмнө
parent
commit
d076d8893b

+ 30 - 0
src/api/system/loginBackground.ts

@@ -0,0 +1,30 @@
+import request from '/@/utils/request';
+
+/**
+ * 背景图片配置API
+ */
+export function useLoginBackgroundApi() {
+  return {
+    /**
+     * 获取背景图片配置
+     */
+    getConfig: () => {
+      return request({
+        url: '/api/system/login-background/config/',
+        method: 'get',
+      });
+    },
+
+    /**
+     * 设置背景图片配置
+     * @param data 配置数据
+     */
+    setConfig: (data: any) => {
+      return request({
+        url: '/api/system/login-background/config/',
+        method: 'post',
+        data,
+      });
+    },
+  };
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 63 - 0
src/api/system/loginBackgroundConnectivityTest.ts


+ 112 - 0
src/api/system/loginBackgroundTest.ts

@@ -0,0 +1,112 @@
+/**
+ * 背景图片配置API测试
+ * 
+ * 使用说明:
+ * 1. 确保后端API接口已经部署
+ * 2. 在浏览器控制台中运行测试函数
+ * 3. 检查API响应是否符合预期格式
+ */
+
+import { useLoginBackgroundApi } from './loginBackground';
+
+export class LoginBackgroundApiTest {
+  private api = useLoginBackgroundApi();
+
+  /**
+   * 测试获取配置接口
+   */
+  async testGetConfig() {
+    console.log('🔍 测试获取背景图片配置...');
+    
+    try {
+      const response = await this.api.getConfig();
+      console.log('✅ 获取配置成功:', response);
+      
+      // 验证响应格式
+      if (response.code === 200 || response.code === 2000) {
+        console.log('📋 配置数据:', response.data);
+      } else if (response.data) {
+        console.log('📋 配置数据:', response.data);
+      } else {
+        console.log('📋 配置数据:', response);
+      }
+      
+      return response;
+    } catch (error) {
+      console.error('❌ 获取配置失败:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 测试设置配置接口
+   */
+  async testSetConfig() {
+    console.log('💾 测试设置背景图片配置...');
+    
+    const testData = {
+      backgroundImage: 'https://example.com/test-background.jpg',
+      description: '测试背景图片',
+      enabled: true
+    };
+    
+    try {
+      const response = await this.api.setConfig(testData);
+      console.log('✅ 设置配置成功:', response);
+      
+      // 验证保存结果
+      if (response.code === 200 || response.code === 2000 || response.success) {
+        console.log('🎉 配置保存成功');
+      } else {
+        console.warn('⚠️ 配置保存可能失败:', response.message);
+      }
+      
+      return response;
+    } catch (error) {
+      console.error('❌ 设置配置失败:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 完整测试流程
+   */
+  async runFullTest() {
+    console.log('🚀 开始完整API测试...');
+    
+    try {
+      // 1. 先获取当前配置
+      console.log('\n--- 步骤1: 获取当前配置 ---');
+      const currentConfig = await this.testGetConfig();
+      
+      // 2. 设置新配置
+      console.log('\n--- 步骤2: 设置新配置 ---');
+      await this.testSetConfig();
+      
+      // 3. 再次获取配置验证
+      console.log('\n--- 步骤3: 验证配置是否已更新 ---');
+      await this.testGetConfig();
+      
+      console.log('\n🎉 所有测试完成!');
+      
+    } catch (error) {
+      console.error('\n💥 测试过程中出现错误:', error);
+    }
+  }
+}
+
+// 导出测试实例以便在控制台中使用
+export const backgroundApiTest = new LoginBackgroundApiTest();
+
+// 使用示例(在浏览器控制台中):
+// 
+// import { backgroundApiTest } from '@/api/system/loginBackgroundTest';
+// 
+// // 测试获取配置
+// backgroundApiTest.testGetConfig();
+//
+// // 测试设置配置  
+// backgroundApiTest.testSetConfig();
+//
+// // 运行完整测试
+// backgroundApiTest.runFullTest();

+ 180 - 0
src/views/system/Clientback/README.md

@@ -0,0 +1,180 @@
+# 客户端背景图片配置功能
+
+## 功能概述
+
+该功能允许管理员通过Web界面配置系统登录页面的背景图片,支持图片上传、网络图片导入、配置历史查看等功能。
+
+## 文件结构
+
+```
+src/
+├── api/system/
+│   ├── loginBackground.ts          # API接口定义
+│   └── loginBackgroundTest.ts      # API测试文件
+└── views/system/Clientback/
+    └── index.vue                   # 主配置页面
+```
+
+## 功能特性
+
+### 1. 图片回显与上传
+- **当前配置回显**: 页面加载时自动获取并显示当前的背景图片配置
+- **图片选择器**: 使用项目现有的`fileSelector`组件,支持:
+  - 本地文件上传
+  - 网络图片导入
+  - 图片预览功能
+
+### 2. 配置管理
+- **图片描述**: 支持为背景图片添加描述信息(可选)
+- **启用状态**: 可以启用或禁用背景图片显示
+- **实时预览**: 选择图片后可立即查看预览效果
+
+### 3. 配置历史
+- **历史记录**: 显示之前的配置变更历史
+- **时间轴展示**: 使用Element Plus的时间轴组件展示历史
+- **缩略图预览**: 历史记录中包含图片缩略图
+
+## API接口说明
+
+### 获取配置接口
+```
+GET /api/system/login-background/config/
+```
+
+**响应格式(支持多种格式兼容):**
+```json
+{
+  "code": 200,
+  "data": {
+    "current": {
+      "backgroundImage": "图片URL",
+      "description": "图片描述",
+      "enabled": true
+    },
+    "history": [
+      {
+        "backgroundImage": "历史图片URL",
+        "description": "历史描述",
+        "enabled": true,
+        "updateTime": "2024-01-01 12:00:00"
+      }
+    ]
+  }
+}
+```
+
+### 设置配置接口
+```
+POST /api/system/login-background/config/
+```
+
+**请求参数:**
+```json
+{
+  "backgroundImage": "图片URL",
+  "description": "图片描述",
+  "enabled": true
+}
+```
+
+## 使用方法
+
+### 1. 页面访问
+访问 `/system/Clientback` 路由即可进入配置页面。
+
+### 2. 配置步骤
+1. **选择图片**: 点击文件选择器选择或上传背景图片
+2. **添加描述**: (可选)为图片添加描述信息
+3. **设置状态**: 选择是否启用该背景图片
+4. **保存配置**: 点击"保存配置"按钮提交设置
+
+### 3. 图片上传方式
+- **本地上传**: 从本地计算机选择图片文件上传
+- **网络导入**: 输入网络图片URL直接导入
+
+## 技术实现
+
+### 1. 技术栈
+- **前端**: Vue 3 + TypeScript + Element Plus
+- **图片处理**: 使用项目现有的fileSelector组件
+- **API请求**: 基于项目的request工具
+
+### 2. 响应格式兼容
+根据项目规范,代码实现了多种API响应格式的兼容处理:
+```typescript
+// 兼容多种响应格式
+if (response.code === 200 || response.code === 2000) {
+  data = response.data;
+} else if (response.data) {
+  data = response.data;
+} else if (response.results) {
+  data = response.results;
+} else {
+  data = response;
+}
+```
+
+### 3. 本地存储
+按照用户偏好,实现了简单的JSON存储方案,无需加密处理。
+
+## 样式设计
+
+### 1. 响应式布局
+- 最大宽度1200px,居中显示
+- 适配不同屏幕尺寸
+
+### 2. 视觉设计
+- **卡片布局**: 使用Element Plus卡片组件
+- **预览区域**: 300x200像素的图片预览
+- **历史时间轴**: 清晰展示配置变更历史
+
+### 3. 交互体验
+- **加载状态**: 保存时显示loading状态
+- **确认对话框**: 保存前进行二次确认
+- **消息提示**: 操作结果的友好提示
+
+## 错误处理
+
+### 1. 网络错误
+- 自动重试机制
+- 友好的错误提示
+- 详细的控制台日志
+
+### 2. 数据验证
+- 图片URL格式验证
+- 必填字段检查
+- 文件大小限制
+
+## 测试说明
+
+### 1. API测试
+使用提供的测试文件进行API接口测试:
+```javascript
+import { backgroundApiTest } from '@/api/system/loginBackgroundTest';
+
+// 运行完整测试
+backgroundApiTest.runFullTest();
+```
+
+### 2. 功能测试
+- 图片上传功能测试
+- 配置保存与回显测试
+- 历史记录展示测试
+- 响应式布局测试
+
+## 注意事项
+
+1. **图片格式**: 建议使用常见的图片格式(jpg, png, gif, webp)
+2. **图片大小**: 建议控制在2MB以内以确保加载速度
+3. **网络图片**: 确保网络图片URL可正常访问
+4. **权限控制**: 该功能需要管理员权限
+5. **浏览器兼容**: 支持现代浏览器,不支持IE11及以下版本
+
+## 后续扩展
+
+### 可能的功能扩展
+1. **多图片轮播**: 支持设置多张背景图片轮播
+2. **定时切换**: 按时间自动切换背景图片
+3. **主题适配**: 根据系统主题自动选择合适的背景
+4. **移动端适配**: 为移动设备提供专门的背景图片
+5. **图片编辑**: 集成图片裁剪、滤镜等编辑功能

+ 534 - 0
src/views/system/Clientback/index.vue

@@ -0,0 +1,534 @@
+<template>
+  <div class="background-config-container">
+    <el-card class="config-card">
+      <template #header>
+        <div class="card-header">
+          <span class="title">登录背景图片配置</span>
+          <el-button type="primary" @click="handleSave" :loading="saveLoading">
+            保存配置
+          </el-button>
+        </div>
+      </template>
+
+      <el-form :model="configForm" label-width="120px" class="config-form">
+        <el-form-item label="当前背景图片">
+          <div class="image-preview-container">
+            <div class="image-preview">
+             <!--   {{VITE_API_URL+configForm.backgroundImage}} -->
+              <el-image
+                v-if="configForm.backgroundImage"
+                :src="configForm.backgroundImage"
+                fit="contain"
+                class="preview-image"
+              >
+                <template #error>
+                  <div class="image-error">
+                    <el-icon><Picture /></el-icon>
+                    <span>加载失败</span>
+                  </div>
+                </template>
+              </el-image>
+              <div v-else class="no-image">
+                <el-icon class="no-image-icon"><Picture /></el-icon>
+                <span>未设置背景图片</span>
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+<!-- /api/system/device/upload-image/ handleCustomUpload-->
+        <el-form-item label="选择图片">
+          <el-upload
+            class="avatar-uploader"
+            :action="VITE_API_URL+'/api/system/upload/'"
+            :headers="{ Authorization: 'JWT '+ Cookies.get('token') }"
+            :show-file-list="false"
+            :on-success="handleAvatarSuccess"
+            :before-upload="beforeAvatarUpload">
+            <img v-if="imageUrl" :src="imageUrl" class="avatar">
+            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 操作历史 -->
+    <el-card class="history-card" v-if="configHistory.length > 0">
+      <template #header>
+        <span class="title">配置历史</span>
+      </template>
+      <el-timeline>
+        <el-timeline-item
+          v-for="(item, index) in configHistory"
+          :key="index"
+          :timestamp="item.updateTime"
+          placement="top"
+        >
+          <el-card class="history-item">
+            <div class="history-content">
+              <div class="history-image">
+                <el-image
+                  v-if="item.backgroundImage"
+                  :src="`${item.backgroundImage}`"
+                  fit="cover"
+                  class="history-thumbnail"
+                >
+                  <template #error>
+                    <div class="thumbnail-error">
+                      <el-icon><Picture /></el-icon>
+                    </div>
+                  </template>
+                </el-image>
+                <div v-else class="no-thumbnail">
+                  <el-icon><Picture /></el-icon>
+                </div>
+              </div>
+              <div class="history-info">
+                <p class="history-desc">{{ item.description || '无描述' }}</p>
+                <p class="history-status">
+                  状态:
+                  <el-tag :type="item.enabled ? 'success' : 'info'" size="small">
+                    {{ item.enabled ? '启用' : '禁用' }}
+                  </el-tag>
+                </p>
+              </div>
+            </div>
+          </el-card>
+        </el-timeline-item>
+      </el-timeline>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { Picture } from '@element-plus/icons-vue';
+import { useLoginBackgroundApi } from '/@/api/system/loginBackground';
+import Cookies from 'js-cookie';
+import { request } from '/@/utils/service';
+import { Plus } from '@element-plus/icons-vue';
+
+// 类型定义
+interface ConfigForm {
+  backgroundImage: string;
+  description: string;
+  enabled: boolean;
+}
+
+interface HistoryItem {
+  backgroundImage: string;
+  description: string;
+  enabled: boolean;
+  updateTime: string;
+}
+
+const fileList = ref<any[]>([]);
+const imageUrl = ref('');
+const VITE_API_URL=ref(import.meta.env.VITE_API_URL);
+
+const handleCustomUpload = async ({ file, onProgress, onSuccess, onError }: any) => {
+  const token = Cookies.get('token'); 
+  const formData = new FormData();
+  formData.append('file', file);
+  console.log("token::::",token);
+
+  try {
+    const res = await request({
+		url: `/api/system/upload/`,
+		method: 'post',
+		headers: {
+			'Content-Type': 'multipart/form-data',
+			Authorization: 'JWT '+ token,
+		},
+		data: formData,
+		timeout: 60000,
+		onUploadProgress: (p: any) => {
+                            onProgress({percent: Math.round((p.loaded / p.total) * 100)});
+		}
+    });
+
+    // 根据API响应格式兼容性规范处理响应数据
+    let uploadData = null;
+    if (res.code === 200 || res.code === 2000) {
+      uploadData = res.data;
+    } else if (res.data) {
+      uploadData = res.data;
+    } else if (res.results) {
+      uploadData = res.results;
+    } else {
+      uploadData = res;
+    }
+
+	// 构造 Element Plus 需要的 file 对象
+	const fileObj = {
+      name: uploadData.name || file.name,
+      url: uploadData.url || uploadData,
+      status: 'success',
+      response: uploadData
+    };
+
+    // 更新配置表单的背景图片
+	configForm.backgroundImage = fileObj.url;
+    // 更新头像显示URL
+    imageUrl.value = fileObj.url;
+    // 更新文件列表用于回显
+    fileList.value = [fileObj];
+
+    ElMessage.success('上传成功');
+    onSuccess(fileObj); 
+  } catch (error) {
+    console.error('上传失败:', error);
+    ElMessage.error('上传失败');
+    onError(error);
+  }
+};
+
+// 头像上传成功处理
+const handleAvatarSuccess = (response: any, file: any) => {
+  // 根据API响应格式兼容性规范处理响应数据
+  let uploadData = null;
+  if (response.code === 200 || response.code === 2000) {
+    uploadData = response.data;
+  } else if (response.data) {
+    uploadData = response.data;
+  } else if (response.results) {
+    uploadData = response.results;
+  } else {
+    uploadData = response;
+  }
+
+  // 更新配置表单的背景图片和显示的图片URL
+  configForm.backgroundImage = uploadData.url || uploadData;
+  imageUrl.value = uploadData.url || uploadData;
+  
+  ElMessage.success('上传成功');
+};
+
+// 上传前验证
+const beforeAvatarUpload = (file: any) => {
+  const isImage = file.type.startsWith('image/');
+  const isLt5M = file.size / 1024 / 1024 < 5;
+
+  if (!isImage) {
+    ElMessage.error('只能上传图片格式的文件!');
+    return false;
+  }
+  if (!isLt5M) {
+    ElMessage.error('图片大小不能超过 5MB!');
+    return false;
+  }
+  return true;
+};
+
+// 处理文件移除
+const handleFileRemove = (file: any) => {
+  // 清空配置表单中的背景图片
+  configForm.backgroundImage = '';
+  // 清空文件列表
+  fileList.value = [];
+  ElMessage.info('已移除图片');
+};
+
+// API实例
+const loginBackgroundApi = useLoginBackgroundApi();
+
+// 响应式数据
+const saveLoading = ref(false);
+const configForm = reactive<ConfigForm>({
+  backgroundImage: '',
+  description: '',
+  enabled: true,
+});
+const configHistory = ref<HistoryItem[]>([]);
+
+// 加载配置数据
+const loadConfig = async () => {
+  try {
+    const response = await loginBackgroundApi.getConfig();
+    
+    // 根据API响应格式兼容性规范处理响应数据
+    let data = null;
+    if (response.code === 200 || response.code === 2000) {
+      data = response.data;
+      console.log(data);
+    } else if (response.data) {
+      data = response.data;
+    } else if (response.results) {
+      data = response.results;
+    } else {
+      data = response;
+    }
+    
+    if (data) {
+      // 回显当前配置
+        configForm.backgroundImage = data.image_url || '';
+        configForm.description = data.description || '';
+        // 同步文件列表回显
+        updateFileListFromConfig();
+      
+      // 加载历史记录
+      if (data.history && Array.isArray(data.history)) {
+        configHistory.value = data.history;
+      }
+    }
+  } catch (error) {
+    console.error('加载配置失败:', error);
+    ElMessage.error('加载配置失败,请稍后重试');
+  }
+};
+
+// 根据配置更新文件列表回显
+const updateFileListFromConfig = () => {
+  if (configForm.backgroundImage) {
+    // 从URL中提取文件名
+    const urlParts = configForm.backgroundImage.split('/');
+    const fileName = urlParts[urlParts.length - 1] || 'background-image';
+    
+    const fileObj = {
+      name: fileName,
+      url: configForm.backgroundImage,
+      status: 'success',
+      response: { url: configForm.backgroundImage }
+    };
+    
+    fileList.value = [fileObj];
+    // 更新头像显示URL
+    imageUrl.value = configForm.backgroundImage;
+  } else {
+    fileList.value = [];
+    imageUrl.value = '';
+  }
+};
+
+// 保存配置
+const handleSave = async () => {
+  if (!configForm.backgroundImage) {
+    ElMessage.warning('请选择背景图片');
+    return;
+  }
+  
+  try {
+    await ElMessageBox.confirm(
+      '确定要保存当前配置吗?',
+      '确认保存',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }
+    );
+    
+    saveLoading.value = true;
+    
+    const configData = {
+      image_url: configForm.backgroundImage,
+      description: configForm.description,
+    };
+    
+    const response = await loginBackgroundApi.setConfig(configData);
+    
+    // 检查保存结果
+    if (response.code === 200 || response.code === 2000 || response.success) {
+      ElMessage.success('配置保存成功');
+      // 重新加载配置以更新历史记录
+      await loadConfig();
+      // 更新文件列表回显
+      updateFileListFromConfig();
+    } else {
+      ElMessage.error(response.message || '保存失败');
+    }
+  } catch (error: any) {
+    if (error !== 'cancel') {
+      console.error('保存配置失败:', error);
+      ElMessage.error(error.message || '保存配置失败,请稍后重试');
+    }
+  } finally {
+    saveLoading.value = false;
+  }
+};
+
+// 组件挂载时加载配置
+onMounted(() => {
+  loadConfig();
+});
+</script>
+
+<style lang="scss" scoped>
+.background-config-container {
+  padding: 20px;
+  max-width: 1200px;
+  margin: 0 auto;
+  
+  .config-card {
+    margin-bottom: 20px;
+    
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      
+      .title {
+        font-size: 18px;
+        font-weight: 600;
+        color: #303133;
+      }
+    }
+    
+    .config-form {
+      .image-preview-container {
+        .image-preview {
+          width: 300px;
+          height: 200px;
+          border: 2px dashed #d9d9d9;
+          border-radius: 8px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background-color: #fafafa;
+          
+          .preview-image {
+            width: 100%;
+            height: 100%;
+            border-radius: 6px;
+          }
+          
+          .image-error,
+          .no-image {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            color: #999;
+            
+            .el-icon {
+              font-size: 48px;
+              margin-bottom: 10px;
+            }
+            
+            span {
+              font-size: 14px;
+            }
+          }
+          
+          .no-image-icon {
+            font-size: 48px;
+            color: #c0c4cc;
+            margin-bottom: 10px;
+          }
+        }
+      }
+    }
+  }
+  
+  .history-card {
+    .title {
+      font-size: 16px;
+      font-weight: 600;
+      color: #303133;
+    }
+    
+    .history-item {
+      .history-content {
+        display: flex;
+        gap: 15px;
+        
+        .history-image {
+          .history-thumbnail {
+            width: 80px;
+            height: 60px;
+            border-radius: 4px;
+          }
+          
+          .thumbnail-error,
+          .no-thumbnail {
+            width: 80px;
+            height: 60px;
+            border: 1px dashed #d9d9d9;
+            border-radius: 4px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            background-color: #fafafa;
+            color: #c0c4cc;
+            
+            .el-icon {
+              font-size: 24px;
+            }
+          }
+        }
+        
+        .history-info {
+          flex: 1;
+          
+          .history-desc {
+            margin: 0 0 8px 0;
+            color: #606266;
+            font-size: 14px;
+            line-height: 1.4;
+          }
+          
+          .history-status {
+            margin: 0;
+            color: #909399;
+            font-size: 12px;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+          }
+        }
+      }
+    }
+  }
+}
+
+:deep(.el-form-item__label) {
+  font-weight: 500;
+  color: #606266;
+}
+
+:deep(.el-card__header) {
+  background-color: #f8f9fa;
+  border-bottom: 1px solid #ebeef5;
+}
+
+/* 头像上传组件样式 */
+.avatar-uploader {
+  :deep(.el-upload) {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+    transition: .3s;
+
+    &:hover {
+      border-color: #409eff;
+    }
+  }
+}
+
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+  object-fit: cover;
+}
+
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+  background-color: #fbfdff;
+  border: 1px dashed #c0ccda;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: .3s;
+
+  &:hover {
+    border-color: #409eff;
+    color: #409eff;
+  }
+}
+</style>

+ 293 - 0
src/views/system/Clientback/test.vue

@@ -0,0 +1,293 @@
+<template>
+  <div class="test-container">
+    <h2>FileSelector组件集成测试</h2>
+    
+    <el-card class="test-card">
+      <template #header>
+        <span>基础功能测试</span>
+      </template>
+      
+      <el-form label-width="120px">
+        <el-form-item label="选择的图片">
+          <div class="result-display">
+            <strong>当前值: </strong>
+            <span v-if="testImage">{{ testImage }}</span>
+            <span v-else class="no-value">未选择</span>
+          </div>
+        </el-form-item>
+        
+        <el-form-item label="图片选择器">
+          <file-selector
+            v-model="testImage"
+            input-type="image"
+            :input-size="120"
+            :show-upload-button="true"
+            :show-net-button="true"
+            :tabs-show="1000"
+            value-key="url"
+            :multiple="false"
+            :selectable="true"
+          />
+        </el-form-item>
+        
+        <el-form-item label="图片预览">
+          <div class="image-preview-test">
+            <el-image
+              v-if="testImage"
+              :src="testImage"
+              fit="contain"
+              style="width: 200px; height: 150px; border: 1px solid #ddd;"
+            >
+              <template #error>
+                <div class="image-error">
+                  <el-icon><Picture /></el-icon>
+                  <span>加载失败</span>
+                </div>
+              </template>
+            </el-image>
+            <div v-else class="no-image-placeholder">
+              <el-icon><Picture /></el-icon>
+              <span>请选择图片</span>
+            </div>
+          </div>
+        </el-form-item>
+        
+        <el-form-item>
+          <el-button type="primary" @click="testSave">测试保存</el-button>
+          <el-button @click="testClear">清除选择</el-button>
+          <el-button @click="testRandomImage">测试网络图片</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    
+    <el-card class="test-log">
+      <template #header>
+        <span>测试日志</span>
+        <el-button size="small" @click="clearLog">清除日志</el-button>
+      </template>
+      
+      <div class="log-content">
+        <div v-for="(log, index) in testLogs" :key="index" class="log-item">
+          <span class="log-time">{{ log.time }}</span>
+          <span :class="['log-level', log.level]">{{ log.level.toUpperCase() }}</span>
+          <span class="log-message">{{ log.message }}</span>
+        </div>
+        <div v-if="testLogs.length === 0" class="no-logs">
+          暂无测试日志
+        </div>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { ElMessage } from 'element-plus';
+import { Picture } from '@element-plus/icons-vue';
+import FileSelector from '/@/components/fileSelector/index.vue';
+
+// 测试数据
+const testImage = ref<string>('');
+const testLogs = ref<Array<{time: string, level: string, message: string}>>([]);
+
+// 添加日志
+const addLog = (level: string, message: string) => {
+  const now = new Date();
+  const time = now.toLocaleTimeString();
+  testLogs.value.unshift({
+    time,
+    level,
+    message
+  });
+  
+  // 限制日志数量
+  if (testLogs.value.length > 50) {
+    testLogs.value = testLogs.value.slice(0, 50);
+  }
+};
+
+// 监听图片选择变化
+watch(testImage, (newValue, oldValue) => {
+  if (newValue !== oldValue) {
+    addLog('info', `图片选择变化: ${oldValue || '无'} -> ${newValue || '无'}`);
+  }
+});
+
+// 测试保存
+const testSave = () => {
+  if (!testImage.value) {
+    ElMessage.warning('请先选择一张图片');
+    addLog('warn', '保存测试失败: 未选择图片');
+    return;
+  }
+  
+  addLog('success', `保存测试成功: ${testImage.value}`);
+  ElMessage.success('测试保存成功!');
+};
+
+// 清除选择
+const testClear = () => {
+  const oldValue = testImage.value;
+  testImage.value = '';
+  addLog('info', `清除选择: ${oldValue} -> 空值`);
+  ElMessage.info('已清除选择');
+};
+
+// 测试网络图片
+const testRandomImage = () => {
+  const testUrls = [
+    'https://picsum.photos/400/300?random=1',
+    'https://picsum.photos/400/300?random=2',
+    'https://picsum.photos/400/300?random=3',
+  ];
+  
+  const randomUrl = testUrls[Math.floor(Math.random() * testUrls.length)];
+  testImage.value = randomUrl;
+  addLog('info', `设置测试网络图片: ${randomUrl}`);
+  ElMessage.success('已设置测试网络图片');
+};
+
+// 清除日志
+const clearLog = () => {
+  testLogs.value = [];
+  ElMessage.info('已清除测试日志');
+};
+
+// 初始化日志
+addLog('info', 'FileSelector组件测试页面已加载');
+</script>
+
+<style lang="scss" scoped>
+.test-container {
+  padding: 20px;
+  max-width: 800px;
+  margin: 0 auto;
+  
+  h2 {
+    text-align: center;
+    color: #303133;
+    margin-bottom: 20px;
+  }
+  
+  .test-card {
+    margin-bottom: 20px;
+    
+    .result-display {
+      padding: 10px;
+      background-color: #f5f7fa;
+      border-radius: 4px;
+      border: 1px solid #e4e7ed;
+      
+      .no-value {
+        color: #909399;
+        font-style: italic;
+      }
+    }
+    
+    .image-preview-test {
+      .no-image-placeholder {
+        width: 200px;
+        height: 150px;
+        border: 1px dashed #ddd;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        color: #999;
+        background-color: #fafafa;
+        
+        .el-icon {
+          font-size: 32px;
+          margin-bottom: 8px;
+        }
+      }
+      
+      .image-error {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        color: #999;
+        
+        .el-icon {
+          font-size: 24px;
+          margin-bottom: 4px;
+        }
+      }
+    }
+  }
+  
+  .test-log {
+    .el-card__header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+    
+    .log-content {
+      max-height: 300px;
+      overflow-y: auto;
+      
+      .log-item {
+        display: flex;
+        align-items: center;
+        padding: 8px 0;
+        border-bottom: 1px solid #f0f0f0;
+        font-family: 'Courier New', monospace;
+        font-size: 12px;
+        
+        &:last-child {
+          border-bottom: none;
+        }
+        
+        .log-time {
+          color: #909399;
+          margin-right: 12px;
+          min-width: 80px;
+        }
+        
+        .log-level {
+          margin-right: 12px;
+          padding: 2px 6px;
+          border-radius: 3px;
+          font-weight: bold;
+          min-width: 60px;
+          text-align: center;
+          
+          &.info {
+            background-color: #e1f3fe;
+            color: #1890ff;
+          }
+          
+          &.success {
+            background-color: #f6ffed;
+            color: #52c41a;
+          }
+          
+          &.warn {
+            background-color: #fff7e6;
+            color: #fa8c16;
+          }
+          
+          &.error {
+            background-color: #fff2f0;
+            color: #ff4d4f;
+          }
+        }
+        
+        .log-message {
+          flex: 1;
+          color: #303133;
+        }
+      }
+      
+      .no-logs {
+        text-align: center;
+        color: #909399;
+        padding: 20px;
+        font-style: italic;
+      }
+    }
+  }
+}
+</style>

+ 4 - 4
src/views/system/login/index.vue

@@ -23,11 +23,11 @@
 					<div class="flex flex-col items-center justify-center p-8 pt-14">
 						<!-- Logo -->
 						<div class="logo-title">
-							<img src="../../../assets/rayLogo.png" alt="奇才AI面试" style="width: 70px; height: 70px;margin-right: 10px;">
+							<img src="../../../assets/rayLogo.png" alt="" style="width: 70px; height: 70px;margin-right: 10px;">
 							<span style="font-size: 20px;font-weight: 600;letter-spacing: 3px;color: #595959;">账号登录</span>
 						</div>
 
-						<!-- <div class="text-4xl font-bold text-red-600 mb-12">奇才AI面试</div> -->
+						<!-- <div class="text-4xl font-bold text-red-600 mb-12"></div> -->
 						
 						<!-- 登录表单区域 -->
 						<div class="w-full max-w-sm">
@@ -67,8 +67,8 @@
 			<!-- </p> -->
 		</div>
 	</div>
-	<div v-if="loginBg">
-		<img :src="loginBg" class="loginBg fixed inset-0 z-1 w-full h-full" />
+	<div v-if="siteBg">
+		<img :src="siteBg" class="loginBg fixed inset-0 z-1 w-full h-full" />
 	</div>
 </template>
 

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно