添加相关页面,调整登录方式

This commit is contained in:
黑小马 2025-07-24 18:15:22 +08:00
parent 9b4f574093
commit 90d40386ac
18 changed files with 1038 additions and 14 deletions

View File

@ -3,17 +3,21 @@ package com.lktx.center.controller;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hserver.core.ioc.annotation.Autowired;
import cn.hserver.core.server.util.JsonResult;
import cn.hserver.plugin.web.annotation.Controller;
import cn.hserver.plugin.web.annotation.RequestMapping;
import cn.hserver.plugin.web.interfaces.HttpRequest;
import cn.hserver.plugin.web.interfaces.HttpResponse;
import com.lktx.center.config.Data;
import com.lktx.center.config.SsoAuthRequest;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthStateUtils;
@Slf4j
@Controller("/oauth")
public class RestAuthController {
@ -27,16 +31,20 @@ public class RestAuthController {
}
@RequestMapping("/callback")
public void login(AuthCallback callback,HttpResponse response) {
public JsonResult login(AuthCallback callback, HttpRequest request) {
try {
String rawData = request.getRawData();
System.out.println(rawData);
AuthResponse<AuthUser> login = authRequest.login(callback);
AuthUser data = login.getData();
AuthToken token = login.getData().getToken();
StpUtil.login(login.getData().getUuid());
SaSession session = StpUtil.getSession();
session.set(Data.AuthToken, token);
response.redirect("/");
return JsonResult.ok().put("data", data);
}catch (Exception e) {
response.redirect("/");
log.error("login error",e);
}
return JsonResult.error();
}
}

View File

@ -0,0 +1,22 @@
package com.lktx.center.filter;
import cn.hserver.core.ioc.annotation.Bean;
import cn.hserver.core.ioc.annotation.Order;
import cn.hserver.plugin.web.context.Webkit;
import cn.hserver.plugin.web.interfaces.FilterAdapter;
import io.netty.handler.codec.http.HttpMethod;
@Bean
@Order(1)
public class CorsFilter implements FilterAdapter {
@Override
public void doFilter(Webkit webkit) throws Exception {
webkit.httpResponse.setHeader("Access-Control-Allow-Origin", "*");
webkit.httpResponse.setHeader("Access-Control-Allow-Methods", "*");
webkit.httpResponse.setHeader("Access-Control-Allow-Credentials", "*");
webkit.httpResponse.setHeader("Access-Control-Allow-Headers", "*");
if (webkit.httpRequest.getRequestType().equals(HttpMethod.OPTIONS)) {
webkit.httpResponse.sendHtml("");
}
}
}

View File

@ -1,5 +1,5 @@
oauth:
client-id: 65013a3d89d14fab8ff3eb2c0f3981a3
client-secret: 22b5ce70d67f41b79b27cbedb57c976a
redirect-uri: http://127.0.0.1:8981/oauth/callback
redirect-uri: http://127.0.0.1:5173/login
url: http://192.168.0.206:8911/21/

View File

@ -10,7 +10,9 @@
},
"dependencies": {
"@tailwindcss/vite": "^4.1.11",
"axios": "^1.11.0",
"element-plus": "^2.10.4",
"font-awesome": "^4.7.0",
"vue": "^3.5.17",
"vue-router": "4",
"vuedraggable": "^4.1.0"

9
web/src/api/login.js Normal file
View File

@ -0,0 +1,9 @@
import http from '../data/http'
export function login(data) {
return http({
url: '/oauth/callback',
method: 'post',
data
})
}

4
web/src/data/host.js Normal file
View File

@ -0,0 +1,4 @@
//API地址
export const host = 'http://127.0.0.1:8981'
//登录回调调整地址
export const login = 'http://127.0.0.1:8981/oauth/render'

66
web/src/data/http.js Normal file
View File

@ -0,0 +1,66 @@
import axios from 'axios'
import userInfo from './userInfo.js'
import {host} from './host.js'
// create an axios instance
const service = axios.create({
// baseURL: "http://127.0.0.1:9090", // url = base url + request url
// baseURL: "http://xxx.com", // url = base url + request url
baseURL: host, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 500000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (userInfo.getUserInfo()) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['token'] = userInfo.getUserInfo().token
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 200) {
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === -2 || res.code === -3 || res.code === -4 || res.code === -5) {
location.href = "/login"
}
return res
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default service

16
web/src/data/userInfo.js Normal file
View File

@ -0,0 +1,16 @@
export default {
KEY: "USER_INFO",
setUserInfo(userInfo) {
localStorage.setItem(this.KEY, JSON.stringify(userInfo))
},
getUserInfo() {
try {
return JSON.parse(localStorage.getItem(this.KEY))
} catch (e) {
return null
}
},
removeUserInfo(){
localStorage.removeItem(this.KEY)
}
}

View File

@ -4,4 +4,5 @@ import App from './App.vue'
import router from './router/index.js'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'font-awesome/css/font-awesome.min.css'
createApp(App).use(router).use(ElementPlus).mount('#app')

View File

@ -1,23 +1,35 @@
import { createMemoryHistory, createRouter } from 'vue-router'
import {createMemoryHistory, createRouter, createWebHashHistory, createWebHistory} from 'vue-router'
import index from '../views/index.vue'
import login from '../views/login.vue'
import home from '../views/home.vue'
import appcenter from '../views/appcenter.vue'
import news from '../views/news.vue'
import announcement from '../views/announcement.vue'
import staffstyle from '../views/staffstyle.vue'
import chat from '../views/chat.vue'
import suggestionbox from '../views/suggestionbox.vue'
const routes = [
{path: '/login', component: login},
{
path: '/', component: index,
children:[
{path: '', redirect: '/home'},
{ path: '/home', component: home },
{ path: '/app-center', component: appcenter }
{ path: '/app-center', component: appcenter },
{ path: '/news', component: news },
{ path: '/announcement', component: announcement },
{ path: '/staff-style', component: staffstyle },
{ path: '/chat', component: chat },
{ path: '/suggestion-box', component: suggestionbox }
]
},
]
const router = createRouter({
history: createMemoryHistory(),
history: createWebHistory(),
routes,
})

View File

@ -0,0 +1,63 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ElCard, ElTag, ElDivider } from 'element-plus';
//
const announcements = ref([
{
id: 1,
title: '系统维护通知',
content: '为了给大家提供更稳定、高效的服务,本系统将于本周日凌晨 02:00 - 04:00 进行维护,届时系统将暂停服务,请您提前做好相应准备,感谢您的理解与支持!',
date: '2025-07-25',
type: '维护'
},
{
id: 2,
title: '假期安排通知',
content: '根据国家法定节假日安排结合公司实际情况现将今年国庆节放假安排通知如下10 月 1 日至 10 月 7 日放假调休,共 7 天。10 月 8 日星期六、10 月 9 日(星期日)上班。请各位员工提前做好工作安排。',
date: '2025-07-24',
type: '假期'
},
{
id: 3,
title: '新政策发布',
content: '公司新的绩效考核政策已经正式发布,该政策将从下个月开始实施。请各位员工仔细阅读政策文件,如有疑问可随时咨询人力资源部门。',
date: '2025-07-23',
type: '政策'
}
]);
const getTagType = (type: string) => {
switch (type) {
case '维护':
return 'warning';
case '假期':
return 'success';
case '政策':
return 'info';
default:
return 'default';
}
};
</script>
<template>
<div class="max-w-7xl mx-auto p-6">
<h1 class="text-3xl font-bold text-gray-800 mb-8 text-center">企业公告</h1>
<div class="space-y-6">
<ElCard v-for="announcement in announcements" :key="announcement.id" class="shadow-md hover:shadow-xl transition-shadow">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-700">{{ announcement.title }}</h2>
<el-tag :type="getTagType(announcement.type)" size="small">{{ announcement.type }}</el-tag>
</div>
<ElDivider />
<p class="text-gray-600 mb-4">{{ announcement.content }}</p>
<div class="text-right text-sm text-gray-500">发布时间{{ announcement.date }}</div>
</ElCard>
</div>
</div>
</template>
<style scoped>
</style>

417
web/src/views/chat.vue Normal file
View File

@ -0,0 +1,417 @@
<script setup lang="ts">
</script>
<template>
<div class="font-inter bg-gray-100 text-dark flex overflow-hidden h-full">
<!-- 左侧聊天列表 -->
<div class="w-80 bg-white border-r border-gray-200 flex flex-col h-full shadow-sm z-10">
<!-- 头部 -->
<div class="p-4 border-b border-gray-200">
<div class="flex items-center justify-between mb-4">
<h1 class="text-xl font-bold text-primary">企业沟通</h1>
<button class="p-2 rounded-full hover:bg-gray-100 transition-colors">
<i class="fa fa-plus text-gray-600"></i>
</button>
</div>
<!-- 搜索框 -->
<div class="relative">
<input type="text" placeholder="搜索聊天..." class="w-full py-2 pl-10 pr-4 rounded-lg bg-gray-100 focus:outline-none focus:ring-2 focus:ring-primary/30 transition-all">
<i class="fa fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<!-- 聊天列表导航 -->
<div class="flex border-b border-gray-200">
<button class="flex-1 py-3 text-primary border-b-2 border-primary font-medium">
最近聊天
</button>
<button class="flex-1 py-3 text-gray-500 hover:text-gray-700 transition-colors">
群聊
</button>
</div>
<!-- 聊天列表内容 -->
<div class="flex-1 overflow-y-auto scrollbar-hide">
<!-- 当前选中的聊天 -->
<div class="flex items-center p-3 bg-primary/5 border-l-4 border-primary cursor-pointer">
<img src="https://picsum.photos/id/1005/200/200" alt="研发部群聊" class="w-12 h-12 rounded-full object-cover">
<div class="ml-3 flex-1 min-w-0">
<div class="flex justify-between items-center">
<h3 class="text-sm font-semibold text-gray-900 truncate">研发部群聊</h3>
<span class="text-xs text-gray-500">14:32</span>
</div>
<p class="text-sm text-gray-600 truncate mt-1">
<span class="font-medium"></span>这个需求我已经完成了
</p>
</div>
</div>
<!-- 未读消息聊天 -->
<div class="flex items-center p-3 hover:bg-gray-50 cursor-pointer transition-colors">
<img src="https://picsum.photos/id/1012/200/200" alt="张经理" class="w-12 h-12 rounded-full object-cover">
<div class="ml-3 flex-1 min-w-0">
<div class="flex justify-between items-center">
<h3 class="text-sm font-semibold text-gray-900 truncate">张经理</h3>
<span class="text-xs text-gray-500">13:45</span>
</div>
<p class="text-sm text-gray-600 truncate mt-1">
下周的项目评审会议需要提前准备
</p>
<div class="absolute right-4 top-4 w-5 h-5 bg-primary rounded-full flex items-center justify-center text-white text-xs font-medium">
2
</div>
</div>
</div>
<!-- 普通聊天 -->
<div class="flex items-center p-3 hover:bg-gray-50 cursor-pointer transition-colors">
<img src="https://picsum.photos/id/1027/200/200" alt="产品组" class="w-12 h-12 rounded-full object-cover">
<div class="ml-3 flex-1 min-w-0">
<div class="flex justify-between items-center">
<h3 class="text-sm font-semibold text-gray-900 truncate">产品组</h3>
<span class="text-xs text-gray-500">昨天</span>
</div>
<p class="text-sm text-gray-500 truncate mt-1">
李华新功能原型已经上传到共享文件夹
</p>
</div>
</div>
<div class="flex items-center p-3 hover:bg-gray-50 cursor-pointer transition-colors">
<img src="https://picsum.photos/id/1025/200/200" alt="王工程师" class="w-12 h-12 rounded-full object-cover">
<div class="ml-3 flex-1 min-w-0">
<div class="flex justify-between items-center">
<h3 class="text-sm font-semibold text-gray-900 truncate">王工程师</h3>
<span class="text-xs text-gray-500">昨天</span>
</div>
<p class="text-sm text-gray-500 truncate mt-1">
数据库优化方案我已经发你邮箱了
</p>
</div>
</div>
<div class="flex items-center p-3 hover:bg-gray-50 cursor-pointer transition-colors">
<img src="https://picsum.photos/id/1066/200/200" alt="市场部" class="w-12 h-12 rounded-full object-cover">
<div class="ml-3 flex-1 min-w-0">
<div class="flex justify-between items-center">
<h3 class="text-sm font-semibold text-gray-900 truncate">市场部</h3>
<span class="text-xs text-gray-500">周一</span>
</div>
<p class="text-sm text-gray-500 truncate mt-1">
张敏下个月的市场活动计划已更新
</p>
</div>
</div>
</div>
</div>
<!-- 中间聊天界面 -->
<div class="flex-1 flex flex-col bg-gray-50 h-full overflow-hidden">
<!-- 聊天头部 -->
<div class="bg-white border-b border-gray-200 p-4 flex items-center justify-between shadow-sm">
<div class="flex items-center">
<img src="https://picsum.photos/id/1005/200/200" alt="研发部群聊" class="w-10 h-10 rounded-full object-cover">
<div class="ml-3">
<h2 class="font-semibold">研发部群聊</h2>
<p class="text-xs text-gray-500">12名成员8人在线</p>
</div>
</div>
<div class="flex space-x-2">
<button class="p-2 rounded-full hover:bg-gray-100 transition-colors text-gray-600">
<i class="fa fa-search"></i>
</button>
<button class="p-2 rounded-full hover:bg-gray-100 transition-colors text-gray-600">
<i class="fa fa-phone"></i>
</button>
<button class="p-2 rounded-full hover:bg-gray-100 transition-colors text-gray-600">
<i class="fa fa-video-camera"></i>
</button>
<button class="p-2 rounded-full hover:bg-gray-100 transition-colors text-gray-600">
<i class="fa fa-ellipsis-v"></i>
</button>
</div>
</div>
<!-- 聊天消息区域 -->
<div class="flex-1 overflow-y-auto p-6 space-y-6 scrollbar-hide" id="chat-messages">
<!-- 日期分隔线 -->
<div class="flex justify-center">
<span class="text-xs bg-gray-200 text-gray-500 px-3 py-1 rounded-full">今天</span>
</div>
<!-- 他人消息 -->
<div class="flex items-start">
<img src="https://picsum.photos/id/1025/200/200" alt="王工程师" class="w-8 h-8 rounded-full object-cover">
<div class="ml-2 max-w-[80%]">
<div class="flex items-center mb-1">
<span class="text-xs font-medium text-gray-700">王工程师</span>
<span class="text-xs text-gray-400 ml-2">09:32</span>
</div>
<div class="bg-white p-3 rounded-lg message-bubble-left shadow-sm">
<p>大家上午好昨天部署的新版本运行情况如何有没有发现什么问题</p>
</div>
</div>
</div>
<!-- 他人消息 - 图片 -->
<div class="flex items-start">
<img src="https://picsum.photos/id/1012/200/200" alt="张经理" class="w-8 h-8 rounded-full object-cover">
<div class="ml-2 max-w-[80%]">
<div class="flex items-center mb-1">
<span class="text-xs font-medium text-gray-700">张经理</span>
<span class="text-xs text-gray-400 ml-2">10:15</span>
</div>
<div class="bg-white p-3 rounded-lg message-bubble-left shadow-sm">
<p>我这边发现一个界面显示问题主要在IE浏览器上</p>
<div class="mt-2 rounded overflow-hidden border border-gray-100">
<img src="https://picsum.photos/id/0/400/200" alt="问题截图" class="w-full h-auto hover:opacity-90 transition-opacity cursor-pointer">
</div>
</div>
</div>
</div>
<!-- 他人消息 - 文件 -->
<div class="flex items-start">
<img src="https://picsum.photos/id/1066/200/200" alt="李华" class="w-8 h-8 rounded-full object-cover">
<div class="ml-2 max-w-[80%]">
<div class="flex items-center mb-1">
<span class="text-xs font-medium text-gray-700">李华</span>
<span class="text-xs text-gray-400 ml-2">11:45</span>
</div>
<div class="bg-white p-3 rounded-lg message-bubble-left shadow-sm">
<p>这是修复方案的文档请查收</p>
<div class="mt-2 flex items-center p-2 bg-gray-50 rounded">
<div class="w-10 h-10 bg-primary/10 rounded flex items-center justify-center text-primary">
<i class="fa fa-file-pdf-o text-xl"></i>
</div>
<div class="ml-3 flex-1">
<p class="text-sm font-medium text-gray-800 truncate">前端兼容性修复方案.pdf</p>
<p class="text-xs text-gray-500">2.4 MB</p>
</div>
<button class="text-primary hover:text-primary/80 transition-colors">
<i class="fa fa-download"></i>
</button>
</div>
</div>
</div>
</div>
<!-- 自己的消息 -->
<div class="flex items-start justify-end">
<div class="mr-2 max-w-[80%]">
<div class="flex items-center justify-end mb-1">
<span class="text-xs text-gray-400">13:20</span>
</div>
<div class="bg-primary text-white p-3 rounded-lg message-bubble-right shadow-sm">
<p>我看了一下这个问题是由于IE对flex布局的支持不完善导致的我会尽快修复</p>
</div>
</div>
<img src="https://picsum.photos/id/1001/200/200" alt="我" class="w-8 h-8 rounded-full object-cover">
</div>
<!-- 自己的消息 - 表情包 -->
<div class="flex items-start justify-end">
<div class="mr-2 max-w-[80%]">
<div class="flex items-center justify-end mb-1">
<span class="text-xs text-gray-400">13:22</span>
</div>
<div class="bg-primary text-white p-3 rounded-lg message-bubble-right shadow-sm">
<p>收到文档了谢谢👍👍</p>
</div>
</div>
<img src="https://picsum.photos/id/1001/200/200" alt="我" class="w-8 h-8 rounded-full object-cover">
</div>
<!-- 他人消息 -->
<div class="flex items-start">
<img src="https://picsum.photos/id/1025/200/200" alt="王工程师" class="w-8 h-8 rounded-full object-cover">
<div class="ml-2 max-w-[80%]">
<div class="flex items-center mb-1">
<span class="text-xs font-medium text-gray-700">王工程师</span>
<span class="text-xs text-gray-400 ml-2">14:30</span>
</div>
<div class="bg-white p-3 rounded-lg message-bubble-left shadow-sm">
<p>数据库性能监控系统已经部署完成大家可以通过内部地址访问查看实时数据</p>
</div>
</div>
</div>
<!-- 自己的消息 -->
<div class="flex items-start justify-end">
<div class="mr-2 max-w-[80%]">
<div class="flex items-center justify-end mb-1">
<span class="text-xs text-gray-400">14:32</span>
</div>
<div class="bg-primary text-white p-3 rounded-lg message-bubble-right shadow-sm">
<p>这个需求我已经完成了</p>
</div>
</div>
<img src="https://picsum.photos/id/1001/200/200" alt="我" class="w-8 h-8 rounded-full object-cover">
</div>
</div>
<!-- 输入区域 -->
<div class="bg-white border-t border-gray-200 p-3">
<div class="flex items-center mb-2 space-x-1">
<button class="p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="表情">
<i class="fa fa-smile-o text-lg"></i>
</button>
<button class="p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="图片">
<i class="fa fa-picture-o text-lg"></i>
</button>
<button class="p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="文件">
<i class="fa fa-paperclip text-lg"></i>
</button>
<button class="p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="视频">
<i class="fa fa-video-camera text-lg"></i>
</button>
<button class="p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="截图">
<i class="fa fa-desktop text-lg"></i>
</button>
<div class="flex-1"></div>
<button class="p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="更多选项">
<i class="fa fa-ellipsis-h text-lg"></i>
</button>
</div>
<div class="flex">
<textarea placeholder="输入消息..." class="flex-1 border border-gray-200 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary p-3 resize-none transition-all" rows="3"></textarea>
<button class="bg-primary hover:bg-primary/90 text-white px-6 rounded-r-lg transition-colors flex items-center">
<span>发送</span>
<i class="fa fa-paper-plane ml-2"></i>
</button>
</div>
</div>
</div>
<!-- 右侧在线人员 -->
<div class="w-72 bg-white border-l border-gray-200 flex flex-col h-full shadow-sm hidden lg:block">
<!-- 头部 -->
<div class="p-4 border-b border-gray-200">
<h2 class="font-semibold">在线成员 (8)</h2>
</div>
<!-- 搜索 -->
<div class="p-3 border-b border-gray-200">
<div class="relative">
<input type="text" placeholder="搜索成员..." class="w-full py-2 pl-10 pr-4 rounded-lg bg-gray-100 focus:outline-none focus:ring-2 focus:ring-primary/30 transition-all text-sm">
<i class="fa fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<!-- 分类 -->
<div class="p-2 border-b border-gray-200">
<button class="text-sm text-primary font-medium">全部</button>
<button class="text-sm text-gray-500 ml-3 hover:text-gray-700 transition-colors">部门</button>
<button class="text-sm text-gray-500 ml-3 hover:text-gray-700 transition-colors">角色</button>
</div>
<!-- 成员列表 -->
<div class="flex-1 overflow-y-auto scrollbar-hide p-2">
<!-- 自己 -->
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1001/200/200" alt="自己" class="w-10 h-10 rounded-full object-cover">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-success border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-900"> (自己)</h3>
<p class="text-xs text-gray-500">前端开发</p>
</div>
</div>
<!-- 在线成员 -->
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1012/200/200" alt="张经理" class="w-10 h-10 rounded-full object-cover">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-success border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-900">张经理</h3>
<p class="text-xs text-gray-500">研发经理</p>
</div>
</div>
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1025/200/200" alt="王工程师" class="w-10 h-10 rounded-full object-cover">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-success border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-900">王工程师</h3>
<p class="text-xs text-gray-500">后端开发</p>
</div>
</div>
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1066/200/200" alt="李华" class="w-10 h-10 rounded-full object-cover">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-success border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-900">李华</h3>
<p class="text-xs text-gray-500">前端开发</p>
</div>
</div>
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1027/200/200" alt="赵设计师" class="w-10 h-10 rounded-full object-cover">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-success border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-900">赵设计师</h3>
<p class="text-xs text-gray-500">UI/UX设计</p>
</div>
</div>
<!-- 离开状态 -->
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1074/200/200" alt="陈测试" class="w-10 h-10 rounded-full object-cover">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-warning border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-900">陈测试</h3>
<p class="text-xs text-gray-500">测试工程师</p>
</div>
</div>
<!-- 离线成员 -->
<div class="mt-4">
<h3 class="text-xs text-gray-500 uppercase font-medium px-2 mb-2">离线成员 (4)</h3>
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1083/200/200" alt="孙产品" class="w-10 h-10 rounded-full object-cover opacity-70">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-gray-300 border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-600">孙产品</h3>
<p class="text-xs text-gray-400">产品经理</p>
</div>
</div>
<div class="flex items-center p-2 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<div class="relative">
<img src="https://picsum.photos/id/1076/200/200" alt="周运维" class="w-10 h-10 rounded-full object-cover opacity-70">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-gray-300 border-2 border-white rounded-full"></span>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-gray-600">周运维</h3>
<p class="text-xs text-gray-400">运维工程师</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import Layout from "./components/layout.vue";
import Layout from "./menu/index.vue";
</script>

128
web/src/views/login.vue Normal file
View File

@ -0,0 +1,128 @@
<script setup>
import {onMounted, ref} from "vue";
import { useRoute } from 'vue-router'
import {login as loginUrl} from "../data/host.js";
import {login} from "../api/login.js";
import userInfo from "../data/userInfo.js";
const route = useRoute()
const loginState = ref(0)
const setBar=(progress)=>{
const progressBar = document.getElementById('progress-bar');
progressBar.style.width = `${progress}%`;
}
const jumpLogin = () => {
location.href=loginUrl
}
const handlerLogin = () => {
const code= route.query.code
const state= route.query.state
if (code&&state) {
setBar(50)
login({code,state}).then(res=>{
console.log(res)
if (res.code===200){
setBar(100)
loginState.value=1
userInfo.setUserInfo(res.data)
location.href="/"
}else {
setBar(0)
loginState.value=2
}
})
}else {
jumpLogin()
}
}
onMounted(()=>{
handlerLogin()
})
</script>
<template>
<div class="font-inter bg-gray-50 min-h-screen flex items-center justify-center p-4">
<div class="max-w-md w-full bg-white rounded-xl shadow-lg overflow-hidden animate-fade-in">
<!-- 头部区域 -->
<div class="bg-primary p-6 text-white text-center">
<div class="flex items-center justify-center">
<i class="fa fa-sign-in text-4xl mr-3"></i>
<h1 class="text-2xl font-bold">企业登录系统</h1>
</div>
<p class="mt-2 text-primary-100 opacity-90">正在验证您的身份请稍候...</p>
</div>
<!-- 内容区域 -->
<div class="p-8">
<!-- 状态指示器 -->
<div id="status-container" class="flex flex-col items-center">
<!-- 加载状态 -->
<div v-if="loginState===0" class="animate-slide-up">
<div class="w-16 h-16 border-4 border-primary/20 border-t-primary rounded-full animate-spin mx-auto"></div>
<p class="mt-4 text-gray-600 text-center">正在验证授权码...</p>
<p id="loading-details" class="mt-2 text-center text-sm text-gray-500">请不要关闭此页面</p>
</div>
<!-- 成功状态 (默认隐藏) -->
<div v-if="loginState===1" class=" animate-slide-up">
<p class="mt-4 text-gray-600 text-center">验证成功</p>
<p id="success-details" class="mt-2 text-sm text-center text-gray-500">正在跳转至应用...</p>
</div>
<!-- 错误状态 (默认隐藏) -->
<div v-if="loginState===2" class=" animate-slide-up text-center">
<p class="mt-4 text-gray-600 text-center">登录失败</p>
<p id="error-details" class="mt-2 text-sm text-gray-500 text-center">授权码无效或已过期</p>
<button @click="jumpLogin" class=" mt-6 px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors">
重试
</button>
</div>
</div>
<!-- 进度条 -->
<div class="mt-8">
<div class="h-2 bg-gray-100 rounded-full overflow-hidden">
<div id="progress-bar" class="h-full bg-primary rounded-full w-0 transition-all duration-300"></div>
</div>
</div>
</div>
<!-- 底部区域 -->
<div class="p-4 bg-gray-50 text-center text-sm text-gray-500">
<p>© 2025 企业内部系统 | 技术支持: it-support@company.com</p>
</div>
</div>
</div>
</template>
<style scoped>
@layer utilities {
.content-auto {
content-visibility: auto;
}
.animate-pulse-slow {
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.animate-fade-in {
animation: fadeIn 0.5s ease-in-out;
}
.animate-slide-up {
animation: slideUp 0.5s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
}
</style>

View File

@ -20,8 +20,8 @@
@open="handleOpen"
router
>
<el-menu-item index="/app-center">新闻中心</el-menu-item>
<el-menu-item index="/home">公告</el-menu-item>
<el-menu-item index="/news">新闻中心</el-menu-item>
<el-menu-item index="/announcement">公告</el-menu-item>
</el-menu>
</div>
</div>
@ -38,8 +38,8 @@
</div>
<div class="flex items-center">
<img src="https://picsum.photos/200/200?random=1" alt="User Avatar" class="w-8 h-8 rounded-full object-cover">
<span class="ml-2 hidden sm:block">管理员</span>
<img :src="userInfoData?.avatar" alt="User Avatar" class="w-8 h-8 rounded-full object-cover">
<span class="ml-2 hidden sm:block">{{ userInfoData?.nickname }}</span>
<el-icon><CaretBottom /></el-icon>
</div>
</div>
@ -61,9 +61,25 @@
<template #title>首页</template>
</el-menu-item>
<el-menu-item index="/app-center">
<el-icon><IconMenu /></el-icon>
<el-icon><Orange /></el-icon>
<template #title>应用中心</template>
</el-menu-item>
<el-menu-item index="/staff-style">
<el-icon><UserFilled /></el-icon>
<template #title>员工风采</template>
</el-menu-item>
<el-menu-item index="/app-center">
<el-icon><StarFilled /></el-icon>
<template #title>企业论坛</template>
</el-menu-item>
<el-menu-item index="/chat">
<el-icon><Comment /></el-icon>
<template #title>企业聊天</template>
</el-menu-item>
<el-menu-item index="/suggestion-box">
<el-icon><QuestionFilled /></el-icon>
<template #title>意见箱</template>
</el-menu-item>
<!-- <el-sub-menu index="2">-->
<!-- <template #title>-->
<!-- <el-icon><Location /></el-icon>-->
@ -102,12 +118,18 @@
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import {onMounted, ref} from 'vue'
import {
Document,
Menu as IconMenu,
Location,
Setting,
Switch,
StarFilled,
UserFilled,
Comment,
QuestionFilled,
Orange,
// 使
Fold,
Platform,
@ -115,6 +137,12 @@ import {
CaretBottom
} from '@element-plus/icons-vue'
import userInfo from '../../data/userInfo.js'
import {useRouter} from "vue-router";
const router=useRouter();
const userInfoData=ref(null);
const isCollapse = ref(false)
const handleOpen = (key: string, keyPath: string[]) => {
@ -124,6 +152,17 @@ const handleOpen = (key: string, keyPath: string[]) => {
const handleClose = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
onMounted(()=>{
const info=userInfo.getUserInfo()
if (!info){
router.push({path: '/login'})
}else {
userInfoData.value=info
}
})
</script>
<style scoped>

52
web/src/views/news.vue Normal file
View File

@ -0,0 +1,52 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ElCard, ElTag } from 'element-plus';
// imageUrl
const newsList = ref([
{
id: 1,
title: '公司年度会议通知',
content: '公司将于下周五召开年度会议,请各位员工提前做好准备。',
date: '2025-07-24',
imageUrl: 'https://picsum.photos/600/400?random=1'
},
{
id: 2,
title: '新产品上线公告',
content: '我们的全新产品已正式上线,欢迎大家体验。',
date: '2025-07-23',
imageUrl: 'https://picsum.photos/600/400?random=2'
},
{
id: 3,
title: '团队建设活动安排',
content: '本周末将组织团队建设活动,具体安排请查看内部通知。',
date: '2025-07-22',
imageUrl: 'https://picsum.photos/600/400?random=3'
},
]);
</script>
<template>
<div class="max-w-7xl mx-auto p-6">
<h1 class="text-2xl font-bold text-gray-800 mb-6 text-center">新闻列表</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<ElCard v-for="news in newsList" :key="news.id" class="shadow-md hover:shadow-lg transition-shadow">
<!-- 展示新闻图片 -->
<img :src="news.imageUrl" alt="news image" class="w-full h-48 object-cover rounded-t-md">
<template #header>
<div class="flex justify-between items-center">
<span class="text-xl font-semibold text-gray-700">{{ news.title }}</span>
<ElTag type="info" size="small">{{ news.date }}</ElTag>
</div>
</template>
<p class="text-gray-600 p-4">{{ news.content }}</p>
</ElCard>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,58 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ElCard, ElTag } from 'element-plus';
//
const staffList = ref([
{
id: 1,
name: '张三',
position: '高级前端开发工程师',
avatar: 'https://picsum.photos/200/200?random=1',
intro: '张三在前端开发领域拥有丰富的经验,擅长 Vue、React 等主流框架,对前端性能优化有深入研究。',
honors: ['优秀员工', '技术创新奖']
},
{
id: 2,
name: '李四',
position: '后端架构师',
avatar: 'https://picsum.photos/200/200?random=2',
intro: '李四专注于后端系统设计与开发,精通 Java、Go 语言,主导过多个大型项目的架构设计。',
honors: ['杰出贡献奖', '最佳团队成员']
},
{
id: 3,
name: '王五',
position: '产品经理',
avatar: 'https://picsum.photos/200/200?random=3',
intro: '王五具备敏锐的市场洞察力和出色的产品规划能力,成功打造过多款用户喜爱的产品。',
honors: ['优秀产品奖', '最佳创意奖']
}
]);
</script>
<template>
<div class="max-w-7xl mx-auto p-6">
<h1 class="text-3xl font-bold text-gray-800 mb-8 text-center">员工风采</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<ElCard v-for="staff in staffList" :key="staff.id" class="shadow-md hover:shadow-xl transition-shadow">
<div class="flex flex-col items-center">
<img :src="staff.avatar" alt="staff avatar" class="w-32 h-32 rounded-full object-cover mb-4">
<h2 class="text-xl font-semibold text-gray-700">{{ staff.name }}</h2>
<p class="text-gray-600 text-sm mb-4">{{ staff.position }}</p>
</div>
<ElDivider />
<p class="text-gray-600 mb-4">{{ staff.intro }}</p>
<div class="flex flex-wrap gap-2">
<ElTag v-for="honor in staff.honors" :key="honor" type="success" size="small">
{{ honor }}
</ElTag>
</div>
</ElCard>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,127 @@
<script setup lang="ts">
import { ref } from 'vue';
//
interface FeedbackForm {
department: string;
contact: string;
phone: string;
content: string;
}
const formData = ref<FeedbackForm>({
department: '',
contact: '',
phone: '',
content: ''
});
//
const handleSubmit = () => {
console.log('提交的表单数据:', formData.value);
// axios
};
</script>
<template>
<div class="feedback-container">
<h2 class="title">意见反馈</h2>
<div class="form-wrapper">
<div class="form-item">
<label class="form-label">部门</label>
<input
class="form-input"
v-model="formData.department"
placeholder="请输入部门名称"
/>
</div>
<div class="form-item">
<label class="form-label">联系人</label>
<input
class="form-input"
v-model="formData.contact"
placeholder="请输入联系人姓名"
/>
</div>
<div class="form-item">
<label class="form-label">联系电话</label>
<input
class="form-input"
v-model="formData.phone"
placeholder="请输入联系电话"
type="tel"
/>
</div>
<div class="form-item">
<label class="form-label">需求内容</label>
<textarea
class="form-textarea"
v-model="formData.content"
placeholder="请详细描述需求内容"
></textarea>
</div>
<button class="submit-btn" @click="handleSubmit">提交意见</button>
</div>
</div>
</template>
<style scoped>
.feedback-container {
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.title {
text-align: center;
margin-bottom: 20px;
font-size: 20px;
font-weight: bold;
}
.form-wrapper {
display: flex;
flex-direction: column;
gap: 15px;
}
.form-item {
display: flex;
flex-direction: column;
}
.form-label {
margin-bottom: 6px;
font-size: 14px;
font-weight: 500;
}
.form-input,
.form-textarea {
caret-color: white;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.form-textarea {
resize: vertical;
min-height: 80px;
}
.submit-btn {
align-self: flex-end;
padding: 10px 20px;
background-color: #0078d4;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.submit-btn:hover {
background-color: #005a9e;
}
</style>