diff --git a/.env.development b/.env.development index 3669283..05d2c29 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,12 @@ +### + # @Author: donghao donghao@supervision.ltd + # @Date: 2025-03-12 15:26:57 + # @LastEditors: donghao donghao@supervision.ltd + # @LastEditTime: 2025-03-13 09:17:19 + # @FilePath: \5G-Loading-Bay-Web\.env.development + # @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE +### # .env.development NODE_ENV = development VITE_APP_ENV = development -VITE_APP_BASE_API = http://192.168.10.14:8888 +VITE_APP_BASE_API = http://192.168.10.14:8000 diff --git a/src/api/dashboard.ts b/src/api/dashboard.ts index 7e37d17..55dea2d 100644 --- a/src/api/dashboard.ts +++ b/src/api/dashboard.ts @@ -1 +1,20 @@ -// 接口层 \ No newline at end of file +/* + * @Author: donghao donghao@supervision.ltd + * @Date: 2025-03-07 15:09:18 + * @LastEditors: donghao donghao@supervision.ltd + * @LastEditTime: 2025-03-13 10:13:49 + * @FilePath: \5G-Loading-Bay-Web\src\api\dashboard.ts + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +// 接口层 + +import request from "@/utils/request/instance"; +import { config } from "@/config"; + +//TODO 定义响应类型 + + +export const getDeviceStatusApi = (params: any) => { + return request.get(`/api/v1/device/device/`, params); +}; + diff --git a/src/api/user.ts b/src/api/user.ts index ff83f21..5bdc07f 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -2,7 +2,7 @@ * @Author: donghao donghao@supervision.ltd * @Date: 2025-03-12 15:13:38 * @LastEditors: donghao donghao@supervision.ltd - * @LastEditTime: 2025-03-12 19:20:43 + * @LastEditTime: 2025-03-13 09:29:20 * @FilePath: \5G-Loading-Bay-Web\src\api\user.ts * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ @@ -19,11 +19,16 @@ interface LoginRes { } export const loginApi = (data: { username: string; password: string }) => { - return request.post<LoginRes>(`${config.baseURL}/api/v1/user/login`, data, { + return request.post<LoginRes>(`/api/v1/user/login/`, data, { showLoading: false // 单独关闭loading }) } +export const loginOutApi = () => { + return request.post(`/api/v1/user/logout/`, {}, { + showLoading: false // 单独关闭loading + }) +} // export const getUserInfo = (userId: number) => { // return request.get(`/user/info/${userId}`) // } \ No newline at end of file diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue index 8bf2ea0..9762a86 100644 --- a/src/components/Navbar.vue +++ b/src/components/Navbar.vue @@ -2,12 +2,13 @@ * @Author: donghao donghao@supervision.ltd * @Date: 2025-03-06 15:42:11 * @LastEditors: donghao donghao@supervision.ltd - * @LastEditTime: 2025-03-11 15:42:37 + * @LastEditTime: 2025-03-13 10:49:12 * @FilePath: \vite-ai\data-dashboard\src\components\Navbar.vue * @Description: 标题栏 --> <script setup lang="ts"> // import { useNav } from "@/layout/hooks/useNav"; +// import router, { resetRouter } from "@/router"; import { useUserStore } from '@/stores/user' const userStore = useUserStore() @@ -46,9 +47,9 @@ function getTime() { _formatNum(nowDate.getHours()) + ":" + _formatNum(nowDate.getMinutes()) - // + - // ":" + - // _formatNum(nowDate.getSeconds()); + // + + // ":" + + // _formatNum(nowDate.getSeconds()); let week = ""; switch (nowDate.getDay()) { case 0: @@ -122,6 +123,7 @@ onMounted(() => { .left { width: 30vw; } + .center_title { // margin-top: 19px; background: url("@/assets/common/nav_title.png") no-repeat; @@ -131,13 +133,16 @@ onMounted(() => { text-align: center; font-size: 32px; } + .right { width: 30vw; + .date_box { color: #009dff; font-size: 16px; margin-right: 24px; font-family: DingTalk JinBuTi; + &>span { padding-left: 5px; } diff --git a/src/stores/user.ts b/src/stores/user.ts index 9726f3d..7acb456 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -2,13 +2,17 @@ * @Author: donghao donghao@supervision.ltd * @Date: 2025-03-06 17:57:05 * @LastEditors: donghao donghao@supervision.ltd - * @LastEditTime: 2025-03-12 15:20:34 + * @LastEditTime: 2025-03-13 10:49:15 * @FilePath: \5G-Loading-Bay-Web\src\stores\user.ts * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ import { defineStore } from "pinia"; import router, { resetRouter } from "@/router"; -import { getLocal, setLocal, removeLocal} from "@/utils/local"; +import { getLocal, setLocal, removeLocal } from "@/utils/local"; +import { loginOutApi } from "@/api/user"; +import { isSuccessApi } from "@/utils/forApi"; +import { ElMessage } from 'element-plus' + interface UserState { token: string | null; } @@ -23,16 +27,20 @@ export const useUserStore = defineStore("user", { form: { remember: boolean; username: string; password: string } ) { this.token = token; - setLocal("token", token) + setLocal("token", token); form.remember ? setLocal("userLoginInfo", form) : removeLocal("userLoginInfo"); }, - logout() { + async logout() { + const res = await loginOutApi(); + if(isSuccessApi(res)){ + ElMessage.success(`退出登录`) + } this.token = null; removeLocal("token"); - // resetRouter(); - // router.replace("/login"); + resetRouter(); + router.replace("/login"); }, }, }); diff --git a/src/utils/forApi.ts b/src/utils/forApi.ts new file mode 100644 index 0000000..b9d5875 --- /dev/null +++ b/src/utils/forApi.ts @@ -0,0 +1,19 @@ +/* + * @Author: donghao donghao@supervision.ltd + * @Date: 2024-08-14 14:42:09 + * @LastEditors: donghao donghao@supervision.ltd + * @LastEditTime: 2025-03-13 10:20:54 + * @FilePath: \General-AI-Platform-Web-Client\src\utils\forApi.ts + * @Description: 这是接口层和业务层的转换工具方法集 + */ +/** + * @判断接口数据是否成功返回 + * @param result 接口返回数据 + * @returns boolean + */ +export function isSuccessApi(result): boolean { + if ([200].includes(result.code)) { + return true; + } + return false; +} diff --git a/src/utils/request/index.ts b/src/utils/request/index.ts index 47bb10d..f6a30d5 100644 --- a/src/utils/request/index.ts +++ b/src/utils/request/index.ts @@ -2,106 +2,113 @@ * @Author: donghao donghao@supervision.ltd * @Date: 2025-03-12 15:11:56 * @LastEditors: donghao donghao@supervision.ltd - * @LastEditTime: 2025-03-12 15:12:06 + * @LastEditTime: 2025-03-13 10:48:58 * @FilePath: \5G-Loading-Bay-Web\src\utils\request\index.ts * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ -import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' -import { ElMessage, ElMessageBox } from 'element-plus' -import type { RequestConfig, RequestInterceptors } from './type' -import { useUserStore } from '@/stores/user' +import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; +import { ElMessage, ElMessageBox } from "element-plus"; +import type { RequestConfig, RequestInterceptors } from "./type"; +import { useUserStore } from "@/stores/user"; class Request { - instance: AxiosInstance - interceptors?: RequestInterceptors + instance: AxiosInstance; + interceptors?: RequestInterceptors; constructor(config: RequestConfig) { - this.instance = axios.create(config) - this.interceptors = config.interceptors + this.instance = axios.create(config); + this.interceptors = config.interceptors; // 全局请求拦截器 this.instance.interceptors.request.use( (config: RequestConfig) => { // 处理 token - const userStore = useUserStore() + const userStore = useUserStore(); if (userStore.token) { - config.headers!.Authorization = `Bearer ${userStore.token}` + config.headers!.Access = `Bearer ${userStore.token}`; } - return config + return config; }, (error: any) => Promise.reject(error) - ) + ); // 实例拦截器 this.instance.interceptors.request.use( this.interceptors?.requestInterceptor, this.interceptors?.requestInterceptorCatch - ) - + ); + this.instance.interceptors.response.use( this.interceptors?.responseInterceptor, this.interceptors?.responseInterceptorCatch - ) + ); // 全局响应拦截器 this.instance.interceptors.response.use( (res: AxiosResponse) => { - const { code, message } = res.data + const { code, message } = res.data; if (code !== 200) { - ElMessage.error(message || '请求失败') - return Promise.reject(message) + ElMessage.error(message || "请求失败"); + return Promise.reject(message); } - return res.data + return res.data; }, (error: any) => { // 处理 HTTP 状态码 if (error.response?.status === 401) { - ElMessageBox.confirm('登录已过期,请重新登录', '提示', { - confirmButtonText: '重新登录', - cancelButtonText: '取消', - type: 'warning' + ElMessageBox.confirm("登录已过期,请重新登录", "提示", { + confirmButtonText: "重新登录", + cancelButtonText: "取消", + type: "warning", }).then(() => { - const userStore = useUserStore() - userStore.logout() - location.reload() - }) + const userStore = useUserStore(); + userStore.logout(); + }); } - ElMessage.error(error.message || '请求错误') - return Promise.reject(error) + ElMessage.error(error.message || "请求错误"); + return Promise.reject(error); } - ) + ); } request<T = any>(config: RequestConfig<T>): Promise<T> { return new Promise((resolve, reject) => { // 单个请求的拦截器 if (config.interceptors?.requestInterceptor) { - config = config.interceptors.requestInterceptor(config) + config = config.interceptors.requestInterceptor(config); } this.instance .request<any, T>(config) - .then(res => { + .then((res) => { if (config.interceptors?.responseInterceptor) { - res = config.interceptors.responseInterceptor(res) + res = config.interceptors.responseInterceptor(res); } - resolve(res) - }) - .catch(err => { - reject(err) + resolve(res); }) - }) + .catch((err) => { + reject(err); + }); + }); } - get<T = any>(url: string, config?: RequestConfig<T>): Promise<T> { - return this.request<T>({ ...config, method: 'GET', url }) + get<T = any>( + url: string, + params?: any, + config?: RequestConfig<T> + ): Promise<T> { + return this.request<T>({ ...config, method: "GET", url, params }); } - post<T = any>(url: string, data?: any, config?: RequestConfig<T>): Promise<T> { - return this.request<T>({ ...config, method: 'POST', url, data }) + post<T = any>( + url: string, + data?: any, + config?: RequestConfig<T> + ): Promise<T> { + return this.request<T>({ ...config, method: "POST", url, data }); } // 其他方法类似... } -export default Request \ No newline at end of file +export default Request; diff --git a/src/views/dashboard/DeviceStatus.vue b/src/views/dashboard/DeviceStatus.vue index e961c75..20bfa5b 100644 --- a/src/views/dashboard/DeviceStatus.vue +++ b/src/views/dashboard/DeviceStatus.vue @@ -10,8 +10,7 @@ </div> <div class="px-[16px] device-status-content-box"> - <div class="mt-[16px] bg-transparent baseTable_wrap full_table" v-loading="dataLoading" - :element-loading-svg="svg" element-loading-svg-view-box="-10, -10, 50, 50"> + <div class="mt-[16px] bg-transparent baseTable_wrap full_table"> <template v-if="pagination.total > 0"> <BaseTable class="bg-transparent baseTable_box" :total="pagination.total" :pageSize="pagination.pageSize" :dataSource="listData" :isFixedPagination="true" @@ -58,12 +57,13 @@ import RealVideoModal from './components/RealVideoModal.vue'; import onLineIcon from '@/assets/common/online_icon.png'; import outLineIcon from '@/assets/common/outline_icon.png'; import errorIcon from '@/assets/common/error_icon.png'; -import Demo from '../../components/videoPlayer/Demo.vue'; +import { getDeviceStatusApi } from '@/api/dashboard'; +import { isSuccessApi } from "@/utils/forApi"; defineOptions({ name: "DeviceStatusIndex" }); -const alarmLevelStatusEnum: Record<string, any>[] = [ - { +const alarmLevelStatusEnum = { + 'online': { color: "#52C41A", value: "1", label: "在线", @@ -71,56 +71,56 @@ const alarmLevelStatusEnum: Record<string, any>[] = [ icon: onLineIcon, id: "1" }, - { + 'offline': { color: "#999999", value: "2", label: "离线", isDelete: false, icon: outLineIcon, - id: "2" }, - { + 'error': { color: "#E80D0D", value: "3", label: "故障", isDelete: false, icon: errorIcon, id: "3" + }, + 'notFound': { // 未获取到设备状态 + color: "#E80D0D", + value: "4444", + label: "未知", + isDelete: false, + icon: errorIcon, + id: "4444" } -]; - -const deleteModel = reactive<{ - isShowDelete: boolean; -}>({ - isShowDelete: false -}); - +} const currentRow = ref<Record<string, any>>({}); -const isRealOpen = ref<Boolean>(false); - - +const isRealOpen = ref<Boolean>(false); // 实时视频弹窗 +const isHistoryOpen = ref<Boolean>(false); // 历史视频弹窗 const columns = [ { label: "设备名称", - property: "name" + property: "device_name" }, { label: "设备ID", - property: "code" + property: "device_number" }, { label: "设备位置", - property: "deviceGroup" + property: "device_position" }, { label: "设备状态", - property: "status", + property: "device_status", formatter: val => { console.log(val); const currentLevelObj = - alarmLevelStatusEnum[Number(val?.status) - 1]; + alarmLevelStatusEnum[val?.device_status] || alarmLevelStatusEnum.notFound; + return h( "div", @@ -130,13 +130,12 @@ const columns = [ display: "flex", alignItems: "center", lineHeight: "20px", - color: currentLevelObj.color + color: currentLevelObj?.color } }, [ h('img', { - // 使用 @ 别名引用 assets/common 目录下的图片 - src: currentLevelObj.icon, + src: currentLevelObj?.icon, style: { width: '20px', height: '20px', @@ -148,7 +147,7 @@ const columns = [ { fontSize: "14px", }, - currentLevelObj.label + currentLevelObj?.label ) ] ); @@ -160,40 +159,27 @@ const columns = [ label: "操作" } ]; - -const svg = ` - <path class="path" d=" - M 30 15 - L 28 17 - M 25.61 25.61 - A 15 15, 0, 0, 1, 15 30 - A 15 15, 0, 1, 1, 27.99 7.5 - L 15 15 - " style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/> - `; - const pagination = ref({ currentPage: 1, pageSize: 10, total: 0 }); const listData = ref([]); -const dataLoading = ref(true); const getList = async () => { - const { currentPage, pageSize } = pagination.value; - const res = await fetch('/api/getDeviceStatusList', { - method: 'POST', - body: JSON.stringify({ page: currentPage, pageSize }) - }) - const { data } = await res.json() - console.log(data, 'getList_data') - listData.value = data.list; - console.log(data.list); - pagination.value = { - ...pagination.value, - total: data.total - }; - dataLoading.value = false; -}; + try { + const { currentPage, pageSize } = pagination.value; + const res = await getDeviceStatusApi({ current: currentPage, pageSize }) + console.log(res.data, 'getList_data') + if (isSuccessApi(res)) { + listData.value = res.data.data; + pagination.value = { + ...pagination.value, + total: res.data.total + }; + } + } catch (error) { + console.error('获取数据失败:', error) + } +} function handleTableChange(record) { console.log("handleTableChange_record", record); pagination.value = { @@ -203,20 +189,20 @@ function handleTableChange(record) { }; getList(); } -/**打开视频 */ +/**打开实时视频 */ function openCurrent(row) { console.log(row, "openCurrent"); currentRow.value = row; isRealOpen.value = true; } - +/**打开历史视频 */ function openHistory(row) { console.log(row, "openHistory"); currentRow.value = row; + // TODO 历史视频模块 + isHistoryOpen.value = true; } - - onMounted(() => { getList(); }); diff --git a/src/views/dashboard/PoleMonitor copy.scss b/src/views/dashboard/PoleMonitor copy.scss deleted file mode 100644 index a9f386a..0000000 --- a/src/views/dashboard/PoleMonitor copy.scss +++ /dev/null @@ -1,93 +0,0 @@ -.pole-monitor-wrap { - background-image: url("@/assets/common/bg_banner_1.png"); - background-size: cover; - background-position: bottom; - background-repeat: no-repeat; - height: 823px; - .search-section { - padding: 16px 0; - } - .pole-main-content { - width: 100%; - } - - .pole-monitor-search-box { - display: flex; - align-items: center; - gap: 12px; - margin: 16px 0; - } - .right-panel { - .el-scrollbar__view { - background: transparent !important; - height: 600px; - } - } - .pole-monitor-main { - .left-panel { - .main-image { - box-sizing: border-box; - min-height: 511px; - position: relative; - background-color: #090F48; - border-radius: 4px; - img { - width: 100%; - max-height: 460px; - } - .image-info{ - position: absolute; - height: 52px; - line-height: 52px; - bottom: 0; - font-size: 14px; - padding: 0 16px; - &> span { - margin-right: 10px; - } - } - } - .thumbnail-container { - width: 100%; - overflow: visible; - .swiper { - width: 100%; - height: 100%; - .swiper-slide { - width: 20%; - border-radius: 4px; - img { - width: 100%; - height: 144px; - border-radius: 4px; - } - } - .active-slide img { - border-radius: 4px; - border: 2px solid #2ecce0; - } - .swiper-button-prev, - .swiper-button-next { - background-color: rgba(0, 0, 0, 0.5); - color: white; - width: 32px; - height: 32px; - border-radius: 50%; - } - - .swiper-button-prev::after, - .swiper-button-next::after { - font-size: 12px; - color: #fff; - } - - /* 修改按钮悬停样式 */ - .swiper-button-prev:hover, - .swiper-button-next:hover { - background-color: rgba(0, 0, 0, 0.8); - } - } - } - } - } -} diff --git a/src/views/login/Login.vue b/src/views/login/Login.vue index d70ee2c..23ac45d 100644 --- a/src/views/login/Login.vue +++ b/src/views/login/Login.vue @@ -2,7 +2,7 @@ * @Author: donghao donghao@supervision.ltd * @Date: 2025-03-06 17:57:05 * @LastEditors: donghao donghao@supervision.ltd - * @LastEditTime: 2025-03-12 19:39:47 + * @LastEditTime: 2025-03-13 10:50:03 * @FilePath: \5G-Loading-Bay-Web\src\views\login\Login.vue * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE? --> @@ -49,54 +49,61 @@ import { ElMessage } from 'element-plus' import { useRouter } from 'vue-router' import accountIconUrl from '@/assets/login/account_icon.png'; import passwordIconUrl from '@/assets/login/password_icon.png'; -import { getLocal} from "@/utils/local"; +import { getLocal } from "@/utils/local"; import { loginApi } from '@/api/user' +import { isSuccessApi } from "@/utils/forApi"; + const router = useRouter() const userStore = useUserStore() const form = reactive({ - username: '', - password: '', + username: 'admin', + password: 'admin', remember: false }) -// TODO 登录对接 -// const handleLogin = async () => { -// try { -// const res = await loginApi({ -// username: 'admin', -// password: 'admin123' -// }) -// ElMessage.success(`欢迎回来,${res.userInfo.username}`) -// } catch (error) { -// console.error('登录失败:', error) -// } -// } - const handleLogin = async () => { - if (form.username !== 'admin' || form.password !== 'admin123') { - ElMessage.error('用户名或密码错误') - return - } try { - // 实际项目中应调用真实接口 - const res = await fetch('/api/login', { - method: 'POST', - body: JSON.stringify(form) + const res = await loginApi({ + username: form.username, + password: form.password }) - const data = await res.json() - if (data.code === 200) { - userStore.login(data.data.token, form) + console.log(res, 'handleLogin') + if (isSuccessApi(res)) { + ElMessage.success(`欢迎回来,${form.username}`) + userStore.login(res.data.access, form) router.push('/dashboard') } - } catch (err) { + } catch (error) { ElMessage.error('登录失败') + console.error('登录失败:', error) } } +// mock登录 +// const handleLogin = async () => { +// if (form.username !== 'admin' || form.password !== 'admin123') { +// ElMessage.error('用户名或密码错误') +// return +// } +// try { +// // 实际项目中应调用真实接口 +// const res = await fetch('/api/login', { +// method: 'POST', +// body: JSON.stringify(form) +// }) +// const data = await res.json() +// if (data.code === 200) { +// userStore.login(data.data.token, form) +// router.push('/dashboard') +// } +// } catch (err) { +// ElMessage.error('登录失败') +// } +// } onMounted(() => { const loginInfoCache = getLocal('userLoginInfo') - if(loginInfoCache) { + if (loginInfoCache) { form.username = loginInfoCache?.username form.remember = loginInfoCache?.remember form.password = loginInfoCache?.password diff --git a/src/views/login/login0.vue b/src/views/login/login0.vue deleted file mode 100644 index 728ef8e..0000000 --- a/src/views/login/login0.vue +++ /dev/null @@ -1,196 +0,0 @@ -<template> - <div class="login-container"> - <div class="login-box"> - <el-form - :model="form" - :rules="rules" - ref="formRef" - class="login-form" - > - <el-form-item prop="username"> - <div class="input-group"> - <img :src="accountIcon" alt="账号图标" class="input-icon"> - <el-input - v-model="form.username" - placeholder="请输入您的账号" - class="custom-input" - /> - </div> - </el-form-item> - <el-form-item prop="password"> - <div class="input-group"> - <img :src="passwordIcon" alt="密码图标" class="input-icon"> - <el-input - v-model="form.password" - placeholder="请输入登录密码" - type="password" - class="custom-input" - /> - </div> - </el-form-item> - <el-form-item> - <el-checkbox v-model="form.remember">记住用户名</el-checkbox> - </el-form-item> - <el-form-item> - <el-button type="primary" @click="handleLogin" class="login-btn"> - 登录 - </el-button> - </el-form-item> - </el-form> - </div> - </div> -</template> - -<script lang="ts" setup> -import { ref } from 'vue'; -import accountIconUrl from '@/assets/icons/account-icon.png'; -import passwordIconUrl from '@/assets/icons/password-icon.png'; - -// 表单数据 -const form = ref({ - username: '', - password: '', - remember: false -}); - -// 表单校验规则 -const rules = ref({ - username: [ - { required: true, message: '请输入账号', trigger: 'blur' }, - { min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' } - ], - password: [ - { required: true, message: '请输入密码', trigger: 'blur' }, - { min: 6, max: 30, message: '长度在 6 到 30 个字符', trigger: 'blur' } - ] -}); - -// 图标响应式绑定 -const accountIcon = ref(accountIconUrl); -const passwordIcon = ref(passwordIconUrl); - -// mock 登录接口 -const mockLogin = (params: { username: string; password: string }) => { - return new Promise<{ success: boolean; message?: string }>((resolve) => { - // 模拟正确账号密码(这里假设账号密码为 admin) - if (params.username === 'admin' && params.password === 'admin') { - resolve({ success: true }); - } else { - resolve({ - success: false, - message: '账号或密码错误' - }); - } - }); -}; - -const handleLogin = async () => { - const formRef = getCurrentInstance()?.$refs.formRef as any; - await formRef.validate(async (valid) => { - if (valid) { - try { - const response = await mockLogin(form.value); - if (response.success) { - console.log('登录成功'); - // 这里可添加跳转逻辑或存储用户信息 - } else { - ElMessage.error(response.message || '登录失败'); - } - } catch (error) { - ElMessage.error('登录请求异常'); - } - } - }); -}; -</script> - -<style scoped lang="scss"> -/* 保留之前的样式代码,此处省略重复样式 */ -.login-container { - height: 100vh; - background: #002a5c; - display: flex; - justify-content: center; - align-items: center; - background-image: url('https://via.placeholder.com/1920x1080?text=铁路货车背景图'); - background-size: cover; - background-position: center; -} - -.login-box { - background: rgba(10, 34, 74, 0.9); - padding: 40px 60px; - border-radius: 20px; - box-shadow: 0 0 30px rgba(58, 145, 255, 0.6); - position: relative; - - &::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border: 2px solid rgba(58, 145, 255, 0.3); - border-radius: 20px; - } -} - -.login-form { - .input-group { - display: flex; - align-items: center; - margin-bottom: 20px; - border: 1px solid rgba(58, 145, 255, 0.3); - border-radius: 10px; - overflow: hidden; - - .input-icon { - width: 30px; - height: 30px; - background: rgba(58, 145, 255, 0.1); - display: flex; - align-items: center; - justify-content: center; - } - - .custom-input { - .el-input__inner { - background: transparent; - border: none; - color: white; - height: 50px; - &::placeholder { - color: rgba(255, 255, 255, 0.6); - } - } - } - } - - .login-btn { - width: 100%; - background: linear-gradient(135deg, #42a5f5, #1976d2); - border: none; - box-shadow: 0 4px 12px rgba(58, 145, 255, 0.4); - color: white; - height: 50px; - border-radius: 10px; - font-size: 18px; - &:hover { - background: linear-gradient(135deg, #1976d2, #42a5f5); - } - } - - .el-checkbox { - margin: 15px 0; - .el-checkbox__input.is-checked .el-checkbox__inner { - background-color: #42a5f5; - border-color: #42a5f5; - } - .el-checkbox__label { - color: white; - font-size: 16px; - } - } -} -</style> \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 0b2f868..cb52394 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,7 +2,7 @@ * @Author: donghao donghao@supervision.ltd * @Date: 2025-03-06 11:27:03 * @LastEditors: donghao donghao@supervision.ltd - * @LastEditTime: 2025-03-12 19:38:36 + * @LastEditTime: 2025-03-13 09:35:33 * @FilePath: \vite-ai\data-dashboard\vite.config.ts * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */