lijiaqi 3 months ago
commit 63efc9bce9
  1. 29
      index.html
  2. 17282
      pnpm-lock.yaml
  3. BIN
      public/newslogo.jpg
  4. 25
      src/api/user/messag.js
  5. 25
      src/api/user/news.js
  6. 7
      src/api/user/send.js
  7. 20
      src/api/user/stud.js
  8. 44
      src/api/user/user.js
  9. 45
      src/layout/logo/index.vue
  10. 4
      src/layout/tabbar/setting/index.vue
  11. 3
      src/permission.ts
  12. 2
      src/router/index.ts
  13. 139
      src/router/routers.ts
  14. 2
      src/setting.ts
  15. 31
      src/store/modules/user.ts
  16. 4
      src/styles/variable.scss
  17. 32
      src/views/MyCourseStudy/learningProcess.vue
  18. 125
      src/views/course/components/KnowledgeGraphUi/FoldAdd.vue
  19. 44
      src/views/course/components/KnowledgeGraphUi/foldInfoUi.vue
  20. 28
      src/views/course/components/course-chapters.vue
  21. 84
      src/views/course/components/course-object.vue
  22. 17
      src/views/course/components/resource-management.vue
  23. 219
      src/views/course/courseChapters.vue
  24. 2
      src/views/home/components/ConHeader.vue
  25. 2
      src/views/home/components/Echart/Echarts.vue
  26. 13
      src/views/home/components/Lessonlist.vue
  27. 148
      src/views/home/components/Status.vue
  28. 87
      src/views/home/components/Welcome.vue
  29. 143
      src/views/home/index.vue
  30. 54
      src/views/login/index.vue
  31. 80
      src/views/message/components/indexContentList.vue
  32. 196
      src/views/message/components/messageContent.vue
  33. 267
      src/views/message/components/sendMessage.vue
  34. 80
      src/views/message/components/sendMessageList.vue
  35. 268
      src/views/message/index.vue
  36. 11
      src/views/news/components/newsContent.vue
  37. 236
      src/views/news/index.vue
  38. 2
      src/views/portal/index.vue
  39. 14
      src/views/portal/view.vue
  40. 210
      src/views/student/components/editStu.vue
  41. 555
      src/views/student/index.vue
  42. 40
      vite.config.ts.timestamp-1723526592229-30b7000bab01c.mjs

@ -1,15 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>教学一体化后师生后台</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>教学一体化后师生后台</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

@ -0,0 +1,25 @@
import request from '@/utils/request'
// 获取发件箱信息列表
export const getSendMessagesListApi = (params) => {
return request.get('/user-inbox/page/sendmessages', {
params,
})
}
// 获取收件箱信息列表
export const getMessagesListApi = (params) => {
return request.get('/user-inbox/page/receivemessages', {
params,
})
}
//删除收件箱
export const deleteSendMessageApi = (params) => {
return request.delete('/user-inbox/deletereceivemessage', {
params,
})
}
//发送邮件
export const SendMessageApi = (params) => {
return request.post('/messages/addmessage', {
params,
})
}

@ -0,0 +1,25 @@
import request from '@/utils/request'
// 获取栏目信息列表
export function queryCategory() {
return request.get(`/api/cms-category/query`);
}
//删除栏目
export function deleteCategory(ids) {
return request.delete(`/api/cms-category/delete?ids=${ids}`);
}
//添加栏目
function parseParams(params){
let str = '?'
try{
for (const paramsKey in params) {
str+=`${paramsKey}=${params[paramsKey]}&`
}
return str.slice(0,-1)
}catch (e) {
return ''
}
}
export function addCategory(params) {
const str = parseParams(params)
return request.post(`/api/cms-category/add${str}`);
}

@ -0,0 +1,7 @@
import request from '@/utils/request'
//发送消息
export const sendMessagesApi = (params) => {
return request.post('/messages/addmessage', {
params,
})
}

@ -1,15 +1,29 @@
import request from '@/utils/request'
//分页查询
export const stuPageService = (params) => {
// console.log(params, 'params')
console.log(params, 'paramsshaibei')
return request.get(
`/api/studentManage/page?name=${params.name}&number=${params.number}&page=${params.page}&pageSize=${params.pagesize}&userId=${params.id}`,
)
}
//获取学生
//学号查询
export const stuNumberService = (params) => {
// console.log(params, 'params')
return request.get(
`/api/studentManage/page?number=${params.number}&page=${params.page}&pageSize=${params.pagesize}&userId=${params.id}`,
)
}
//获取一行的学生信息
export const stuListService = (id) => {
return request.get(`/api/studentManage/${id}`)
}
// export function getById(id) {
// return request.get(`/api/studentManage/${id}`);
// }
//修改
export const updateStuService = (params) => {
return request.put(`/api/studentManage`, params)
}
//删除和批量删除学生
export const delStuListService = (ids) => {
return request.delete(`/api/studentManage/batchDelete?ids=${ids}`)
@ -18,7 +32,7 @@ export const delStuListService = (ids) => {
export const reStuPassService = (ids) => {
return request.put(`/api/studentManage/initialPassword?ids=${ids}`)
}
//重置学生密码
//重置状态
export const changeStaService = (status, id) => {
return request.post(`/api/studentManage/status/${status}?id=${id}`)
}

@ -33,36 +33,24 @@ export const userGetInfoService = (token) =>
})
//修改用户信息
export const userChangeService = (params) => {
// return request.post('/api/user/update', jsonData, {
// headers: { 'Content-Type': 'application/json;charset=UTF-8' },
// })
return request.post(`/api/user/update/PersonalInfo`, params)
return request.post(`/api/user/update`, params)
}
//获取个人信息
export const userIdenService = (id) => {
return request.post('/api/user/personal/info?id=' + id)
}
//修改个人信息
export const userIdenChangeService = (dataToSend) => {
return request.post('/api/user/update/PersonalInfo', dataToSend, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
export const userIdenChangeService = (params) => {
// return request.post('/api/user/update/PersonalInfo', params, {
// headers: { 'Content-Type': 'application/json;charset=UTF-8' },
// })
return request.post(`/api/user/update/PersonalInfo`, params)
}
//修改密码
export const userChangePasswordService = (dataToSend) => {
return request.post('/api/user/updatePassword', dataToSend)
}
//获取排名靠前的课程
export const userToplessonService = () => request.get('#')
//获取最新收藏的课程
export const userNewLikeService = (id) => {
return request.post('/api/course_favour/newCollect?userId=' + id)
}
//获取学生列表
export const userStudentListService = (id) => {
return request.post('/api/coursesteacher/studentList?userId=' + id)
}
//获取课程列表
export const userLessonListService = (id) => {
return request.post('/api/courselearingrecord/courseList?userId=' + id)
@ -79,3 +67,23 @@ export const userPicService = () => {
export const userMaxKnowService = (id) => {
return request.post('/api/knowledgelearning/studyMaxKnowledge?userId=' + id)
}
//获取推荐课程
export const userToplessonService = (userId) => {
return request.post(
`/api/courselearingrecord/recommendCourses?userId=${userId}`,
)
}
//获取推荐知识点
export const userTopKnowService = (userId) => {
return request.post(
`/api/knowledgelearning/recommendKnowledge?userId=${userId}`,
)
}
//获取最新收藏的课程
export const userNewLikeService = (id) => {
return request.post('/api/course_favour/newCollect?userId=' + id)
}
//获取学生列表
export const userStudentListService = (id) => {
return request.post('/api/coursesteacher/studentList?userId=' + id)
}

@ -1,7 +1,9 @@
<template>
<div class="logo" v-if="setting.logoHidden" @click="goHome">
<img :src="setting.logo" alt="" />
<p v-show="!fold">{{ setting.title }}</p>
<div class="font">
<p v-show="!fold">{{ setting.title }}</p>
</div>
</div>
</template>
@ -32,24 +34,59 @@ export default {
}
</script>
<style lang="scss" scoped>
// .logo {
// width: 100%;
// height: $base-menu-logo-height;
// color: #000000;
// font-weight: 700;
// display: flex;
// align-items: center;
// cursor: pointer;
// // padding: 20px;
// padding: 0 0 0 15px;
// text-overflow: hidden;
// img {
// width: 40px;
// height: 40px;
// }
// p {
// // width: 80%;
// margin-left: 10px;
// font-size: 13px;
// font-size: $base-menu-logo-title-size;
// white-space: nowrap;
// }
// }
.logo {
width: 100%;
height: $base-menu-logo-height;
// background-color: aqua;
// height: 10%;
color: #000000;
font-weight: 700;
display: flex;
align-items: center;
cursor: pointer;
// padding: 20px;
padding: 0 0 0 15px;
padding: 8px 15px 0 15px;
justify-content: center;
flex-direction: column;
img {
width: 40px;
width: 100%;
height: 40px;
// margin: 0 auto;
}
p {
margin-left: 10px;
// margin-left: 10px;
font-size: $base-menu-logo-title-size;
// font-size: $base-menu-logo-title-size;
white-space: nowrap;
padding: 10px 10px;
}
// .logo-img {
// width: 100%;
// }
}
</style>

@ -87,11 +87,11 @@ const openWeb = (url, name) => {
//
onMounted(() => {
userStore.getUserInfo()
console.log(userStore.data.icon, 'icon')
// console.log(userStore.data.icon, 'icon')
})
const logout = () => {
console.log($route)
// console.log($route)
userStore.logout()
$router.push({
path: '/login',

@ -6,7 +6,7 @@ import permissionStore from '@/store/modules/permission'
//@ts-expect-error 修复ts校验
import nprogress from 'nprogress'
import 'nprogress/nprogress.css'
const userStore = useUserStore(pinia)
const userStore :any = useUserStore(pinia)
const usePermissionStore = permissionStore(pinia)
// const whitelist = ['/login', '/404']
router.beforeEach(async (to, form, next) => {
@ -45,6 +45,7 @@ router.beforeEach(async (to, form, next) => {
hidden: true,
},
})
next({ ...to, replace: true }) // 这里相当于push到一个页面 不在进入路由拦截
} catch (error) {
// 如果获取用户信息失败了则执行登出操作让重新登录

@ -14,7 +14,7 @@ const routerList = [
]
const router = createRouter({
history: createWebHashHistory(),
routes: [...routerList, ...constantRoute],
routes: [...routerList],
scrollBehavior() {
return {
left: 0,

@ -1,3 +1,6 @@
import component from 'element-plus/es/components/tree-select/src/tree-select-option.mjs';
import { pa } from 'element-plus/es/locales.mjs';
export const constantRoute: any = [
{
path: '/',
@ -21,7 +24,7 @@ export const constantRoute: any = [
// },
// },
// ],
redirect: '/curriculumCenter/basicCourseInformation',
redirect: '/home',
children: [
{
path: '/home',
@ -45,6 +48,28 @@ export const constantRoute: any = [
icon: 'Notebook',
},
},
// {
// path: '/mssageManagement',
// component: () => import('@/layout/index.vue'),
// name: 'MssageManagement',
// meta: {
// title: '',
// hidden: false,
// icon: 'HomeFilled', // 菜单图标
// },
// children: [
// {
// path: '/mssageManagement/message',
// component: () => import('@/views/message/index.vue'),
// name: 'Message',
// meta: {
// title: '消息',
// hidden: false,
// icon: 'ChatDotSquare',
// },
// },
// ],
// },
{
path: '/curriculumCenter',
redirect: '/curriculumCenter/basicCourseInformation',
@ -140,51 +165,101 @@ export const constantRoute: any = [
},
],
},
// {
// path: '/groupManagement',
// component: () => import('@/layout/index.vue'),
// name: 'GroupManagement',
// meta: {
// title: '',
// hidden: false,
// icon: 'HomeFilled', // 菜单图标
// },
// children: [
// {
// path: '/groupManagement/group',
// component: () => import('@/views/group/index.vue'),
// name: 'Group',
// meta: {
// title: '分组',
// hidden: false,
// icon: 'Operation',
// },
// },
// ],
// },
{
path: '/mssageManagement',
path: '/messageManagement',
component: () => import('@/layout/index.vue'),
name: 'MssageManagement',
name: 'MessageManagement',
meta: {
title: '',
title: '消息',
hidden: false,
icon: 'HomeFilled', // 菜单图标
icon: 'ChatLineRound', // 菜单图标
},
children: [
{
path: '/mssageManagement/message',
path: '/messageManagement/message',
component: () => import('@/views/message/index.vue'),
name: 'Message',
meta: {
title: '消息',
title: '消息详情',
hidden: false,
icon: 'ChatDotSquare',
},
}
},
{
path: '/messageManagement/sendMessage',
component: ()=>import('@/views/message/components/sendMessage.vue'),
name:'SendMessage',
meta: {
title: '个人发出',
hidden: true,
icon: '',
}
},
{
path: '/messageContentList',
component:()=>import('@/views/message/components/indexContentList.vue'),
name:'MessageContentList',
meta: {
title: '收到信息详情',
hidden: true,
icon: ''
}
},
{
path: '/sendMessageList',
component:()=>import('@/views/message/components/sendMessageList.vue'),
name:'SendMessageList',
meta: {
title: '发送信息详情',
hidden: true,
icon: ''
}
},
],
},
{
path: '/messageContent',
component: () => import('@/views/message/components/messageContent.vue'),
name: 'MessageContent',
meta:{
title: '写栈内信函',
hidden: true,
icon: 'EditPen',
}
},
{
path: '/news',
component: () => import('@/layout/index.vue'),
name:'NewsManagement',
meta: {
title: '资讯',
hidden: false,
icon: 'BellFilled'
},
children: [
{
path: '/news/newsContent',
component:()=>import('@/views/news/index.vue'),
name:'NewsContent',
meta: {
title: '资讯管理',
hidden: false,
icon: 'BellFilled'
}
},
{
path: '/newsDetails',
component:()=>import('@/views/news/components/newsContent.vue'),
name:'NewsDetails',
meta: {
title: '新闻详情',
hidden: true,
icon: ''
},
},
]
},
{
path: '/myCourseStudyManagement',
component: () => import('@/layout/index.vue'),
@ -272,7 +347,7 @@ export const constantRoute: any = [
},
{
path: '/portal',
component: () => import('@/layout/index.vue'),
component: () => import('@/views/portal/view.vue'),
name: 'Portal',
meta: {
title: '门户',

@ -1,6 +1,6 @@
// 用于项目logo 标题配置
export default {
title: '教学一体化后台', //项目标题
title: '课图——首个开源课程知识图谱管理系统', //项目标题
logo: '/public/vite.svg', //项目logo
logoHidden: true, // logo隐藏设置
'files.associations': {

@ -11,6 +11,7 @@ import { GET_TKOEN, SET_TKOEN, REMOVE_TOKEN } from '@/utils/token'
// 引入常量路由
import { constantRoute } from '@/router/routers'
import permissionStore from './permission'
import {useRouter} from 'vue-router'
// 引入路由
import {
userLoginService,
@ -87,15 +88,12 @@ const useUserStore = defineStore('User', {
async getUserInfo() {
const result: any = await userGetInfoService(GET_TKOEN('TOKEN'))
if (result.code === 200) {
console.log(result, 'result')
// console.log(result, 'result')
// 获取用户个人信息
const res = await userIdenService(result.data.id)
// console.log(res, 'res')
this.userInfo = res.data
// console.log(this.userInfo, 'this.userInfo')
this.data = result.data
// console.log(result, '123')
this.userName = result.data.username
// this.avatar = result.data.checkUser.avatar
this.routes = result.data.permissions
return {
result,
@ -114,13 +112,22 @@ const useUserStore = defineStore('User', {
// @ts-expect-error
(this.userName = ''),
// @ts-expect-error
(this.avatar = '')
this.token = ''
this.routes = ''
usePermissionStore.removeRouter()
location.reload()
this.token = ''
// @ts-expect-error
this.routes = ''
usePermissionStore.removeRouter()
location.reload()
const router = useRouter()
console.log(router.getRoutes());
},
},
getters: {},
},
{
persist: true, // 持久化
},
getters: {},
})
)
export default useUserStore

@ -9,6 +9,6 @@ $base-menu-background:#ffffff;
// 顶部导航高度
$base-tabbar-height:50px;
// 左侧菜单logo高度
$base-menu-logo-height:50px;
$base-menu-logo-height:70px;
// 左侧菜单标题文字大小
$base-menu-logo-title-size:20px;
$base-menu-logo-title-size:13px;

@ -15,7 +15,7 @@ const selectedIds = ref([])
const loading = ref(false)
const showCheckbox = ref(false)
onMounted(()=>{
onMounted(() => {
userStore.getUserInfo()
})
const params = ref({
@ -23,13 +23,13 @@ const params = ref({
// pagenum: '1',
userId: userStore.data.id,
})
console.log(userStore.data.id);
console.log(userStore.data.id)
//
const getrecordList = async () => {
loading.value = true
// const res = await getRecordListService(params.value)
const res = await LearningRecordsControllerService.courseLearningRecords(
params.value.userId,
params.value.userId,
params.value.pagenum,
params.value.pagesize,
)
@ -108,9 +108,7 @@ const deleteSelected = async () => {
})
//
await LearningRecordsControllerService.deleteCourseRecords(
selectedIds.value,
)
await LearningRecordsControllerService.deleteCourseRecords(selectedIds.value)
.then(() => {
recordList.value = recordList.value.filter(
(item) => !selectedIds.value.includes(item.id),
@ -125,12 +123,12 @@ const deleteSelected = async () => {
}
//
const router = useRouter()
const goToAnotherPage = (address,courseId) => {
const goToAnotherPage = (address, courseId) => {
router.push({
path:address,
query:{
courseId: courseId
}
path: address,
query: {
courseId: courseId,
},
})
}
//
@ -162,7 +160,17 @@ const goToAnotherPage = (address,courseId) => {
<!-- 中间 -->
<div class="record-list">
<ul v-if="recordList.length > 0">
<li v-for="record in recordList" :key="record.id" v-loading="loading" @click="goToAnotherPage('/myCourseStudyManagement/learningProcess1' , record.courseId)">
<li
v-for="record in recordList"
:key="record.id"
v-loading="loading"
@click="
goToAnotherPage(
'/myCourseStudyManagement/learningProcess1',
record.courseId,
)
"
>
<input
style="display: flex"
type="checkbox"

@ -1,5 +1,4 @@
<template>
<<<<<<< HEAD
<div class="add-or-edit">
<el-drawer v-model="isDrawer" title="新增章节" direction="rtl" size="50%">
<el-form
@ -48,31 +47,6 @@
</el-form>
</el-drawer>
</div>
=======
<div class="add-or-edit">
<el-drawer v-model="isDrawer" title="新增章节" direction="rtl" size="50%">
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"
class="demo-ruleForm" :size="formSize" status-icon>
<el-form-item label="章节" prop="pid">
<el-cascader v-model="activePidArr" :options="options1" :props="CascaderProps"
:show-all-levels="false" clearable />
</el-form-item>
<el-form-item label="章节名" prop="name">
<el-input v-model="ruleForm.name" style="width: 240px" :placeholder="placeholder" />
</el-form-item>
<el-form-item label="总学时" prop="totalclasshours">
<el-input v-model="ruleForm.totalclasshours" style="width: 240px" placeholder="请输入总学时" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="submitForm(ruleFormRef)">
提交
</el-button>
<el-button @click="resetForm(ruleFormRef)">重置</el-button>
</el-form-item>
</el-form>
</el-drawer>
</div>
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
</template>
<script lang="ts" setup>
function comNum(n: number | undefined) {
@ -87,7 +61,6 @@ const updLoading = (boo: boolean) => (loading.value = boo)
const isDrawer = ref(props.isD)
watch(
<<<<<<< HEAD
() => props.isD,
(newVal) => (isDrawer.value = newVal),
)
@ -108,30 +81,6 @@ const options1 = computed(() => {
obj.chapterSection.push({
name: `${i + 1}章第${comNum(item.chapterSection?.length)}`,
pid: item.id,
=======
() => props.isD,
(newVal) => (isDrawer.value = newVal),
)
watch(
() => isDrawer.value,
(newVal) => emits('update:isD', newVal),
)
const options1 = computed(() => {
const arr = props.data.map((item: any, i: number) => {
const obj = { ...item }
if (item.chapterSection)
obj.chapterSection = item.chapterSection.map((chap: any) => ({
...chap,
disabled: true,
}))
else obj.chapterSection = []
obj.chapterSection.push({
name: `${i + 1}章第${comNum(item.chapterSection?.length)}`,
pid: item.id,
})
return obj
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
})
return obj
})
@ -139,15 +88,9 @@ const options1 = computed(() => {
return arr
})
const CascaderProps = {
<<<<<<< HEAD
label: 'name',
value: 'pid',
children: 'chapterSection',
=======
label: 'name',
value: 'pid',
children: 'chapterSection',
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
import type { ComponentSize, FormInstance, FormRules } from 'element-plus'
@ -177,7 +120,6 @@ interface RuleForm {
const formSize = ref<ComponentSize>('default')
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive<RuleForm>({
<<<<<<< HEAD
content: '',
courseid: '2fa0fd63262230639d2c45a3acd9045c',
courseobjectivesid: '',
@ -222,52 +164,6 @@ const submitForm = async (formEl: FormInstance | undefined) => {
console.log('error submit!', fields)
}
})
=======
content: '',
courseid: '2fa0fd63262230639d2c45a3acd9045c',
courseobjectivesid: '',
createBy: '',
createTime: '',
id: '',
name: '',
num: 0,
numshow: '',
onlinclasshours: '',
pid: undefined,
requirement: '',
sysOrgCode: '',
totalclasshours: '',
updateBy: '',
updateTime: '',
zc: '',
ziyuan: '',
zywj: '',
})
// @ts-ignore
const validateChapterOrSection = (rule, value, callback) => {
nextTick(() => {
const value = ruleForm.pid
if (!value && value !== '') callback(new Error('请选择新增的章或节'))
else callback()
})
}
//
const rules = reactive<FormRules<RuleForm>>({
pid: [
{ required: true, validator: validateChapterOrSection, trigger: 'change' },
],
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
emits('submit', ruleForm, updLoading)
} else {
console.log('error submit!', fields)
}
})
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
const resetForm = (formEl: FormInstance | undefined) => {
@ -277,7 +173,6 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
const placeholder = ref('请输入章节名')
watch(
<<<<<<< HEAD
() => ruleForm.pid,
(newVal) => {
if (newVal === undefined) placeholder.value = '请输入章节名'
@ -296,26 +191,6 @@ watch(
}
ruleForm.pid = newVal.slice(-1)[0]
},
=======
() => ruleForm.pid,
(newVal) => {
if (newVal === undefined) placeholder.value = '请输入章节名'
else if (newVal === '') placeholder.value = '请输入章名'
else placeholder.value = '请输入节名'
},
)
const activePidArr = ref<string[] | undefined>([])
watch(
() => activePidArr.value,
(newVal) => {
// pid
if (newVal === undefined) {
ruleForm.pid = undefined
return
}
ruleForm.pid = newVal.slice(-1)[0]
},
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
)
</script>
<style lang="scss"></style>

@ -2,15 +2,11 @@
<div class="fold-info-ui">
<div class="demo-collapse">
<el-collapse class="collapse">
<<<<<<< HEAD
<button
class="my-button"
style="position: absolute; left: 0; top: 0"
@click.stop="emits('add')"
>
=======
<button class="my-button" style="position: absolute; left: 0; top: 0" @click.stop="emits('add')">
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
新增
</button>
<el-collapse-item v-for="item in foldInfoData" :key="item.id">
@ -30,7 +26,6 @@
</div>
<div class="my-tag">本章资源</div>
</div>
<<<<<<< HEAD
<button
class="my-button"
@click.stop="emits('edit', item)"
@ -44,12 +39,6 @@
class="is-el-button"
style="margin-right: 10px"
>
=======
<button class="my-button" @click.stop="emits('edit', item)" style="margin-right: 10px">
编辑
</button>
<my-btn :id="item.id" type="danger" class="is-el-button" style="margin-right: 10px">
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
删除
</my-btn>
</div>
@ -63,15 +52,11 @@
<div class="left"></div>
<div class="right">
<div class="structure-item">
<<<<<<< HEAD
<div
class="structure-item-titile-box"
v-for="(obj, ind) in item.chapterSection"
:key="item.id"
>
=======
<div class="structure-item-titile-box" v-for="(obj, ind) in item.chapterSection" :key="item.id">
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
<div class="titile-box-titile">
<div class="sequence">{{ ind + 1 }}</div>
<div class="tit-box">
@ -85,7 +70,6 @@
</div>
</div>
<div class="tit-box-edit">
<<<<<<< HEAD
<button
class="my-button"
@click.stop="emits('edit', obj)"
@ -99,12 +83,6 @@
class="is-el-button"
style="margin-right: 10px"
>
=======
<button class="my-button" @click.stop="emits('edit', obj)" style="margin-right: 10px">
编辑
</button>
<my-btn :id="obj.id" type="danger" class="is-el-button" style="margin-right: 10px">
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
删除
</my-btn>
</div>
@ -308,11 +286,7 @@ const MyBtn = {
gap: 38px;
flex-wrap: wrap;
<<<<<<< HEAD
& > * {
=======
&>* {
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
border: 1px solid #0052d9;
font-weight: bold;
font-size: 12px;
@ -363,7 +337,6 @@ const MyBtn = {
cursor: pointer;
&:hover {
<<<<<<< HEAD
background: linear-gradient(
128deg,
lighten($color-start, 5%) 0%,
@ -385,23 +358,6 @@ const MyBtn = {
lighten($color-start, 20%) 0%,
lighten($color-end, 20%) 100%
);
=======
background: linear-gradient(128deg,
lighten($color-start, 5%) 0%,
lighten($color-end, 5%) 100%);
}
&:active {
background: linear-gradient(128deg,
darken($color-start, 10%) 0%,
darken($color-end, 10%) 100%);
}
&:disabled {
background: linear-gradient(128deg,
lighten($color-start, 20%) 0%,
lighten($color-end, 20%) 100%);
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
}

@ -106,7 +106,7 @@ const remove = async (node: Node, data: Tree) => {
})
}
//
const getDetails = (id: any) => { }
const getDetails = (id: any) => {}
const dataSource = ref<Tree[]>([
{
id,
@ -134,18 +134,36 @@ const dataSource = ref<Tree[]>([
<h1>课程章节</h1>
<!-- <button @click="getChaptersList">获取</button> -->
<div class="custom-tree-container">
<el-tree style="max-width: 600px" :data="dataSource" node-key="id" default-expand-all
:expand-on-click-node="false" empty-text="暂无章节">
<el-tree
style="max-width: 600px"
:data="dataSource"
node-key="id"
default-expand-all
:expand-on-click-node="false"
empty-text="暂无章节"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<span @click="getDetails(data.id)">{{ data.name }}</span>
<span>
<el-button @click="append(data)" circle size="small" type="primary" plain>
<el-button
@click="append(data)"
circle
size="small"
type="primary"
plain
>
<el-icon :size="15">
<Plus />
</el-icon>
</el-button>
<el-button @click="remove(node, data)" circle size="small" type="danger" plain>
<el-button
@click="remove(node, data)"
circle
size="small"
type="danger"
plain
>
<el-icon :size="15">
<Delete />
</el-icon>

@ -181,7 +181,13 @@ const submit = async () => {
<template #header>
<div class="card-header">
<span>整体目标</span>
<el-button type="primary" :icon="Edit" circle plain @click="updateContent"></el-button>
<el-button
type="primary"
:icon="Edit"
circle
plain
@click="updateContent"
></el-button>
</div>
</template>
<div class="el-card__body" style="">
@ -211,35 +217,61 @@ const submit = async () => {
</div>
<div class="objectdetails">
<el-scrollbar height="89px">
<li v-for="(obj, i) in item.contents" :key="obj.id">
<span>目标{{ i + 1 }}{{ obj.content }}</span>
<div class="icon">
<el-button type="primary" :icon="Edit" circle size="small" plain @click="updateObjectContent(obj.id)" />
<el-button type="danger" :icon="Delete" circle size="small" plain @click="delObject(obj.id)" />
<li v-for="(obj, i) in item.contents" :key="obj.id">
<span>目标{{ i + 1 }}{{ obj.content }}</span>
<div class="icon">
<el-button
type="primary"
:icon="Edit"
circle
size="small"
plain
@click="updateObjectContent(obj.id)"
/>
<el-button
type="danger"
:icon="Delete"
circle
size="small"
plain
@click="delObject(obj.id)"
/>
</div>
</li>
</el-scrollbar>
</div>
</div>
<el-divider style="margin: 0; width: 100%" />
</li>
</el-scrollbar>
</ul>
<el-dialog
v-if="dialogVisible"
v-model="dialogVisible"
:title="flog ? '编辑' : '新增'"
width="500"
:before-close="handleClose"
>
<el-form
:model="formData"
label-width="auto"
style="max-width: 600px"
>
<el-form-item label="内容" prop="description">
<el-input
v-model="formData.description"
placeholder="请输入内容"
></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submit">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</div>
<el-divider style="margin: 0; width: 100%" />
</li>
</ul>
<el-dialog v-if="dialogVisible" v-model="dialogVisible" :title="flog ? '编辑' : '新增'" width="500"
:before-close="handleClose">
<el-form :model="formData" label-width="auto" style="max-width: 600px">
<el-form-item label="内容" prop="description">
<el-input v-model="formData.description" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submit">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>

@ -48,7 +48,10 @@ const tableData: User[] = [
<template>
<div class="grid-content resource-management" style="height: 500px">
<h1>资源管理</h1>
<el-table :data="filterTableData" style="width: 90%; margin-left: 30px; font-size: 16px">
<el-table
:data="filterTableData"
style="width: 90%; margin-left: 30px; font-size: 16px"
>
<el-table-column label="相关内容" prop="chapter" />
<el-table-column label="文件名称" prop="name" />
<el-table-column label="文件类型" prop="type" />
@ -57,13 +60,21 @@ const tableData: User[] = [
<el-table-column label="文件类型" prop="name" /> -->
<el-table-column align="right">
<template #header>
<el-input v-model="search" size="small" placeholder="输入内容进行搜索" />
<el-input
v-model="search"
size="small"
placeholder="输入内容进行搜索"
/>
</template>
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>

@ -1,45 +1,10 @@
<template>
<<<<<<< HEAD
<div class="course-chapters">
<div class="flex-layout">
<div class="flex-left">
<div class="left" ref="atlasCon">
<!-- 知识图谱 -->
<atlas-ui @hadRouter="handle" />
=======
<div class="course-chapters">
<div class="flex-layout">
<div class="flex-left">
<div class="left" ref="atlasCon">
<!-- 知识图谱 -->
<atlas-ui @hadRouter="handle" />
</div>
<div class="right-top radius-10 vertical-line">
<!-- 计算机导论 -->
<theory-ui :learnInfo="learnInfo" />
</div>
<div class="right-bottom radius-10 vertical-line">
<!-- 课程目标 -->
<objective-ui :orogramObjective="orogramObjective" />
</div>
<div class="bottom">
<!-- 可折叠基本概念 -->
<fold-info-ui :foldInfoData="foldInfoData" @add="showHide = true" @edit="hanEdit" @del="flodDel" />
<FoldAdd v-model:is-d="showHide" v-if="showHide" :data="foldInfoData" @submit="foldAddSubmit" />
<FoldEdit v-model:is-d="showHide1" v-if="showHide1" :editData="activeEditData" :data="foldInfoData"
@submit="foldEditSubmit" />
</div>
</div>
<div class="flex_right">
<div class="top-container">
<img src="" alt="" />
</div>
<div class="bottom-container radius-10">
<!-- 查看资源 -->
<look-resource-ui :lookResource="lookResource" />
</div>
</div>
>>>>>>> d7b4e7a9c5db447232028c52d0cc49bb8a28830e
</div>
<div class="right-top radius-10 vertical-line">
<!-- 计算机导论 -->
@ -83,15 +48,11 @@
</div>
</div>
</div>
<<<<<<< HEAD
</div>
=======
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
</template>
<script lang="ts" setup>
import {
<<<<<<< HEAD
getLearnInfoApi,
getOrogramObjectiveApi,
} from '@/api/courseChaptersApi'
@ -109,25 +70,6 @@ import {
foldInfoUi,
FoldAdd,
FoldEdit,
=======
getLearnInfoApi,
getOrogramObjectiveApi,
} from '@/api/courseChaptersApi'
import {
getChapterApi,
deleteSectionApi,
addChapterApi,
updateChapterApi,
} from '@/api/sectionApi'
import {
theoryUi,
objectiveUi,
atlasUi,
lookResourceUi,
foldInfoUi,
FoldAdd,
FoldEdit,
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
} from './components/KnowledgeGraphUi/index'
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
@ -142,7 +84,6 @@ const handle = () => $router.push('/knowledgeAtlas')
const learnInfo: object[] = reactive([])
getLearnInfoApi().then((res) => learnInfo.push(...(res as object[])))
// getByIdCourseVoApi('2fa0fd63262230639d2c45a3acd9045c')
// objective-ui
const orogramObjective: any = reactive({})
getOrogramObjectiveApi().then((res) => Object.assign(orogramObjective, res))
@ -153,23 +94,15 @@ const lookResource: any = reactive({})
// fold-info-ui
const foldInfoData: any = reactive([])
function getFold() {
<<<<<<< HEAD
getChapterApi('2fa0fd63262230639d2c45a3acd9045c').then((res) => {
foldInfoData.length = 0
foldInfoData.push(...res.data)
})
=======
getChapterApi('2fa0fd63262230639d2c45a3acd9045c').then((res) => {
foldInfoData.length = 0
foldInfoData.push(...res.data)
})
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
getFold()
//
const showHide = ref(false)
function foldAddSubmit(data: any, updLoading: Function) {
<<<<<<< HEAD
updLoading(true)
addChapterApi(data)
.then(() => {
@ -189,7 +122,6 @@ function foldAddSubmit(data: any, updLoading: Function) {
.finally(() => updLoading(false))
}
//
<<<<<<< HEAD
const foldDelLoading = ref(false)
function flodDel(id: string) {
foldDelLoading.value = true
@ -208,57 +140,6 @@ function flodDel(id: string) {
})
})
.finally(() => (foldDelLoading.value = false))
=======
function flodDel(id: string,setLoading:Function) {
setLoading(true)
deleteSectionApi(id).then(() => {
ElMessage({
message: `删除成功`,
type: 'success'
=======
updLoading(true)
addChapterApi(data)
.then(() => {
ElMessage({
message: `添加${data.pid === '' ? '章' : '节'}成功`,
type: 'success',
})
showHide.value = false
getFold()
})
.catch(() => {
ElMessage({
message: `添加${data.pid === '' ? '章' : '节'}失败`,
type: 'error',
})
})
.finally(() => updLoading(false))
}
//
const foldDelLoading = ref(false)
function flodDel(id: string) {
foldDelLoading.value = true
deleteSectionApi(id)
.then(() => {
ElMessage({
message: `删除成功`,
type: 'success',
})
getFold()
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
})
.catch(() => {
ElMessage({
message: `删除失败`,
type: 'error',
})
})
<<<<<<< HEAD
}).finally(() =>setLoading(false))
>>>>>>> d7b4e7a9c5db447232028c52d0cc49bb8a28830e
=======
.finally(() => (foldDelLoading.value = false))
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
//
const showHide1 = ref(false)
@ -268,7 +149,6 @@ function hanEdit(data: any) {
showHide1.value = true
}
function foldEditSubmit(data: any, updLoading: Function) {
<<<<<<< HEAD
updLoading(true)
updateChapterApi(data)
.then(() => {
@ -286,25 +166,6 @@ function foldEditSubmit(data: any, updLoading: Function) {
})
})
.finally(() => updLoading(false))
=======
updLoading(true)
updateChapterApi(data)
.then(() => {
ElMessage({
message: `更新${data.pid === '' ? '章' : '节'}成功`,
type: 'success',
})
showHide1.value = false
getFold()
})
.catch(() => {
ElMessage({
message: `更新${data.pid === '' ? '章' : '节'}失败`,
type: 'error',
})
})
.finally(() => updLoading(false))
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
</script>
@ -327,10 +188,6 @@ function foldEditSubmit(data: any, updLoading: Function) {
.vertical-line {
position: relative;
<<<<<<< HEAD
=======
background: #f2f7fb;
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
&::before {
content: '';
@ -378,7 +235,6 @@ function foldEditSubmit(data: any, updLoading: Function) {
}
}
<<<<<<< HEAD
.flex_right {
width: 355px;
@ -389,23 +245,6 @@ function foldEditSubmit(data: any, updLoading: Function) {
img {
width: 100%;
height: 100%;
=======
.radius-10 {
border-radius: 10px;
overflow: hidden;
}
.vertical-line {
position: relative;
&::before {
content: '';
display: block;
position: absolute;
height: 100%;
width: 12px;
background-color: #2147fb;
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
}
@ -413,64 +252,6 @@ function foldEditSubmit(data: any, updLoading: Function) {
min-height: 945px;
}
}
<<<<<<< HEAD
}
=======
.flex-layout {
display: flex;
justify-content: space-between;
align-items: flex-start;
.flex-left {
display: grid;
grid-template-columns: 664px 555px;
justify-content: space-between;
width: 1243px;
.left {
grid-row: 1/3;
height: 424px;
}
.right-top {
width: 100%;
height: 243px;
}
.right-bottom {
align-self: end;
width: 100%;
height: 168px;
}
.bottom {
margin-top: 15px;
grid-column: 1/3;
width: 100%;
min-width: 100%;
min-height: 521px;
}
}
.flex_right {
width: 355px;
.top-container {
height: 231px;
margin-bottom: 23px;
img {
width: 100%;
height: 100%;
}
}
.bottom-container {
min-height: 945px;
}
}
}
>>>>>>> 0b7db324dad8347654933e91ae7e6ca8d2dabd05
}
</style>

@ -20,7 +20,7 @@ const props = defineProps({
modelValue: Boolean,
urouter: String,
})
console.log(props.title, 'router111')
// console.log(props.title, 'router111')
import { useRouter } from 'vue-router'
const router = useRouter()
</script>

@ -12,7 +12,7 @@ const props = defineProps({
})
onMounted(() => {
console.log(props.count.dateList, 'count')
// console.log(props.count.dateList, 'count')
const myChart = echarts.init(echarsDom.value)
//
myChart.setOption({

@ -16,7 +16,7 @@ const getLesList = async () => {
const res = await userLessonListService(userStore.data.id)
lessonList.value = res.data
loading.value = false
// console.log(res.data, 'less')
console.log(res.data, 'less')
}
const onGetCourseObject = async (id) => {
router.push({
@ -30,7 +30,7 @@ const drawer = ref()
const flog = ref(false)
const onEditCourse = (item) => {
flog.value = true
console.log(item, 'item')
// console.log(item, 'item')
drawer.value.open(item)
}
const onSuccess = () => {
@ -67,13 +67,7 @@ onMounted(() => {
</div>
</div>
</div>
<course-edit
ref="drawer"
@success="onSuccess"
@couresNameChange="couresNameChangeEvent"
@CloseCouresNameChange="CloseCouresNameChangeEvent"
:flog="flog"
></course-edit>
<course-edit ref="drawer" @success="onSuccess" :flog="flog"></course-edit>
</div>
<courseedit @click="open"></courseedit>
</template>
@ -108,6 +102,7 @@ onMounted(() => {
border: 1px solid #e8e8f2;
margin: 10px 0px;
}
.lessonlist-item-info {
box-sizing: border-box;
padding: 6px 5px;

@ -1,12 +1,11 @@
<script setup>
import add from '@/assets/images/add.png'
// import tool from '@/utils/oss.js'
import { userChangeService } from '@/api/user/user.js'
import { userIdenChangeService } from '@/api/user/user.js'
import { ref, defineEmits, onMounted, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { Edit } from '@element-plus/icons-vue'
import { tool, client } from '@/utils/alioss.js'
// import { error } from 'echarts/types/src/util/log.js'
import useUserStore from '@/store/modules/user'
const props = defineProps({
data: {
type: Object,
@ -15,11 +14,14 @@ const props = defineProps({
type: Object,
},
})
const userStore = useUserStore()
const imageUrl = ref()
//
imageUrl.value = props.data.icon
// console.log(props.userData, 'img')
// console.log(props.userData, 'img1')
// console.log(props.data, 'imgs')
const beforeUpload = (file) => {
const validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp']
if (!validImageTypes.includes(file.type)) {
@ -34,7 +36,6 @@ const upload = async (option) => {
const res = await tool.oss.upload(option.file)
imageUrl.value = res.url
changeInfo()
// console.log(imageUrl.value, 'img')
}
//
const emit = defineEmits(['get-avater', 'getInfo'])
@ -48,10 +49,8 @@ const changeInfo = async () => {
username: props.data.username,
}
const jsonData = JSON.stringify(userInfo)
await userChangeService(jsonData).then(() => {
await userIdenChangeService(jsonData).then(() => {
ElMessage.success('修改成功')
// console.log(imageUrl.value, 'fs')
emit('get-avater', imageUrl.value)
})
isUploading.value = false
@ -59,10 +58,20 @@ const changeInfo = async () => {
}
//
const dialog = ref(false)
const formLabelWidth = '80px'
// const defineForm = ref({
// name: props.userData.name,
// sex: props.userData.sex,
// nationality: props.userData.nationality,
// professionalTitle: props.userData,
// politicalStatus: props.userData,
// profession: props.userData,
// education: props.userData,
// academicDegree: props.userData,
// id: props.data.id,
// })
const formLabelWidth = '68px'
const form = ref({
name: '',
sex: '',
@ -73,10 +82,64 @@ const form = ref({
profession: '',
education: '',
academicDegree: '',
id: props.data.id,
})
form.value = { ...props.userData }
// console.log(props.userData, '')
// console.log(form.value, '')
//
const rules = reactive({
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 5, message: '长度在2-5之间', trigger: 'blur' },
],
sex: [
{
required: true,
message: '请选择性别',
trigger: 'change',
},
],
nationality: [
{
message: '请输入民族',
trigger: 'blur',
},
],
professionalTitle: [
{ required: true, message: '职位不能为空', trigger: 'blur' },
{
min: 2,
max: 10,
message: '职位名称至少2个字符最多10个字符',
trigger: 'blur',
},
],
politicalStatus: [
{
required: true,
trigger: 'change',
},
],
profession: [
{
required: true,
trigger: 'change',
},
],
education: [
{
required: true,
trigger: 'change',
},
],
academicDegree: [
{
required: true,
trigger: 'change',
},
],
})
form.value.sex === '男' ? 0 : 1
const value = ref('')
value.value = form.value.politicalStatus
@ -87,15 +150,16 @@ const onCancel = () => {
dialog.value = false
}
//
const infoform = ref()
const res = ref()
const onSubmit = async () => {
// const jsonData = JSON.stringify(form.value)
console.log(form.value, 'jj')
await userChangeService(form).then(() => {
await infoform.value.validate()
// console.log(form.value, 'form')
await await userIdenChangeService(form.value).then(() => {
ElMessage.success('修改成功')
console.log(form.value, 'aaaa')
emit('getInfo', form.value)
// console.log(form.value, 'aaaa')
// res.value = userStore.getUserInfo()
// emit('getInfo', res.value)
dialog.value = false
})
}
@ -115,26 +179,28 @@ const onSubmit = async () => {
<el-upload
class="click"
:show-file-list="false"
auto-upload="false"
:auto-upload="false"
:before-upload="beforeUpload"
:http-request="upload"
>
<img :src="add" />
</el-upload>
<div class="info_content" style="height: 58px">
<div class="name">{{ props.data.username }}</div>
<div class="na">
{{ props.userData.professionalTitle }}
<div class="name">
{{ props.data.username }}
<el-icon class="icon" @click="openInfo">
<Edit />
</el-icon>
</div>
<!-- <div class="na">
{{ props.userData.professionalTitle }}
</div> -->
</div>
</div>
</div>
<el-drawer v-model="dialog" title="个人信息" class="demo-drawer">
<div class="content">
<el-form :model="form">
<el-form :model="form" class="infocont" :rules="rules" ref="infoform">
<el-form-item label="姓名" :label-width="formLabelWidth" prop="name">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
@ -144,16 +210,6 @@ const onSubmit = async () => {
<el-radio value="女"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="手机号"
:label-width="formLabelWidth"
prop="emergencyContact"
>
<el-input
v-model="form.emergencyContact"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item
label="民族"
:label-width="formLabelWidth"
@ -216,9 +272,7 @@ const onSubmit = async () => {
</el-form>
<div class="footer">
<el-button @click="onCancel">取消</el-button>
<el-button type="primary" :loading="loading" @click="onSubmit">
确认
</el-button>
<el-button type="primary" @click="onSubmit">确认</el-button>
</div>
</div>
</el-drawer>
@ -253,6 +307,9 @@ const onSubmit = async () => {
font-style: normal;
text-transform: none;
}
.el-form-item {
margin-bottom: 30px;
}
.na {
/* height: 28px; */
font-family: Inter, Inter;
@ -276,9 +333,9 @@ const onSubmit = async () => {
}
.footer {
display: flex;
justify-content: space-between;
justify-content: end;
align-items: center;
margin-top: 20px;
margin-top: 200px;
}
.iden {
width: 126px;
@ -299,6 +356,19 @@ const onSubmit = async () => {
border-radius: 50px 50px 50px 50px;
display: flex;
}
.content {
display: flex;
flex-direction: column;
justify-content: space-between;
}
::v-deep .el-drawer__body {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.infocont {
}
span {
width: 24px;
height: 44px;

@ -10,6 +10,7 @@ import useUserStore from '@/store/modules/user'
import { useRouter } from 'vue-router'
import { ElMessage, dayjs } from 'element-plus'
import { Edit, Back } from '@element-plus/icons-vue'
const props = defineProps({
name: {
type: String,
@ -28,7 +29,7 @@ const pwdForm = ref({
new_pwd: '',
re_pwd: '',
})
// console.log(props.userData.username, 'props.data')
// console.log(props.userData, '22222')
//dialog
//
const dialogFormVisible = ref(false)
@ -133,7 +134,8 @@ const rules = {
const infoRef = ref()
//
const emit = defineEmits(['get-message'])
const emit = defineEmits(['get-message', 'userInfoModified'])
const change = ref(true)
//
const countdown = ref(0)
@ -148,7 +150,6 @@ const toggleVisibility = () => {
const codecode = ref()
const codeSubmit = async () => {
await infoRef.value.validate()
// console.log('')
const phoness = form.value.newphone
userCodeRegisterService(phoness)
.then((response) => {
@ -156,69 +157,76 @@ const codeSubmit = async () => {
console.log(codecode.value, 'codecode')
})
.catch((error) => {
console.log(error.message, 'error')
ElMessage.error(error.newphone.message)
// console.log(error.newphone.message, 'error')
})
}
// console.log(change.value == true, 'toggleVisibility.value == false')
// if (toggleVisibility.value == 'false') {
// console.log(' false')
// } else {
// console.log('')
// }
const res = ref()
const changeInfo = async () => {
await infoRef.value.validate()
if (change.value === true) {
// console.log(form.value.phone, '')
// console.log(props.userData, 'props.data')
if (
// change.value === true ||
(change.value === true &&
props.userData.nickName !== form.value.nickName) ||
(change.value === false &&
form.value.newphone === '' &&
props.userData.nickName !== form.value.nickName &&
form.value.code === '')
) {
await userChangeService({
phone: form.value.phone,
id: form.value.id,
nickName: form.value.nickName,
}).then(() => {
}).then(async () => {
// console.log('11')
ElMessage.success('修改成功')
res.value = await userStore.getUserInfo()
res.value = res.value.result.data
// console.log(res.result.data, '1')
emit('get-message', form.value.nickName)
emit('userInfoModified', res)
dialogFormVisible.value = false
form.value.newphone = ''
form.value.code = ''
})
dialogFormVisible.value = false
} else {
} else if (
change.value === false &&
form.value.newphone !== form.value.phone &&
form.value.code !== ''
) {
// console.log('22')
if (codecode.value === form.value.code) {
// console.log(form.value.newphone, '')
// console.log('')
await userChangeService({
phone: form.value.newphone,
id: form.value.id,
nickName: form.value.nickName,
}).then(() => {
}).then(async () => {
ElMessage.success('修改成功')
res.value = userStore.getUserInfo()
// res.value = res.result.data
// console.log(res.value, 'hsouji')
// userStore.getUserInfo()
form.value.phone = form.value.newphone
emit('get-phone', form.value.phone)
// emit('get-phone', form.value.phone)
emit('get-message', form.value.nickName)
emit('userInfoModified', res)
dialogFormVisible.value = false
form.value.newphone = ''
form.value.code = ''
})
dialogFormVisible.value = false
} else {
ElMessage.error('验证码错误')
}
} else {
// console.log('33')
dialogFormVisible.value = false
}
}
//
const pwdRef = ref()
const changePassword = async () => {
await pwdRef.value.validate()
await userChangeService({
phone: form.value.phone,
id: form.value.id,
nickName: form.value.nickName,
}).then(() => {
ElMessage.success('修改成功')
emit('get-message', form.value.nickName)
form.value.newphone = ''
form.value.code = ''
})
dialogFormVisible.value = false
}
const cancleInfo = () => {
dialogFormVisible.value = false
form.value = { ...props.userData, code: '', newphone: '' }
}
//
const userStore = useUserStore()
@ -341,7 +349,12 @@ onUnmounted(() => {
<el-input v-model="form.nickName" autocomplete="off" />
</el-form-item>
<el-form-item label="手机" :label-width="formLabelWidth" prop="phone">
<el-input v-model="form.phone" autocomplete="off" style="width: 92%" />
<el-input
v-model="form.phone"
autocomplete="off"
style="width: 92%"
readonly
/>
<el-button
type="primary"
plain

@ -2,24 +2,20 @@
import status from '@/views/home/components/Status.vue'
import welcome from './components/Welcome.vue'
import conheader from '@/views/home/components/ConHeader.vue'
// import Info from './components/Info.vue'
import echarts from './components/Echarts.vue'
import lessonlist from './components/Lessonlist.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 {
userNewLikeService,
userLookService,
userMaxKnowService,
userTopKnowService,
userToplessonService,
} from '@/api/user/user'
import useUserStore from '@/store/modules/user'
// import fe from '@/assets/images/fe.jpg'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
// import { Picture } from 'element-plus/icons-vue'
const router = useRouter()
const userStore = useUserStore()
const username = ref()
const data = ref({})
@ -34,34 +30,55 @@ userStore.getUserInfo().catch((error) => {
const name = ref()
const getMessage = (msg) => {
name.value = msg
// console.log(name.value, 'msg')
}
const getPhone = (phone) => {
//?
// const getData = async () => {
// const res = await userLookService(userStore.data.id)
// // console.log(res.data, '11111')
// data.value = res.data
// }
const getUserName = (username) => {
// console.log(msg, 'msg')
userStore.data.phone = phone
// console.log(data.value.icon, 'icon')
userStore.userName = username
// console.log(data.value.icon, 'icon');
}
const getAvater = (avater) => {
// console.log(msg, 'msg')
userStore.data.icon = avater
// console.log(data.value.icon, 'icon')
}
const getInfo = (info) => {
console.log(userStore.userInfo, 'msg')
// console.log(userStore.userInfo, 'msg')
userStore.userInfo = info
// console.log(userStore.userData, 'icon111')
console.log(userStore.userData, 'icon111')
}
const courselist = ref([])
const img = ref()
const courseName = ref()
const courseTea = ref()
const toplist = ref([])
// const img = ref()
// const courseName = ref()
// const courseTea = ref()
//
const maxKnow = ref([])
const getmaxKnow = async () => {
const res = await userMaxKnowService(userStore.data.id)
maxKnow.value = res.data
console.log(maxKnow.value, 'max')
// console.log(maxKnow.value, 'max')
}
function fetchNewUserInfo(res) {
// userInfo
data.value = res
}
//
const topKnow = ref([])
const gettopKnow = async () => {
const res = await userTopKnowService(userStore.data.id)
topKnow.value = res.data
// console.log(maxKnow.value, 'max')
}
onMounted(() => {
// getData()
username.value = userStore.userName
@ -85,19 +102,21 @@ onMounted(() => {
//
userNewLikeService(userStore.data.id).then((res) => {
loading.value = true
// console.log(res.data, '')
courselist.value = res.data
loading.value = false
// console.log(courselist.value.id, 'course')
// img.value = res.data.img
// courseName.value = res.data.name
// courseTea.value = res.data.teacher
})
//
userToplessonService(userStore.data.id).then((res) => {
loading.value = true
toplist.value = res.data
loading.value = false
})
//echart
userLookService()
//
getmaxKnow()
//
gettopKnow()
})
</script>
<template>
@ -106,10 +125,10 @@ onMounted(() => {
<div class="nav">
<welcome
:data="infoData"
:userData="data"
:userData="userStore.data"
:name="name"
@get-message="getMessage"
@get-phone="getPhone"
@userInfoModified="fetchNewUserInfo"
v-if="flag"
></welcome>
</div>
@ -124,7 +143,7 @@ onMounted(() => {
<el-col :span="6">
<div class="info">
<status
:data="data"
:data="userStore.data"
:userData="infoData"
@get-avater="getAvater"
@getInfo="getInfo"
@ -141,8 +160,10 @@ onMounted(() => {
style="
font-size: 14px;
display: flex;
/* margin: 0px auto; */
margin: 0px 10px;
width: 90%;
margin: 0px auto;
/* margin: 0px 10px; */
margin-bottom: 5px;
"
>
<conheader
@ -150,8 +171,8 @@ onMounted(() => {
:urouter="'/curriculumCenter/knowledgePoints'"
></conheader>
</a>
<div class="con">
<p class="button" round v-for="item in maxKnow" :key="item.id">
<div class="contt">
<p class="button" round v-for="item in topKnow" :key="item.id">
{{ item.label }}
</p>
</div>
@ -161,12 +182,20 @@ onMounted(() => {
<div class="lesson">
<a
href="#"
style="width: 90%; font-size: 14px; display: flex; margin: 0 auto"
style="
width: 90%;
font-size: 14px;
display: flex;
margin: 0 auto;
margin-bottom: 5px;
"
>
<conheader :title="`推荐知识点`"></conheader>
</a>
<div class="con">
<p class="button" round v-for="item in 30" :key="item">Roshabei</p>
<div class="contt">
<p class="button" round v-for="item in maxKnow" :key="item.id">
{{ item.label }}
</p>
</div>
</div>
</el-col>
@ -176,16 +205,21 @@ onMounted(() => {
href="#"
style="width: 90%; font-size: 14px; display: flex; margin: 0 auto"
>
<conheader :title="`推荐课程`"></conheader>
<conheader
:title="`推荐课程`"
:urouter="'/curriculumCenter/basicCourseInformation'"
></conheader>
</a>
<div class="con">
<ul>
<li class="lessonlist-item" v-for="(item, index) in 4" :key="item">
<!-- <img :src="fe" class="lessonlist-item-img" /> -->
<li
class="lessonlist-item"
v-for="(item, index) in toplist"
:key="item.id"
>
<div class="lessonlist-item-info">
<p>{{ index + 1 }}</p>
<h5>课程名称</h5>
<h5>{{ item.name || '暂无' }}</h5>
</div>
</li>
</ul>
@ -198,7 +232,10 @@ onMounted(() => {
href="#"
style="width: 90%; font-size: 12px; display: flex; margin: 0 auto"
>
<conheader :title="`最新收藏课程`"></conheader>
<conheader
:title="`最新收藏课程`"
:urouter="'/curriculumCenter/basicCourseInformation'"
></conheader>
</a>
<div class="con">
<ul>
@ -206,13 +243,10 @@ onMounted(() => {
class="lessonlist-item"
v-for="(item, index) in courselist"
:key="item.id"
v-loading="loading"
>
<!-- <img :src="item.img || fe" class="lessonlist-item-img" /> -->
<div class="lessonlist-item-info">
<p>{{ index + 1 }}</p>
<h5>{{ item.name || '暂无' }}</h5>
<!-- <p>{{ item.teacher }}</p> -->
</div>
</li>
</ul>
@ -264,7 +298,6 @@ onMounted(() => {
justify-content: space-between;
}
.lesson {
background-color: pink;
padding-top: 30px;
// padding-bottom: 30px;
padding: 25px 10px 0px 10px;
@ -284,24 +317,32 @@ onMounted(() => {
// align-items: center;
// margin: 0 auto;
// background-color: yellow;
padding: 5px;
margin-top: 5px;
// margin-top: 5px;
flex-wrap: wrap;
justify-content: space-evenly;
padding-bottom: 30px;
}
.contt {
// background-color: yellow;
padding: 5px;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 11px;
}
.button {
justify-content: space-between;
margin-bottom: 5px;
height: 30px;
border-radius: 10px;
// color: #0080ff;
text-align: center;
line-height: 30px;
margin: 5px 10px;
// border: 2px solid #ccc;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
width: 40%;
width: 100%;
padding: 0px 15px;
overflow: hidden;
text-overflow: ellipsis;
@ -344,6 +385,8 @@ ul {
}
.lessonlist-item {
// background-color: pink;
height: 50px;
// margin: 0 auto;
border-radius: 10px;

@ -1,6 +1,26 @@
<template>
<div id="your-element-selector" style="width: 100%; height: 100vh"></div>
<div class="login_container">
<el-row :span="24" style="height: 5%">
<el-col>
<div class="grid-content ep-bg-purple-dark">
<div class="logo">
<div class="logo_main">
<img :src="setting.logo" alt="" />
<div class="font">
<p>{{ setting.title }}</p>
</div>
</div>
<a @click.prevent="goToPage">
<el-icon class="link">
<Link />
</el-icon>
</a>
</div>
</div>
</el-col>
</el-row>
<el-row>
<el-col
:span="12"
@ -19,7 +39,7 @@
:rules="rules"
v-if="isRegister"
>
<h1>Hello</h1>
<h1>课图开源智慧课程管理系统</h1>
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
@ -282,7 +302,7 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, onBeforeUnmount, watch } from 'vue'
import { User, Lock, Phone, Check } from '@element-plus/icons-vue'
import { User, Lock, Phone, Check, Link } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user'
import { useRouter, useRoute } from 'vue-router'
@ -293,6 +313,18 @@ import {
userCodeRegisterService,
} from '@/api/user/user'
// let loginForm = reactive({ username: 'admin', password: '111111' })
//logo
import useLayoutSettingStoe from '@/store/modules/setting'
// logo
import setting from '@/setting'
const goToPage = (event) => {
event.preventDefault()
// window.open('https:www.baidu.com', '_blank')
window.location.href = 'https:www.baidu.com'
}
const fold = ref(false)
const LayoutSettingStoe = useLayoutSettingStoe()
//
let userStore = useUserStore()
//
@ -565,6 +597,24 @@ onUnmounted(() => {
height: 100vh;
position: absolute;
top: 0;
.logo {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 35px 0px 20px;
.logo_main {
display: flex;
align-items: center;
}
.font {
font-size: 20px;
font-family: 'YourChosenArtisticFont', sans-serif;
// text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
color: #171615;
// transform: rotateX(5deg) rotateY(5deg);
margin-left: 5px;
}
}
// background: url('../../assets/images/background.jpg') no-repeat;
.loin_form {
position: relative;

@ -0,0 +1,80 @@
<template>
<div class="common-layout">
<el-container>
<el-header class="header">
<p>收到信息详情</p>
</el-header>
<el-main >
<el-card>
<template #header>
<div class="card-header">
<el-button type="primary" style="width: 80px" @click="back" round plain>返回</el-button>
</div>
</template>
<div class="container">
<div class="header_content">
<span class="sender">发送人{{ Message.senderName }}</span>
<span class="time">发送时间{{ Message.sendTime }}</span>
<span class="read-status">是否阅读{{Message.isRead}}</span>
</div>
<div class="recipient">标题{{ Message.title }}</div>
<div class="content">
内容{{ Message.content }}
</div>
</div>
<!-- <template #footer>Footer content</template>-->
</el-card>
</el-main>
</el-container>
</div>
</template>
<script setup lang="ts">
import {useRouter,useRoute} from "vue-router";
const router = useRouter()
const route = useRoute()
//
if(!Object.keys(route.query).length){
router.go(-1)
}
//
const back = ()=>{
router.push('/messageManagement/message')
}
import {ref} from "vue";
//
const Message = ref(route.query)
</script>
<style scoped>
.header{
background: #7f98cb;
height: 50px;
font-size: 20px;
text-align: center;
padding: 15px;
}
.container {
padding: 10px; /* 容器内边距 */
margin-top: 10px; /* 容器上边距 */
height: 65vh;
}
.header_content {
margin-bottom: 10px; /* 与下面的收件人信息保持一定间距 */
}
.header_content span {
margin-right: 50px; /* 发件人和时间之间的间距 */
}
.header_content .read-status {
margin-right: 0; /* 已读状态右侧不需要间距 */
}
.recipient, .content {
font-size: 20px;
color: #333;
margin-top: 10px;
margin-bottom: 20px;
}
</style>

@ -0,0 +1,196 @@
<template>
<div class="common-layout">
<el-container>
<el-header class="header">
<p>写栈内信函</p>
</el-header>
<el-main class="mainContainer">
<el-card>
<template #header>
<div class="card-header">
<span>写栈内信函</span>
<div>
<el-button style="width: 100px;text-align: center" type="primary" round @click="back" plain>返回</el-button>
<el-button style="width: 100px;text-align: center" type="primary" round @click="send">发送</el-button>
</div>
</div>
</template>
<!-- 输入框内容-->
<el-input
v-model="inputPerson"
size="large"
placeholder="收件人:"
>
<template #append>
<el-button @click="addPerson = true">
<el-icon><CirclePlus /></el-icon>
</el-button>
</template>
</el-input>
<el-input
v-model="inputText"
size="large"
placeholder="标题:"
>
</el-input>
<el-input
v-model="textarea"
:rows="15"
type="textarea"
placeholder="内容:"
/>
<!--添加收件人信息-->
<el-dialog class="dialogType" v-model="addPerson" title="选择收件人">
<el-divider/>
<div class="content-container">
<div class="left-panel" style="flex: 1; display: flex; align-items: center; justify-content: center;">
<!-- <el-input-->
<!-- v-model="inputSearch"-->
<!-- style="max-width: 300px;-->
<!-- margin-bottom: 20px"-->
<!-- placeholder="搜索"-->
<!-- >-->
<!-- <template #append>-->
<!-- <el-button :icon="Search" @click="editSearch"/>-->
<!-- </template>-->
<!-- </el-input>-->
<!-- 组织架构-->
<div>
组织框架:
<el-tree-select
v-model="inputPerson"
:data="formattedData"
multiple
:render-after-expand="false"
show-checkbox
style="width: 240px"
/>
</div>
</div>
<div class="right-panel" style="flex: 1;">
<p>已选人:</p>
<el-divider/>
<li v-for="item in inputPerson">
<p>{{item}}</p>
</li>
</div>
</div>
<el-divider/>
<div style="text-align: right">
<el-button type="primary" @click="disappoint" plain round>取消</el-button>
<el-button type="primary" @click="confirm" round>确定</el-button>
</div>
</el-dialog>
<template #footer>
<p>发送此站内信函即表示您已阅读并接受<el-link href="https://element-plus.org/zh-CN/component/button.html" target="_blank" type="primary">用户协议</el-link></p>
</template>
</el-card>
</el-main>
</el-container>
</div>
</template>
<script setup lang="ts">
import { ref} from 'vue'
// import { Search } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router';
import { userStudentListService } from '@/api/user/user'
import useUserStore from '@/store/modules/user'
import { ElMessageBox, ElMessage } from 'element-plus'
import {SendMessageApi} from "@/api/user/messag";
const inputPerson = ref([])
const inputText = ref('')
const textarea = ref('')
// const inputSearch = ref('')
const addPerson = ref(false)
const router = useRouter()
//
const userStore = useUserStore()
const stuList = ref([])
const formattedData = ref([])
const getStuList = async () => {
const res = await userStudentListService(userStore.data.id)
stuList.value = res.data
// console.log(stuList.value, 'rrr')
dataForTree(stuList.value);//
};
//
const dataForTree = (data) => {
// name
const formatted = data.map(student => ({
label: student.name,
value: student.name, // value
// children: [],
}));
formattedData.value = formatted;
};
getStuList()
//
const back= ()=>{
router.push('/messageManagement/message')
}
//
const send = async (id: any) => {
await ElMessageBox.confirm('您确定发送这条信息吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await SendMessageApi({
messageIds:id,
content:textarea,
userId:userStore.userInfo.id
})
}
//
const confirm = ()=>{
addPerson.value = false
}
//
const disappoint = ()=>{
addPerson.value = false
}
</script>
<style scoped>
.header{
background: steelblue;
text-align: center;
font-size: 18px;
line-height: 45px;
height: 45px;
width: 100%;
margin-bottom: 30px;
}
.header p{
color: gainsboro;
}
li{
list-style: none;
}
.mainContainer{
width: 80%;
margin: auto;
}
.card-header{
display: flex; /* 启用Flexbox布局 */
justify-content: space-between; /* 在主轴上分布项目,这里让h2和button分布在两端 */
align-items: center; /* 在交叉轴上居中项目,这里是垂直居中 */
height: 30px; /* 示例高度,根据实际需求调整 */
padding: 0 20px; /* 添加一些内边距以避免内容紧贴边缘 */
}
.el-input{
margin-bottom: 30px;
}
.content-container {
display: flex;
height: 100%; /* 根据需要调整高度 */
}
.left-panel, .right-panel {
display: flex;
flex-direction: column;
justify-content: center; /* 垂直居中内容 */
align-items: center; /* 水平居中内容 */
}
</style>

@ -0,0 +1,267 @@
<template>
<el-card>
<template #header>
<div class="card-header">
<el-button type="primary" @click="back" round plain>返回</el-button>
<el-button type="primary" @click="sendContent" round>写栈内信函</el-button>
</div>
</template>
<div class="container">
<div class="checkboxHeader">
<input type="checkbox" class="custom-checkbox" @change="toggleAll" :checked="allSelected" />
<p class="selected-count">已选 {{ selectedCount }} 条记录</p>
</div>
<li v-for="item in sendMessage"
:key="item.title"
class="message-list-item"
>
<!--添加悬浮删除按钮-->
<!-- <div class="message-list" @mouseover="hoveringOver = item.id" @mouseleave="hoveringOver = null">-->
<div class="message-list">
<!-- 添加勾选框 -->
<div class="message-check">
<input type="checkbox" class="custom-checkbox" :value="item.id" @change="toggleSelection(item.id)" :checked="isSelected(item.id)"/>
</div>
<div class="message-icon">
<el-icon><Comment /></el-icon>
</div>
<div class="message-content" @click="handleClick(item)">
<h2 class="message-title">{{item.title}}</h2>
<div class="message-details">
<span class="sender">收件人:{{item.receiverName}}</span>
<span class="read-status">阅读人数{{item.readUserNum}}</span>
</div>
<div class="message-time">{{item.sendTime}}</div>
</div>
<!-- 删除按钮默认不显示hover时显示 -->
<!-- <el-button v-if="hoveringOver === item.id" class="delete-btn" @click="deleteSendMessage(item.id)" type="danger" round >删除</el-button>-->
</div>
<el-divider/>
</li>
</div>
<template #footer>
<!-- 分页-->
<el-pagination
v-model:current-page="params.pageNo"
v-model:page-size="params.pageSize"
:page-sizes="[2,3, 5, 7, 10]"
:background="true"
layout="jumper,total, sizes, prev, pager, next "
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
style="margin-top: 10px; justify-content: center"
/>
</template>
</el-card>
</template>
<script setup lang="ts">
import { ref,computed,onMounted} from 'vue';
import { useRouter } from 'vue-router';
import useUserStore from "@/store/modules/user";
import {getSendMessagesListApi,deleteSendMessageApi} from "@/api/user/messag";
import { ElMessageBox, ElMessage } from 'element-plus'
const userStore = useUserStore()
//
const params = ref({
userId: userStore.userInfo.id,
// userId: 4,
isAsc:true,
isDelete:0,
isRead:'',
isSend:1,
// messageId:false,
pageNo:1,
pageSize:3,
sortBy:false,//
})
const total = ref(0)
const sendMessage = ref([])
const loading = ref(false)
// const hoveringOver = ref(null)
//
const selectedIds = ref([]); // ID
const allSelected = computed(() => {
// true
return sendMessage.value.length > 0 && sendMessage.value.every(item => selectedIds.value.includes(item.id));
});
const selectedCount = computed(() => {
//
return selectedIds.value.length;
});
function toggleAll(event) {
//
const target = event.target;
if (target.checked) {
// IDselectedIds
selectedIds.value = sendMessage.value.map(item => item.id);
} else {
// selectedIds
selectedIds.value = [];
}
}
function toggleSelection(id: number) {
//
const index = selectedIds.value.indexOf(id);
if (index > -1) {
selectedIds.value.splice(index, 1);
} else {
selectedIds.value.push(id);
}
}
function isSelected(id) {
return selectedIds.value.includes(id);
}
//
const getSendMessageList = async () => {
loading.value = true
const res = await getSendMessagesListApi(params.value)
sendMessage.value = res.data.list
total.value = res.data.total
loading.value = false
}
//
onMounted(() => {
getSendMessageList()
})
//
// const deleteSendMessage = async (id: any) => {
// await ElMessageBox.confirm('', '', {
// confirmButtonText: '',
// cancelButtonText: '',
// type: 'warning',
// })
// await deleteSendMessageApi({
// messageIds:id,
// userId:userStore.userInfo.id
// })
// .then(() => {
// console.log(id, 'id')
// getSendMessageList() //
// ElMessage.success('')
// // console.log(res)
// })
// .catch((err: any) => {
// console.log(id, 'id')
// ElMessage.error(err.response.data.message)
// })
//
// await getSendMessageList()
// }
//
const handleSizeChange = (size: any) => {
// loading.value = true
// console.log(size)
params.value.pageNo = 1
params.value.pageSize = size
//
getSendMessageList()
}
const handleCurrentChange = (page: any) => {
console.log(page)
params.value.pageNo = page
//
getSendMessageList()
}
const userId = userStore.userInfo.id
const router = useRouter()
//
const back =()=>{
router.push('/messageManagement/message')
}
//
const sendContent = ()=>{
router.push('/messageContent')
}
//
const handleClick = (item)=> {
//
router.push({ path: '/sendMessageList', query: item })
}
</script>
<style scoped>
.container{
margin: auto;
width: 95%;
padding: 15px;
}
.checkboxHeader {
width: 100%;
padding: 10px;
height: 50px;
display: flex; /* 使用Flex布局使子元素在同一行显示 */
align-items: center; /* 垂直居中对齐子元素 */
gap: 10px; /* 设置子元素之间的间距 */
}
.custom-checkbox {
transform: scale(1.5); /* 将勾选框放大1.5倍 */
}
.selected-count {
}
.message-check{
margin-right: 20px;
}
.message-list {
display: flex;
align-items: center; /* 垂直居中 */
height: 80px;
/*background-color: #f2f2f2;*/
padding: 0 10px;
box-sizing: border-box;
}
.message-icon {
margin-right: 30px; /* 图标右侧间距 */
font-size: 34px; /* 图标大小 */
}
li{
list-style: none;
}
.message-content {
display: flex;
flex-direction: column;
justify-content: center; /* 标题和详情内容垂直居中 */
flex: 1; /* 填充剩余空间 */
}
.message-title {
line-height: 30px;
font-size: 20px;
}
.message-details {
display: flex;
align-items: center; /* 细节内容水平居中 */
margin-top: 10px; /* 与标题的间距 */
}
.sender {
margin-right: 88px; /* 发件人和已读状态的间距 */
}
.read-status {
color: #888; /* 已读状态的颜色 */
}
.message-time {
margin-left: auto; /* 发送时间靠右显示 */
font-size: 15px; /* 发送时间字体大小 */
color: #666; /* 发送时间颜色 */
}
.message-list-item {
cursor: pointer; /* 鼠标悬停时显示手指形状,表示可点击 */
}
.message-list-item:hover{
background-image: linear-gradient(60deg,powderblue,darkgrey,snow);
}
</style>

@ -0,0 +1,80 @@
<template>
<div class="common-layout">
<el-container>
<el-header class="header">
<p>收到信息详情</p>
</el-header>
<el-main >
<el-card>
<template #header>
<div class="card-header">
<el-button type="primary" style="width: 80px" @click="back" round plain>返回</el-button>
</div>
</template>
<div class="container">
<div class="header_content">
<span class="sender">收件人{{ sendMessage.receiverName }}</span>
<span class="time">发送时间{{ sendMessage.sendTime }}</span>
<span class="read-status">是否阅读{{sendMessage.isRead}}</span>
</div>
<div class="recipient">标题{{ sendMessage.title }}</div>
<div class="content">
内容{{ sendMessage.content }}
</div>
</div>
<!-- <template #footer>Footer content</template>-->
</el-card>
</el-main>
</el-container>
</div>
</template>
<script setup lang="ts">
import {useRouter,useRoute} from "vue-router";
const router = useRouter()
const route = useRoute()
//
if(!Object.keys(route.query).length){
router.go(-1)
}
//
const back = ()=>{
router.push('/messageManagement/sendMessage')
}
import {ref} from "vue";
//
const sendMessage = ref(route.query)
</script>
<style scoped>
.header{
background: #7f98cb;
height: 50px;
font-size: 20px;
text-align: center;
padding: 15px;
}
.container {
padding: 10px; /* 容器内边距 */
margin-top: 10px; /* 容器上边距 */
height: 65vh;
}
.header_content {
margin-bottom: 10px; /* 与下面的收件人信息保持一定间距 */
}
.header_content span {
margin-right: 50px; /* 发件人和时间之间的间距 */
}
.header_content .read-status {
margin-right: 0; /* 已读状态右侧不需要间距 */
}
.recipient, .content {
font-size: 20px;
color: #333;
margin-top: 10px;
margin-bottom: 20px;
}
</style>

@ -1,5 +1,269 @@
<template>
<div>消息</div>
<el-card>
<template #header>
<div class="card-header">
<el-button type="primary" @click="editContent" round>写栈内信函</el-button>
<el-button type="primary" @click="sendContent" round>个人发出</el-button>
</div>
</template>
<div class="container">
<div class="checkboxHeader">
<input type="checkbox" class="custom-checkbox" @change="toggleAll" :checked="allSelected" />
<p class="selected-count">已选 {{ selectedCount }} 条记录</p>
</div>
<li v-for="item in Message"
:key="item.title"
class="message-list-item"
>
<!--添加悬浮删除按钮-->
<div class="message-list" @mouseover="hoveringOver = item.id" @mouseleave="hoveringOver = null">
<!-- 添加勾选框 -->
<div class="messageCheck">
<input type="checkbox" class="custom-checkbox" :value="item.id" @change="toggleSelection(item.id)" :checked="isSelected(item.id)"/>
</div>
<!-- 图标 -->
<div class="message-icon">
<el-icon><Comment /></el-icon>
</div>
<div class="message-content" @click="handleClick(item)">
<h2 class="message-title">{{item.title}}</h2>
<div class="message-details">
<span class="sender">发件人{{item.sendPerson}}</span>
<span class="read-status">已读状态{{item.isRead}}</span>
</div>
<div class="message-time">发送时间{{item.sendTime}}</div>
</div>
<!-- 删除按钮默认不显示hover时显示 -->
<el-button v-if="hoveringOver === item.id" class="delete-btn" @click="deleteMessage(item.id)" type="danger" round>删除</el-button>
</div>
<el-divider/>
</li>
</div>
<template #footer>
<el-pagination
v-model:current-page="params.pageNo"
v-model:page-size="params.pageSize"
:page-sizes="[2,4, 5, 7, 10]"
:background="true"
layout="jumper,total, sizes, prev, pager, next "
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
style="margin-top: 10px; justify-content: center"
/>
</template>
</el-card>
</template>
<script setup lang="ts">
import { ref,computed,onMounted} from 'vue';
import { useRouter } from 'vue-router';
import useUserStore from "@/store/modules/user";
import {getMessagesListApi,deleteSendMessageApi} from "@/api/user/messag";
import { ElMessageBox, ElMessage } from 'element-plus'
const userStore = useUserStore()
const params = ref({
userId: userStore.userInfo.id,
// userId: 4,
isAsc:true,
isDelete:0,
isRead:'',
isSend:1,
// messageId:false,
pageNo:1,
pageSize:4,
sortBy:false,//
})
const total = ref(0)
const Message = ref([])
const loading = ref(false)
const hoveringOver = ref(null)
//
const selectedIds = ref([]); // ID
const allSelected = computed(() => {
// true
return Message.value.length > 0 && Message.value.every(item => selectedIds.value.includes(item.id));
});
<script></script>
const selectedCount = computed(() => {
//
return selectedIds.value.length;
});
function toggleAll(event) {
//
const target = event.target;
if (target.checked) {
// IDselectedIds
selectedIds.value = Message.value.map(item => item.id);
} else {
// selectedIds
selectedIds.value = [];
}
}
function toggleSelection(id: number) {
//
const index = selectedIds.value.indexOf(id);
if (index > -1) {
selectedIds.value.splice(index, 1);
} else {
selectedIds.value.push(id);
}
}
function isSelected(id) {
return selectedIds.value.includes(id);
}
//
const getMessageList = async () => {
loading.value = true
const res = await getMessagesListApi(params.value)
Message.value = res.data.list
total.value = res.data.total
loading.value = false
}
//
onMounted(() => {
getMessageList()
})
//
const deleteMessage = async (id: any) => {
await ElMessageBox.confirm('您确定删除这条课程信息吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await deleteSendMessageApi({
messageIds:id,
userId:userStore.userInfo.id
})
.then(() => {
console.log(id, '删除id')
getMessageList() //
ElMessage.success('删除成功')
// console.log(res)
})
.catch((err: any) => {
console.log(id, 'id')
ElMessage.error(err.response.data.message)
})
await getMessageList()
}
//
const handleSizeChange = (size: any) => {
// loading.value = true
// console.log(size)
params.value.pageNo = 1
params.value.pageSize = size
//
getMessageList()
}
const handleCurrentChange = (page: any) => {
console.log(page)
params.value.pageNo = page
//
getMessageList()
}
const router = useRouter()
//
const editContent =()=>{
router.push('/messageContent')
}
//
const sendContent = ()=>{
router.push('/messageManagement/sendMessage')
}
//
const handleClick = (item)=> {
// console.log(item)
//
router.push({ path: '/messageContentList', query: item })
}
</script>
<style scoped>
.container{
/*background: darkgray;*/
margin: auto;
width: 95%;
padding: 15px;
}
.checkboxHeader {
/*background: lightgrey;*/
width: 100%;
padding: 10px;
height: 50px;
display: flex; /* 使用Flex布局使子元素在同一行显示 */
align-items: center; /* 垂直居中对齐子元素 */
gap: 10px; /* 设置子元素之间的间距 */
}
.custom-checkbox {
transform: scale(1.5); /* 将勾选框放大1.5倍 */
}
.selected-count {
}
.messageCheck{
margin-right: 20px;
}
.message-list {
display: flex;
align-items: center; /* 垂直居中 */
height: 80px;
/*background-color: #f2f2f2;*/
padding: 0 10px;
box-sizing: border-box;
}
.message-icon {
margin-right: 30px; /* 图标右侧间距 */
font-size: 34px; /* 图标大小 */
}
li{
list-style: none;
}
.message-content {
display: flex;
flex-direction: column;
justify-content: center; /* 标题和详情内容垂直居中 */
flex: 1; /* 填充剩余空间 */
}
.message-title {
line-height: 30px;
font-size: 20px;
}
.message-details {
display: flex;
align-items: center; /* 细节内容水平居中 */
margin-top: 10px; /* 与标题的间距 */
}
.sender {
margin-right: 88px; /* 发件人和已读状态的间距 */
}
.read-status {
color: #888; /* 已读状态的颜色 */
}
.message-time {
margin-left: auto; /* 发送时间靠右显示 */
font-size: 15px; /* 发送时间字体大小 */
color: #666; /* 发送时间颜色 */
}
.message-list-item {
cursor: pointer; /* 鼠标悬停时显示手指形状,表示可点击 */
}
.message-list-item:hover{
background-image: linear-gradient(60deg,powderblue,darkgrey,snow);
}
</style>

@ -0,0 +1,11 @@
<template>
</template>
<script>
</script>
<style scoped>
</style>

@ -0,0 +1,236 @@
<template>
<div class="header_title">
<span>资讯</span>
</div>
<div>
<el-card shadow="always">
<div class="header-flex-container">
<ul class="header-list">
<li v-for="item in column">
<el-dropdown placement="bottom">
<span>{{ item.categoryName }}</span> <!-- 使用span代替p -->
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<router-link to="/newsDetails">{{ item.createTime }}</router-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
</ul>
<div>
<el-link href="https://element-plus.org" target="_blank" style="margin-right: 30px;font-size: 19px">关于我们</el-link>
<el-button name="editColumnButton" @click="editHandle" round>栏目编辑</el-button>
</div>
</div>
</el-card>
</div>
<!-- 栏目编辑弹框-->
<el-drawer v-model="drawer" title="title" :with-header="false">
<span>栏目编辑</span><br/>
<el-button type="success" round style="margin: 10px" @click="addFormVisible = true">添加</el-button>
<div>
<el-table :data="column" style="width: 100%">
<el-table-column prop="categoryName" label="名称" width="200" />
<el-table-column width="200" >
<template #header>
操作
</template>
<template #default="{row}">
<!-- 在这里定义你的删除按钮 -->
<el-button size="default" type="danger" @click="deleteHandle(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-drawer>
<!-- 栏目添加对话框-->
<el-dialog v-model="addFormVisible" title="栏目添加" width="500">
<el-form :model="form">
<el-form-item label="栏目名称" label-width="140px">
<el-input v-model="form.categoryName" autocomplete="off" />
</el-form-item>
<el-form-item label="栏目父id" label-width="140px">
<el-input v-model="form.categoryPid" autocomplete="off" />
</el-form-item>
<el-form-item label="创建时间" label-width="140px">
<el-input v-model="form.createTime" autocomplete="off" type="date"/>
</el-form-item>
<el-form-item label="创建盖栏目的用户id" label-width="140px">
<el-input v-model="form.creatorId" autocomplete="off" />
</el-form-item>
<el-form-item label="栏目id" label-width="140px">
<el-input v-model="form.id" autocomplete="off" />
</el-form-item>
<el-form-item label="排序" label-width="140px">
<el-input v-model="form.sort" autocomplete="off" />
</el-form-item>
<el-form-item label="是否显示" label-width="140px">
<el-input v-model="form.status" autocomplete="off" />
</el-form-item>
<el-form-item label="更新时间" label-width="140px">
<el-input v-model="form.updateTime" autocomplete="off" type="date"/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="addFormVisible = false">取消</el-button>
<el-button type="primary" @click="addFormConfirm">
确定
</el-button>
</div>
</template>
</el-dialog>
<!-- 新闻内容-->
<el-card class="container">
<template #header>
<div class="card-header">
<span class="contentTitle">Content Title</span>
<el-card shadow="hover">
<div class="flex-container">
<p>发布人{{ }}</p>
<p>信息来源{{ }}</p>
<p>日期{{ }}</p>
</div>
</el-card>
</div>
</template>
<div style="text-align: center">
<router-view></router-view>
</div>
<template #footer>
</template>
</el-card>
</template>
<script setup lang="ts">
import {queryCategory,deleteCategory,addCategory} from "@/api/user/news.js"
import {ref,onMounted} from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus'
const drawer = ref(false)
const addFormVisible = ref(false)
const column =ref([])
const form = ref(
{
categoryName:'',
categoryPid:'',
createTime:'',
creatorId:'',
id:'',
sort:'',
status:'',
updateTime:''
}
)
const params = ref({
categoryName:form.value.categoryName,
createTime:form.value.createTime,
id:form.value.id
})
//
const CategoryList = async () => {
const res = await queryCategory()
column.value = res.data
}
//
onMounted(() => {
CategoryList()
})
//
const editHandle =() =>{
drawer.value = true
}
//
const addFormConfirm = async ()=>{
addFormVisible.value = false
await addCategory({
categoryName:form.value.categoryName,
categoryPid:form.value.categoryPid,
createTime:form.value.createTime,
creatorId:form.value.creatorId,
id:form.value.id,
sort:form.value.sort,
status:form.value.status,
updateTime:form.value.updateTime
})
}
//
const deleteHandle = async (row)=>{
console.log(row,'row.id')
await ElMessageBox.confirm('您确定删除这条栏目信息吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await deleteCategory(row.id).then(() => {
CategoryList() //
ElMessage({
message: '删除成功',
type: 'success',
})
})
await CategoryList()
}
</script>
<style scoped>
.container{
height: 65vh;
}
.header_title{
font-size: 30px;
text-align: center;
padding: 20px;
margin: auto;
background-image: linear-gradient(60deg,honeydew,powderblue,honeydew);
}
.header-flex-container {
display: flex;
align-items: center; /* 垂直居中 */
padding: 20px;
background: #4b81d7;
justify-content: space-between; /* 左边对齐和右边对齐 */
}
.header-list {
display: flex; /* 使li元素水平排列 */
flex-wrap: wrap; /* 如果需要换行 */
align-items: center; /* 如果需要,也可以使li元素垂直居中 */
margin: 0; /* 移除默认外边距 */
padding: 0; /* 移除默认内边距 */
}
.header-list span{
font-size: 17px;
color: whitesmoke;
}
.header-list li {
list-style: none;
margin-right: 10px;
}
.el-dropdown {
margin-right: 30px;
}
.contentTitle{
color: red;
font-size: 30px;
text-align: center;
display: block;
width: 100%; /* 或者设置一个具体的宽度 */
line-height: 40px;
margin-bottom: 20px;
}
.flex-container {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中*/
flex-direction: row; /* 默认就是 row, */
}
.flex-container p {
margin-right: 100px;
}
.flex-container p:last-child {
margin-right: 0;
}
</style>

@ -26,7 +26,7 @@
</div>
</div>
<!-- <div class="inco"></div> -->
<h1>知识图谱</h1>
<h1>课图开源智慧课程管理系统首次开源</h1>
<!-- <p>(专业代码 080902H)</p> -->
</div>
<div class="container-box" ref="containerRef" @scroll="handleScroll">

@ -0,0 +1,14 @@
<template>
<div>
<router-view></router-view>
</div>
</template>
<script lang='ts' setup>
// import { onMounted, reactive, ref, toRefs, watch } from 'vue'
</script>
<style lang='scss' scoped>
</style>

@ -0,0 +1,210 @@
<script setup>
import { ref } from 'vue'
import { stuListService, updateStuService } from '@/api/user/stud'
import { tool, client } from '@/utils/alioss.js'
import { ElMessage } from 'element-plus'
const drawer = ref(false)
const formLabelWidth = '80px'
const emit = defineEmits(['success'])
const imgUrl = ref()
const formModel = ref({
icon: '',
number: '',
name: '',
sex: '',
nickName: '',
username: '',
major: '',
yearAge: '',
createTime: '',
})
formModel.value.sex === '男' ? 0 : 1
const open = async (row) => {
drawer.value = true
await stuListService(row.id).then((res) => {
formModel.value = res.data
imgUrl.value = formModel.value.icon
// console.log(formModel.value.icon, 'defauleForm.value')
})
}
const beforeUpload = (file) => {
const validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp']
if (!validImageTypes.includes(file.type)) {
ElMessage.error('请上传图片')
return false
}
return true
}
const upload = async (option) => {
const res = await tool.oss.upload(option.file)
// console.log(res, 'res')
formModel.value.icon = res.url
imgUrl.value = formModel.value.icon
}
const formRef = ref(null)
//
const rules = {
number: [
{ required: true, message: '请输入学号', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在2-20之间', trigger: 'blur' },
],
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 5, message: '长度在2-5之间', trigger: 'blur' },
],
sex: [
{
required: true,
message: '请选择性别',
trigger: 'change',
},
],
nickName: [
{
message: '请输入昵称',
trigger: 'blur',
},
],
username: [{ required: true }],
createTime: [{ required: true }],
major: [
{ required: true, message: '专业不能为空', trigger: 'blur' },
{
min: 2,
max: 20,
message: '职位名称至少2个字符最多20个字符',
trigger: 'blur',
},
],
yearAge: [
{
required: true,
trigger: 'change',
message: '请选择入学年份',
},
],
}
//
const onSubmit = async () => {
await formRef.value.validate()
console.log(formModel.value, 'form')
await await updateStuService({
id: formModel.value.id,
userId: formModel.value.userId,
icon: formModel.value.icon,
number: formModel.value.number,
name: formModel.value.name,
sex: formModel.value.sex,
nickName: formModel.value.nickName,
major: formModel.value.major,
}).then(() => {
ElMessage.success('修改成功')
drawer.value = false
emit('success', 'edit')
})
}
const onCancel = () => {
drawer.value = false
}
defineExpose({
open,
})
</script>
<template>
<el-drawer v-model="drawer" title="编辑信息" style="">
<el-form
:model="formModel"
:rules="rules"
ref="formRef"
:label-width="formLabelWidth"
>
<el-form-item label="头像" prop="icon">
<el-upload
v-model="formModel.icon"
class="avatar-uploader"
:http-request="upload"
:show-file-list="false"
:before-upload="beforeUpload"
>
<img v-if="imgUrl" :src="imgUrl" class="avatar" />
<!-- <img v-if="imageUrl" :src="imageUrl" class="avatar" /> -->
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
<template #tip>
<div class="el-upload__tip">图片文件要为jpg/png格式小于500kb</div>
</template>
</el-upload>
</el-form-item>
<el-form-item label="学号" prop="number">
<el-input v-model="formModel.number" placeholder="请输入学号" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="formModel.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formModel.sex">
<el-radio value="男"></el-radio>
<el-radio value="女"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户名" prop="userName">
<el-input v-model="formModel.username" readonly />
</el-form-item>
<el-form-item label="昵称" prop="nickName">
<el-input v-model="formModel.nickName" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="专业" prop="major">
<el-input v-model="formModel.major" placeholder="请输入专业" />
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-input v-model="formModel.createTime" readonly />
</el-form-item>
</el-form>
<div class="footer">
<el-button @click="onCancel">取消</el-button>
<el-button type="primary" @click="onSubmit">确认</el-button>
</div>
</el-drawer>
</template>
<style scoped lang="scss">
.avatar-uploader {
:deep() {
.avatar {
width: 178px;
height: 178px;
display: block;
}
.el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
}
}
.footer {
display: flex;
justify-content: end;
align-items: center;
margin-top: 100px;
}
</style>

@ -1,19 +1,26 @@
<script setup>
import { Search, Edit, Delete } from '@element-plus/icons-vue'
import { ref, onMounted } from 'vue'
import { Search, Edit, Delete, CircleClose } from '@element-plus/icons-vue'
import { ref, onMounted, computed } from 'vue'
import {
stuListService,
// stuListService,
stuPageService,
delStuListService,
reStuPassService,
changeStaService,
stuNumberService,
} from '@/api/user/stud'
import { ElMessage, ElMessageBox } from 'element-plus'
import useUserStore from '@/store/modules/user'
import editStu from './components/editStu.vue'
import { tryOnMounted } from '@vueuse/core'
const userStore = useUserStore()
// console.log(userStore.data.id, 'icon') //
const formLabelWidth = '60px'
const tableData = ref([])
const total = ref()
const show = ref(false)
const show1 = ref(false)
//
const isFirstLoad = ref(true)
const params = ref({
@ -27,24 +34,17 @@ const params = ref({
//
const getData = async () => {
loading.value = true
console.log(params.value, 'params')
await stuPageService(params.value).then((res) => {
tableData.value = res.data.Records
// console.log(tableData.value, 'params')
total.value = res.data.totalCount
loading.value = false
console.log(res.data, 'res.data')
// console.log(res.data.totalCount, 'res.data')
isFirstLoad.value = false
})
}
//
// const getData = async () => {
// // id.value = userStore.data.id
// // console.log(id.value, 'id.value')
// stuListService(2).then((res) => {
// tableData.value = res.data
// console.log(res.data, 'res.data')
// })
// }
//
const onSizeChange = (size) => {
//访
@ -60,28 +60,20 @@ const onCurrentChange = (page) => {
params.value.page = page
getData()
}
//
const onReset = () => {
params.value.page = 1
getData()
}
//
const onSearch = () => {
params.value.pagenum = 1
// getArticleService()
}
const loading = ref(false)
//
const drawer = ref(false)
//
const stuChange = ref()
const editTable = (row) => {
drawer.value = true
stuChange.value.open(row)
console.log(row)
drawer.value = true
}
//
const delTable = async (row) => {
console.log(row.userId, 'delrow')
// console.log(row.userId, 'delrow')
await ElMessageBox.confirm('您确定删除这条学生信息吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
@ -99,286 +91,300 @@ const delTable = async (row) => {
const selectedIds = ref([])
function handleSelectionChange(selection) {
selectedIds.value = selection.map((row) => row.userId)
console.log(selectedIds.value, 'selectedIds.value')
// console.log(selectedIds.value, 'selectedIds.value')
}
const onDel = async () => {
await ElMessageBox.confirm('您确定删除这些学生信息吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await delStuListService(selectedIds.value).then(() => {
if (selectedIds.value.length !== 0) {
await ElMessageBox.confirm('确定删除学生信息吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await delStuListService(selectedIds.value).then(() => {
ElMessage({
message: '删除成功',
type: 'success',
})
})
getData()
} else {
ElMessage({
message: '删除成功',
type: 'success',
message: '请选择要删除的学生',
})
})
getData()
}
}
//
const onResetPass = async () => {
await ElMessageBox.confirm('您确定重置这些学生密码吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await reStuPassService(selectedIds.value).then(() => {
if (selectedIds.value.length !== 0) {
await ElMessageBox.confirm('确定重置密码吗', '温馨提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
await reStuPassService(selectedIds.value).then(() => {
ElMessage({
message: '重置成功',
type: 'success',
})
})
getData()
} else {
ElMessage({
message: '重置成功',
type: 'success',
message: '请选择要重置密码的学生',
})
})
getData()
}
}
//
const status = ref()
tableData.value.forEach((item) => {
item.status = item.status === 1
})
async function handleStatusChange(row) {
status.value = row.status
// console.log(status.value, 'status')
// console.log(`: ${row.status}`)
// if (!isFirstLoad.value) {
// await changeStaService(status.value, row.userId).then(() => {
// ElMessage({
// message: '',
// type: 'success',
// })
// })
// }
// getData()
console.log(`${row.status}` === 1 ? true : false, 'status')
console.log(status.value === 0 ? true : false, 'status1')
console.log(`状态更改: ${row.status}`)
if (!isFirstLoad.value) {
await changeStaService(status.value, row.userId).then(() => {
ElMessage({
message: '状态更改成功',
type: 'success',
})
})
}
getData()
}
//
const studentName = ref()
function onFocus1() {
show1.value = true
}
const closeName = () => {
show1.value = false
studentName.value = ''
}
const searchInfo = async () => {
params.value.name = studentName.value
if (studentName.value.trim() !== '') {
await stuPageService({
page: 1,
pagesize: 3,
id: userStore.data.id,
name: studentName.value,
number: '',
}).then((res) => {
params.value.page = 1
console.log(res, 'change')
getData()
// studentName.value = ''
})
}
}
//
const studentId = ref()
function onFocus() {
show.value = true
}
function onblur1() {
show.value = false
}
const clearIdinput = () => {
show.value = false
studentId.value = ''
}
const searchNumber = async () => {
params.value.number = studentId.value
console.log(params.value.number, 'params.value.number')
await stuPageService(params.value).then((res) => {
params.value.page = 1
console.log(res, 'change')
})
// getData()
if (studentId.value > 0) {
await stuPageService({
page: 1,
pagesize: 3,
id: userStore.data.id,
name: '',
number: studentId.value,
}).then((res) => {
params.value.page = 1
getData()
})
}
}
//
const studentName = ref()
const searchInfo = async () => {
params.value.name = studentName.value
console.log(params.value.name, 'params.value.number')
await stuPageService(params.value).then((res) => {
params.value.page = 1
console.log(res, 'change')
})
// getData()
//
const onReset = () => {
params.value.page = 1
params.value.name = ''
params.value.number = ''
getData()
}
function clearInput() {
studentId.value = ''
}
function clearInput1() {
studentName.value = ''
}
const changeStatus = async (row) => {}
const value1 = ref(true)
let switchValue = ref()
switchValue.value = tableData.value.status
const handleSwitchChange = (value) => {
switchValue.value === 1 ? true : false
}
const onSuccess = () => {
getData()
}
onMounted(() => {
getData()
})
</script>
<template>
<page-container title="文章分类">
<el-card class="page-container">
<template #header>
<div class="card-header">
<span>学生信息</span>
<div>
<el-button type="primary" @click="onResetPass">重置密码</el-button>
<el-button type="danger" @click="onDel">批量删除</el-button>
</div>
<el-card class="page-container">
<template #header>
<div class="card-header">
<span>学生信息</span>
<div>
<el-button type="primary" @click="onResetPass">重置密码</el-button>
<el-button type="danger" @click="onDel">批量删除</el-button>
</div>
</template>
<div class="content">
<el-form style="display: flex">
<el-form-item style="width: 250px">
<!-- <p>学号</p> -->
<el-input
class="input_search"
v-model="studentId"
@blur="clearInput"
></el-input>
<el-icon><Search /></el-icon>
</el-form-item>
<el-button class="btn_search" @click="searchNumber">
学号搜索
</el-button>
<el-form-item style="width: 250px; margin-left: 30px">
<el-input
class="input_search"
@blur="clearInput1"
v-model="studentName"
></el-input>
<el-icon><Search /></el-icon>
</el-form-item>
<el-button class="btn_search" @click="searchInfo">搜索</el-button>
<el-button type="primary" @click="onReset">重置</el-button>
</el-form>
<el-table
class="tableBox"
:data="tableData"
v-loading="loading"
@selection-change="handleSelectionChange"
:header-cell-style="{
background: '#FAFAFA',
color: '#666',
fontSize: '14px',
fontWeight: 'bold',
height: '45px',
lineHeight: '45px',
borderTop: '1px solid #ebeef5',
textAlign: 'center',
}"
:cell-style="{
color: '#666',
fontSize: '14px',
height: '40px',
lineHeight: '40px',
textAlign: 'center',
}"
:row-style="{
height: '40px',
}"
style="width: 100%"
>
<el-table-column type="selection" width="55" height="100" />
<el-table-column prop="icon" label="头像">
<template v-slot="scope">
<img
:src="scope.row.icon"
alt="头像"
width="40"
height="40"
class="rounded-circle"
/>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" />
<el-table-column prop="number" label="学号" width="120" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="nickName" label="昵称" />
<el-table-column prop="phone" label="手机号" width="120" />
<el-table-column prop="major" label="专业" />
<el-table-column prop="yearAge" label="年级" />
<!-- <el-table-column prop="className" label="班级" /> -->
<el-table-column prop="createTime" label="创建时间" width="200" />
<el-table-column prop="status" label="状态">
<template v-slot="scope">
<el-switch
v-model="scope.row.status"
active-value="1"
inactive-value="0"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" min-width="125">
<template #default="{ row }">
<el-button
type="primary"
size="small"
:icon="Edit"
circle
plain
@click="editTable(row)"
></el-button>
<el-button
:icon="Delete"
type="danger"
size="small"
circle
plain
@click="delTable(row)"
></el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="没有数据" />
</template>
</el-table>
<el-pagination
v-model:current-page="params.page"
v-model:page-size="params.pagesize"
:page-sizes="[3, 5, 7, 10]"
:size="small"
:disabled="disabled"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="10"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</el-card>
<el-drawer v-model="drawer" title="编辑信息" style="">
<el-form :model="tableData" :rules="rules" ref="formRef">
<el-form-item label="头像" prop="icon">
<el-upload
v-model="tableData.icon"
class="avatar-uploader"
:http-request="upload"
:show-file-list="false"
</template>
<div class="content">
<el-form style="display: flex">
<el-form-item style="width: 250px">
<el-input
class="input_search"
v-model="studentId"
@focus="onFocus"
placeholder="输入学号进行搜索"
></el-input>
<el-icon
color="gray"
:size="12"
style="margin-right: 20px"
@click="clearIdinput"
v-if="show"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
<template #tip>
<div class="el-upload__tip">
图片文件要为jpg/png格式小于500kb
</div>
</template>
</el-upload>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="tableData.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="学号" prop="number">
<el-input v-model="tableData.number" placeholder="请输入学号" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="tableData.sex">
<el-radio value="1"></el-radio>
<el-radio value="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="姓名" prop="username">
<el-input v-model="tableData.username" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="用户名" prop="nickName">
<el-input v-model="tableData.nickName" />
</el-form-item>
<el-form-item label="昵称" prop="nickName">
<el-input v-model="tableData.nickName" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="tableData.phone" />
</el-form-item>
<el-form-item label="专业" prop="major">
<el-input v-model="tableData.major" placeholder="请输入专业" />
<CircleClose />
</el-icon>
<el-icon><Search /></el-icon>
</el-form-item>
<el-form-item label="年级" prop="yearAge">
<el-input v-model="tableData.yearAge" placeholder="请输入年级" />
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-input v-model="tableData.createTime" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch
v-model="tableData.status"
inline-prompt
active-text="启用"
inactive-text="禁用"
/>
<!-- <el-input v-model="tableData.status" placeholder="请输入姓名" /> -->
<el-button class="btn_search" @click="searchNumber">搜索</el-button>
<el-form-item style="width: 250px; margin-left: 30px">
<el-input
class="input_search"
v-model="studentName"
@focus="onFocus1"
placeholder="输入姓名进行搜索"
></el-input>
<el-icon
color="gray"
:size="12"
style="margin-right: 20px"
v-if="show1"
@click="closeName"
>
<CircleClose />
</el-icon>
<el-icon><Search /></el-icon>
</el-form-item>
<el-button class="btn_search" @click="searchInfo">搜索</el-button>
<el-button type="primary" @click="onReset">重置</el-button>
</el-form>
</el-drawer>
</page-container>
<el-table
class="tableBox"
:data="tableData"
v-loading="loading"
@selection-change="handleSelectionChange"
:header-cell-style="{
background: '#FAFAFA',
color: '#666',
fontSize: '14px',
fontWeight: 'bold',
height: '45px',
lineHeight: '45px',
borderTop: '1px solid #ebeef5',
textAlign: 'center',
}"
:cell-style="{
color: '#666',
fontSize: '14px',
height: '40px',
lineHeight: '40px',
textAlign: 'center',
}"
:row-style="{
height: '40px',
}"
style="width: 100%"
>
<el-table-column type="selection" width="55" height="100" />
<el-table-column prop="icon" label="头像">
<template v-slot="scope">
<img
:src="scope.row.icon"
alt="头像"
width="40"
height="40"
class="rounded-circle"
/>
</template>
</el-table-column>
<el-table-column prop="number" label="学号" width="120" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="nickName" label="昵称" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="major" label="专业" />
<el-table-column prop="createTime" label="创建时间" width="200" />
<el-table-column prop="status" label="状态">
<template v-slot="scope">
<el-switch
v-model="scope.row.status"
active-value="1"
inactive-value="0"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" min-width="125">
<template #default="{ row }">
<el-button
type="primary"
:icon="Edit"
circle
plain
@click="editTable(row)"
></el-button>
<el-button
:icon="Delete"
type="danger"
circle
plain
@click="delTable(row)"
></el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="没有数据" />
</template>
</el-table>
<el-pagination
v-model:current-page="params.page"
v-model:page-size="params.pagesize"
:page-sizes="[3, 5, 7, 10]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</el-card>
<!-- <edit-stu ref="stuChange"></edit-stu> -->
<edit-stu ref="stuChange" @success="onSuccess()"></edit-stu>
</template>
<style lang="scss" scoped>
@ -388,6 +394,8 @@ onMounted(() => {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
flex: 1;
font-family: Inter, 'Helvetica Neue', Helvetica, 'PingFang SC',
'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
}
.card-header {
display: flex;
@ -395,7 +403,7 @@ onMounted(() => {
justify-content: space-between;
}
.content {
min-height: 440px;
min-height: 660px;
}
.input_search {
@ -407,9 +415,13 @@ onMounted(() => {
right: 10px;
top: 50%;
transform: translateY(-50%);
.close {
margin-right: 10px;
}
}
.btn_search {
margin-left: 15px;
margin: 0px 15px;
// margin-top: 12px;
border-radius: 8px;
font-size: 13px;
background-color: #377be5;
@ -446,6 +458,9 @@ onMounted(() => {
justify-content: end;
font-size: 12px;
}
h5 {
margin-top: 22px;
}
.avatar-uploader {
:deep() {
.avatar {

@ -0,0 +1,40 @@
// vite.config.ts
import vue from "file:///C:/project/Teaching_integration_platform_admin_template/node_modules/.pnpm/@vitejs+plugin-vue@5.1.2_vite@5.4.0_@types+node@22.2.0_sass@1.77.8__vue@3.4.37_typescript@5.5.4_/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import path from "path";
import { viteMockServe } from "file:///C:/project/Teaching_integration_platform_admin_template/node_modules/.pnpm/vite-plugin-mock@3.0.2_esbuild@0.21.5_mockjs@1.1.0_vite@5.4.0_@types+node@22.2.0_sass@1.77.8_/node_modules/vite-plugin-mock/dist/index.mjs";
import { createSvgIconsPlugin } from "file:///C:/project/Teaching_integration_platform_admin_template/node_modules/.pnpm/vite-plugin-svg-icons@2.0.1_vite@5.4.0_@types+node@22.2.0_sass@1.77.8_/node_modules/vite-plugin-svg-icons/dist/index.mjs";
var vite_config_default = ({ command }) => {
return {
plugins: [
vue(),
viteMockServe({
enable: command === "serve"
}),
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
// Specify symbolId format
symbolId: "icon-[dir]-[name]"
})
],
resolve: {
alias: {
"@": path.resolve("./src")
// 相对路径别名配置,使用 @ 代替 src
}
},
// 配置scss
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
additionalData: '@import "./src/styles/variable.scss";'
}
}
}
};
};
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxwcm9qZWN0XFxcXFRlYWNoaW5nX2ludGVncmF0aW9uX3BsYXRmb3JtX2FkbWluX3RlbXBsYXRlXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJDOlxcXFxwcm9qZWN0XFxcXFRlYWNoaW5nX2ludGVncmF0aW9uX3BsYXRmb3JtX2FkbWluX3RlbXBsYXRlXFxcXHZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9DOi9wcm9qZWN0L1RlYWNoaW5nX2ludGVncmF0aW9uX3BsYXRmb3JtX2FkbWluX3RlbXBsYXRlL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHZ1ZSBmcm9tICdAdml0ZWpzL3BsdWdpbi12dWUnXHJcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnXHJcbi8vIFx1NUJGQ1x1NTE2NW1vY2tcdTYzRDJcdTRFRjZcclxuaW1wb3J0IHsgdml0ZU1vY2tTZXJ2ZSB9IGZyb20gJ3ZpdGUtcGx1Z2luLW1vY2snXHJcbi8vIFx1NUJGQ1x1NTE2NXN2Z1x1OTE0RFx1N0Y2RVx1NjNEMlx1NEVGNlxyXG5pbXBvcnQgeyBjcmVhdGVTdmdJY29uc1BsdWdpbiB9IGZyb20gJ3ZpdGUtcGx1Z2luLXN2Zy1pY29ucydcclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgKHsgY29tbWFuZCB9OiBhbnkpID0+IHtcclxuICByZXR1cm4ge1xyXG4gICAgcGx1Z2luczogW1xyXG4gICAgICB2dWUoKSxcclxuICAgICAgdml0ZU1vY2tTZXJ2ZSh7XHJcbiAgICAgICAgZW5hYmxlOiBjb21tYW5kID09PSAnc2VydmUnLFxyXG4gICAgICB9KSxcclxuICAgICAgY3JlYXRlU3ZnSWNvbnNQbHVnaW4oe1xyXG4gICAgICAgIC8vIFNwZWNpZnkgdGhlIGljb24gZm9sZGVyIHRvIGJlIGNhY2hlZFxyXG4gICAgICAgIGljb25EaXJzOiBbcGF0aC5yZXNvbHZlKHByb2Nlc3MuY3dkKCksICdzcmMvYXNzZXRzL2ljb25zJyldLFxyXG4gICAgICAgIC8vIFNwZWNpZnkgc3ltYm9sSWQgZm9ybWF0XHJcbiAgICAgICAgc3ltYm9sSWQ6ICdpY29uLVtkaXJdLVtuYW1lXScsXHJcbiAgICAgIH0pLFxyXG4gICAgXSxcclxuICAgIHJlc29sdmU6IHtcclxuICAgICAgYWxpYXM6IHtcclxuICAgICAgICAnQCc6IHBhdGgucmVzb2x2ZSgnLi9zcmMnKSwgLy8gXHU3NkY4XHU1QkY5XHU4REVGXHU1Rjg0XHU1MjJCXHU1NDBEXHU5MTREXHU3RjZFXHVGRjBDXHU0RjdGXHU3NTI4IEAgXHU0RUUzXHU2NkZGIHNyY1xyXG4gICAgICB9LFxyXG4gICAgfSxcclxuICAgIC8vIFx1OTE0RFx1N0Y2RXNjc3NcclxuICAgIGNzczoge1xyXG4gICAgICBwcmVwcm9jZXNzb3JPcHRpb25zOiB7XHJcbiAgICAgICAgc2Nzczoge1xyXG4gICAgICAgICAgamF2YXNjcmlwdEVuYWJsZWQ6IHRydWUsXHJcbiAgICAgICAgICBhZGRpdGlvbmFsRGF0YTogJ0BpbXBvcnQgXCIuL3NyYy9zdHlsZXMvdmFyaWFibGUuc2Nzc1wiOycsXHJcbiAgICAgICAgfSxcclxuICAgICAgfSxcclxuICAgIH0sXHJcbiAgfVxyXG59XHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFDQSxPQUFPLFNBQVM7QUFDaEIsT0FBTyxVQUFVO0FBRWpCLFNBQVMscUJBQXFCO0FBRTlCLFNBQVMsNEJBQTRCO0FBRXJDLElBQU8sc0JBQVEsQ0FBQyxFQUFFLFFBQVEsTUFBVztBQUNuQyxTQUFPO0FBQUEsSUFDTCxTQUFTO0FBQUEsTUFDUCxJQUFJO0FBQUEsTUFDSixjQUFjO0FBQUEsUUFDWixRQUFRLFlBQVk7QUFBQSxNQUN0QixDQUFDO0FBQUEsTUFDRCxxQkFBcUI7QUFBQTtBQUFBLFFBRW5CLFVBQVUsQ0FBQyxLQUFLLFFBQVEsUUFBUSxJQUFJLEdBQUcsa0JBQWtCLENBQUM7QUFBQTtBQUFBLFFBRTFELFVBQVU7QUFBQSxNQUNaLENBQUM7QUFBQSxJQUNIO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxPQUFPO0FBQUEsUUFDTCxLQUFLLEtBQUssUUFBUSxPQUFPO0FBQUE7QUFBQSxNQUMzQjtBQUFBLElBQ0Y7QUFBQTtBQUFBLElBRUEsS0FBSztBQUFBLE1BQ0gscUJBQXFCO0FBQUEsUUFDbkIsTUFBTTtBQUFBLFVBQ0osbUJBQW1CO0FBQUEsVUFDbkIsZ0JBQWdCO0FBQUEsUUFDbEI7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRjsiLAogICJuYW1lcyI6IFtdCn0K
Loading…
Cancel
Save