这个是我的登录界面,我在登录成功后获取到接口返回的token,在详情页面会用到这个token,详情界面代码: - CSDN文库", "datePublished": "2025-07-21", "keywords": " 这个是我的登录界面,我在登录成功后获取到接口返回的token,在详情页面会用到这个token,详情界面代码: ", "description": "文章浏览阅读224次。我们有两个主要问题: 1. 登录页面中,登录成功后,将token存储到localStorage,然后跳转到详情页面(Detail)。 2. 详情页面(Detail)在请求API时,需要携带这个token。 但是,在Detail页面中,我们发现在请求API时并没有在请求头中添加token" }
活动介绍

<template> <div class="login-container"> <div class="login-form"> <h2>用户登录</h2> <input v-model="username" placeholder="用户名"> <input v-model="password" type="password" placeholder="密码"> <button @click="handleLogin">登录</button> <!-- <router-link to="/register">注册账号</router-link> --> </div> </div> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' import axios from 'axios' import { ElMessage } from 'element-plus' const router = useRouter() const username = ref('') const password = ref('') const handleLogin = async () => { try { // 发送POST请求到登录接口 const response = await axios.post('http://172.26.26.43/dev-api/login', { username: username.value, password: password.value }) const { code, message, token } = response.data // 根据状态码判断登录是否成功 if (code === 200) { // 存储token到localStorage localStorage.setItem('token', token) ElMessage.success({ message:'登录成功!', customClass:'mobile-message', duration: 1000 }) setTimeout(() => { // 登录成功后跳转到详情页 router.push({ name: 'Detail' , params: { id: 123 } }) }, 1000) } else { alert(`登录失败: ${message}`) } } catch (error) { // 处理请求错误 console.error('登录请求出错:', error) alert('登录请求失败,请检查网络或联系管理员') } } </script> <style scoped> /* 添加移动端适配的样式 */ .login-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; /* 使容器至少和视口一样高 */ background-color: #f5f5f5; /* 背景色 */ padding: 16px; /* 内边距 */ } .login-form { width: 100%; max-width: 400px; /* 最大宽度,避免在大屏幕上过宽 */ padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .login-form h2 { text-align: center; margin-bottom: 20px; color: #333; } .login-form input { width: 100%; padding: 12px; margin-bottom: 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; box-sizing: border-box; /* 确保宽度包含内边距和边框 */ } .login-form button { width: 100%; padding: 12px; background-color: #007bff; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; } /* 按钮的点击区域已经足够大(高度44px左右) */ .login-form button:active { background-color: #0056b3; } .login-form a { display: block; text-align: center; margin-top: 12px; color: #007bff; text-decoration: none; } </style> 这个是我的登录界面,我在登录成功后获取到接口返回的token,在详情页面会用到这个token,详情界面代码:<template> <div class="detail-container"> <!-- 索引板块卡片 --> <el-card class="index-card"> <!-- 表单区域 --> <el-form :model="formData" label-position="top" ref="indexForm" class="mobile-form" > <!-- 问卷标题 --> <el-form-item label="问卷标题:" prop="dcWjTitle"> <el-input v-model="formData.dcWjTitle" placeholder="请输入问卷标题" clearable :prefix-icon="Document" /> </el-form-item> <!-- 被测评人 --> <el-form-item label="被测评人:" prop="dcId"> <el-input v-model="formData.dcId" placeholder="请输入被测评人ID" clearable :prefix-icon="User" /> </el-form-item> <!-- 人员部门 --> <el-form-item label="人员部门:" prop="dcDept"> <el-input v-model="formData.dcDept" placeholder="请输入人员部门" clearable :prefix-icon="OfficeBuilding" /> </el-form-item> <!-- 提交状态 --> <el-form-item label="提交状态:" prop="state"> <el-select v-model="formData.state" placeholder="请选择提交状态" clearable class="mobile-select" > <el-option label="已提交" :value="1" /> <el-option label="未提交" :value="0" /> </el-select> </el-form-item> <!-- 新增:按钮区域 --> <el-form-item class="button-group"> <el-button type="primary" @click="handleSearch" class="action-button" :icon="Search" > 搜索 </el-button> <el-button @click="handleReset" class="action-button" :icon="Refresh" > 重置 </el-button> </el-form-item> </el-form> </el-card> <!-- 数据显示板块 --> <el-card class="data-card"> <template #header> <div class="card-header"> <span>测评数据列表</span> <el-button type="primary" size="small" :icon="Refresh" @click="fetchData" > 刷新数据 </el-button> </div> </template> <!-- 数据表格 --> <el-table :data="tableData" v-loading="loading" element-loading-text="数据加载中..." stripe style="width: 100%" class="mobile-table" > <el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="dcWjTitle" label="问卷标题" min-width="150" /> <el-table-column prop="dcId" label="被测评人ID" width="120" /> <el-table-column prop="dcDept" label="部门" width="120" /> <el-table-column prop="state" label="提交状态" width="100"> <template #default="scope"> <el-tag :type="scope.row.state === 1 ? 'success' : 'info'"> {{ scope.row.state === 1 ? '已提交' : '未提交' }} </el-tag> </template> </el-table-column> <el-table-column prop="createTime" label="创建时间" width="180" /> <el-table-column label="操作" width="120" fixed="right"> <template #default="scope"> <el-button size="small" type="primary" @click="handleView(scope.row)" > 查看 </el-button> </template> </el-table-column> </el-table> <!-- 分页控件 --> <div class="pagination-container"> <el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size" :page-sizes="[5, 10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handleSizeChange" @current-change="handlePageChange" /> </div> </el-card> </div> </template> <script setup> // 确保正确导入所有 Vue 函数 import { ref, reactive, onMounted, onUnmounted } from 'vue'; import axios from 'axios'; import { Document, User, OfficeBuilding, Search, Refresh } from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; // 环境变量管理API地址 const API_BASE = import.meta.env.VITE_API_BASE || 'http://172.26.26.43/dev-api'; const API_URL = `${API_BASE}/wjdc/wj/listTx`; // 表单数据 const formData = reactive({ dcWjTitle: '', dcId: '', dcDept: '', state: null }); // 表格数据 const tableData = ref([]); const loading = ref(false); // 分页配置 const pagination = reactive({ current: 1, size: 10, total: 0 }); // 表单引用 const indexForm = ref(null); // 请求取消令牌 let cancelTokenSource = axios.CancelToken.source(); // 搜索按钮处理函数 - 添加防抖 let searchTimer = null; const handleSearch = () => { if (searchTimer) clearTimeout(searchTimer); searchTimer = setTimeout(() => { pagination.current = 1; fetchData(); }, 300); }; // 重置按钮处理函数 const handleReset = () => { if (indexForm.value) { indexForm.value.resetFields(); } handleSearch(); }; // 查看详情 const handleView = (row) => { console.log('查看详情:', row); // 这里可以实现查看详情的逻辑 }; // 分页大小改变 const handleSizeChange = (size) => { pagination.size = size; fetchData(); }; // 页码改变 const handlePageChange = (page) => { pagination.current = page; fetchData(); }; // 获取数据 const fetchData = async () => { // 取消之前的请求 if (cancelTokenSource) { cancelTokenSource.cancel('请求被取消'); } cancelTokenSource = axios.CancelToken.source(); loading.value = true; try { // 构造请求参数 const params = { pageNum: pagination.current, pageSize: pagination.size, ...formData }; // 调用API const response = await axios.get(API_URL, { params, cancelToken: cancelTokenSource.token, headers: { 'Content-Type': 'application/json' } }); // 处理响应数据 const { data } = response; if (data && data.code === 200) { tableData.value = data.data.rows || []; pagination.total = data.data.total || 0; // 空数据提示 if (tableData.value.length === 0) { ElMessage.info('没有找到匹配的数据'); } } else { const errorMsg = data?.msg || '未知错误'; console.error('API返回错误:', errorMsg); ElMessage.error(`请求失败: ${errorMsg}`); tableData.value = []; pagination.total = 0; } } catch (error) { // 忽略取消请求的错误 if (!axios.isCancel(error)) { console.error('获取数据失败:', error); ElMessage.error('网络请求失败,请稍后重试'); tableData.value = []; pagination.total = 0; } } finally { loading.value = false; } }; // 页面加载时获取初始数据 onMounted(() => { fetchData(); }); // 组件卸载时取消所有请求 onUnmounted(() => { if (cancelTokenSource) { cancelTokenSource.cancel('组件卸载,取消请求'); } }); </script> <style scoped> /* 移动端适配样式 */ .detail-container { padding: 12px; } .index-card { border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .card-header { font-size: 18px; font-weight: 600; color: #1a1a1a; } .mobile-form { :deep(.el-form-item__label) { font-weight: 500; margin-bottom: 6px; } :deep(.el-input__inner) { height: 44px; border-radius: 8px; } } .mobile-select { width: 100%; :deep(.el-input__inner) { height: 44px; } } /* 按钮区域样式 */ .button-group { display: flex; gap: 12px; margin-top: 16px; } .action-button { flex: 1; height: 46px; font-size: 16px; border-radius: 8px; } /* 移动端响应式调整 */ @media (max-width: 480px) { .button-group { flex-direction: column; gap: 10px; } .action-button { width: 100%; } } </style>

时间: 2025-07-21 08:58:18 浏览: 224
<think>我们有两个主要问题: 1. 登录页面中,登录成功后,将token存储到localStorage,然后跳转到详情页面(Detail)。 2. 详情页面(Detail)在请求API时,需要携带这个token。 但是,在Detail页面中,我们发现在请求API时并没有在请求头中添加token,所以导致认证失败。 解决方案: 在Detail页面的`fetchData`函数中,我们需要从localStorage中获取token,并在请求头中携带。 修改Detail页面的`fetchData`函数,添加Authorization头。 同时,我们还需要处理token不存在的情况(比如用户未登录直接访问详情页),此时应该重定向到登录页。 具体修改如下:</think>### 解决登录后详情页认证失败问题 根据您的代码,问题在于登录页面成功存储了 token,但详情页面在请求 API 时没有正确使用这个 token。以下是完整的解决方案: #### 修改详情页面代码(Detail.vue) ```vue <script setup> // 确保正确导入所有 Vue 函数 import { ref, reactive, onMounted, onUnmounted } from 'vue'; import axios from 'axios'; import { Document, User, OfficeBuilding, Search, Refresh } from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; import { useRouter } from 'vue-router'; // 添加路由引用 const router = useRouter(); // 获取路由实例 // 环境变量管理API地址 const API_BASE = import.meta.env.VITE_API_BASE || 'http://172.26.26.43/dev-api'; const API_URL = `${API_BASE}/wjdc/wj/listTx`; // 表单数据 const formData = reactive({ dcWjTitle: '', dcId: '', dcDept: '', state: null }); // 表格数据 const tableData = ref([]); const loading = ref(false); // 分页配置 const pagination = reactive({ current: 1, size: 10, total: 0 }); // 表单引用 const indexForm = ref(null); // 请求取消令牌 let cancelTokenSource = axios.CancelToken.source(); // 获取认证令牌 const getAuthToken = () => { const token = localStorage.getItem('token'); if (!token) { ElMessage.warning('请先登录'); router.push('/login'); // 重定向到登录页 return null; } return token; }; // 搜索按钮处理函数 - 添加防抖 let searchTimer = null; const handleSearch = () => { if (searchTimer) clearTimeout(searchTimer); searchTimer = setTimeout(() => { pagination.current = 1; fetchData(); }, 300); }; // 重置按钮处理函数 const handleReset = () => { if (indexForm.value) { indexForm.value.resetFields(); } handleSearch(); }; // 查看详情 const handleView = (row) => { console.log('查看详情:', row); // 这里可以实现查看详情的逻辑 }; // 分页大小改变 const handleSizeChange = (size) => { pagination.size = size; fetchData(); }; // 页码改变 const handlePageChange = (page) => { pagination.current = page; fetchData(); }; // 获取数据 const fetchData = async () => { // 获取认证令牌 const token = getAuthToken(); if (!token) return; // 取消之前的请求 if (cancelTokenSource) { cancelTokenSource.cancel('请求被取消'); } cancelTokenSource = axios.CancelToken.source(); loading.value = true; try { // 构造请求参数 const params = { pageNum: pagination.current, pageSize: pagination.size, ...formData }; // 调用API - 添加认证头 const response = await axios.get(API_URL, { params, cancelToken: cancelTokenSource.token, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` // 添加认证头 } }); // 处理响应数据 const { data } = response; if (data && data.code === 200) { tableData.value = data.data.rows || []; pagination.total = data.data.total || 0; // 空数据提示 if (tableData.value.length === 0) { ElMessage.info('没有找到匹配的数据'); } } else { const errorMsg = data?.msg || '未知错误'; console.error('API返回错误:', errorMsg); ElMessage.error(`请求失败: ${errorMsg}`); tableData.value = []; pagination.total = 0; } } catch (error) { // 处理认证失败 if (error.response && error.response.status === 401) { ElMessage.error('认证过期,请重新登录'); localStorage.removeItem('token'); // 清除无效token router.push('/login'); // 重定向到登录页 return; } // 忽略取消请求的错误 if (!axios.isCancel(error)) { console.error('获取数据失败:', error); const errorMsg = error.response?.data?.message || '网络请求失败'; ElMessage.error(`请求失败: ${errorMsg}`); tableData.value = []; pagination.total = 0; } } finally { loading.value = false; } }; // 页面加载时获取初始数据 onMounted(() => { fetchData(); }); // 组件卸载时取消所有请求 onUnmounted(() => { if (cancelTokenSource) { cancelTokenSource.cancel('组件卸载,取消请求'); } }); </script> ``` ### 关键修改点解释 1. **添加路由功能**: ```javascript import { useRouter } from 'vue-router'; const router = useRouter(); ``` - 允许在代码中进行页面导航(如重定向到登录页) 2. **添加认证令牌获取函数**: ```javascript const getAuthToken = () => { const token = localStorage.getItem('token'); if (!token) { ElMessage.warning('请先登录'); router.push('/login'); return null; } return token; }; ``` - 从 localStorage 获取登录时存储的 token - 如果 token 不存在,提示并重定向到登录页 3. **在请求头中添加认证信息**: ```javascript headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` // JWT 标准格式 } ``` - 使用 `Bearer` 方案是 JWT 认证的标准做法 - 确保后端能正确识别认证信息 4. **添加 401 错误处理**: ```javascript if (error.response && error.response.status === 401) { ElMessage.error('认证过期,请重新登录'); localStorage.removeItem('token'); router.push('/login'); return; } ``` - 处理 token 过期或无效的情况 - 清除无效 token 并重定向到登录页 ### 登录页面改进建议 在您的登录页面中,可以添加以下改进: ```vue <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' import axios from 'axios' import { ElMessage } from 'element-plus' const router = useRouter() const username = ref('') const password = ref('') const loading = ref(false) // 添加加载状态 const handleLogin = async () => { if (!username.value || !password.value) { ElMessage.warning('请输入用户名和密码'); return; } loading.value = true; try { const response = await axios.post('http://172.26.26.43/dev-api/login', { username: username.value, password: password.value }); const { code, message, token } = response.data; if (code === 200) { localStorage.setItem('token', token); ElMessage.success({ message: '登录成功!', customClass: 'mobile-message', duration: 1000 }); setTimeout(() => { router.push({ name: 'Detail' }); }, 1000); } else { ElMessage.error(`登录失败: ${message || '未知错误'}`); } } catch (error) { console.error('登录请求出错:', error); let errorMsg = '登录请求失败'; if (error.response) { if (error.response.status === 401) { errorMsg = '用户名或密码错误'; } else { errorMsg = error.response.data?.message || `服务器错误: ${error.response.status}`; } } else if (error.request) { errorMsg = '网络错误,请检查网络连接'; } ElMessage.error(errorMsg); } finally { loading.value = false; } } </script> ``` ### 完整认证流程说明 1. **登录流程**: ```mermaid sequenceDiagram 用户->>前端: 输入用户名密码 前端->>后端: POST /login {username, password} 后端-->>前端: 返回JWT令牌 前端->>localStorage: 存储token 前端->>详情页: 导航到Detail页面 ``` 2. **详情页请求流程**: ```mermaid sequenceDiagram 详情页->>localStorage: 获取token 详情页->>后端: GET /api/data + Authorization头 后端-->>详情页: 返回数据或401错误 详情页->>用户: 显示数据或重定向到登录页 ``` ### 最佳实践建议 1. **使用 Vuex/Pinia 管理认证状态**: ```javascript // authStore.js import { defineStore } from 'pinia'; import { ref } from 'vue'; import router from '@/router'; export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem('token') || null); const setToken = (newToken) => { token.value = newToken; localStorage.setItem('token', newToken); }; const clearToken = () => { token.value = null; localStorage.removeItem('token'); }; const isAuthenticated = () => !!token.value; return { token, setToken, clearToken, isAuthenticated }; }); ``` 2. **全局路由守卫**: ```javascript // router.js import { createRouter } from 'vue-router'; import { useAuthStore } from '@/stores/auth'; const router = createRouter({ ... }); router.beforeEach((to, from, next) => { const authStore = useAuthStore(); const isAuthRequired = to.meta.requiresAuth; if (isAuthRequired && !authStore.isAuthenticated()) { next({ name: 'Login' }); } else { next(); } }); ``` 3. **Axios 请求拦截器**: ```javascript // axios.js import axios from 'axios'; import { useAuthStore } from '@/stores/auth'; const api = axios.create(); api.interceptors.request.use(config => { const authStore = useAuthStore(); if (authStore.token) { config.headers.Authorization = `Bearer ${authStore.token}`; } return config; }); api.interceptors.response.use( response => response, error => { if (error.response.status === 401) { const authStore = useAuthStore(); authStore.clearToken(); router.push('/login'); } return Promise.reject(error); } ); ``` ### 常见问题排查 1. **token 未正确存储**: - 检查浏览器开发者工具 -> Application -> LocalStorage - 确保登录后 token 已存储 2. **请求头未正确发送**: - 在浏览器网络面板检查请求头 - 确保有 `Authorization: Bearer <token>` 头 3. **跨域问题**: - 确保后端设置了正确的 CORS 头: ```http Access-Control-Allow-Origin: * Access-Control-Allow-Headers: Authorization, Content-Type ``` 4. **token 格式问题**: - 后端可能期望特定格式的 token(如 JWT) - 确保登录接口返回的是有效的 JWT 令牌
阅读全文

相关推荐

<!doctype html> <html lang="en" dir="ltr" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="ceosdesigns.sk"> <title>登录</title> </head> <body> <main id="page-content" class="d-flex nm-aic nm-vh-md-100"> 书画展览管理系统 <form th:action="@{/loginUser}" method="post"> 登录 输入您的账户和密码以访问帐户 <label for="username">账户</label> <input type="text" class="form-control" id="username" name="username" tabindex="1" placeholder="请输入有效账户" required> <label for="password"> 密码 </label> <input type="password" class="form-control" tabindex="2" placeholder="请输入密码" id="password" name="password" required> <label for="username">账户</label> <select class="form-select" name="type"> <option value="1" selected>管理员</option> <option value="2">用户</option> </select> <button type="submit" class="btn btn-primary btn-block nm-hvr nm-btn-1"> 登录</button> </form> </main> <script src="login/js/jquery-3.6.0.min.js"></script> <script src="login/js/bootstrap.bundle.min.js"></script> <script src="login/js/script.js"></script> </body> </html>登录页面如何实现的

<?php $show_title="$MSG_LOGIN - $OJ_NAME"; ?> <?php include("template/$OJ_TEMPLATE/header.php");?> <style> .login-container { background: url('背景图URL') no-repeat center/cover; min-height: 100vh; display: flex; align-items: center; } .login-box { background: rgba(255, 255, 255, 0.95); padding: 2.5rem; border-radius: 12px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); width: 100%; max-width: 420px; margin: 0 auto; } .login-icon { color: #2185d0 !important; } #vcode-img { height: 42px; border-radius: 6px; margin-top: 8px; cursor: pointer; border: 1px solid #ddd; } </style> <?php echo $MSG_LOGIN ?> <form class="ui form" id="login" action="login.php" method="post" onSubmit="return jsMd5();"> <input name="user_id" placeholder="<?php echo $MSG_USER_ID ?>" type="text" id="username" autofocus> <input name="password" placeholder="<?php echo $MSG_PASSWORD ?>" type="password" id="password"> <?php if($OJ_VCODE){ ?> <input name="vcode" placeholder="<?php echo $MSG_VCODE ?>" type="text" autocomplete="off"> <?php } ?> <button type="submit" class="ui fluid large blue submit button" style="margin-top: 2rem; padding: 14px; font-size: 1.1em;"> <?php echo $MSG_LOGIN ?> </button> </form> <?php if ($OJ_REGISTER){ ?> <?php echo $MSG_REGISTER ?> <?php } ?> <script> // 自动刷新验证码 function refreshVcode() { $("#vcode-img").attr("src", "vcode.php?" + Math.random()); } <?php if ($OJ_VCODE) { ?> $(document).ready(refreshVcode); <?php } ?> </script> <?php include("template/$OJ_TEMPLATE/footer.php");?> 美化页面、固定登录框不要移动、不要更改别的文件、不要影响原来的功能

<template> 欢迎登录 <el-form status-icon ref=“formRef” :rules=“data.rules” :model=“data.form” style=“margin-bottom: 20px;”> <el-form-item prop=“username”> <el-input v-model=“data.form.username” prefix-icon=“User” placeholder=“请输入账号” clearable /> </el-form-item> <el-form-item prop=“password”> <el-input v-model=“data.form.password” prefix-icon=“Lock” placeholder=“请输入密码” show-password clearable /> </el-form-item> {{ role.label }} <el-button @click=“login” type=“primary” class=“login-btn”>登 录</el-button> 还没有账号? 立即注册 </el-form> </template> <script setup> import { reactive, ref } from ‘vue’ import { User, Lock } from ‘@element-plus/icons-vue’ import { ElMessage } from ‘element-plus’ import request from ‘@/utils/request’ import axios from ‘axios’ const roles = [ { label: ‘管理员’, value: ‘ADMIN’ }, { label: ‘教师’, value: ‘TEACHER’ }, { label: ‘学生’, value: ‘STUDENT’ } ] const data = reactive({ form: { role: ‘ADMIN’, username: ‘’, password: ‘’ }, rules: { username: [{ required: true, message: ‘请输入账号’, trigger: ‘blur’ }], password: [{ required: true, message: ‘请输入密码’, trigger: ‘blur’ }] } }) const formRef = ref() const login = () => { formRef.value.validate(async (valid) => { if (!valid) return try { const res = await request.post('/login', data.form) if (res.code === '200') { localStorage.setItem('xm-pro-user', JSON.stringify({ ...res.data, token: res.data.token // 确保token字段存在 })) ElMessage.success('登录成功') // 添加路由跳转容错处理 setTimeout(() => { if (res.data.role === 'ADMIN') { window.location.href = '/' } else { window.location.href = '/front/home' } }, 500) } } catch (error) { ElMessage.error(res.msg) } }) } </script> <template v-if=“!data.user”> <router-link to=“/front/home” class=“nav-item”>首页</router-link> <router-link to=“/courses” class=“nav-item”>视频课程</router-link> <router-link to=“/live” class=“nav-item”>直播课程</router-link> </template> <router-link to=“/login” class=“auth-link”>登录</router-link> | <router-link to=“/register” class=“auth-link”>注册</router-link> 为什么点击登录的时候页面一点反应都没有,也登录不成功,浏览器报错POST http://localhost:8080/login net::ERR_FAILEDUncaught (in promise) ReferenceError: res is not defined

<template> <router-link to="/" class="nav-item">首页</router-link> <router-link to="/playmusic" class="nav-item">播放页</router-link> <router-link to="/login" class="login-logout">登录</router-link> <router-link to="/logout" class="login-logout">登出</router-link> <router-view></router-view> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'App', }) </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } .main-nav { padding: 20px; background: #f8f9fa; margin-bottom: 2rem; display: flex; /* 启用flex布局 */ justify-content: space-between; /* 左右分组自动分开 */ align-items: center; /* 垂直居中 */ } .nav-group { display: flex; gap: 15px; /* 现代浏览器间距设置 */ } .left{ margin-right: auto; } .right { margin-left: auto; /* 右侧组推到最右边 */ } .nav-item { text-decoration: none; color: #2c3e50; font-weight: bold; } .login-logout { margin: 0 15px; color: #2c3e50; text-align: right; text-decoration: none; /* 新增 */ } .login-logout:hover { opacity: 0.8; } .router-link-exact-active { color: #42b983; border-bottom: 2px solid #42b983; } </style> 实现登录/登出的逻辑,渲染登录的页面(需要提供无账号的时候提供注册的选项),登录与后端api访问用户是否存在,然后整个前端都要可以获取到登录状态,(登录状态下才可以进去播放页,否组提示登录)

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>原神登录注册表单</title> <style> body { font-family: 'Arial', sans-serif; background: url('https://i.imgur.com/3QZQZ9m.jpg') no-repeat center center fixed; background-size: cover; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .form-container { background: rgba(0, 0, 0, 0.7); padding: 30px; border-radius: 10px; box-shadow: 0 0 20px rgba(255, 255, 255, 0.2); width: 350px; text-align: center; color: white; } .form-container h2 { font-size: 24px; margin-bottom: 20px; color: #ffcc00; } .form-container input { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ccc; border-radius: 5px; background: rgba(255, 255, 255, 0.1); color: white; font-size: 16px; } .form-container input::placeholder { color: #ccc; } .form-container button { width: 100%; padding: 10px; background: #ffcc00; color: #000; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold; margin-top: 10px; } .form-container button:hover { background: #e6b800; } .toggle-button { background: none; border: none; color: #ffcc00; cursor: pointer; text-decoration: underline; font-size: 14px; } .toggle-button:hover { color: #e6b800; } .hidden { display: none; } </style> </head> <body> 欢迎登录提瓦特大陆 <input type="text" id="login-username" placeholder="用户名" required> <input type="password" id="login-password" placeholder="密码" required> <button type="submit" onclick="login()">登录</button> 没有账号?<button class="toggle-button" onclick="toggleForm('register-form', 'login-form')">注册</button> 注册提瓦特大陆 <input type="text" id="register-username" placeholder="用户名" required> <input type="password" id="register-password" placeholder="密码" required> <input type="email" id="register-email" placeholder="邮箱" required> <button type="submit" onclick="register()">注册</button> 已有账号?<button class="toggle-button" onclick="toggleForm('login-form', 'register-form')">登录</button> <script> // 切换表单显示 function toggleForm(showId, hideId) { document.getElementById(showId).classList.remove('hidden'); document.getElementById(hideId).classList.add('hidden'); } // 登录功能 function login() { const username = document.getElementById('login-username').value; const password = document.getElementById('login-password').value; alert(欢迎回到提瓦特大陆,${username}!\n登录成功!); } // 注册功能 function register() { const username = document.getElementById('register-username').value; const password = document.getElementById('register-password').value; const email = document.getElementById('register-email').value; alert(欢迎加入提瓦特大陆,${username}!\n注册成功!); } </script> </body> </html> 创建一个首页页面

<template> 体育馆管理系统 <el-form ref="loginFormRef" :rules="loginrules" :model="LoginForm" class="login_form" label-width="0"> <el-form-item prop="username"> <el-input placeholder="请选择用户名" v-model="LoginForm.username" prefix-icon="el-icon-user"></el-input> </el-form-item> <el-form-item prop="password"> <el-input placeholder="请输入密码" v-model="LoginForm.password" prefix-icon="el-icon-lock" show-password></el-input> </el-form-item> <el-form-item class="btns"> <el-button type="primary" @click="login">登录</el-button> <el-button type="success" ><router-link to='/register'>注册</router-link></el-button> </el-form-item> </el-form> </template> <script> export default { data(){ return{ LoginForm:{ username:"admin", password:"123456", }, loginrules:{ username:[ { required: true, message: "请输入用户名", trigger: 'blur' } ], password:[ { required: true, message: "请输入密码", trigger: 'blur' }, { min: 6, max: 12, message: "密码长度为6~12位", trigger: 'blur' } ], } } }, methods:{ login(){ this.$refs.loginFormRef.validate(async valid =>{ if (!valid) { console.log("fail"); return; } const {data:res} = await this.$http.post("http://localhost:9000/login",this.LoginForm); if(res.msg == "success"){ window.localStorage.setItem("user",JSON.stringify(res.data)); this.$message.success("登陆成功!"); this.$router.push({path:"/home"}); }else{ this.$message.error("登陆失败!"); } }) } } } </script> <style lang="less" scoped> .login_container{ height:100%; } h2{ margin: 69px; } .login_box{ width: 450px; height: 300px; border-radius: 3px; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); } .avatar_box{ width: 130px; height: 130px; border-radius: 50%; padding: 10px; box-shadow: 0 0 10px #ddd; position: absolute; left: 50%; transform: translate(-50%,-50%); } img{ width: 100%; height: 100%; border-radius: 50%; } .btns{ display:flex; justify-content: flex-end; } .login_form{ position: absolute; bottom: 0%; width: 100%; padding: 0 10px; box-sizing: border-box; } a{ text-decoration: none; color: white; } router-link-active { text-decoration: none; } </style> 简单修改代码,使其更加美观,不要修改背景

<template> 云盘系统 欢迎回来 请登录您的账户 <el-form :model="loginForm" :rules="loginRules" label-width="0" ref="loginFormRef" > <el-form-item prop="email"> <el-input v-model="loginForm.email" placeholder="邮箱" prefix-icon="el-icon-message" /> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="el-icon-lock" @keyup.enter.native="handleLogin" /> </el-form-item> <el-form-item> <el-checkbox v-model="loginForm.remember">记住密码</el-checkbox> </el-form-item> <el-form-item> <el-button type="primary" class="login-btn" @click="handleLogin" :loading="loginLoading" > 登录 </el-button> </el-form-item> </el-form> 其他登录方式 <el-button type="link" icon="el-icon-qq" class="social-btn" @click="handleQQLogin"> QQ登录 </el-button> <el-button type="link" icon="el-icon-wechat" class="social-btn" @click="handleWechatLogin"> 微信登录 </el-button> <router-link to="/register">还没有账号? 立即注册</router-link> 忘记密码? </template> <script setup lang="ts"> import { reactive, ref, onMounted } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; const router = useRouter(); const loginFormRef = ref(); const loginLoading = ref(false); const loginForm = reactive({ email: '', password: '', remember: false }); const loginRules = reactive({ email: [ { required: true, message: '请输入邮箱', trigger: 'blur' }, { pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, message: '请输入正确的邮箱格式', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 15, message: '密码长度应为6-15位', trigger: 'blur' } ] }); // 处理登录 const handleLogin = async () => { (loginFormRef.value as any).validate(async valid => { if (!valid) return; loginLoading.value = true; // 模拟登录请求 await new Promise(resolve => setTimeout(resolve, 1500)); // 实际项目中应调用API验证用户 ElMessage.success('登录成功,即将进入系统'); setTimeout(() => { router.push('/dashboard'); }, 1500); loginLoading.value = false; }); }; // 处理第三方登录 const handleQQLogin = () => { ElMessage.info('QQ登录功能开发中...'); }; const handleWechatLogin = () => { ElMessage.info('微信登录功能开发中...'); }; // 处理忘记密码 const handleForgotPassword = () => { ElMessage.info('密码找回功能开发中...'); }; // 页面加载时检查是否有记住的密码 onMounted(() => { const savedEmail = localStorage.getItem('savedEmail'); const savedPassword = localStorage.getItem('savedPassword'); if (savedEmail && savedPassword) { loginForm.email = savedEmail; loginForm.password = savedPassword; loginForm.remember = true; } }); </script> <style scoped> /* 确保根元素占满屏幕 */ html, body { width: 100%; height: 100%; margin: 0; padding: 0; overflow: auto; } .login-container { min-width: 100vw; min-height: 100vh; background: linear-gradient(135deg, #e6f0ff, #f0f2ff); display: flex; justify-content: center; align-items: center; box-sizing: border-box; padding: 20px; } .login-box { width: 100%; max-width: 400px; background: #fff; border-radius: 12px; padding: 40px 30px; box-shadow: 0 8px 24px rgba(0, 0, 255, 0.1); text-align: center; } .logo { width: 60px; height: 60px; background: #6b6df4; border-radius: 50%; color: #fff; line-height: 60px; font-size: 24px; margin: 0 auto 20px; } h2 { font-size: 24px; color: #333; margin-bottom: 8px; } .sub-title { font-size: 14px; color: #999; margin-bottom: 30px; } .el-input { --el-input-bg-color: #f8f9fe; --el-input-border-color: transparent; --el-input-hover-border-color: transparent; --el-input-focus-border-color: transparent; margin-bottom: 20px; } .login-btn { width: 100%; background: linear-gradient(90deg, #6b6df4, #8490ff); border: none; color: #fff; padding: 12px 0; border-radius: 8px; cursor: pointer; } .login-btn:hover { opacity: 0.9; } .social-login { margin-top: 30px; } .divider { display: flex; align-items: center; margin-bottom: 20px; } .divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: #eee; } .divider span { color: #999; font-size: 14px; padding: 0 15px; } .social-buttons { display: flex; justify-content: center; } .social-btn { color: #6b6df4; font-size: 20px; margin: 0 10px; } .extra-links { display: flex; justify-content: space-between; margin-top: 20px; font-size: 14px; } .extra-links a { color: #6b6df4; text-decoration: none; } .extra-links a:hover, .forgot-password:hover { text-decoration: underline; cursor: pointer; } .forgot-password { color: #6b6df4; } </style> 这个登录的vue组件为什么不能占满整个页面

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>硬件运维系统 - 报告管理</title> </head> <body> 报告管理 <input type="text" id="search-report" placeholder="搜索报告名称/类型"> <button class="btn" id="search-btn">搜索</button> <button class="btn" id="sort-by-date">按日期排序</button> <button class="btn btn-download" id="download-all">下载全部报告</button> <button class="btn" id="add-report">新增报告</button> 新增报告 × <form id="report-form"> <input type="hidden" id="report-id"> <label for="report-name">报告名称</label> <input type="text" id="report-name" required> <label for="report-type">报告类型</label> <select id="report-type" required> <option value="硬件故障处理报告">硬件故障处理报告</option> <option value="软件运维报告">软件运维报告</option> <option value="设备健康度报告">设备健康度报告</option> <option value="产线运行分析报告">产线运行分析报告</option> <option value="硬件日志采集报告">硬件选择 - 获取指定时间段的日志</option> </select> <label for="log-start-date">日志开始日期</label> <input type="date" id="log-start-date" required> <label for="log-end-date" style="margin-top: 10px;">日志结束日期</label> <input type="date" id="log-end-date" required> <label for="report-date">报告生成日期</label> <input type="date" id="report-date" required> <label for="report-content">报告内容</label> 请根据报告类型填写内容... <textarea id="report-content" required></textarea> <button type="button" class="btn" id="cancel-btn">取消</button> <button type="submit" class="btn">保存</button> </form> <script src="style/js/jquery.min.js"></script> <script> // 检查登录状态 if (localStorage.getItem('isLogin') !== '1') { window.location.href = 'login.html'; } // 初始化报告数据和硬件日志数据 let reports = []; let hardwareLogs = []; // DOM 元素缓存 const $reportList = $('#report-list'); const $reportModal = $('#report-modal'); const $modalTitle = $('#modal-title'); const $reportForm = $('#report-form'); const $reportId = $('#report-id'); const $reportName = $('#report-name'); const $reportType = $('#report-type'); const $reportDate = $('#report-date'); const $reportContent = $('#report-content'); const $addReportBtn = $('#add-report'); const $cancelBtn = $('#cancel-btn'); const $closeBtn = $('.close'); const $searchBtn = $('#search-btn'); const $searchInput = $('#search-report'); const $sortByDateBtn = $('#sort-by-date'); const $downloadAllBtn = $('#download-all'); const $logTimeRange = $('.log-time-range'); const $contentDesc = $('#content-desc'); // 报告类型模板映射 const typeTemplates = { '硬件故障处理报告': 1. 故障现象: 2. 故障排查过程: 3. 处理方案: 4. 处理结果: 5. 预防建议:, '软件运维报告': 1. 运维内容: 2. 操作过程: 3. 运行状态: 4. 遗留问题: 5. 后续计划:, '设备健康度报告': 1. 设备状态: 2. 检查项目: 3. 异常项: 4. 维护建议: 5. 预测分析:, '产线运行分析报告': 1. 生产数据: 2. 设备表现: 3. 瓶颈分析: 4. 改进措施: 5. 提升建议:, '硬件日志采集报告': ### 日志采集说明 - 采集时间段:{startDate} 至 {endDate} - 涉及设备:自动从日志中提取 - 异常日志数量:{errorCount} ### 关键日志摘要 {logSummary} }; // 从API加载报告数据 function loadReports() { $.ajax({ url: 'api/reports.php', method: 'GET', dataType: 'json', success: function (data) { reports = data || []; renderReports(reports); }, error: function (xhr, status, error) { console.error('加载报告失败:', error); $reportList.html('加载报告数据失败,请稍后重试'); } }); } // 加载硬件日志数据 function loadHardwareLogs() { $.getJSON('data/hardware_logs.json', (data) => { hardwareLogs = data.logs || []; console.log('硬件日志加载成功'); }).fail((xhr, status, error) => { console.error('加载硬件日志失败:', error); hardwareLogs = []; }); } // 渲染报告列表 function renderReports(reportData) { $reportList.empty(); // 解绑之前的事件处理器,避免重复绑定 $reportList.off('click', '.edit-btn'); $reportList.off('click', '.delete-btn'); $reportList.off('click', '.download-btn'); if (reportData.length === 0) { $reportList.html('暂无报告数据,请点击"新增报告"创建'); return; } reportData.forEach(report => { const $card = $( ${report.name} ${report.type} | 生成日期:${report.date} <button class="action-btn download-btn">下载</button> <button class="action-btn edit-btn">编辑</button> <button class="action-btn delete-btn">删除</button> ); $reportList.append($card); }); // 绑定事件 $reportList.on('click', '.edit-btn', function () { const reportId = $(this).closest('.report-card').data('id'); editReport(reportId); }); $reportList.on('click', '.delete-btn', function () { const reportId = $(this).closest('.report-card').data('id'); deleteReport(reportId); }); $reportList.on('click', '.download-btn', function () { const reportId = $(this).closest('.report-card').data('id'); downloadReport(reportId); }); } // 打开模态框 function openModal(isEdit = false, report = null) { $reportForm[0].reset(); $logTimeRange.hide(); $reportContent.val(''); if (isEdit && report) { $modalTitle.text('编辑报告'); $reportId.val(report.id); $reportName.val(report.name); $reportType.val(report.type); $reportDate.val(report.date); $reportContent.val(report.content); if (report.type === '硬件日志采集报告') { $logTimeRange.show(); $('#log-start-date').val(report.logStartDate || ''); $('#log-end-date').val(report.logEndDate || ''); } } else { $modalTitle.text('新增报告'); $reportId.val(''); $reportDate.val(new Date().toISOString().split('T')[0]); } $reportModal.show(); } // 关闭模态框 function closeModal() { $reportModal.hide(); } // 编辑报告 function editReport(id) { console.log('编辑报告:', reports); console.log('报告ID:', id); const stringId = String(id); const report = reports.find(r => r.id === stringId); console.log('编辑报告:', report); // console.log(r.id); if (report) { openModal(true, report); } else { alert('未找到该报告'); loadReports(); } } // 删除报告 function deleteReport(id) { const stringId = String(id); const report = reports.find(r => r.id === stringId); if (!report) { alert('未找到该报告'); loadReports(); return; } if (confirm(确定要删除"${report.name}"这份报告吗?删除后无法恢复!)) { $.ajax({ url: 'api/reports.php', method: 'DELETE', contentType: 'application/json', data: JSON.stringify({ id: id }), dataType: 'json', success: function (response) { if (response.success) { loadReports(); // 重新加载数据 } else { alert('删除失败:' + (response.message || '未知错误')); } }, error: function (xhr, status, error) { console.error('删除报告失败:', error); alert('删除报告失败,请检查网络连接'); } }); } } // 下载单个报告 function downloadReport(id) { const report = reports.find(r => r.id === id); if (!report) return; let content = 报告名称:${report.name} 报告类型:${report.type} 生成日期:${report.date} 报告内容: ${report.content} .trim(); if (report.type === '硬件日志采集报告' && report.attachments && report.attachments.length) { content += \n\n=== 日志附件 ===\n; report.attachments.forEach(attach => { content += \n【附件:${attach.filename}】\n${attach.content}\n; }); } const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = $('').attr({ href: url, download: ${report.name.replace(/[\/:*?"<>|]/g, '-')}.txt }); $('body').append(a); a[0].click(); a.remove(); URL.revokeObjectURL(url); } // 下载全部报告 function downloadAllReports() { if (reports.length === 0) { alert('没有报告可下载'); return; } let allContent = '=== 所有报告汇总 ===\n\n'; reports.forEach((report, idx) => { allContent += 【报告 ${idx + 1}】\n; allContent += 名称:${report.name}\n类型:${report.type}\n日期:${report.date}\n\n; allContent += 内容:\n${report.content}\n\n; if (report.type === '硬件日志采集报告' && report.attachments && report.attachments.length) { allContent += === 日志附件 ===\n; report.attachments.forEach(attach => { allContent += \n【附件:${attach.filename}】\n${attach.content}\n; }); } allContent += '========================================\n\n'; }); const blob = new Blob([allContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = $('').attr({ href: url, download: 所有报告汇总_${new Date().toISOString().split('T')[0]}.txt }); $('body').append(a); a[0].click(); a.remove(); URL.revokeObjectURL(url); } // 保存报告 $reportForm.on('submit', function (e) { e.preventDefault(); const formData = { id: $reportId.val(), name: $reportName.val(), type: $reportType.val(), date: $reportDate.val(), content: $reportContent.val(), attachments: [] }; if (formData.type === '硬件日志采集报告') { const startDate = $('#log-start-date').val(); const endDate = $('#log-end-date').val(); formData.logStartDate = startDate; formData.logEndDate = endDate; const filteredLogs = hardwareLogs.filter(log => { const logTime = new Date(log.timestamp); const start = new Date(startDate); const end = new Date(endDate); return logTime >= start && logTime <= end; }); if (filteredLogs.length > 0) { const errorCount = filteredLogs.filter(log => log.level === 'ERROR').length; const logSummary = filteredLogs.map(log => [${log.timestamp}] [${log.device}] [${log.level}]:${log.content} ).join('\n'); formData.content = typeTemplates['硬件日志采集报告'] .replace('{startDate}', startDate) .replace('{endDate}', endDate) .replace('{errorCount}', errorCount) .replace('{logSummary}', logSummary); formData.attachments.push({ filename: hardware_logs_${startDate}_${endDate}.txt, content: filteredLogs.map(log => ${log.timestamp}\n${log.device}\n${log.level}\n${log.content}\n-----\n ).join('') }); } else { formData.content = '未找到指定时间段的日志数据'; } } const method = formData.id ? 'PUT' : 'POST'; $.ajax({ url: 'api/reports.php', method: method, contentType: 'application/json', data: JSON.stringify(formData), dataType: 'json', success: function (response) { if (response.success) { loadReports(); closeModal(); } else { alert('保存失败:' + (response.message || '未知错误')); } }, error: function (xhr, status, error) { console.error('保存报告失败:', error); alert('保存报告失败,请检查网络连接'); } }); }); // 搜索报告 function handleSearch() { const keyword = $searchInput.val().toLowerCase(); const filtered = reports.filter(report => report.name.toLowerCase().includes(keyword) || report.type.toLowerCase().includes(keyword) ); renderReports(filtered); } // 按日期排序 function sortReportsByDate() { const sorted = [...reports].sort((a, b) => new Date(b.date) - new Date(a.date) ); renderReports(sorted); } // 事件绑定 function bindEvents() { // 报告类型切换 // / 修改报告类型切换事件 $reportType.on('change', function () { const type = $(this).val(); const $startDate = $('#log-start-date'); const $endDate = $('#log-end-date'); $logTimeRange.hide(); $contentDesc.text('请根据报告类型填写内容...'); if (type === '硬件日志采集报告') { $logTimeRange.show(); $contentDesc.text('选择时间段后将自动生成日志摘要'); $reportContent.val(''); // 仅在日志类型下添加必填属性 $startDate.attr('required', true); $endDate.attr('required', true); } else { $reportContent.val(typeTemplates[type] || ''); // 其他类型下移除必填属性 $startDate.removeAttr('required'); $endDate.removeAttr('required'); } }); // 新增报告 $addReportBtn.on('click', () => openModal()); // 关闭模态框 $closeBtn.on('click', closeModal); $cancelBtn.on('click', closeModal); // 点击模态框外部关闭 $(window).on('click', (e) => { if ($(e.target).is($reportModal)) closeModal(); }); // 搜索 $searchBtn.on('click', handleSearch); $searchInput.on('keypress', (e) => { if (e.key === 'Enter') handleSearch(); }); // 排序 $sortByDateBtn.on('click', sortReportsByDate); // 下载全部 $downloadAllBtn.on('click', downloadAllReports); } // 初始化 function init() { bindEvents(); loadHardwareLogs(); loadReports(); } // 页面加载完成后初始化 $(document).ready(init); </script> </body> </html> 优化代码.让拟态窗更加好看

<template> <el-card class="index-card"> <el-form :model="formData" label-position="top" ref="indexForm" class="mobile-form" > <el-form-item label="问卷标题:" prop="dcWjTitle"> <el-input v-model="formData.dcWjTitle" placeholder="请输入问卷标题" clearable :prefix-icon="Document" /> </el-form-item> <el-form-item label="被测评人:" prop="dcId" class="inline-form-item"> <el-select v-model="formData.dcId" multiple filterable remote reserve-keyword clearable placeholder="请选择被测评人" :remote-method="searchBdr" :loading="bdrLoading" @focus="handleBdrFocus" > <el-option v-for="item in bdrOptions" :key="item.dcId" :label="item.dcName" :value="item.dcId" /> </el-select> </el-form-item> <el-form-item label="人员部门:" prop="dcDept" class="inline-form-item"> <el-input v-model="formData.dcDept" placeholder="请输入人员部门" clearable :prefix-icon="OfficeBuilding" /> </el-form-item> <el-form-item label="提交状态:" prop="state"> <el-select v-model="formData.state" placeholder="请选择提交状态" clearable class="mobile-select" > <el-option label="已提交" :value="1" /> <el-option label="未提交" :value="0" /> </el-select> </el-form-item> <el-form-item class="button-group"> <el-button type="primary" @click="handleSearch" class="action-button" :icon="Search" > 搜索 </el-button> <el-button @click="handleReset" class="action-button" :icon="Refresh" > 重置 </el-button> </el-form-item> </el-form> </el-card> <el-card class="data-card"> <template #header> <el-button type="primary" size="small" :icon="Refresh" @click="fetchData" > 刷新数据 </el-button> </template> {{ item.dcWjTitle }} 被测评人: {{ item.dcName }} 部门: {{ item.dcDept }} 创建时间: {{ item.createTime }} 提交时间: {{ item.updateTime || '-' }} <el-tag :type="item.state === '1' ? 'success' : 'info'"> {{ item.state === '1' ? '已提交' : '未提交' }} </el-tag> 总分: {{ item.score || '0' }} <el-button size="small" type="primary" @click="handleView(item)" class="action-btn" > 编辑/查看 </el-button> <el-empty v-if="tableData.length === 0" description="暂无数据" /> <el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size" :page-sizes="[5, 10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handleSizeChange" @current-change="handlePageChange" /> </el-card> </template> <script setup> // 确保正确导入所有 Vue 函数 import { ref, reactive, onMounted, onUnmounted } from 'vue'; import axios from 'axios'; import { Document, User, OfficeBuilding, Search, Refresh } from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; import { useRouter } from 'vue-router'; const router = useRouter(); // 环境变量管理API地址 const API_BASE = import.meta.env.VITE_API_BASE || 'http://172.26.26.43/dev-api'; const API_URL = ${API_BASE}/wjdc/wj/listTx; const BDR_API_URL = ${API_BASE}/wjdc/wj/getBdrList; // 被测评人相关数据 const bdrOptions = ref([]); // 被测评人选项列表 const bdrLoading = ref(false); // 加载状态 const bdrCache = ref([]); // 缓存所有被测评人数据 // 表单数据 const formData = reactive({ dcWjTitle: '', dcId: [], dcDept: '', state: null }); // 表格数据 const tableData = ref([]); const loading = ref(false); // 分页配置 const pagination = reactive({ current: 1, size: 10, total: 0 }); // 表单引用 const indexForm = ref(null); // 请求取消令牌 let cancelTokenSource = axios.CancelToken.source(); // 处理被测评人输入框获取焦点 const handleBdrFocus = () => { if (bdrCache.value.length === 0) { fetchBdrList(''); } }; // 获取被测评人列表 const fetchBdrList = async (keyword = '') => { const token = getAuthToken(); if (!token) return; bdrLoading.value = true; try { const response = await axios.get(BDR_API_URL, { headers: { 'Authorization': Bearer ${token} } }); // 判断返回的数据是否是数组 if (Array.isArray(response.data)) { // 缓存所有数据 bdrCache.value = response.data; // 根据关键字过滤 if (keyword) { const searchTerm = keyword.toLowerCase(); bdrOptions.value = bdrCache.value.filter(item => item.dcName && item.dcName.toLowerCase().includes(searchTerm) ).slice(0, 10); // 最多显示10条 } else { // 未输入关键字时显示前10条 bdrOptions.value = bdrCache.value.slice(0, 10); } } else { // 如果不是数组,则按照原有格式处理(假设有code和data) if (response.data && response.data.code === 200) { bdrCache.value = response.data.data || []; // 同样的过滤逻辑 if (keyword) { const searchTerm = keyword.toLowerCase(); bdrOptions.value = bdrCache.value.filter(item => item.dcName.toLowerCase().includes(searchTerm) ).slice(0, 10); } else { bdrOptions.value = bdrCache.value.slice(0, 10); } } else { const msg = response.data?.msg || '返回数据格式不正确'; ElMessage.error('获取被测评人列表失败: ' + msg); } } } catch (error) { console.error('获取被测评人列表失败:', error); ElMessage.error('获取被测评人列表失败'); } finally { bdrLoading.value = false; } }; // 搜索被测评人(防抖) let searchBdrTimer = null; const searchBdr = (query) => { if (searchBdrTimer) clearTimeout(searchBdrTimer); searchBdrTimer = setTimeout(() => { if (bdrCache.value.length === 0) { fetchBdrList(query); } else { // 本地过滤 if (query) { const searchTerm = query.toLowerCase(); bdrOptions.value = bdrCache.value.filter(item => item.dcName.toLowerCase().includes(searchTerm) ).slice(0, 10); } else { bdrOptions.value = bdrCache.value.slice(0, 10); } } }, 300); }; // 获取认证令牌 const getAuthToken = () => { const token = localStorage.getItem('token'); if (!token) { ElMessage.warning('请先登录'); router.push('/login'); return null; } return token; }; // 搜索按钮处理函数 - 防抖 let searchTimer = null; const handleSearch = () => { // 检查被测评人选择数量 if (formData.dcId.length > 1) { ElMessage.warning({ message: '当前只能搜索一个被测人员', duration: 3000 }); return; } if (searchTimer) clearTimeout(searchTimer); searchTimer = setTimeout(() => { pagination.current = 1; fetchData(); }, 300); }; // 重置按钮处理函数 const handleReset = () => { if (indexForm.value) { indexForm.value.resetFields(); // 确保重置后 dcId 是空数组 formData.dcId = []; } handleSearch(); }; // 编辑/查看 const handleView = (row) => { router.push({ name: 'Operation', // 路由名称 params: { id: row.dcWjId // 传递问卷ID作为参数 } }); }; // 分页大小改变 const handleSizeChange = (size) => { pagination.size = size; fetchData(); }; // 页码改变 const handlePageChange = (page) => { pagination.current = page; fetchData(); }; // 获取数据 const fetchData = async () => { // 获取认证令牌 const token = getAuthToken(); if (!token) return; // 取消之前的请求 if (cancelTokenSource) { cancelTokenSource.cancel('请求被取消'); } cancelTokenSource = axios.CancelToken.source(); loading.value = true; try { // 构造请求参数 const params = { pageNum: pagination.current, pageSize: pagination.size, ...formData, // 安全处理:确保 dcId 是数组再 join dcId: Array.isArray(formData.dcId) ? formData.dcId.join(',') : '' // 将数组转换为逗号分隔的字符串 }; // 调用API - 添加认证头 const response = await axios.get(API_URL, { params, cancelToken: cancelTokenSource.token, headers: { 'Content-Type': 'application/json', 'Authorization': Bearer ${token} } }); // 处理响应数据 const { data } = response; if (data && data.code === 200) { // 修改点:直接使用 data.rows 和 data.total tableData.value = data.rows || []; pagination.total = data.total || 0; // 空数据提示 if (tableData.value.length === 0) { ElMessage.info('没有找到匹配的数据'); } } else { const errorMsg = data?.msg || '未知错误'; console.error('API返回错误:', errorMsg); ElMessage.error(请求失败: ${errorMsg}); tableData.value = []; pagination.total = 0; } } catch (error) { // 处理认证失败 if (error.response && error.response.status === 401) { ElMessage.error('认证过期,请重新登录'); localStorage.removeItem('token'); router.push('/login'); return; } // 忽略取消请求的错误 if (!axios.isCancel(error)) { console.error('获取数据失败:', error); const errorMsg = error.response?.data?.message || '网络请求失败'; ElMessage.error(请求失败: ${errorMsg}); tableData.value = []; pagination.total = 0; } } finally { loading.value = false; } }; // 页面加载时获取初始数据 onMounted(() => { fetchData(); }); // 组件卸载时取消所有请求 onUnmounted(() => { if (cancelTokenSource) { cancelTokenSource.cancel('组件卸载,取消请求'); } }); </script> <style scoped> .form-row { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 16px; } .inline-form-item { flex: 1; min-width: 250px; } .inline-form-item :deep(.el-form-item__label) { float: left; width: auto; padding-right: 12px; line-height: 32px; } .inline-form-item :deep(.el-form-item__content) { display: flex; flex: 1; } /* 移动端响应式 */ @media (max-width: 768px) { .form-row { flex-direction: column; gap: 16px; } .inline-form-item { min-width: 100%; } .inline-form-item :deep(.el-form-item__label) { float: none; width: 100%; padding-right: 0; padding-bottom: 8px; } } /* 卡片容器样式 */ .card-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(500px, 1fr)); gap: 16px; } /* 单个卡片样式 */ .data-card-item { background: #fff; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); padding: 16px; display: flex; flex-direction: column; transition: all 0.3s ease; border: 1px solid #ebeef5; } .data-card-item:hover { transform: translateY(-4px); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); } /* 卡片头部(序号+标题) */ .card-header-section { padding-bottom: 12px; border-bottom: 1px solid #f0f2f5; margin-bottom: 12px; } .card-id { font-size: 14px; color: #909399; margin-bottom: 4px; } .card-title { font-size: 16px; font-weight: 600; color: #303133; line-height: 1.4; word-break: break-word; } /* 卡片主体(其他信息) */ .card-body-section { flex: 1; margin-bottom: 12px; } .card-row { display: flex; margin-bottom: 8px; font-size: 14px; } .card-label { color: #606266; min-width: 70px; text-align: right; } .card-value { color: #303133; flex: 1; word-break: break-word; } /* 卡片底部(状态+按钮) */ .card-footer-section { display: flex; justify-content: space-between; align-items: center; padding-top: 12px; border-top: 1px solid #f0f2f5; } .status-container { display: flex; align-items: center; gap: 8px; } .score { font-size: 14px; color: #e6a23c; font-weight: 500; } .action-btn { flex-shrink: 0; } /* 移动端响应式 */ @media (max-width: 768px) { .card-container { grid-template-columns: 1fr; } .card-row { flex-direction: column; margin-bottom: 12px; } .card-label { text-align: left; margin-bottom: 4px; font-weight: 500; } .card-footer-section { flex-direction: column; align-items: stretch; gap: 12px; } .status-container { justify-content: space-between; } } /* 添加选择器样式 */ :deep(.el-select) .el-input__inner { height: auto !important; min-height: 44px; padding: 5px 15px; } /* 标签样式 */ :deep(.el-tag) { margin: 2px 6px 2px 0; } /* 下拉菜单样式 */ :deep(.el-select-dropdown) { max-height: 300px; overflow-y: auto; } .index-card { border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .card-header { font-size: 18px; font-weight: 600; color: #1a1a1a; } .mobile-form { :deep(.el-form-item__label) { font-weight: 500; margin-bottom: 6px; } :deep(.el-input__inner) { height: 44px; border-radius: 8px; } } .mobile-select { width: 100%; :deep(.el-input__inner) { height: 44px; } } /* 按钮区域样式 */ .button-group { display: flex; gap: 12px; margin-top: 16px; } .action-button { flex: 1; height: 46px; font-size: 16px; border-radius: 8px; } /* 移动端响应式调整 */ @media (max-width: 480px) { .button-group { flex-direction: column; gap: 10px; } .action-button { width: 100%; } } </style> 帮我优化一下代码,去掉未使用或者多余的布局样式

<template> 欢迎登录 <el-form ref=“formRef” :rules=“data.rules” :model=“data.form” style=“margin-left: 35px;margin-bottom: 50px;”> <el-form-item prop=“username”> <input type=“text” v-model=“data.form.username” autocomplete=“off” prefix-icon=“Uaer” placeholder=“请输入账号” required </el-form-item> <el-form-item prop=“password”> <input show-password type=“password” v-model=“data.form.password” placeholder=“请输入密码” required </el-form-item> <el-form-item prop=“role”> </el-form-item> <el-button @click=“login” type=“primary” style=“color: black;width: 91%;margin-top: 3px;margin-bottom: 10px;”>登 录</el-button> 还没有账号? 请注册 </el-form> </template> <script setup> import { reactive } from ‘vue’; import { ref } from ‘vue’ import { User } from ‘@element-plus/icons-vue’; import request from ‘@/utils/request’; import { ElMessage } from ‘element-plus’; const data=reactive({ form:{ role:‘admin’ }, rules:{ username:[{ required:true,message:‘请输入账号’,trigger:‘blur’ }], password:[{ required:true, message:‘请输入密码’,trigger:‘blur’ }], }, user: null }) const formRef=ref() const username = ref(‘’) const password = ref(‘’) const login = () => { // 处理登录逻辑 formRef.value.validate((valid)=>{ if(valid){ request.post(‘/login’,data.form).then(res=>{ if(res.code===‘200’){ //存储后台返回的用户数据信息this.$set(this.data, ‘user’, response.data.user) localStorage.setItem(‘xm-pro-user’,JSON.stringify(res.data))//把json对象转换成json字符串存储 ElMessage.success(‘登录成功’) setTimeout(()=>{ location.href=‘/manager/home’ },500) } else{ ElMessage.error(res.msg) } })}})} </script> <style scoped> .background{ background: url(“@/assets/bg.png”); width:100%; height:100%; /**宽高100%是为了图片铺满屏幕 */ position: absolute; background-size: 100% 100%; } .login-box{ width: 50%; height: 100%; display: flex; align-items: center; position: absolute; } .blur-box { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-radius: 16px; padding: 40px; width: 400px; height: 270px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.18); position: relative; align-items: center; } h2 { color: rgba(255, 255, 255, 0.9); text-align: center; margin-bottom: 30px; } .input-group input { width: 80%; padding: 12px 20px; margin: 8px 0; background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 8px; color: #0c0b0b; transition: all 0.3s ease; } .input-group input:focus { outline: none; border-color: rgba(255, 255, 255, 0.6); background: rgba(255, 255, 255, 0.2); } button { width: 100%; padding: 12px; margin-top: 20px; background: #0742b1(2, 66, 139, 0.2); border: 1px solid rgba(160, 99, 99, 0.3); color: #43bbef; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; } button:hover { background: rgba(255, 255, 255, 0.3); } </style>修改这个登录的前端页面,要求样式不要改变还是半透明磨砂,输入账号框里加一个用户图标,密码框里加一个密码图标.再在密码框下面给出多角色权限登录,其中角色有管理员,老师和学生.给出小圆圈的样式选择角色,要求整体设计美观.要求角色选择在同一行上面而且选择时按钮的颜色要小清新的蓝色,登录框整体要在页面正中间,而且整体登录框占整个页面的大小也要十分合适.在给出的代码上作出修改之后再给我,需要完整代码

<template> <CustomForm class="login-form"> <CustomFormItem label="账号"> <CustomInput v-model="form.employeeId" placeholder="请输入账号" @blur="validateEmployeeId" /> </CustomFormItem> <CustomFormItem label="密码"> <CustomInput v-model="form.password" type="password" placeholder="请输入密码" :show-password="true" @blur="validatePassword" /> </CustomFormItem> <CustomFormItem> <button class="custom-button" @click.prevent="onSubmit" :disabled="loading"> 提交 登录中... </button> </CustomFormItem> <CustomFormItem v-if="errorMessage" class="error-container"> {{ errorMessage }} </CustomFormItem> </CustomForm> </template> <script setup> import { ref, reactive } from 'vue' import { useRouter } from 'vue-router' import useUserStore from '@/store/modules/user' import productApi from '@/api/products' import CustomInput from '@/components/zhangaipiao/customInput.vue' import CustomForm from '@/components/zhangaipiao/customForm.vue' import CustomFormItem from '@/components/zhangaipiao/customFormItem.vue' const userStore = useUserStore() const router = useRouter() const loading = ref(false) const errorMessage = ref('') const form = reactive({ employeeId: '', password: '', role: '', employeeName: '' }) // 记录用户是否触碰过输入框,用于控制错误提示是否显示 const touched = reactive({ employeeId: false, password: false }) // 字段验证规则 const rules = { employeeId: { required: true, message: '账号不能为空' }, password: { required: true, message: '密码不能为空' } } // 字段错误信息 const errors = reactive({ employeeId: '', password: '' }) const validateField = (field) => { const rule = rules[field] const value = form[field] if (rule && rule.required && (!value || !value.trim())) { errors[field] = rule.message return false } errors[field] = '' return true } const validateForm = () => { // 标记所有字段为已触碰 Object.keys(touched).forEach(key => { touched[key] = true }) // 验证所有字段 const validResults = Object.keys(rules).map(field => validateField(field)) return validResults.every(result => result) } const onSubmit = async () => { if (!validateForm()) { errorMessage.value = '请填写账号和密码' return } errorMessage.value = '' loading.value = true try { const userData = await loginWithBackend() userStore.login({ ...userData, employeeId: form.employeeId }) router.push('home') } catch (error) { errorMessage.value = error.message } finally { loading.value = false } } const loginWithBackend = async () => { try { const response = await productApi.selectLogin(form) if (response?.status === 200 && response.data) { return response.data } throw new Error(response?.message || '登录失败,请检查账号密码') } catch (error) { throw new Error(error.message || '网络错误,请稍后再试') } } </script> <style scoped src="@/assets/styles.css"></style> <style scoped> :deep(.form-item) { margin-bottom: 0.5rem; } .login-container { display: flex; min-height: 100vh; background-color: #e0f7ff; /* 浅蓝色背景 */ } </style> 详细解释代码

<template> <el-card class="login-card"> 系统登录 <el-form :model="loginForm" :rules="loginRules" ref="formRef" @submit.prevent="handleLogin" class="login-form" > <el-form-item prop="username"> <el-input v-model="loginForm.username" placeholder="请输入用户名" prefix-icon="User" size="large" class="input-item" /> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" prefix-icon="Lock" show-password size="large" class="input-item" /> </el-form-item> <el-button type="primary" class="login-btn" native-type="submit" :loading="loading"> 登 录 </el-button> <el-button class="reset-btn" @click="handleReset"> 重 置 </el-button> </el-form> </el-card> </template> <script setup lang="ts"> import { ref, reactive } from 'vue' import { useRouter } from 'vue-router' import { ElMessage, type FormInstance, type FormRules } from 'element-plus' import { loginApi } from '@/api/login' // 根据你的实际路径调整 const router = useRouter() const formRef = ref<FormInstance>() const loading = ref(false) // 登录表单数据 const loginForm = reactive({ username: '', password: '', }) // 表单验证规则 const loginRules = reactive<FormRules>({ username: [ { required: true, message: '请输入用户名', trigger: 'blur' }, { min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }, ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }, ], }) // 处理登录提交 const handleLogin = async () => { // 表单验证 const valid = await formRef.value?.validate() if (!valid) return loading.value = true try { // 调用登录API const { code, data, msg } = await loginApi(loginForm) if (code === 1) { // 登录成功处理 ElMessage.success('登录成功') // 存储token到localStorage localStorage.setItem('token', data.token) // 跳转到首页 router.push('/index') } else { // 其他错误处理 ElMessage.error(msg || '登录失败,请重试') } } catch (error: any) { // 401错误处理 if (error.response?.status === 401) { ElMessage.error('用户名或密码错误') } else { ElMessage.error('登录请求失败:' + (error.message || '未知错误')) } } finally { loading.value = false } } // 新增:处理重置表单 const handleReset = () => { formRef.value?.resetFields() } </script> <style scoped lang="scss"> .login-container { display: flex; justify-content: center; align-items: center; height: 100vh; position: relative; overflow: hidden; .login-bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); z-index: 1; } &::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: url('https://images.unsplash.com/photo-1497366754035-f200968a6e72?ixlib=rb-4.0.3&auto=format&fit=crop&w=1950&q=80') no-repeat center center; background-size: cover; opacity: 0.1; z-index: 2; } } .login-card { width: 420px; padding: 40px 30px; border-radius: 16px; box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25); z-index: 3; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); border: none; .logo-container { text-align: center; margin-bottom: 30px; .logo { width: 80px; height: 80px; margin: 0 auto 15px; border-radius: 50%; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 10px rgba(37, 117, 252, 0.3); &::after { content: 'EM'; font-size: 28px; font-weight: bold; color: white; } } .login-title { text-align: center; margin-bottom: 5px; color: #2c3e50; font-size: 24px; font-weight: 600; letter-spacing: 1px; } } .login-form { .input-item { margin-bottom: 20px; :deep(.el-input__wrapper) { border-radius: 12px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); padding: 0 15px; height: 48px; &:hover { box-shadow: 0 2px 8px rgba(37, 117, 252, 0.2); } } } } .button-group { display: flex; gap: 15px; margin-top: 10px; .login-btn { flex: 1; height: 48px; font-size: 16px; font-weight: 500; letter-spacing: 2px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); border: none; border-radius: 12px; box-shadow: 0 4px 10px rgba(37, 117, 252, 0.3); transition: all 0.3s ease; &:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(37, 117, 252, 0.4); } } .reset-btn { flex: 1; height: 48px; font-size: 16px; font-weight: 500; letter-spacing: 2px; background: #f5f7fa; color: #606266; border: none; border-radius: 12px; transition: all 0.3s ease; &:hover { background: #e4e7ed; color: #2c3e50; } } } } </style> 优化下样式美化下页面,可以保持原有的功能上,重构样式

<template> <el-container class="layout-container-demo" style="height: 500px"> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <el-icon style="margin-right: 8px; margin-top: 1px"> <setting /> </el-icon> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="logOut()">退出登录</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> 关宇航 </el-header> <el-container> <el-aside width="200px"> <el-scrollbar> <el-menu :default-active="$route.path" class="el-menu-vertical-demo" :router="true"> <template v-for="(item, index) in menuList"> <el-menu-item v-if="isShow(item)" :index="index" :route="item.path">{{ item.label }}</el-menu-item> </template> </el-menu> </el-scrollbar> </el-aside> <el-main> <router-view /> </el-main> </el-container> </el-container> </template> <script setup> import { Menu as IconMenu, Setting } from '@element-plus/icons-vue' import useUserStore from '@/store/modules/user'; import { ElMessage } from 'element-plus' const userStore = useUserStore(); const router = useRouter(); const menuList = ref([]); function isShow(item) { return item?.aaaaa != 'asaf' } onMounted(() => { if (userStore.isLogin()) { setMenu(router.options.routes); } else { ElMessage.error(‘用户未登录,跳转至登录页面’); router.push(‘/’); } }); function setMenu(routes) { const menu = []; for (const route of routes) { if (route.children) { for (const child of route.children) { if (child.meta && child.meta.title) { const menuItem = { index: child.path, path: route.path + ‘/’ + child.path, label: child.meta.title, aaaaa: child.meta.aaaaa, }; menu.push(menuItem); } } } } menuList.value = menu } function logOut() { userStore.logOut(); ElMessage.success(‘已退出登录’); router.push(‘/’); } </script> <style scoped> .layout-container-demo .el-header { position: relative; background-color: var(--el-color-primary-light-7); color: var(--el-text-color-primary); } .layout-container-demo .el-aside { color: var(--el-text-color-primary); background: var(--el-color-primary-light-8); height: 100vh; } .layout-container-demo .el-menu { border-right: none; } .layout-container-demo .el-main { padding: 0; } .layout-container-demo .toolbar { display: inline-flex; align-items: center; justify-content: center; height: 100%; right: 20px; } </style> 我想把 用户管理画面,项目一览画面,项目管理画面 ,障碍票一览画面,障碍票详细画面 加入上部导航栏中,然后在丰富一下画面内容 用<template>画

登录页 <template> 草池农产品网站 <el-form ref="loginFormRef" style="max-width: 600px" :model="loginForm" status-icon :rules="loginRules" label-width="90px" class="loginBox" > <el-form-item label="用户名" prop="username" > <el-input v-model="loginForm.username" autocomplete="off" /> </el-form-item> <el-form-item label="密码" prop="password" > <el-input v-model="loginForm.password" type="password" autocomplete="off" /> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm()"> 登录 </el-button> </el-form-item> </el-form> </template> <script setup> import {reactive,ref} from 'vue' import {useRouter} from 'vue-router' import axios from 'axios' import {ElMessage} from 'element-plus' //响应式对象 const loginForm = reactive({ username:'lihua', password:'123456' }) //引用式对象, const loginFormRef = ref() // 规则 const loginRules = reactive({ username:[ {required:true,message:'请输入用户名',trigger:'blur'}, {min:1,max:10,message:'用户名长度为1-10个字符',trigger:'blur'} ], password:[ {required:true,message:'请输入密码',trigger:'blur'}, {min:6,max:14,message:'密码长度为6-14个字符',trigger:'blur'} ], }) const router = useRouter() // 提交表单函数 const submitForm = ()=>{ //校验表单 loginFormRef.value.validate((valid)=>{ // console.log(valid); if(valid){ // console.log(loginForm); // localStorage.setItem('token','niaho') axios.post('/api/login',loginForm).then(res=>{ console.log(res.data); if(res.data.status=="0"){ router.push('/index') // localStorage.setItem('token','niaho') localStorage.setItem('token', res.data.token); }else{ ElMessage.error('用户名或密码错误')

大家在看

recommend-type

基于 ADS9110的隔离式数据采集 (DAQ) 系统方案(待编辑)-电路方案

描述 该“可实现最大 SNR 和采样率的 18 位 2Msps 隔离式数据采集参考设计”演示了如何应对隔离式数据采集系统设计中的典型性能限制挑战: 通过将数字隔离器引入的传播延迟降至最低,使采样率达到最大 通过有效地减轻数字隔离器引入的 ADC 采样时钟抖动,使高频交流信号链性能 (SNR) 达到最大 特性 18 位、2Msps、1 通道、差分输入、隔离式数据采集 (DAQ) 系统 利用 ADS9110 的 multiSPI:trade_mark: 数字接口实现 2MSPS 采样率,同时保持低 SPI 数据速率 源同步 SPI 数据传输模式,可将隔离器传播延迟降至最低并提高采样率 可降低隔离器引入的抖动的技术,能够将 SNR 提高 12dB(100kHz Fin,2MSPS) 经测试的设计包含理论和计算、组件选择、PCB 设计和测量结果 原理图 附件文档: 方案相关器件: ISO1541:低功耗、双向 I2C 隔离器 ISO7840:高性能 5.7kVRMS 增强型四通道数字隔离器 ISO7842:高性能 5.7kVRMS 增强型四通道数字隔离器
recommend-type

自动化图书管理系统 v7.0

自动化图书馆管理系统包含了目前图书馆管理业务的每个环节,能同时管理图书和期刊,能打印条码、书标,并制作借书证,最大藏书量在300万册以上。系统采用CNMARC标准及中图法第四版分类,具有Web检索与发布功能,条码扫描,支持一卡通,支持触摸屏。系统包括系统管理、读者管理、编目、流通、统计、查询等功能。能够在一个界面下实现图书、音像、期刊的管理,设置假期、设置暂离锁(提高安全性)、暂停某些读者的借阅权、导入导出读者、交换MARC数据、升级辅助编目库等。安装本系统前请先安装SQL 2000SQL 下载地址 http://pan.baidu.com/s/145vkr安装过程如有问题可咨询: TEL 13851381727  QQ 306404635
recommend-type

真正的VB6.0免安装,可以装U盘启动了

这个,,资源都来自CSDN大神们,在这里声明下。
recommend-type

详细说明 VC++的MFC开发串口调试助手源代码,包括数据发送,接收,显示制式等29782183com

详细说明 VC++的MFC开发串口调试助手源代码,包括数据发送,接收,显示制式等29782183com
recommend-type

文档编码批量转换UTF16toUTF8.rar

将UTF16编码格式的文件转换编码到UTF8 使用格式:U16toU8.exe [output] 如果没有output,则覆盖源文件,否则输出到output中 方便命令行使用,批量转换文件编码

最新推荐

recommend-type

C#类库封装:简化SDK调用实现多功能集成,构建地磅无人值守系统

内容概要:本文介绍了利用C#类库封装多个硬件设备的SDK接口,实现一系列复杂功能的一键式调用。具体功能包括身份证信息读取、人证识别、车牌识别(支持臻识和海康摄像头)、LED显示屏文字输出、称重数据读取、二维码扫描以及语音播报。所有功能均被封装为简单的API,极大降低了开发者的工作量和技术门槛。文中详细展示了各个功能的具体实现方式及其应用场景,如身份证读取、人证核验、车牌识别等,并最终将这些功能整合到一起,形成了一套完整的地磅称重无人值守系统解决方案。 适合人群:具有一定C#编程经验的技术人员,尤其是需要快速集成多种硬件设备SDK的应用开发者。 使用场景及目标:适用于需要高效集成多种硬件设备SDK的项目,特别是那些涉及身份验证、车辆管理、物流仓储等领域的企业级应用。通过使用这些封装好的API,可以大大缩短开发周期,降低维护成本,提高系统的稳定性和易用性。 其他说明:虽然封装后的API极大地简化了开发流程,但对于一些特殊的业务需求,仍然可能需要深入研究底层SDK。此外,在实际部署过程中,还需考虑网络环境、硬件兼容性等因素的影响。
recommend-type

基于STM32F1的BLDC无刷直流电机与PMSM永磁同步电机源码解析:传感器与无传感器驱动详解

基于STM32F1的BLDC无刷直流电机和PMSM永磁同步电机的驱动实现方法,涵盖了有传感器和无传感两种驱动方式。对于BLDC电机,有传感器部分采用霍尔传感器进行六步换相,无传感部分则利用反电动势过零点检测实现换相。对于PMSM电机,有传感器部分包括霍尔传感器和编码器的方式,无传感部分则采用了滑模观测器进行矢量控制(FOC)。文中不仅提供了详细的代码片段,还分享了许多调试经验和技巧。 适合人群:具有一定嵌入式系统和电机控制基础知识的研发人员和技术爱好者。 使用场景及目标:适用于需要深入了解和实现BLDC和PMSM电机驱动的开发者,帮助他们掌握不同传感器条件下的电机控制技术和优化方法。 其他说明:文章强调了实际调试过程中可能遇到的问题及其解决方案,如霍尔传感器的中断触发换相、反电动势过零点检测的采样时机、滑模观测器的参数调整以及编码器的ABZ解码等。
recommend-type

基于Java的跨平台图像处理软件ImageJ:多功能图像编辑与分析工具

内容概要:本文介绍了基于Java的图像处理软件ImageJ,详细阐述了它的跨平台特性、多线程处理能力及其丰富的图像处理功能。ImageJ由美国国立卫生研究院开发,能够在多种操作系统上运行,包括Windows、Mac OS、Linux等。它支持多种图像格式,如TIFF、PNG、GIF、JPEG、BMP、DICOM、FITS等,并提供图像栈功能,允许多个图像在同一窗口中进行并行处理。此外,ImageJ还提供了诸如缩放、旋转、扭曲、平滑处理等基本操作,以及区域和像素统计、间距、角度计算等高级功能。这些特性使ImageJ成为科研、医学、生物等多个领域的理想选择。 适合人群:需要进行图像处理的专业人士,如科研人员、医生、生物学家,以及对图像处理感兴趣的普通用户。 使用场景及目标:适用于需要高效处理大量图像数据的场合,特别是在科研、医学、生物学等领域。用户可以通过ImageJ进行图像的编辑、分析、处理和保存,提高工作效率。 其他说明:ImageJ不仅功能强大,而且操作简单,用户无需安装额外的运行环境即可直接使用。其基于Java的开发方式确保了不同操作系统之间的兼容性和一致性。
recommend-type

Teleport Pro教程:轻松复制网站内容

标题中提到的“复制别人网站的软件”指向的是一种能够下载整个网站或者网站的特定部分,然后在本地或者另一个服务器上重建该网站的技术或工具。这类软件通常被称作网站克隆工具或者网站镜像工具。 描述中提到了一个具体的教程网址,并提到了“天天给力信誉店”,这可能意味着有相关的教程或资源可以在这个网店中获取。但是这里并没有提供实际的教程内容,仅给出了网店的链接。需要注意的是,根据互联网法律法规,复制他人网站内容并用于自己的商业目的可能构成侵权,因此在此类工具的使用中需要谨慎,并确保遵守相关法律法规。 标签“复制 别人 网站 软件”明确指出了这个工具的主要功能,即复制他人网站的软件。 文件名称列表中列出了“Teleport Pro”,这是一款具体的网站下载工具。Teleport Pro是由Tennyson Maxwell公司开发的网站镜像工具,允许用户下载一个网站的本地副本,包括HTML页面、图片和其他资源文件。用户可以通过指定开始的URL,并设置各种选项来决定下载网站的哪些部分。该工具能够帮助开发者、设计师或内容分析人员在没有互联网连接的情况下对网站进行离线浏览和分析。 从知识点的角度来看,Teleport Pro作为一个网站克隆工具,具备以下功能和知识点: 1. 网站下载:Teleport Pro可以下载整个网站或特定网页。用户可以设定下载的深度,例如仅下载首页及其链接的页面,或者下载所有可访问的页面。 2. 断点续传:如果在下载过程中发生中断,Teleport Pro可以从中断的地方继续下载,无需重新开始。 3. 过滤器设置:用户可以根据特定的规则过滤下载内容,如排除某些文件类型或域名。 4. 网站结构分析:Teleport Pro可以分析网站的链接结构,并允许用户查看网站的结构图。 5. 自定义下载:用户可以自定义下载任务,例如仅下载图片、视频或其他特定类型的文件。 6. 多任务处理:Teleport Pro支持多线程下载,用户可以同时启动多个下载任务来提高效率。 7. 编辑和管理下载内容:Teleport Pro具备编辑网站镜像的能力,并可以查看、修改下载的文件。 8. 离线浏览:下载的网站可以在离线状态下浏览,这对于需要测试网站在不同环境下的表现的情况十分有用。 9. 备份功能:Teleport Pro可以用来备份网站,确保重要数据的安全。 在实际使用此类工具时,需要注意以下几点: - 著作权法:复制网站内容可能侵犯原作者的版权,因此在使用此类工具时,必须确保有合法权利去下载和使用目标网站的内容。 - 服务条款:许多网站的服务条款明确禁止未经授权的网站克隆。因此,在使用此类软件之前,应当仔细阅读并遵守目标网站的服务条款。 - 数据隐私:下载含有个人数据的网站可能触及隐私保护法律,特别是在欧洲通用数据保护条例(GDPR)等法规的环境下。 - 网络安全:随意下载网站可能导致恶意软件和病毒的传播,用户应当使用最新的反病毒软件,并在安全的环境中进行操作。 综上所述,虽然“复制别人网站的软件”在技术上是可行的,且可以用于多种正当目的,但在使用此类工具时,必须遵守法律法规和道德规范,不可侵犯他人的合法权益。
recommend-type

【跨平台开发者的必读】:解决Qt5Widgetsd.lib目标计算机类型冲突终极指南

# 1. 跨平台开发与Qt框架简介 跨平台开发已经成为现代软件开发中不可或缺的一部分,它使得开发者能够构建出能够在多种操作系统上运行的应用程序,从而触及更广泛的用户群体。在这其中,Qt框架作为一套成熟的跨平台应用程序和用户界面框架,扮演着关键角色。Qt不仅仅是一个跨平台的工具集,它的设计哲学还包含了模块化和可扩展性,使其能够适应各种规模和复杂度的项目。本章节我们将简要介绍
recommend-type

普通RNN结构和特点

<think>首先,用户的问题是:“我想了解普通RNN的结构及其特点请问RNN循环神经网络结构特点工作原理”。这涉及到RNN的结构、特点和工作原理。根据系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式必须使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-回答中引用的段落末尾自然地添加引用标识。用户可见层指令:-回答结构清晰,帮助用户逐步解决问题。-保证回答真实可靠。参考站内引用:-引用[1]:关于RNN的基本介绍,为什么需要RNN。-引用[2]:关于RNN的工作原理、结构图,以及与其他网络的比较。用户上一次的问题和我的回答:用户是第一次
recommend-type

探讨通用数据连接池的核心机制与应用

根据给定的信息,我们能够推断出讨论的主题是“通用数据连接池”,这是一个在软件开发和数据库管理中经常用到的重要概念。在这个主题下,我们可以详细阐述以下几个知识点: 1. **连接池的定义**: 连接池是一种用于管理数据库连接的技术,通过维护一定数量的数据库连接,使得连接的创建和销毁操作更加高效。开发者可以在应用程序启动时预先创建一定数量的连接,并将它们保存在一个池中,当需要数据库连接时,可以直接从池中获取,从而降低数据库连接的开销。 2. **通用数据连接池的概念**: 当提到“通用数据连接池”时,它意味着这种连接池不仅支持单一类型的数据库(如MySQL、Oracle等),而且能够适应多种不同数据库系统。设计一个通用的数据连接池通常需要抽象出一套通用的接口和协议,使得连接池可以兼容不同的数据库驱动和连接方式。 3. **连接池的优点**: - **提升性能**:由于数据库连接创建是一个耗时的操作,连接池能够减少应用程序建立新连接的时间,从而提高性能。 - **资源复用**:数据库连接是昂贵的资源,通过连接池,可以最大化现有连接的使用,避免了连接频繁创建和销毁导致的资源浪费。 - **控制并发连接数**:连接池可以限制对数据库的并发访问,防止过载,确保数据库系统的稳定运行。 4. **连接池的关键参数**: - **最大连接数**:池中能够创建的最大连接数。 - **最小空闲连接数**:池中保持的最小空闲连接数,以应对突发的连接请求。 - **连接超时时间**:连接在池中保持空闲的最大时间。 - **事务处理**:连接池需要能够管理不同事务的上下文,保证事务的正确执行。 5. **实现通用数据连接池的挑战**: 实现一个通用的连接池需要考虑到不同数据库的连接协议和操作差异。例如,不同的数据库可能有不同的SQL方言、认证机制、连接属性设置等。因此,通用连接池需要能够提供足够的灵活性,允许用户配置特定数据库的参数。 6. **数据连接池的应用场景**: - **Web应用**:在Web应用中,为了处理大量的用户请求,数据库连接池可以保证数据库连接的快速复用。 - **批处理应用**:在需要大量读写数据库的批处理作业中,连接池有助于提高整体作业的效率。 - **微服务架构**:在微服务架构中,每个服务可能都需要与数据库进行交互,通用连接池能够帮助简化服务的数据库连接管理。 7. **常见的通用数据连接池技术**: - **Apache DBCP**:Apache的一个Java数据库连接池库。 - **C3P0**:一个提供数据库连接池和控制工具的开源Java框架。 - **HikariCP**:目前性能最好的开源Java数据库连接池之一。 - **BoneCP**:一个高性能的开源Java数据库连接池。 - **Druid**:阿里巴巴开源的一个数据库连接池,提供了对性能监控的高级特性。 8. **连接池的管理与监控**: 为了保证连接池的稳定运行,开发者需要对连接池的状态进行监控,并对其进行适当的管理。监控指标可能包括当前活动的连接数、空闲的连接数、等待获取连接的请求队列长度等。一些连接池提供了监控工具或与监控系统集成的能力。 9. **连接池的配置和优化**: 连接池的性能与连接池的配置密切相关。需要根据实际的应用负载和数据库性能来调整连接池的参数。例如,在高并发的场景下,可能需要增加连接池中连接的数量。另外,适当的线程池策略也可以帮助连接池更好地服务于多线程环境。 10. **连接池的应用案例**: 一个典型的案例是电商平台在大型促销活动期间,用户访问量激增,此时通用数据连接池能够保证数据库操作的快速响应,减少因数据库连接问题导致的系统瓶颈。 总结来说,通用数据连接池是现代软件架构中的重要组件,它通过提供高效的数据库连接管理,增强了软件系统的性能和稳定性。了解和掌握连接池的原理及实践,对于任何涉及数据库交互的应用开发都至关重要。在实现和应用连接池时,需要关注其设计的通用性、配置的合理性以及管理的有效性,确保在不同的应用场景下都能发挥出最大的效能。
recommend-type

【LabVIEW网络通讯终极指南】:7个技巧提升UDP性能和安全性

# 摘要 本文系统介绍了LabVIEW在网络通讯中的应用,尤其是针对UDP协议的研究与优化。首先,阐述了UDP的原理、特点及其在LabVIEW中的基础应用。随后,本文深入探讨了通过调整数据包大小、实现并发通信及优化缓冲区管理等技巧来优化UDP性能的LabVIEW方法。接着,文章聚焦于提升UDP通信安全性,介绍了加密技术和认证授权机制在LabVIEW中的实现,以及防御网络攻击的策略。最后,通过具体案例展示了LabVIEW在实时数据采集和远程控制系统中的高级应用,并展望了LabVIEW与UDP通讯技术的未来发展趋势及新兴技术的影响。 # 关键字 LabVIEW;UDP网络通讯;性能优化;安全性;
recommend-type

简要介绍cnn卷积神经网络

<think>首先,用户的问题是:“我想了解卷积神经网络的基本概念和原理 请问CNN卷积神经网络简介及其工作原理”。所以,我需要基于提供的引用材料,构建一个清晰的回答。 引用材料总结: - [^1]: CNN的核心思想是局部感受野、权值共享和时间或空间亚采样,提供位移、尺度、形变不变性。三大特色:局部感知、权重共享和多卷积核。 - [^2]: CNN是一种前馈神经网络,由卷积层和池化层组成,特别在图像处理方面出色。与传统多层神经网络相比,CNN加入了卷积层和池化层,使特征学习更有效。 - [^3]: CNN与全连接神经网络的区别:至少有一个卷积层提取特征;神经元局部连接和权值共享,减少参数数
recommend-type

基于ASP的深度学习网站导航系统功能详解

从给定文件中我们可以提取以下IT知识点: ### 标题知识点 #### "ASP系统篇" - **ASP技术介绍**:ASP(Active Server Pages)是一种服务器端的脚本环境,用于创建动态交互式网页。ASP允许开发者将HTML网页与服务器端脚本结合,使用VBScript或JavaScript等语言编写代码,以实现网页内容的动态生成。 - **ASP技术特点**:ASP适用于小型到中型的项目开发,它可以与数据库紧密集成,如Microsoft的Access和SQL Server。ASP支持多种组件和COM(Component Object Model)对象,使得开发者能够实现复杂的业务逻辑。 #### "深度学习网址导航系统" - **深度学习概念**:深度学习是机器学习的一个分支,通过构建深层的神经网络来模拟人类大脑的工作方式,以实现对数据的高级抽象和学习。 - **系统功能与深度学习的关系**:该标题可能意味着系统在进行网站分类、搜索优化、内容审核等方面采用了深度学习技术,以提供更智能、自动化的服务。然而,根据描述内容,实际上系统并没有直接使用深度学习技术,而是提供了一个传统的网址导航服务,可能是命名上的噱头。 ### 描述知识点 #### "全后台化管理,操作简单" - **后台管理系统的功能**:后台管理系统允许网站管理员通过Web界面执行管理任务,如内容更新、用户管理等。它通常要求界面友好,操作简便,以适应不同技术水平的用户。 #### "栏目无限分类,自由添加,排序,设定是否前台显示" - **动态网站结构设计**:这意味着网站结构具有高度的灵活性,支持创建无限层级的分类,允许管理员自由地添加、排序和设置分类的显示属性。这种设计通常需要数据库支持动态生成内容。 #### "各大搜索和站内搜索随意切换" - **搜索引擎集成**:网站可能集成了外部搜索引擎(如Google、Bing)和内部搜索引擎功能,让用户能够方便地从不同来源获取信息。 #### "网站在线提交、审阅、编辑、删除" - **内容管理系统的功能**:该系统提供了一个内容管理平台,允许用户在线提交内容,由管理员进行审阅、编辑和删除操作。 #### "站点相关信息后台动态配置" - **动态配置机制**:网站允许管理员通过后台系统动态调整各种配置信息,如网站设置、参数调整等,从而实现快速的网站维护和更新。 #### "自助网站收录,后台审阅" - **网站收录和审核机制**:该系统提供了一套自助收录流程,允许其他网站提交申请,由管理员进行后台审核,决定是否收录。 #### "网站广告在线发布" - **广告管理功能**:网站允许管理员在线发布和管理网站广告位,以实现商业变现。 #### "自动生成静态页 ver2.4.5" - **动态与静态内容**:系统支持动态内容的生成,同时也提供了静态页面的生成机制,这可能有助于提高网站加载速度和搜索引擎优化。 #### "重写后台网址分类管理" - **系统优化与重构**:提到了后台网址分类管理功能的重写,这可能意味着系统进行了一次重要的更新,以修复前一个版本的错误,并提高性能。 ### 标签知识点 #### "ASP web 源代码 源码" - **ASP程序开发**:标签表明这是一个ASP语言编写的网站源代码,可能是一个开源项目,供开发者下载、研究或部署到自己的服务器上。 ### 压缩包子文件名称列表知识点 #### "深度学习(asp)网址导航程序" - **文件内容和类型**:文件列表中提到的“深度学习(asp)网址导航程序”表明这是一个ASP语言编写的网址导航系统程序,可能包含了系统安装和配置需要的所有源文件。 通过以上分析,我们可以得出这个ASP系统是一个传统的网址导航系统,以后台管理为核心功能,并没有实际运用到深度学习技术。系统的主要功能包括对网站内容、分类、搜索引擎、广告位、以及其他网站相关信息的管理。它可能还提供了一个平台,供用户提交网址,供管理员审核并收录到导航中。源代码可能以ASP语言编写,并在文件中包含了所有必要的程序文件。