Compare commits

...

4 Commits

Author SHA1 Message Date
ww b315a93138 zy 5 months ago
ww a74923f1e9 zy 5 months ago
ww f88b793800 zylogin 5 months ago
ww 1f7a44466c 1-完成基本样式 6 months ago
  1. 15
      mock/user.ts
  2. 28
      mock/user_1.ts
  3. 8
      package.json
  4. 1315
      pnpm-lock.yaml
  5. 14
      src/App.vue
  6. 2
      src/api/user/index.ts
  7. 48
      src/api/user/user.js
  8. BIN
      src/assets/images/add.png
  9. BIN
      src/assets/images/ellipsis.png
  10. 2
      src/main.ts
  11. 115
      src/permission.ts
  12. 3
      src/store/index.ts
  13. 1
      src/store/modules/type/types.ts
  14. 163
      src/store/modules/user.ts
  15. 25
      src/utils/img.js
  16. 1
      src/utils/request.ts
  17. 122
      src/views/course/CourseObjectives.vue
  18. 9
      src/views/group/index.vue
  19. 108
      src/views/home/components/Class1.vue
  20. 108
      src/views/home/components/Class2.vue
  21. 64
      src/views/home/components/ConHeader.vue
  22. 103
      src/views/home/components/Echart/Echarts.vue
  23. 28
      src/views/home/components/Echarts.vue
  24. 267
      src/views/home/components/Info.vue
  25. 211
      src/views/home/components/Status.vue
  26. 97
      src/views/home/components/Student1.vue
  27. 97
      src/views/home/components/Student2.vue
  28. 46
      src/views/home/components/Welcome.vue
  29. 181
      src/views/home/index.vue
  30. 548
      src/views/login/index.vue
  31. 6
      src/views/message/index.vue
  32. 31
      src/views/student/index.vue
  33. 68
      src/views/student/login.vue

@ -10,7 +10,22 @@ function createUserList() {
desc: '平台管理员',
roles: ['平台管理员'],
buttons: ['cuser.detail'],
<<<<<<< HEAD
routes: [
'Home',
'Course',
'Student',
'Group',
'Message',
'BasicCourseInformation',
'CourseObjectives',
'CourseChapters',
'KnowledgePoints',
'CurriculumMap',
], //老师权限
=======
routes: ['Home', 'Course', 'Student', 'Group', 'Message','BasicCourseInformation','CourseObjectives','CourseChapters','KnowledgePoints','CurriculumMap','knowledgeAtlas'], //老师权限
>>>>>>> c410b660ea058afeb8db74ec244fc38f8928cf30
token: 'Admin Token',
},
{

@ -10,7 +10,18 @@ function createUserList() {
desc: '平台管理员',
roles: ['平台管理员'],
buttons: ['cuser.detail'],
routes: ['Home', 'Course', 'Student', 'Group', 'Message','BasicCourseInformation','CourseObjectives','CourseChapters','KnowledgePoints','CurriculumMap'], //老师权限
routes: [
'Home',
'Course',
'Student',
'Group',
'Message',
'BasicCourseInformation',
'CourseObjectives',
'CourseChapters',
'KnowledgePoints',
'CurriculumMap',
], //老师权限
token: 'Admin Token',
},
{
@ -22,7 +33,20 @@ function createUserList() {
desc: '系统管理员',
roles: ['系统管理员'],
buttons: ['cuser.detail', 'cuser.user'],
routes: ['Home', 'MyCourseStudy', 'CourseResources', 'Message','LearningProcess','CourseCollections','Courselikes','WebHome','CourseHome','LearningPathRecommendations','KnowledgePointLearning','CourseReports'], //学生权限
routes: [
'Home',
'MyCourseStudy',
'CourseResources',
'Message',
'LearningProcess',
'CourseCollections',
'Courselikes',
'WebHome',
'CourseHome',
'LearningPathRecommendations',
'KnowledgePointLearning',
'CourseReports',
], //学生权限
token: 'System Token',
},
]

@ -20,12 +20,19 @@
"3d-force-graph": "^1.73.3",
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.9.0",
"ali-oss": "^6.20.0",
"axios": "^1.6.8",
"echarts": "^5.5.0",
<<<<<<< HEAD
"element-plus": "^2.6.0",
"jwt-decode": "^4.0.0",
"nanoid": "^5.0.7",
=======
"echarts-liquidfill": "^3.1.0",
"element-plus": "^2.6.0",
"form-data": "^4.0.0",
"jquery": "^3.7.1",
>>>>>>> c410b660ea058afeb8db74ec244fc38f8928cf30
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"three": "^0.163.0",
@ -45,6 +52,7 @@
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.22.0",
"mockjs": "^1.1.0",
"pinia-plugin-persistedstate": "^3.2.1",
"postcss": "^8.4.35",
"postcss-html": "^1.6.0",
"postcss-scss": "^4.0.9",

File diff suppressed because it is too large Load Diff

@ -3,7 +3,19 @@
</template>
<script lang="ts" setup>
import {} from 'vue'
// import { useRouter, useRoute } from 'vue-router'
// vue3 compositionAPI
// 1. router useRouter
// const router = userRouter()
// 2. route useRoute()
// const route = useRooute()
// const router = useRouter()
// const goList = () => {
// router.push('/')
// }
// import {} from 'vue'
// import { useUserStore } from '@/stores/user.js'
// const userStore = useUserStore()
</script>
<style lang="scss" scoped></style>

@ -4,9 +4,7 @@ import { loginType, loginResponseType, userInfoType } from './types'
enum API {
LOGIN_URL = '/user/login',
USERINFO_URL = '/user/info',
LOGOUT_URL = '/user/logout',
}
// 用户登录

@ -0,0 +1,48 @@
import request from '@/utils/request'
//账号登录接口
export const userLoginService = (loginData) => {
return request.post('/user/login', loginData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//手机号登录接口
export const userPhoneLoginService = (phones) => {
return request.post('/user/phone/login?phone=' + phones)
}
//账号注册接口
export const userRegisterService = (jsonData) => {
return request.post('/user/register', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//手机号注册接口
export const userPhoneRegisterService = (jsonData) => {
return request.post('/user/phone/register', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//手机号验证码请求
export const userCodeRegisterService = (phoness) => {
return request.post('/user/code?phone=' + phoness)
}
//用户信息接口
export const userGetInfoService = (token) =>
request.get('/user/info', {
headers: { Authorization: `Bearer ${token}` },
})
//修改用户信息
export const userChangeService = (jsonData) => {
return request.post('/user/update', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//获取身份信息
export const userIdenService = (id) => {
return request.post('/user/personal/info?id=' + id)
}
//修改身份信息
export const userIdenChangeService = (dataToSend) => {
return request.post('/user/update/PersonalInfo', dataToSend, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

@ -19,6 +19,8 @@ import 'virtual:svg-icons-register'
// 获取并创建实例对象
const app = createApp(App)
// 引入自动注册全局组件脚本
//持久化
import gloablComponent from '@/components/index'
import 'echarts'
import ECharts from 'vue-echarts';

@ -9,64 +9,65 @@ import 'nprogress/nprogress.css'
const userStore = useUserStore(pinia)
const usePermissionStore = permissionStore(pinia)
// const whitelist = ['/login', '/404']
// router.beforeEach(async (to, form, next) => {
// // 进度条开始\
// nprogress.configure({ showSpinner: false })
// nprogress.start()
// // 判断是否登录
// if (userStore.token) {
// // 登录成功访问登录页则跳转到首页
// if (to.path == '/login') {
// next({ path: '/' })
// } else {
// // 登录成功判断是否获取到了用户信息
// if (userStore.userName) {
// next()
// } else {
// try {
// // 没有获取到用户信息 就获取用户信息 然后放行
// await userStore.getUserInfo()
// // 获取筛选到的路由
// const asyncRouter = await usePermissionStore.getAsyncRoutes(
// userStore.routes,
// )
// // 遍历筛选出来的路由通过addRoute添加到路由表
// asyncRouter.forEach((item: any) => {
// router.addRoute(item)
// })
// // 在最后向路由表添加一个404规则
// // 切记不要写到路由表内 否者刷新页面会跳转到404页面
// router.addRoute({
// path: '/:pathMatch(.*)*',
// component: () => import('@/views/404/index.vue'),
// name: 'Any',
// meta: {
// title: '任意',
// hidden: true,
// },
// })
// next({ ...to, replace: true }) // 这里相当于push到一个页面 不在进入路由拦截
// } catch (error) {
// // 如果获取用户信息失败了则执行登出操作让重新登录
// console.log(error)
// userStore.logout()
// next({ path: '/login' })
// }
// }
// }
// } else {
// // 没有token访问登录页放行
// if (to.path == '/login') {
// next()
// } else {
// // 访问其他页面则阻止
// next({ path: '/login', query: { redirect: to.path } })
// }
// }
// })
router.beforeEach((to,form,next) => {
next()
router.beforeEach(async (to, form, next) => {
// 进度条开始\
nprogress.configure({ showSpinner: false })
nprogress.start()
// 判断是否登录
if (userStore.token) {
// 登录成功访问登录页则跳转到首页
if (to.path == '/login') {
next({ path: '/' })
} else {
// 登录成功判断是否获取到了用户信息
if (userStore.userName) {
next()
} else {
try {
// 没有获取到用户信息 就获取用户信息 然后放行
await userStore.getUserInfo()
// 获取筛选到的路由
const asyncRouter = await usePermissionStore.getAsyncRoutes(
userStore.routes,
)
// 遍历筛选出来的路由通过addRoute添加到路由表
asyncRouter.forEach((item: any) => {
router.addRoute(item)
})
// 在最后向路由表添加一个404规则
// 切记不要写到路由表内 否者刷新页面会跳转到404页面
router.addRoute({
path: '/:pathMatch(.*)*',
component: () => import('@/views/404/index.vue'),
name: 'Any',
meta: {
title: '任意',
hidden: true,
},
})
next({ ...to, replace: true }) // 这里相当于push到一个页面 不在进入路由拦截
} catch (error) {
// 如果获取用户信息失败了则执行登出操作让重新登录
console.log(error)
userStore.logout()
next({ path: '/login' })
}
}
}
} else {
// 没有token访问登录页放行
if (to.path == '/login') {
next()
} else {
// 访问其他页面则阻止
next({ path: '/login', query: { redirect: to.path } })
}
}
})
// router.beforeEach((to, form, next) => {
// console.log(to, 'to')
// next()
// })
router.afterEach((to, form, next) => {
nprogress.done()
})

@ -1,6 +1,9 @@
// 引入仓库
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建仓库
const pinia = createPinia()
// 暴露仓库
pinia.use(persist)
export default pinia
export * from './modules/user'

@ -5,4 +5,5 @@ export interface LoginStoreType {
userName: string // 用户姓名
avatar: string // 用户头像
routes: string
phone: string
}

@ -5,7 +5,7 @@ import { reqLogin, getUserInfo } from '@/api/user/index'
// 引入登录参数类型
import { loginType, loginResponseType } from '@/api/user/types'
// 引入ElementPlus通知插件
import { ElNotification } from 'element-plus'
import { ElMessage, ElNotification } from 'element-plus'
import { getTime } from '@/utils/time'
import { LoginStoreType } from '../modules/type/types'
// 引入设置token的方法
@ -14,68 +14,113 @@ import { GET_TKOEN, SET_TKOEN, REMOVE_TOKEN } from '@/utils/token'
import { constantRoute } from '@/router/routers'
import permissionStore from './permission'
// 引入路由
const useUserStore = defineStore('User', {
state: (): LoginStoreType => {
return {
token: GET_TKOEN('TOKEN') || '',
menuRoutes: constantRoute, //存储生成菜单路由数据
userName: '', // 用户姓名
avatar: '', // 用户头像
routes: '',
}
},
actions: {
// 登录事件
async userLogin(data: loginType) {
const res: loginResponseType = await reqLogin(data)
this.token = res.data.token as string
if (res.code === 200) {
SET_TKOEN('TOKEN', this.token)
// localStorage.setItem('TOKEN', this.token)
ElNotification({
type: 'success',
message: '登录成功!',
title: `Hi ${getTime()}!`,
})
return 'ok'
} else {
ElNotification({
type: 'error',
message: res.data.message,
})
return Promise.reject(new Error(res.data.message))
}
},
// 获取用户信息事件
async getUserInfo() {
const result = await getUserInfo()
if (result.code === 200) {
console.log(result)
this.userName = result.data.checkUser.username
this.avatar = result.data.checkUser.avatar
this.routes = result.data.checkUser.routes
import {
userLoginService,
userGetInfoService,
userPhoneLoginService,
} from '@/api/user/user.js'
return 'ok'
} else {
return Promise.reject('登录过期')
const useUserStore = defineStore(
'User',
{
state: (): LoginStoreType => {
return {
token: GET_TKOEN('TOKEN') || '',
menuRoutes: constantRoute, //存储生成菜单路由数据
userName: '', // 用户姓名
avatar: '', // 用户头像
routes: '',
}
},
// 退出登录事件
logout() {
const usePermissionStore = permissionStore()
console.log(usePermissionStore)
actions: {
// 登录事件
async userLogin(data: loginType) {
console.log(data, 'pinia')
const res: loginResponseType = await userLoginService(data).catch(
(error) => {
ElMessage.error(error.response.data.message)
console.log(error, 'error')
},
)
// 清除token
REMOVE_TOKEN('TOKEN')
;(this.userName = ''), (this.avatar = '')
this.token = ''
this.routes = ''
usePermissionStore.removeRouter()
location.reload()
if (res.code === 200) {
this.token = res.data.token as string
SET_TKOEN('TOKEN', this.token)
// localStorage.setItem('TOKEN', this.token)
ElNotification({
type: 'success',
message: '登录成功!',
title: `Hi ${getTime()}!`,
})
return 'ok'
} else {
console.log(res, '33333')
// ElNotification({
// // type: 'error',
// message: res.message,
// })
ElMessage.error(res.message)
return Promise.reject(new Error(res.data.message))
}
},
//手机号登录
async userPhoneLogin(data: loginType) {
const res: loginResponseType = await userPhoneLoginService(data)
console.log(res, 11111)
if (res.code === 200) {
this.token = res.data.token as string //接收返回的token
SET_TKOEN('TOKEN', this.token)
// localStorage.setItem('TOKEN', this.token)
ElNotification({
type: 'success',
message: '登录成功!',
title: `Hi ${getTime()}!`,
})
return 'ok'
} else {
ElNotification({
type: 'error',
message: res.message,
})
return Promise.reject(new Error(res.data.message))
}
},
// 获取用户信息事件
async getUserInfo() {
const result = await userGetInfoService(GET_TKOEN('TOKEN'))
if (result.code === 200) {
// console.log(result)
this.userName = result.data.username
// this.avatar = result.data.checkUser.avatar
this.routes = result.data.menus.map((item) => {
return item.name
})
return {
result,
}
} else {
return Promise.reject('登录过期')
}
},
// 退出登录事件
logout() {
const usePermissionStore = permissionStore()
console.log(usePermissionStore)
// 清除token
REMOVE_TOKEN('TOKEN'),
// ;(this.userName = ''), (this.avatar = '')
(this.userName = ''),
(this.avatar = '')
this.token = ''
this.routes = ''
usePermissionStore.removeRouter()
location.reload()
},
},
getters: {},
},
{
persist: true, // 持久化
},
getters: {},
})
)
export default useUserStore

@ -0,0 +1,25 @@
import OSS from 'ali-oss'
import { nanoid } from 'nanoid'
export const client = new OSS({
region: 'oss-cn-beijing', //创建的时候,bucket所在的区域,华北2->oss-cn-beijing ;其他的可以去百度
accessKeyId: 'LTAI5tPutTqQcDZjPXTVmuLy', // 阿里云控制台创建的AccessKey
accessKeySecret: '3PmB75969OJt6uOMkRJTZjpSbVI4iL', //阿里云控制台创建的AccessSecret
bucket: 'teaching-edu123', //创建的bucket的名称
endpoint: 'oss-cn-beijing.aliyuncs.com', //地域节点
secure: false, //http:false,https:ture
})
export const tool = {
oss: {
async upload(file) {
// // console.log(11, file, client)
// 文件名
const uuid = nanoid() // 文件后缀名
const index = file.name.lastIndexOf('.')
const suffix = file.name.substring(index + 1)
let fileName = uuid + '.' + suffix
console.log(uuid, file, suffix) // return await client.multipartUpload(fileName, file, {
return await client.put(fileName, file)
},
},
}

@ -2,7 +2,6 @@ import axios from 'axios'
import { ElMessage } from 'element-plus'
// 获取用户相关小仓库
import useUserStore from '@/store/modules/user'
//创建axios实例
const request = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,

@ -1,9 +1,121 @@
<template>
<div>课程目标</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
// import allclass from '@/assets/images/allclass.png'
</script>
<style lang="scss" scoped></style>
<template>
<el-row :gutter="20">
<el-col :span="8">
<div class="grid-content main">
<div class="title"><p>|&nbsp;&nbsp;课程总目标</p></div>
<div class="ac_content">
<div class="ma_content">
通过本课程的学习使学生进一步了解计算机的工作原理更好地理解和应用计算机
握用计算机处理问题的方法和技能:培养学生分析问题解决问题的能力以及编制程序实现算
法的能力
</div>
</div>
</div>
<div class="grid-content picture">
<div class="title"><p>|&nbsp;&nbsp;课程总目标</p></div>
<div class="ac_pic"></div>
</div>
</el-col>
<el-col :span="16">
<div class="grid-content object">
<div class="title"><p>|&nbsp;&nbsp;课程分目标</p></div>
<div class="slice"></div>
</div>
</el-col>
</el-row>
</template>
<style>
.el-row {
margin-bottom: 20px;
}
.el-col {
border-radius: 4px;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
display: flex;
flex-direction: column;
}
.ac_content {
background: linear-gradient(180deg, #c7e3ff 0%, #ffffff 100%);
border-radius: 29px 29px 29px 29px;
display: flex;
align-items: center;
justify-content: center;
height: 206px;
}
.ma_content {
height: 160px;
font-family: Inter, Inter;
font-weight: 400;
font-size: 16px;
color: #333333;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
margin-left: 33px;
margin-right: 33px;
}
.ac_pic {
height: 488px;
background: linear-gradient(180deg, #ffe9c7 0%, #ffffff 100%);
border-radius: 29px 29px 29px 29px;
}
img {
order: -1;
}
.object {
height: 857px;
background: linear-gradient(180deg, #4984ff 0%, #74deff 100%);
border-radius: 29px 29px 29px 29px;
}
.slice {
height: 791px;
background: linear-gradient(180deg, #c7e3ff 0%, #ffffff 100%);
border-radius: 29px 29px 29px 29px;
}
.picture {
height: 553px;
margin-top: 28px;
border-radius: 29px 29px 29px 29px;
background-image: linear-gradient(to right, #f9e397, #ffa674);
}
.main {
height: 272px;
border-radius: 29px 29px 29px 29px;
background-image: linear-gradient(to right, #4984ff, #74deff);
display: flex;
justify-content: end;
}
.title {
flex: 1;
display: flex;
align-items: center;
}
p {
height: 29px;
font-family: Inter, Inter;
font-weight: bold;
margin-left: 34px;
font-size: 24px;
color: #ffffff;
line-height: 28px;
text-align: left;
font-style: normal;
text-transform: none;
}
</style>

@ -1,9 +1,6 @@
<script setup></script>
<template>
<div>分组</div>
<div></div>
</template>
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang="scss" scoped></style>
<style></style>

@ -0,0 +1,108 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
const courses = [
{
name: 'Vue.js Basics',
image: 'vuejs-basics.jpg',
hours: 20,
},
{
name: 'Reac',
image: 'react-fundamentals.jpg',
hours: 25,
},
{
name: 'Reacts',
image: 'react-fundamentals.jpg',
hours: 25,
},
{
name: 'React Fund',
image: 'react-fundamentals.jpg',
hours: 25,
},
]
</script>
<template>
<div class="info_container" style="width: 214px; height: 326px">
<conheader title="已开科目"></conheader>
<div class="info_content" style="width: 214px; height: 264px">
<div class="course-list">
<div
v-for="(course, index) in courses"
:key="index"
class="course-item"
>
<img :src="course.image" alt="course image" class="course-image" />
<div class="course-info">
<p class="course-name">{{ course.name }}</p>
<p class="course-hours">{{ course.hours }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.class {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: center;
align-items: center;
}
.info_container {
display: flex;
justify-content: space-between;
}
.info_content {
display: flex;
justify-content: space-between;
}
.course-list {
display: flex;
flex-direction: column;
width: 132px;
height: 48px;
}
.course-item {
display: flex;
margin: 10px;
}
.course-image {
width: 48px;
height: 48px;
margin-right: 16px;
}
.course-info {
display: flex;
flex-direction: column;
width: 75px;
height: 46px;
}
.course-name {
font-family: Inter, Inter;
font-weight: normal;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.course-hours {
font-family: Inter, Inter;
font-weight: 400;
font-size: 12px;
color: rgba(0, 0, 0, 0.4);
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
}
</style>

@ -0,0 +1,108 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
const courses = [
{
name: 'Vue.js Basics',
image: 'vuejs-basics.jpg',
hours: 20,
},
{
name: 'Reac',
image: 'react-fundamentals.jpg',
hours: 25,
},
{
name: 'Reacts',
image: 'react-fundamentals.jpg',
hours: 25,
},
{
name: 'React Fund',
image: 'react-fundamentals.jpg',
hours: 25,
},
]
</script>
<template>
<div class="info_container" style="width: 214px; height: 326px">
<conheader title="我的科目"></conheader>
<div class="info_content" style="width: 214px; height: 264px">
<div class="course-list">
<div
v-for="(course, index) in courses"
:key="index"
class="course-item"
>
<img :src="course.image" alt="course image" class="course-image" />
<div class="course-info">
<p class="course-name">{{ course.name }}</p>
<p class="course-hours">{{ course.hours }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.class {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: center;
align-items: center;
}
.info_container {
display: flex;
justify-content: space-between;
}
.info_content {
display: flex;
justify-content: space-between;
}
.course-list {
display: flex;
flex-direction: column;
width: 132px;
height: 48px;
}
.course-item {
display: flex;
margin: 10px;
}
.course-image {
width: 48px;
height: 48px;
margin-right: 16px;
}
.course-info {
display: flex;
flex-direction: column;
width: 75px;
height: 46px;
}
.course-name {
font-family: Inter, Inter;
font-weight: normal;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.course-hours {
font-family: Inter, Inter;
font-weight: 400;
font-size: 12px;
color: rgba(0, 0, 0, 0.4);
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
}
</style>

@ -0,0 +1,64 @@
<template>
<div class="info_title">
<div class="title_name">{{ props.title }}</div>
<div class="icon">
<img :src="ellipsis" @click="handleClick" />
</div>
</div>
<slot name="drawer"></slot>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue'
import ellipsis from '@/assets/images/ellipsis.png'
const props = defineProps({
title: String,
modelValue: Boolean,
})
const emit = defineEmits(['update:modelValue'])
function handleClick() {
emit('update:modelValue', true)
}
</script>
<style lang="scss" scoped>
.info_title {
display: flex;
justify-content: space-between;
align-items: center;
.title_name {
font-family: Inter, Inter;
font-weight: 400;
font-size: 20px;
color: rgba(0, 0, 0, 0.9);
line-height: 30px;
text-align: left;
font-style: normal;
text-transform: none;
}
}
.icon {
width: 32px;
height: 32px;
border-radius: 3px 3px 3px 3px;
display: flex;
justify-content: center;
align-items: center;
}
.overlay {
position: absolute;
top: 181px;
left: 1278px;
width: 40px;
height: 30px;
// background-color: lightgray;
border: 1px solid black;
z-index: 1000;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
margin-bottom: 10px;
}
</style>

@ -0,0 +1,103 @@
<script setup lang="ts">
import * as echarts from 'echarts'
import { ref, onMounted, Ref } from 'vue'
const echarsDom: Ref<HTMLElement | any> = ref(null)
onMounted(() => {
const myChart = echarts.init(echarsDom.value)
//
myChart.setOption({
//
grid: {
left: '4%', //
right: '10%', //
bottom: '20%', //
top: '10%', //
},
tooltip: {
trigger: 'axis',
},
legend: {
left: 'center', //
bottom: 0, //
orient: 'horizontal', //
itemWidth: 16, // 0
itemHeight: 0, // 0
data: ['本月', '上月'],
},
// grid: {
// left: '3%',
// right: '4%',
// bottom: '3%',
// containLabel: true
// },
toolbox: {
feature: {
saveAsImage: {},
},
},
xAxis: {
type: 'category',
boundaryGap: true,
data: [
'09-01',
'09-03',
'09-05',
'09-07',
'09-09',
'09-11',
'09-13',
'09-15',
],
},
yAxis: {
type: 'value',
},
series: [
{
name: '本月',
// itemStyle: {
// color: '#0052D9'
// },
symbol: 'circle',
itemStyle: {
color: '#0052D9', //
borderWidth: 1.5, //
borderColor: 'white', //
},
symbolSize: 8, //
type: 'line',
data: [20, 38, 60, 65, 90, 55, 36, 30],
},
{
name: '上月',
symbol: 'circle',
itemStyle: {
color: '#B5C7FF', //
borderWidth: 1.5, //
borderColor: 'white', //
},
symbolSize: 8, //
type: 'line',
data: [22, 25, 48, 70, 80, 45, 24, 38],
},
],
})
/**
* 自适应大小
*/
window.onresize = function () {
myChart.resize()
}
})
</script>
<template>
<div ref="echarsDom" class="main"></div>
</template>
<style>
.main {
width: 766px;
height: 352px;
}
</style>

@ -0,0 +1,28 @@
<script setup>
import echarts from '@/views/home/components/Echart/Echarts.vue'
import conheader from '@/views/home/components/ConHeader.vue'
</script>
<template>
<div class="info_container" style="width: 1257px; height: 384px">
<conheader title="主页访问数据(次)"></conheader>
<div class="info_content" style="width: 766px; height: 352px">
<echarts></echarts>
</div>
</div>
</template>
<style scoped>
.info_container {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.echarts {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
margin-bottom: 16px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

@ -0,0 +1,267 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
import { onMounted, ref, computed } from 'vue'
import { userIdenService, userIdenChangeService } from '@/api/user/user.js'
import { Edit } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
const props = defineProps({
data: {
type: Object,
},
})
const id = props.data.id
const formModel = ref()
const loading = ref()
//true
const editDialogVisible = ref(false)
const editForm = ref({
name: '',
content: '',
})
const readonlyField = computed(() => editForm.value.name)
const editRowIndex = ref()
const openEditDialog = (row) => {
editForm.value = { ...row }
editRowIndex.value = formModel.value.indexOf(row)
editDialogVisible.value = true
}
//
const saveEdit = () => {
console.log(editRowIndex.value, 'rowww')
const index = editRowIndex.value
if (index !== -1) {
formModel.value[index] = { ...editForm.value }
}
console.log(editRowIndex.value, 'edit')
editDialogVisible.value = false
}
//
const cancelEdit = () => {
editDialogVisible.value = false
//
editForm.value = {
name: '',
content: '',
}
}
const identInfo = ref()
//
const getInfo = async (id) => {
const res = await userIdenService(id)
identInfo.value = res
return res
}
console.log()
onMounted(() => {
// getInfo(id)
getInfo(id)
.then((res) => {
// 访 getData
console.log(res.data, '000000')
identInfo.value = res.data
//
if (res.data.roleId === 1) {
formModel.value = [
{ par: 'name', name: '姓名', content: res.data.name },
{ par: 'sex', name: '性别', content: res.data.sex },
{ par: 'nationality', name: '民族', content: res.data.nationality },
{ par: 'profession', name: '专业', content: res.data.profession },
{ par: 'education', name: '学历', content: res.data.education },
{ par: 'degree', name: '学位', content: res.data.degree },
{
par: 'professionalTitle',
name: '职称',
content: res.data.professionalTitle,
},
{
par: 'emergencyContact',
name: '手机号',
content: res.data.emergencyContact,
},
{
par: 'joinWorkTime',
name: '参加工作时间',
content: res.data.joinWorkTime,
},
{
par: 'politicalStatus',
name: '政治面貌',
content: res.data.politicalStatus,
},
]
} else if (res.data.roleId === 2) {
formModel.value = [
{ par: 'name', name: '姓名', content: res.data.name },
{ par: 'sex', name: '性别', content: res.data.sex },
{ par: 'nationality', name: '民族', content: res.data.nationality },
{ par: 'number', name: '学号', content: res.data.number },
{ par: 'birthday', name: '生日', content: res.data.birthday },
{ par: 'phone', name: '手机号', content: res.data.phone },
{ par: 'faculty', name: '院系', content: res.data.faculty },
{ par: 'major', name: '专业', content: res.data.major },
{ par: 'year_age', name: '入学年份', content: res.data.year_age },
{ par: 'class_name', name: '班级', content: res.data.class_name },
]
}
// else {
// formModel.value = [
// { name: '', content: res.data.name },
// { name: '', content: res.data.id },
// { name: '', content: 9.9 },
// { name: '', content: 9.9 },
// { name: '', content: 9.9 },
// { name: '', content: 9.9 },
// { name: '', content: 9.9 },
// { name: '', content: 9.9 },
// { name: '', content: 9.9 },
// ]
// }
})
.catch((error) => {
console.error(error)
})
})
// 1
const drawer2 = ref(false)
const direction = ref('rtl')
function cancelClick() {
drawer2.value = false
}
const data1 = ref()
const confirmClick = () => {
drawer2.value = false
data1.value = formModel.value
const values = {}
data1.value.map((item) => (values[item.par] = item.content))
const dataToSend = {
...values,
roleId: identInfo.value.roleId,
id: identInfo.value.id,
}
userIdenChangeService(dataToSend).then(() => {
ElMessage.success('修改成功')
})
}
</script>
<template>
<div class="info_container" style="width: 1265px; height: 306px">
<conheader title="个人信息" v-model="drawer2">
<template #drawer>
<el-drawer v-model="drawer2" :direction="direction" class="table">
<template #header>
<h4>个人信息</h4>
</template>
<template #default>
<div>
<el-table :data="formModel" style="width: 100%">
<el-table-column prop="name" label="标题"></el-table-column>
<el-table-column prop="content" label="内容"></el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button
type="primary"
class="el-icon-edit"
:icon="Edit"
plain
circle
@click="openEditDialog(row)"
></el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancelClick" size="medium">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</template>
</el-drawer>
<el-dialog
v-model="editDialogVisible"
title="详细信息"
style="width: 800px; text-align: center"
:readonly-field="readonlyField"
>
<el-form :model="editForm" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="editForm.name"></el-input>
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input v-model="editForm.content"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div>
<el-button @click="cancelEdit">取消</el-button>
<el-button type="primary" @click="saveEdit">保存</el-button>
</div>
</template>
</el-dialog>
</template>
</conheader>
<div
class="info_content"
style="width: 776px; height: 244px"
v-loading="loading"
>
<div class="item" :key="item.name" v-for="item in formModel">
<div class="name">{{ item.name }}</div>
<div class="content">{{ item.content }}</div>
</div>
</div>
</div>
</template>
<style scoped>
.info_container {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.info_content {
display: grid;
display: wrap;
grid-template-columns: repeat(4, 1fr);
gap: 10px; /* 设置元素之间的间隔 */
}
.item {
width: 176px;
height: 60px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.name {
font-family: Inter, Inter;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.content {
font-family: Inter, Inter;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.table {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.custom-input {
margin-bottom: 20px;
height: 40px; /* 自定义输入框高度 */
padding: 0 10px; /* 自定义输入框内边距 */
font-size: 16px; /* 自定义输入框字体大小 */
/* 其他样式自定义 */
}
</style>

@ -0,0 +1,211 @@
<script setup>
import add from '@/assets/images/add.png'
// import { Plus } from '@element-plus/icons-vue'
import { userChangeService } from '@/api/user/user.js'
import { tool } from '@/utils/img'
const dialogFormVisible = ref(false)
import { ref } from 'vue'
const props = defineProps({
data: {
type: Object,
},
})
// console.log(props.data.icon, 44444)
const imageUrl = ref('')
//
const form = ref()
//
// const rules = {
// username: [
// { required: true, message: '', trigger: 'blur' },
// {
// max: 10,
// min: 5,
// message: '',
// trigger: ['change', 'blur'],
// },
// ],
// nickName: [{ trigger: 'blur' }],
// phone: [
// { trigger: 'blur' },
// {
// pattern: /^1[3456789]\d{9}$/,
// message: '',
// trigger: 'blur',
// // pattern
// },
// ],
// code: [{ message: '', trigger: 'blur' }],
// }
form.value = props.data
const formLabelWidth = '140px'
//
imageUrl.value = props.data.icon
//
const openFileInput = () => {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.style.display = 'none'
input.onchange = (event) => {
const file = event.target.files[0]
if (file) {
const reader = new FileReader()
reader.onload = () => {
imageUrl.value = reader.result
}
reader.readAsDataURL(file)
changeInfo()
}
}
document.body.appendChild(input)
input.click()
document.body.removeChild(input)
}
const upload = async (option) => {
const res = await tool.oss.upload(option.file)
imageUrl.value = res.url
console.log(imageUrl.value)
}
//
const changeInfo = async () => {
const userInfo = {
username: form.value.username,
phone: form.value.phone,
id: form.value.id,
nickName: form.value.nickName,
icon: imageUrl.value,
}
const jsonData = JSON.stringify(userInfo)
console.log(jsonData)
await userChangeService(jsonData)
dialogFormVisible.value = false
}
</script>
<template>
<div class="ident" style="width: 278px; height: 274px; background: #375fff">
<div class="info_container" style="width: 201px; height: 178px">
<div class="avater">
<img :src="imageUrl" alt="Uploaded" v-if="imageUrl" class="avater" />
</div>
<div class="click" @click="openFileInput" :http-request="upload">
<img :src="add" />
</div>
<div
class="info_content"
style="height: 58px"
@click="dialogFormVisible = true"
>
<div class="name">{{ props.data.username }}</div>
</div>
</div>
</div>
<el-dialog
v-model="dialogFormVisible"
title="登录信息"
style="width: 800px; text-align: center"
>
<el-form :model="form" style="" :data="form" :rules="rules">
<el-form-item label="用户名" :label-width="formLabelWidth">
<el-input v-model="form.username" autocomplete="off" />
</el-form-item>
<el-form-item label="昵称" :label-width="formLabelWidth">
<el-input v-model="form.nickName" autocomplete="off" />
</el-form-item>
<el-form-item label="手机" :label-width="formLabelWidth">
<el-input v-model="form.phone" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="changeInfo">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.el-form-item {
display: flex;
}
.ident {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: center;
align-items: center;
}
.info_container {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.info_content {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.name {
height: 28px;
font-family: Inter, Inter;
font-weight: normal;
font-size: 20px;
color: rgba(255, 255, 255, 0.9);
line-height: 28px;
text-align: left;
font-style: normal;
text-transform: none;
}
.iden {
width: 126px;
height: 22px;
font-family: Inter, Inter;
font-weight: 400;
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.avater {
width: 80px;
height: 80px;
background: #b5c7ff;
border-radius: 40px 40px 40px 40px;
display: flex;
}
span {
width: 24px;
height: 44px;
font-family: Inter, Inter;
font-weight: bold;
font-size: 36px;
color: #0052d9;
line-height: 44px;
text-align: center;
font-style: normal;
text-transform: none;
grid-area: element1;
position: relative;
margin-left: 27px;
top: 18px;
}
.click {
width: 24px;
height: 24px;
border-radius: 100px 100px 100px 100px;
border: 2px solid #ffffff;
grid-area: element2;
background: #0052d9;
border-radius: 32px 32px 32px 32px;
display: flex;
justify-content: center;
align-items: center;
margin-left: 60px;
margin-top: -60px;
}
</style>

@ -0,0 +1,97 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
import { ref } from 'vue'
const name = ['Aa', 'Bb', 'Cc', 'Dd']
const getRandomColor = () => {
//
return '#' + Math.floor(Math.random() * 16777215).toString(16)
}
const drawer2 = ref(false)
const direction = ref('rtl')
function cancelClick() {
drawer2.value = false
}
function confirmClick() {
drawer2.value = false
}
</script>
<template>
<div class="info_container" style="width: 222px; height: 174px">
<conheader title="学生分组" v-model="drawer2">
<div>编辑</div>
<template #drawer>
<el-drawer v-model="drawer2" :direction="direction">
<template #header>
<h4>78</h4>
</template>
<template #default>
<div></div>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancelClick">cancel</el-button>
<el-button type="primary" @click="confirmClick">
confirm
</el-button>
</div>
</template>
</el-drawer>
</template>
</conheader>
<div class="info_content" style="width: 194px; height: 112px">
<div
v-for="(item, index) in name"
:key="index"
class="stu_color"
:style="{ backgroundColor: getRandomColor() }"
>
{{ item }}
</div>
</div>
</div>
</template>
<style scoped>
.stu {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: center;
align-items: center;
}
.info_container {
display: flex;
justify-content: space-between;
}
.info_content {
width: 194px;
height: 112px;
flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px; /* 设置元素之间的间隔 */
grid-row-gap: 16px;
}
.stu_color {
display: flex;
justify-content: center;
align-items: center;
width: 49px;
height: 48px;
border-radius: 40px 40px 40px 40px;
font-family: Inter, Inter;
font-weight: normal;
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
transition: background-color 0.3s;
}
.stu_color:hover {
background-color: #f2f2f2; /* 鼠标移入时的背景色变化 */
filter: brightness(1.1);
}
</style>

@ -0,0 +1,97 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
import { ref } from 'vue'
const name = ['Aa', 'Bb', 'Cc', 'Dd']
const getRandomColor = () => {
//
return '#' + Math.floor(Math.random() * 16777215).toString(16)
}
const drawer2 = ref(false)
const direction = ref('rtl')
function cancelClick() {
drawer2.value = false
}
function confirmClick() {
drawer2.value = false
}
</script>
<template>
<div class="info_container" style="width: 222px; height: 174px">
<conheader title="已分组别" v-model="drawer2">
<div>编辑</div>
<template #drawer>
<el-drawer v-model="drawer2" :direction="direction">
<template #header>
<h4>78</h4>
</template>
<template #default>
<div></div>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancelClick">cancel</el-button>
<el-button type="primary" @click="confirmClick">
confirm
</el-button>
</div>
</template>
</el-drawer>
</template>
</conheader>
<div class="info_content" style="width: 194px; height: 112px">
<div
v-for="(item, index) in name"
:key="index"
class="stu_color"
:style="{ backgroundColor: getRandomColor() }"
>
{{ item }}
</div>
</div>
</div>
</template>
<style scoped>
.stu {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: center;
align-items: center;
}
.info_container {
display: flex;
justify-content: space-between;
}
.info_content {
width: 194px;
height: 112px;
flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px; /* 设置元素之间的间隔 */
grid-row-gap: 16px;
}
.stu_color {
display: flex;
justify-content: center;
align-items: center;
width: 49px;
height: 48px;
border-radius: 40px 40px 40px 40px;
font-family: Inter, Inter;
font-weight: normal;
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
transition: background-color 0.3s;
}
.stu_color:hover {
background-color: #f2f2f2; /* 鼠标移入时的背景色变化 */
filter: brightness(1.1);
}
</style>

@ -0,0 +1,46 @@
<script setup>
const props = defineProps({
username: String,
})
</script>
<template>
<div class="nav_info">
<div class="name">
{{ props.username }}
</div>
<div class="intro">今天是你的第一天~~</div>
</div>
</template>
<style scoped>
.nav_info {
width: 1265px;
height: 32px;
display: flex;
justify-content: start;
align-items: center;
}
.name {
height: 28px;
font-family: Inter, Inter;
font-weight: bold;
font-size: 20px;
color: rgba(0, 0, 0, 0.9);
line-height: 28px;
text-align: left;
font-style: normal;
text-transform: none;
margin-right: 68px;
}
.intro {
width: 257px;
height: 22px;
font-family: Inter, Inter;
font-weight: bold;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
line-height: 26px;
text-align: left;
font-style: normal;
text-transform: none;
}
</style>

@ -1,41 +1,158 @@
<script lang="ts" setup>
import status from '@/views/home/components/Status.vue'
import welcome from './components/Welcome.vue'
import Info from './components/Info.vue'
import echarts from './components/Echarts.vue'
import Class1 from './components/Class1.vue'
import Student1 from './components/Student1.vue'
import Class2 from './components/Class2.vue'
import Student2 from './components/Student2.vue'
import useUserStore from '@/store/modules/user'
import { ref, onMounted } from 'vue'
import { toRaw } from 'vue'
const userStore = useUserStore()
// const info = userStore.getUserInfo()
// console.log(info)
const username = ref()
const data = ref(null)
const flag = ref(false)
const role = ref(null)
const rightVisible = ref(false)
const getData = async () => {
await userStore
.getUserInfo()
.then((response) => {
const res = toRaw(response.result.data)
username.value = res.username
data.value = res
flag.value = true
role.value = data.value.roles[0]
if (role.value === '老师') {
rightVisible.value = true
} else {
rightVisible.value = false
}
// console.log(data.value.roles[0], 333333)
})
.catch((error) => {
console.log(error)
})
}
onMounted(() => {
getData()
})
</script>
<template>
<div>home</div>
<svg-icon name="count" width="300px" height="300px" color="#000"></svg-icon>
<div class="box">
<div class="box1"></div>
<div class="box2"></div>
<div class="container">
<div class="left">
<div class="nav">
<welcome :username="username"></welcome>
</div>
<div class="info" style="width: 1321px; height: 368px">
<Info :data="data" v-if="flag"></Info>
</div>
<div class="echarts" style="width: 1321px; height: 444px">
<echarts></echarts>
</div>
</div>
<div v-if="rightVisible" class="right">
<status :data="data" v-if="flag"></status>
<div class="class" style="width: 278px; height: 388px">
<Class1></Class1>
</div>
<div class="stu" style="width: 278px; height: 236px">
<Student1></Student1>
</div>
</div>
<div v-else class="right">
<status :data="data" v-if="flag"></status>
<div class="class" style="width: 278px; height: 388px">
<Class2></Class2>
</div>
<div class="stu" style="width: 278px; height: 236px">
<Student2></Student2>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
// import { onMounted } from 'vue'
// import useUserStore from '@/store/modules/user'
// // const userStore = useUserStore()
// onMounted(() => {
// //
// // userStore.getUserInfo()
// })
</script>
<style lang="scss" scoped>
.box {
width: 500px;
height: 400px;
background-color: skyblue;
.container {
display: flex;
// flex-direction: column;
justify-content: space-evenly;
align-items: center;
div {
width: 100px;
height: 100px;
}
.box1 {
background-color: pink;
.left {
width: 1321px;
height: 936px;
margin-right: 16px;
display: grid;
grid-template-rows: 1fr 1fr 1fr; /* 使用fr单位来平均分布 */
height: 300px; /* 设置容器高度 */
.nav {
width: 1321px;
height: 92px;
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
margin-bottom: 16px;
display: flex;
flex-direction: column; //
justify-content: center;
align-items: center;
}
.info,
.echarts {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
margin-bottom: 16px;
display: flex;
// flex-direction: column; //
justify-content: center;
align-items: center;
.info_container {
display: flex;
flex-direction: column; //
justify-content: space-between;
}
}
.class {
width: 1321px;
height: 444px;
background: #c57676;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
// flex-direction: column; //
justify-content: center;
align-items: center;
}
}
.box2 {
background-color: greenyellow;
.right {
width: 278px;
height: 930px;
display: flex;
flex-direction: column; //
justify-content: space-between;
.ident,
.class,
.stu {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
// flex-direction: column; //
justify-content: center;
align-items: center;
.info_container {
display: flex;
flex-direction: column; //
justify-content: space-between;
.info_content {
display: flex;
flex-direction: column; //
justify-content: space-between;
}
}
}
}
}
</style>

@ -14,40 +14,266 @@
>
<el-form
class="loin_form"
:model="loginForm"
ref="formRules"
:model="formModel"
ref="form"
:rules="rules"
v-if="isRegister"
>
<h1>Hello</h1>
<h2>同学</h2>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
登录
</el-button>
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
</div>
<!-- 账号登录 -->
<div v-if="isToggle">
<el-form-item prop="username">
<el-input
v-model="formModel.username"
:prefix-icon="User"
size="large"
placeholder="请输入账号"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
type="password"
:prefix-icon="Lock"
size="large"
placeholder="请输入密码"
show-password
></el-input>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
登录
</el-button>
</el-form-item>
</div>
<!-- 手机号登录 -->
<div v-else>
<el-form-item prop="phone" class="content">
<el-input
type="text"
v-model="formModel.phone"
:prefix-icon="Phone"
placeholder="请输入手机号"
size="large"
style="flex: 1"
/>
</el-form-item>
<el-form-item prop="code" class="content">
<el-input
type="text"
v-model="formModel.code"
placeholder="请输入验证码"
@click="userCodeLogin"
size="large"
style="flex: 1"
/>
<div class="p" v-if="countdown > 0">
<p>{{ countdown }} </p>
</div>
<el-button
class="code"
v-if="countdown <= 0"
:disabled="countdown > 0"
@click="codeSubmit"
>
发送验证码
</el-button>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="phoneLogin"
>
登录
</el-button>
</el-form-item>
</div>
<el-form-item class="flex">
<el-link type="info" :underline="false" @click="isRegister = false">
注册
</el-link>
</el-form-item>
</el-form>
<!-- 注册 -->
<el-form
class="loin_form"
:model="formModel"
ref="form"
:rules="rules"
v-else
>
<h1>Hello</h1>
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
</div>
<!-- 账号注册 -->
<div v-if="isToggle">
<el-form-item prop="username">
<el-input
v-model="formModel.username"
placeholder="请输入用户名"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
placeholder="请输入密码"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item prop="repassword">
<el-input
v-model="formModel.repassword"
placeholder="请再次输入密码"
type="password"
:prefix-icon="Check"
size="large"
show-repassword
@change="checkPasswordMatch"
></el-input>
</el-form-item>
<el-form-item>
<el-button
size="large"
class="login-btn"
type="primary"
@click="handleSubmit"
>
注册
</el-button>
</el-form-item>
<el-form-item class="flex">
<el-link
type="info"
:underline="false"
@click="isRegister = true"
>
登录
</el-link>
</el-form-item>
</div>
<!-- 手机号注册 -->
<div v-else>
<div v-if="isinfo">
<el-form-item prop="phone">
<el-input
v-model="formModel.phone"
:prefix-icon="Phone"
size="large"
placeholder="请输入手机号"
></el-input>
</el-form-item>
<el-form-item prop="code" class="content">
<el-input
type="text"
v-model="formModel.code"
placeholder="请输入验证码"
size="large"
style="
flex: 1;
/* height: 40px;
line-height: 40px;
border: 1px solid #dcdfe6;
width: 50%;
color: #dcdfe6; */
"
/>
<div class="p" v-if="countdown > 0">
<p>{{ countdown }} </p>
</div>
<el-button
class="code1"
v-if="countdown <= 0"
@click="codeSubmit"
:disabled="countdown > 0"
>
发送验证码
</el-button>
</el-form-item>
<el-form-item>
<el-button
size="large"
class="login-btn"
type="primary"
@click="phoneChange"
>
注册
</el-button>
</el-form-item>
</div>
<div v-else>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
type="password"
:prefix-icon="Lock"
size="large"
placeholder="请输入密码"
show-password
></el-input>
</el-form-item>
<el-form-item>
<div
style="display: flex; justify-content: space-between; flex: 1"
>
<el-button
class="code"
@click="phoneReturn"
style="
background-color: #5577ff;
color: #fff;
justify-content: center;
"
>
返回
</el-button>
</div>
<div style="display: flex; justify-content: end; flex: 1">
<el-button
class="code"
@click="phoneSubmit"
style="
background-color: #5577ff;
color: #fff;
justify-content: center;
"
>
确认
</el-button>
</div>
</el-form-item>
</div>
<el-form-item class="flex">
<el-link
type="info"
:underline="false"
@click="isRegister = true"
>
登录
</el-link>
</el-form-item>
</div>
</el-form>
</el-col>
<el-col :span="12" :xs="0"></el-col>
</el-row>
@ -55,19 +281,53 @@
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted, onUnmounted } from 'vue'
import { User, Lock } from '@element-plus/icons-vue'
import {
// reactive,
ref,
onMounted,
onUnmounted,
onBeforeUnmount,
watch,
} from 'vue'
import { User, Lock, Phone, Check } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user'
import { useRouter, useRoute } from 'vue-router'
let loginForm = reactive({ username: 'admin', password: '111111' })
//token
// import jwtDecode from 'jwt-decode'
//
import {
userRegisterService,
userPhoneRegisterService,
userCodeRegisterService,
} from '@/api/user/user.js'
// let loginForm = reactive({ username: 'admin', password: '111111' })
//
let userStore = useUserStore()
//
const $router = useRouter()
const $route = useRoute()
const isBtnLoading = ref<boolean>(false)
//
//model,form
const formModel = ref({
username: '',
password: '',
repassword: '',
phone: '',
code: '',
nickName: '',
icon: '',
})
const form = ref()
//
const isRegister = ref(true)
//
const isToggle = ref(true)
//
const isinfo = ref(true)
// form
const formRules = ref<any>(null)
// const formRules = ref<any>(null)
//
const rules = {
username: [
@ -88,23 +348,197 @@ const rules = {
trigger: ['change', 'blur'],
},
],
repassword: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{
pattern: /^\S{6,15}$/,
// pattern
max: 15,
min: 6,
message: '密码长度最小六位最大十五位',
trigger: ['change', 'blur'],
},
],
phone: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{
pattern: /^1[3456789]\d{9}$/,
message: '手机号格式不正确',
trigger: 'blur',
// pattern
},
],
code: [{ message: '验证码不能为空', trigger: 'blur' }],
}
//--
const countdown = ref(0)
let timer
const updateCountdown = () => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(timer) //
}
}
onBeforeUnmount(() => {
clearInterval(timer) //
})
//
// watch(isRegister, () => {
// formModel.value = {
// username: '',
// password: '',
// repassword: '',
// phone: '',
// code: '',
// }
// })
//
watch(isToggle, () => {
formModel.value = {
username: '',
password: '',
repassword: '',
phone: '',
code: '',
}
})
//
const passwordMatchError = ref('')
const checkPasswordMatch = () => {
if (formModel.value.password !== formModel.value.repassword) {
// passwordMatchError.value = ''
ElMessage('两次输入的密码不一致')
} else {
passwordMatchError.value = ''
}
}
//json
const handleSubmit = async () => {
//
await form.value.validate()
console.log('开始注册')
if (formModel.value.password === formModel.value.repassword) {
//
const dataToSend = {
username: formModel.value.username,
password: formModel.value.password,
phone: formModel.value.phone,
nickName: formModel.value.nickName,
icon: formModel.value.icon,
}
const jsonData = JSON.stringify(dataToSend)
console.log(jsonData)
//
userRegisterService(jsonData)
.then(() => {
//
ElMessage.success('注册成功')
// //
isRegister.value = true
})
.catch((error) => {
ElMessage.error(error.response.data.message)
})
} else {
//
passwordMatchError.value = '两次输入的密码不一致'
}
}
// const userGetInfoService
//
const codecode = ref()
const codeSubmit = async () => {
await form.value.validate()
console.log('开始发起注册验证码')
const phoness = formModel.value.phone
userCodeRegisterService(phoness)
.then((response) => {
codecode.value = response.data
console.log(codecode.value)
})
.catch((error) => {
console.log(error.message, 'error')
})
}
//
const phoneChange = () => {
console.log('开始手机号注册')
isinfo.value = false
}
//
const phoneReturn = () => {
isinfo.value = true
}
const phoneSubmit = async () => {
await form.value.validate()
//
if (formModel.value.code == codecode.value) {
isinfo.value = false
const dataToSend = {
phone: formModel.value.phone,
password: formModel.value.password,
}
const jsonData = JSON.stringify(dataToSend)
//
userPhoneRegisterService(jsonData)
.then(() => {
//
ElMessage.success('注册成功')
// //
isRegister.value = true
})
.catch((error) => {
ElMessage.error(error.response.data.message)
})
}
}
//
const login = async () => {
//
await formRules.value.validate()
await form.value.validate()
isBtnLoading.value = true
try {
isBtnLoading.value = true
await userStore.userLogin(loginForm)
isBtnLoading.value = false
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
$router.push('/')
}
await userStore.userLogin(formModel.value).then(() => {
isBtnLoading.value = false
$router.push('/home')
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
$router.push('/')
}
})
} catch (error) {
isBtnLoading.value = false
}
}
//
const phoneLogin = async () => {
//
await form.value.validate()
isBtnLoading.value = true
if (formModel.value.code == codecode.value) {
const phones = formModel.value.phone
// console.log(dataToSend)
userStore
.userPhoneLogin(phones)
.then(() => {
isBtnLoading.value = false
$router.push('/home')
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
$router.push('/')
}
})
.catch((error) => {
isBtnLoading.value = false
ElMessage.error(error.response.data.message)
})
}
}
const keyDown = (e: any) => {
//
if (e.keyCode == 13) {
@ -112,6 +546,7 @@ const keyDown = (e: any) => {
login()
}
}
onMounted(() => {
//@ts-expect-error tsVANTAGLOBE cdn
VANTA.GLOBE({
@ -155,6 +590,10 @@ onUnmounted(() => {
padding: 40px;
border-radius: 10px;
box-shadow: 7px 7px 42px rgba(0, 0, 0, 0.17);
.taggle {
display: flex;
justify-content: space-between;
}
h1 {
font-size: 40px;
color: #5577ff;
@ -167,6 +606,31 @@ onUnmounted(() => {
.login-btn {
width: 100%;
}
.p {
height: 40px;
text-align: center;
line-height: 40px;
width: 90px;
}
.codeVerify {
display: flex;
justify-content: end;
}
.code,
.code1 {
height: 40px;
width: 100px;
border: 1px solid #dcdfe6;
// background-color: #dcdfe6;
color: #000;
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: space-between;
flex-wrap: none;
line-height: 40px;
}
}
}
</style>

@ -2,8 +2,4 @@
<div>消息</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang="scss" scoped></style>
<script></script>

@ -1,9 +1,30 @@
<template>
<div>学生</div>
<div>
<!-- Dialog组件 -->
<Dialog
v-model="dialogVisible"
@confirm="collectData"
:name="readonlyName"
:age="readonlyAge"
/>
</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
</script>
<script setup>
import { ref, readonly } from 'vue'
const dialogVisible = ref(false) // Dialog
const name = ref('John') //
const age = ref(25) //
const readonlyName = readonly(name) // name
const readonlyAge = readonly(age) // age
<style lang="scss" scoped></style>
const collectData = () => {
// Dialog
// ...
console.log(readonlyName.value) // 访
console.log(readonlyAge.value) // 访
}
</script>

@ -0,0 +1,68 @@
<template>
<div>
<el-button type="primary" @click="openDrawer">Open Drawer</el-button>
<el-drawer
title="Drawer Title"
v-model:visible="drawerVisible"
:direction="drawerDirection"
>
<el-table :data="formModel" style="width: 100%">
<el-table-column prop="name" label="Name"></el-table-column>
<el-table-column prop="content" label="Content">
<template v-slot="scope">
<el-input
v-model="scope.row.content"
@input="handleInput(scope.row)"
></el-input>
</template>
</el-table-column>
</el-table>
<template v-slot:footer>
<div>
<el-button @click="cancelEdit">Cancel</el-button>
<el-button type="primary" @click="saveEdit">Save</el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script>
export default {
data() {
return {
formModel: [
{ name: 'Macbook', content: 9.9 },
{ name: 'iPhone', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
],
drawerVisible: false,
drawerDirection: 'ltr',
}
},
methods: {
openDrawer() {
this.drawerVisible = true
},
cancelEdit() {
this.drawerVisible = false
},
saveEdit() {
//
console.log('Save Edit:', this.formModel)
this.drawerVisible = false
},
handleInput(row) {
//
console.log('Handle Input:', row)
},
},
}
</script>
Loading…
Cancel
Save