提交 1ff6e7fe authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 d9677ec0
......@@ -2,7 +2,7 @@ import httpRequest from '@/utils/axios'
// 获取用户信息
export function getUser() {
return httpRequest.get('/api/resource/v1/util/info')
return httpRequest.get('/api/lab/v1/common/permission/role')
}
// 退出登录
......
......@@ -77,5 +77,9 @@ textarea:focus {
}
:root {
--main-color: #aa1941;
--main-color: #ba143e;
}
.el-dialog__header {
margin-right: 0 !important;
border-bottom: 1px solid #e6e6e6;
}
......@@ -3,7 +3,7 @@
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': #aa1941
'base': #ba143e
)
),
$dialog: (
......
......@@ -20,10 +20,10 @@ defineProps<{ title?: string }>()
<style lang="scss">
.app-card {
background: #fff;
box-shadow: 0 1px 6px 0 rgb(228 232 235 / 20%);
border-radius: 6px;
padding: 20px;
// background: #fff;
// box-shadow: 0 1px 6px 0 rgb(228 232 235 / 20%);
// border-radius: 6px;
// padding: 20px;
}
.app-card + .app-card {
margin-top: 20px;
......@@ -33,11 +33,11 @@ defineProps<{ title?: string }>()
}
.app-card-hd__title {
flex: 1;
padding-left: 5px;
font-size: 18px;
font-weight: 500;
margin-bottom: 20px;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
line-height: 1;
margin-bottom: 24px;
border-left: 3px solid #aa1941;
color: #333333;
}
</style>
......@@ -224,10 +224,13 @@ defineExpose({ refetch, tableRef })
.table-list-hd {
display: flex;
margin-bottom: 10px;
margin-bottom: 20px;
}
.table-list-filter {
flex: 1;
padding: 30px 30px 10px;
background: #fff;
border-radius: 12px;
}
.table-list-buttons {
margin-bottom: 20px;
......
......@@ -13,7 +13,8 @@ const route = useRoute()
const userStore = useUserStore()
const userInfo = userStore.user
const menus = useMenuStore().menus
const menuStore = useMenuStore()
const menus = $computed(() => menuStore.menus)
const defaultActive = computed(() => {
// 扁平菜单
......@@ -126,17 +127,23 @@ const logout = async () => {
--el-menu-item-height: 63px;
--el-menu-item-font-size: 16px;
.el-menu--horizontal {
font-weight: 500;
border-bottom: 0;
> .el-menu-item {
margin-left: 40px;
padding: 0;
background-color: unset !important;
border-bottom: 0 !important;
}
}
.el-sub-menu__title {
margin-left: 40px;
padding: 0;
border-bottom: 0 !important;
}
.el-sub-menu__icon-arrow {
display: none;
}
.el-sub-menu {
font-weight: 500;
}
}
.sub-menu-popper {
border: 0 !important;
......
......@@ -7,22 +7,37 @@ defineProps<Props>()
<template>
<div class="list-item">
<div class="icon"></div>
<div class="list-item-main">
<div class="list-item-hd">
<div class="icon-file"></div>
<div class="button-group">
<div class="button icon-view">
<router-link :to="`/admin/lab/book/${data.id}`" target="_blank"></router-link>
</div>
<div class="button icon-edit" @click="$emit('clickEdit', data)"></div>
</div>
</div>
<div class="list-item-title">
<h2>{{ data.name }}</h2>
<h6>{{ data.status_name }}</h6>
<p>文件大小:{{ data.size_name }}</p>
<p>观看次数:{{ data.pv }}</p>
<p>创建人:{{ data.created_operator_name }}</p>
<p>创建时间:{{ data.created_time }}</p>
</div>
<div class="cover">
<div class="cover-inner">
<router-link :to="`/admin/lab/book/${data.id}`" target="_blank">
<el-button type="primary" round plain auto-insert-space>查看</el-button>
</router-link>
<el-button type="primary" round plain auto-insert-space @click="$emit('clickEdit', data)">编辑</el-button>
</div>
<div class="list-item-main">
<ol>
<li>
<p>文件大小:{{ data.size_name }}</p>
</li>
<li>
<p>观看次数:{{ data.pv }}</p>
</li>
<li>
<p>创建人:{{ data.created_operator_name }}</p>
</li>
<li>
<p>创建时间:{{ data.created_time }}</p>
</li>
<li>
<p>更新时间:{{ data.updated_time }}</p>
</li>
</ol>
</div>
</div>
</template>
......@@ -30,39 +45,85 @@ defineProps<Props>()
<style lang="scss" scoped>
.list-item {
position: relative;
display: flex;
padding: 20px;
background: #f2f2f2;
border-radius: 5px;
overflow: hidden;
padding: 30px;
background: linear-gradient(to bottom left, rgba(186, 20, 62, 0.1) 0%, rgba(255, 255, 255, 0.8) 60%);
border-radius: 12px;
&:hover {
.cover {
display: block;
box-shadow: 0 3px 18px rgba(0, 0, 0, 0.27);
}
.list-item-hd {
display: flex;
justify-content: space-between;
.button-group {
display: flex;
.button {
a {
display: block;
width: 100%;
height: 100%;
}
}
.button + .button {
margin-left: 12px;
}
}
}
.icon-file {
width: 30px;
height: 30px;
background: url(@/assets/images/icon_word.png) no-repeat;
background-size: contain;
}
.icon-edit {
width: 16px;
height: 16px;
background: url(@/assets/images/icon_edit.png) no-repeat;
background-size: contain;
cursor: pointer;
}
.icon-view {
width: 16px;
height: 16px;
background: url(@/assets/images/icon_view.png) no-repeat;
background-size: contain;
cursor: pointer;
}
.list-item-main {
line-height: 30px;
}
.cover {
display: none;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
.el-button {
width: 130px;
margin: 10px;
ol {
margin-left: 20px;
}
li {
margin-top: 24px;
font-size: 16px;
line-height: 1;
color: var(--main-color);
list-style: disc;
}
p {
color: #666;
}
}
.cover-inner {
width: 100%;
height: 100%;
.list-item-title {
margin: 26px 0 36px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
justify-content: space-between;
h2 {
flex: 1;
font-size: 18px;
font-family: Source Han Sans CN;
font-weight: bold;
line-height: 20px;
color: #333333;
}
h6 {
margin-left: 20px;
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 500;
line-height: 20px;
color: var(--main-color);
}
}
}
</style>
<script setup lang="ts">
import type { BookItem } from '../types'
import { CirclePlusFilled } from '@element-plus/icons-vue'
import { CirclePlus } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue'
import ListItem from '../components/ListItem.vue'
import { getBookList } from '../api'
......@@ -11,13 +11,15 @@ const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.v
const { experiments, updateExperiments } = useGetExperimentList()
updateExperiments()
const route = useRoute()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getBookList,
params: { name: '', experiment_id: '' }
params: { name: '', experiment_id: route.query.experiment_id || '' }
},
filters: [
{ type: 'input', prop: 'name', label: '实验指导书名称', placeholder: '请输入实验指导书名称' },
......@@ -57,7 +59,7 @@ function onUpdateSuccess() {
<AppCard title="实验指导书管理">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="CirclePlusFilled" @click="handleAdd">新增实验指导书</el-button>
<el-button type="primary" :icon="CirclePlus" @click="handleAdd">新增实验指导书</el-button>
</template>
<template #body="{ data }">
<div class="list-card" v-if="data.length">
......
<script setup lang="ts">
import { VideoPlay } from '@element-plus/icons-vue'
interface Props {
data: any
}
......@@ -7,16 +8,19 @@ defineProps<Props>()
<template>
<div class="list-item">
<div class="list-item-pic">
<router-link :to="`/admin/lab/video/${data.id}`" target="_blank">
<img :src="data.cover" />
<el-icon><VideoPlay /></el-icon>
</router-link>
</div>
<div class="list-item-main">
<img :src="data.cover" />
<p>{{ data.name }}</p>
</div>
<div class="cover">
<div class="cover-inner">
<div class="buttons">
<router-link :to="`/admin/lab/video/${data.id}`" target="_blank">
<el-button type="primary" round plain auto-insert-space>查看</el-button>
<el-button type="primary" auto-insert-space>查看</el-button>
</router-link>
<el-button type="primary" round plain auto-insert-space @click="$emit('clickEdit', data)">编辑</el-button>
<el-button plain auto-insert-space @click="$emit('clickEdit', data)" style="margin-left: 10px">编辑</el-button>
</div>
</div>
</div>
......@@ -25,57 +29,65 @@ defineProps<Props>()
<style lang="scss" scoped>
.list-item {
position: relative;
display: flex;
background: #f2f2f2;
border-radius: 5px;
height: 185px;
transition: all 0.25s ease-in-out;
border-radius: 6px;
overflow: hidden;
&:hover {
.cover {
overflow: unset;
transform: scale(1.2);
z-index: 1;
.el-icon {
display: block;
}
}
.list-item-main {
position: relative;
width: 100%;
img {
width: 100%;
height: 200px;
object-fit: cover;
.list-item-main {
top: 100%;
bottom: unset;
padding: 20px;
background-color: #fff;
p {
color: #000;
}
}
p {
position: absolute;
left: 0;
bottom: 0;
right: 0;
line-height: 40px;
color: #fff;
padding: 0 10px;
background-color: rgba(0, 0, 0, 0.5);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
.buttons {
display: block;
}
}
.cover {
}
.list-item-pic {
position: relative;
height: 185px;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.el-icon {
display: none;
font-size: 40px;
color: #fff;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
.el-button {
width: 130px;
margin: 10px;
}
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.cover-inner {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.list-item-main {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 10px 16px;
p {
line-height: 1;
color: #fff;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.buttons {
margin-top: 16px;
display: none;
}
}
</style>
<script setup lang="ts">
import type { VideoItem } from '../types'
import { CirclePlusFilled } from '@element-plus/icons-vue'
import { CirclePlus } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue'
import ListItem from '../components/ListItem.vue'
import { getVideoList } from '../api'
......@@ -11,13 +11,15 @@ const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.v
const { experiments, updateExperiments } = useGetExperimentList()
updateExperiments()
const route = useRoute()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getVideoList,
params: { name: '', experiment_id: '' }
params: { name: '', experiment_id: route.query.experiment_id || '' }
},
filters: [
{ type: 'input', prop: 'name', label: '实验操作视频名称', placeholder: '请输入实验操作视频名称' },
......@@ -57,7 +59,7 @@ function onUpdateSuccess() {
<AppCard title="实验操作视频管理">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="CirclePlusFilled" @click="handleAdd">新增操作视频</el-button>
<el-button type="primary" :icon="CirclePlus" @click="handleAdd">新增操作视频</el-button>
</template>
<template #body="{ data }">
<div class="list-card" v-if="data.length">
......@@ -73,7 +75,7 @@ function onUpdateSuccess() {
<style lang="scss" scoped>
.list-card {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(5, 1fr);
gap: 20px;
}
</style>
<script setup lang="ts">
import type { ExperimentItem, ClassItem, GroupItem } from '../types'
import { CirclePlusFilled } from '@element-plus/icons-vue'
import { CirclePlus } from '@element-plus/icons-vue'
import StudentGroupFormDialog from './StudentGroupFormDialog.vue'
import AppList from '@/components/base/AppList.vue'
import { getExperimentClassGroupsList } from '../api'
......@@ -46,7 +46,7 @@ function handleRefetch() {
</el-descriptions>
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="CirclePlusFilled" @click="dialogVisible = true">新增分组</el-button>
<el-button type="primary" round :icon="CirclePlus" @click="dialogVisible = true">新增分组</el-button>
</template>
<template #table-x="{ row }">
<el-button type="primary" link>
......
<script setup lang="ts">
import type { GroupItem, StudentItem } from '../types'
import { CirclePlusFilled } from '@element-plus/icons-vue'
import { CirclePlus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import AppList from '@/components/base/AppList.vue'
......@@ -71,9 +71,7 @@ function handleRemoveStudent(row: StudentItem) {
</el-descriptions>
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="CirclePlusFilled" @click="selectStudentVisible = true"
>添加小组成员</el-button
>
<el-button type="primary" round :icon="CirclePlus" @click="selectStudentVisible = true">添加小组成员</el-button>
</template>
<template #table-x="{ row }">
<el-button type="primary" round @click="handleRemoveStudent(row)">移除</el-button>
......
<script setup lang="ts">
import type { ExperimentItem } from '../types'
import { CirclePlusFilled } from '@element-plus/icons-vue'
import { CirclePlus } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue'
import { getExperimentList } from '../api'
......@@ -60,7 +60,7 @@ function onUpdateSuccess() {
<AppCard title="实验管理">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="CirclePlusFilled" @click="handleAdd">新增实验</el-button>
<el-button type="primary" :icon="CirclePlus" @click="handleAdd">新增实验</el-button>
</template>
<template #table-x="{ row }: { row: ExperimentItem }">
......
<script setup lang="ts">
import type { ExperimentItem, ClassItem } from '../types'
import { CirclePlusFilled } from '@element-plus/icons-vue'
import { CirclePlus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import AppList from '@/components/base/AppList.vue'
......@@ -81,7 +81,7 @@ function handleRemoveClass(row: ClassItem) {
</el-descriptions>
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="CirclePlusFilled" @click="selectClassVisible = true">关联班级</el-button>
<el-button type="primary" :icon="CirclePlus" @click="selectClassVisible = true">关联班级</el-button>
</template>
<template #table-x="{ row }">
<el-button type="primary" round @click="handleViewStudent(row)">查看学生</el-button>
......
......@@ -4,3 +4,8 @@ import httpRequest from '@/utils/axios'
export function getTopInfo() {
return httpRequest.get('/api/lab/v1/common/permission/top')
}
// 获取实验列表
export function getExperimentList() {
return httpRequest.get('/api/lab/v1/teacher/index/index')
}
<script setup lang="ts"></script>
<script setup lang="ts">
import type { ExperimentItem } from '../types'
import { getExperimentList } from '../api'
const router = useRouter()
let list = $ref<ExperimentItem[]>([])
function fetchList() {
getExperimentList().then(res => {
list = res.data.list
})
}
onMounted(() => {
fetchList()
})
function handleChange(id: string, type: number) {
if (type === 1) {
router.push({ path: '/admin/lab/book', query: { experiment_id: id } })
} else if (type === 2) {
router.push({ path: '/admin/lab/video', query: { experiment_id: id } })
}
}
</script>
<template>
<div class="bg"></div>
<el-select placeholder="实验指导书"> </el-select>
<el-select placeholder="实验视频"> </el-select>
<el-select placeholder="实验答疑"> </el-select>
<el-select placeholder="实验成绩"> </el-select>
<el-select placeholder="大赛评分"> </el-select>
<div class="select-group">
<el-select size="large" placeholder="实验指导书" @change="handleChange($event, 1)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select size="large" placeholder="实验视频" @change="handleChange($event, 2)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<!-- <el-select size="large" placeholder="实验答疑" @change="handleChange($event, 3)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select size="large" placeholder="实验成绩" @change="handleChange($event, 4)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select size="large" placeholder="大赛评分" @change="handleChange($event, 5)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select> -->
</div>
</template>
<style lang="scss" scoped>
.bg {
margin: 40px;
margin: 20px 0;
background: url(@/assets/images/home_bg.png) no-repeat center center;
background-size: contain;
height: 636px;
}
.select-group {
position: fixed;
bottom: 40px;
.el-select {
width: 144px;
:deep(.el-input__wrapper) {
box-shadow: none;
}
}
.el-select + .el-select {
margin-left: 20px;
}
}
</style>
<script setup lang="ts"></script>
<script setup lang="ts">
import type { ExperimentItem } from '../types'
import { getExperimentList } from '../api'
const router = useRouter()
let list = $ref<ExperimentItem[]>([])
function fetchList() {
getExperimentList().then(res => {
list = res.data.list
})
}
onMounted(() => {
fetchList()
})
function handleChange(id: string, type: number) {
if (type === 1) {
router.push({ path: '/admin/lab/book', query: { experiment_id: id } })
} else if (type === 2) {
router.push({ path: '/admin/lab/video', query: { experiment_id: id } })
}
}
</script>
<template>
<div class="bg"></div>
<el-select placeholder="我的实验课程"> </el-select>
<el-select placeholder="我的陪练项目"> </el-select>
<el-select placeholder="我的大赛项目"> </el-select>
<el-select placeholder="我的大赛成绩"> </el-select>
<div class="select-group">
<el-select size="large" placeholder="我的实验课程" @change="handleChange($event, 1)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select size="large" placeholder="我的陪练项目" @change="handleChange($event, 2)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select size="large" placeholder="我的大赛项目" @change="handleChange($event, 3)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-select size="large" placeholder="我的大赛成绩" @change="handleChange($event, 4)">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</div>
</template>
<style lang="scss" scoped>
.bg {
margin: 20px 0;
background: url(@/assets/images/home_bg.png) no-repeat center center;
background-size: contain;
height: 636px;
}
.select-group {
position: fixed;
bottom: 40px;
.el-select {
width: 144px;
:deep(.el-input__wrapper) {
box-shadow: none;
}
}
.el-select + .el-select {
margin-left: 20px;
}
}
</style>
export interface ExperimentItem {
id: string
name: string
}
<script setup lang="ts">
import Total from '../components/Total.vue'
// import StudentHome from '../components/StudentHome.vue'
import AdminHome from '../components/AdminHome.vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const StudentHome = defineAsyncComponent(() => import('../components/StudentHome.vue'))
const AdminHome = defineAsyncComponent(() => import('../components/AdminHome.vue'))
</script>
<template>
<Total></Total>
<AdminHome></AdminHome>
<div class="home">
<Total></Total>
<AdminHome v-if="userStore.role?.id === 5"></AdminHome>
<StudentHome v-else></StudentHome>
</div>
</template>
<style lang="scss" scoped>
.home {
margin: 0 30px;
}
</style>
import type { IMenuItem } from '@/types'
import { defineStore } from 'pinia'
import { Notebook, VideoCamera, ChatSquare, Finished, ScaleToOriginal } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
interface State {
studentMenus: IMenuItem[]
......@@ -13,6 +14,14 @@ const studentMenus: IMenuItem[] = [
name: '智能营销',
path: '/student/lab'
},
{
name: '智能陪练',
path: '/student/ai'
},
{
name: '成绩分析',
path: '/admin/contest/score'
},
{
name: '技能大赛',
path: '/student/contest'
......@@ -46,14 +55,18 @@ const adminMenus: IMenuItem[] = [
}
]
},
// {
// name: '技能大赛',
// path: '/admin/contest'
// },
// {
// name: '大赛成绩',
// path: '/admin/contest/score'
// },
{
name: '智能陪练',
path: '/admin/ai'
},
{
name: '成绩分析',
path: '/admin/contest/score'
},
{
name: '技能大赛',
path: '/admin/contest'
},
{
name: '系统管理',
path: '/admin/system',
......@@ -74,7 +87,10 @@ export const useMenuStore = defineStore({
adminMenus
}),
getters: {
menus: state => state.adminMenus
menus: state => {
const userStore = useUserStore()
return userStore.role?.id === 5 ? state.adminMenus : state.studentMenus
}
},
actions: {}
})
......@@ -2,8 +2,14 @@ import { defineStore } from 'pinia'
import { getUser, logout } from '@/api/base'
import type { UserType, ProjectType, OrganizationType, RoleType, PermissionType } from '@/types'
// 角色信息(1学员;5教师)
interface Role {
id: 1 | 5
name: string
}
interface State {
user: UserType | null
role: Role | null
project: ProjectType | null
organization: OrganizationType | null
roles: RoleType[]
......@@ -14,6 +20,7 @@ export const useUserStore = defineStore({
id: 'user',
state: (): State => ({
user: null,
role: null,
organization: null,
project: null,
roles: [],
......@@ -25,9 +32,10 @@ export const useUserStore = defineStore({
actions: {
async getUser() {
const res = await getUser()
const { info } = res.data
const { organization, project, roles, permissions } = res.data.permissions
const { info, role } = res.data
const { organization, project, roles = [], permissions = [] } = res.data.permissions || {}
this.user = info
this.role = role
this.organization = organization
this.project = project
this.roles = roles
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论