提交 a1956e7e authored 作者: lhh's avatar lhh

update

上级 6eb32f0e
......@@ -20,7 +20,8 @@ import {
QuestionFilled,
EditPen,
DataAnalysis,
ChatDotRound
ChatDotRound,
Calendar
} from '@element-plus/icons-vue'
export const menus: IMenuItem[] = [
{
......@@ -28,6 +29,12 @@ export const menus: IMenuItem[] = [
name: '资源管理',
path: '/resource',
children: [
{
// tag: 'v1-resource-video-list',
icon: Calendar,
name: '课程表',
path: '/resource/timetable'
},
{
tag: 'v1-resource-video-list',
icon: VideoCamera,
......
......@@ -30,11 +30,16 @@ function genNavClassName(data: IMenuItem) {
<header class="app-header">
<div class="app-header-left">
<div class="logo">
<router-link to="/"><img src="https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo-white.svg" /></router-link>
<router-link to="/"
><img style="height: 60px;" src="https://zws-imgs-pub.oss-cn-beijing.aliyuncs.com/pc/base/logo%E5%89%AF%E6%9C%AC2.png"
/></router-link>
</div>
<h1 class="app-name">统一资源管理平台</h1>
<h1 class="app-name">运维管理系统</h1>
</div>
<div class="app-header-nav">
<div class="app-header-nav-item">
<a href="https://xjjt.obe.ezjing.com" target="_blank">人才培养</a>
</div>
<div
class="app-header-nav-item"
v-for="(item, index) in menus"
......@@ -84,7 +89,7 @@ function genNavClassName(data: IMenuItem) {
background-color: var(--main-color);
color: #fff;
.logo {
width: 120px;
// width: 120px;
}
}
.app-header-left {
......
......@@ -40,7 +40,11 @@ const form: any = reactive({
mobile: '',
role: [],
email: '',
status: '1'
status: '1',
age: '',
education: '',
title: '',
work_experience: ''
})
const rules = reactive<FormRules>({
......@@ -130,6 +134,34 @@ if (props.isEdit === '2' || props.isEdit === '1') {
<el-radio v-for="(item, index) in sexList" :key="index" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="年龄">
<el-input v-model="form.age" :disabled="props.isEdit === '2' || props.isEdit === '1'"></el-input>
</el-form-item>
<el-form-item label="学历">
<el-select
style="width: 100%"
v-model="form.education"
placeholder="请选择"
:disabled="props.isEdit === '2' || props.isEdit === '1'"
>
<el-option key="博士" label="博士" value="博士" />
<el-option key="硕士" label="硕士" value="硕士" />
<el-option key="本科" label="本科" value="本科" />
<el-option key="专科" label="专科" value="专科" />
</el-select>
</el-form-item>
<el-form-item label="职称">
<el-select
style="width: 100%"
v-model="form.title"
placeholder="请选择"
:disabled="props.isEdit === '2' || props.isEdit === '1'"
>
<el-option key="初级" label="初级" value="初级" />
<el-option key="中级" label="中级" value="中级" />
<el-option key="高级" label="高级" value="高级" />
</el-select>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="form.mobile" :disabled="props.isEdit === '2' || props.isEdit === '1'"></el-input>
</el-form-item>
......@@ -141,6 +173,9 @@ if (props.isEdit === '2' || props.isEdit === '1') {
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" :disabled="props.isEdit === '2'" />
</el-form-item>
<el-form-item label="工作经历">
<el-input type="textarea" v-model="form.work_experience" :disabled="props.isEdit === '2'" />
</el-form-item>
<el-form-item label="生效状态" prop="status">
<el-radio-group v-model="form.status" :disabled="props.isEdit === '2'">
<el-radio v-for="(item, index) in statusList" :key="index" :label="item.value">{{ item.label }}</el-radio>
......
......@@ -27,7 +27,9 @@ const form: any = reactive({
organ_id: '',
specialty_id: '',
class_id: '',
status: '1'
status: '1',
address: '',
join_time: ''
})
const rules = reactive<FormRules>({
sno_number: [{ required: true, message: '请输入学号', trigger: 'blur' }],
......@@ -196,6 +198,21 @@ if (props.isEdit === '2' || props.isEdit === '1') {
<el-form-item label="证件号码" prop="id_number">
<el-input v-model="form.id_number" :disabled="props.isEdit === '2'" />
</el-form-item>
<el-form-item label="家庭住址">
<el-input v-model="form.address" :disabled="props.isEdit === '2'" />
</el-form-item>
<el-form-item label="入学时间">
<!-- <el-input v-model="form.join_time" :disabled="props.isEdit === '2'" /> -->
<el-date-picker
style="width: 100%"
v-model="form.join_time"
type="month"
format="YYYY/MM"
placeholder="请选择"
:disabled="props.isEdit === '2'"
/>
<!-- <el-date-picker v-model="form.join_time" type="datetime" :disabled="props.isEdit === '2'" placeholder="请选择" format="YYYY/MM/DD HH:mm:ss" /> -->
</el-form-item>
<el-form-item label="所属部门/学校" prop="organ_id">
<el-select
clearable
......
......@@ -4,8 +4,3 @@ import httpRequest from '@/utils/axios'
export function getUtilData() {
return httpRequest.get('/api/resource/v1/util/index')
}
export function aa() {
return httpRequest.get('/api/otn/leftTicket/queryZ?leftTicketDTO.train_date=2023-04-28&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=DTV&purpose_codes=ADULT')
}
// https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2023-04-28&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=DTV&purpose_codes=ADULT
\ No newline at end of file
<script setup lang="ts">
import { aa } from '../api'
aa().then(res => {
console.log(res, 'res')
import Statistics from '../components/Statistics.vue'
import Newest from '../components/Newest.vue'
import Hottest from '../components/Hottest.vue'
import RecentLearning from '../components/RecentLearning.vue'
import { getUtilData } from '../api'
let flag = $ref(false)
const data = reactive<{ statistics: object[]; latest: object[]; hot: object[]; learn: object[] }>({
statistics: [],
latest: [],
hot: [],
learn: []
})
// 所有数据
getUtilData().then(res => {
flag = true
let { latest = [], hot = [], learn = [] }: { latest: object[]; hot: object[]; learn: object[] } = res.data
if (learn === null) learn = []
// 统计资源数据整合
const statisticsData = [
{ key: 'video_count', unit: '个', number: 0, text: '视频资源' },
{ key: 'courseware_count', unit: '个', number: 0, text: '课件资源' },
{ key: 'other_information_count', unit: '个', number: 0, text: '其他资料' },
{ key: 'question_count', unit: '个', number: 0, text: '题库资源' },
{ key: 'paper_count', unit: '套', number: 0, text: '试卷资源' },
{ key: 'memory_size', unit: '', number: 0, text: '资源总体量' },
{ key: 'length', unit: '小时', number: 0, text: '资源总时长' },
{ key: 'learn_times', unit: '人次', number: 0, text: '总学习人次' },
{ key: 'learn_time_length', unit: '小时', number: 0, text: '总学习时长' }
]
data.statistics = statisticsData.map((item: any) => {
item.number = res.data[item.key] || 0
return item
})
// 最近数据
data.latest =
latest.map((item: any, index: number) => {
item.index = index + 1
return item
}) || []
// 最热资源
data.hot =
hot.map((item: any, index: number) => {
item.index = index + 1
return item
}) || []
data.learn =
learn.map((item: any, index: number) => {
item.index = index + 1
return item
}) || []
})
// aa().then(res => {
// console.log(res, 'res')
// })
</script>
<template>
<div>111</div>
<div v-if="flag">
<AppCard title="资源统计">
<Statistics :data="data.statistics"></Statistics>
</AppCard>
<AppCard title="最新资源">
<Newest :data="data.latest"></Newest>
</AppCard>
<AppCard :data="data.hot" title="最热资源">
<Hottest :data="data.hot"></Hottest>
</AppCard>
<AppCard title="最近学习">
<RecentLearning :data="data.learn"></RecentLearning>
</AppCard>
</div>
</template>
import httpRequest from '@/utils/axios'
// 获取课程表列表
export function getTimetableList(params: { tab: string; status?: string; authorized?: string; classification?: string; page?: number; page_size?: number }) {
return httpRequest.get('/api/resource/v1/resource/school-timetable/list', { params })
}
// 创建课程表
export function createTimetable(data: any) {
return httpRequest.post('/api/resource/v1/resource/school-timetable/create', data)
}
// 获取学期
export function getSemestersList() {
return httpRequest.get('/api/resource/v1/resource/school-timetable/semesters')
}
// 获取课程表详情
export function getTimetableDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/school-timetable/view', { params })
}
// 更新课程表
export function updateTimetable(data: { id: string; name: string; classification: string; knowledge_points?: string; cover?: string }) {
return httpRequest.post('/api/resource/v1/resource/school-timetable/update', data)
}
// 部门共享
export function setDepartment(data: { id: string }) {
return httpRequest.post('/api/resource/v1/resource/school-timetable/set-department', data)
}
// 平台共享
export function setPlatform(data: { id: string }) {
return httpRequest.post('/api/resource/v1/resource/school-timetable/set-platform', data)
}
// 上下线
export function setStatus(data: { id: string }) {
return httpRequest.post('/api/resource/v1/resource/school-timetable/set-status', data)
}
// 更改负责人
export function setBelong(data: { id: string; belong_operator: string }) {
return httpRequest.post('/api/resource/v1/resource/school-timetable/set-belong', data)
}
<script setup lang="ts">
const props: any = defineProps<{ data: object; tabIndex: string }>()
</script>
<template>
<div class="card-item">
<div class="card-item-top">
<div class="title">{{ props.data.name }}</div>
<!-- <img :src="props.data.cover" /> -->
<div class="cover-img" :style="`background-image:url(${props.data.url})`"></div>
<div class="tool-pop-btn">
<div style="min-width: 100%">
<router-link v-if="props.data.auth_edit" :to="`/resource/timetable/update?id=${props.data.id}`">
<div class="edit-btn btn">编辑</div>
</router-link>
</div>
<div style="min-width: 100%">
<router-link v-if="props.data.auth_view" :to="`/resource/timetable/view?id=${props.data.id}`">
<div class="view-btn btn">查看</div>
</router-link>
</div>
</div>
</div>
<div class="card-item-bottom">
<div class="item-t">
<el-popover trigger="hover" placement="right">
<div v-html="props.data.created_operator_name + '/' + props.data.organ_id_name"></div>
<template #reference>
<div class="text">{{ props.data.created_operator_name }}/{{ props.data.organ_id_name }}</div>
</template>
</el-popover>
<div :class="props.data.status == 1 ? 'tag green' : 'tag'">{{ props.data.status_name }}</div>
</div>
<div class="item-b">
<div class="time">{{ props.data.updated_time }}</div>
<div class="tag1" v-if="tabIndex === '2'">
{{ props.data.department_public === '0' ? '未共享' : '已共享' }}
</div>
<div class="tag1" v-if="tabIndex === '3'">
{{ props.data.platform_public === '0' ? '未共享' : '已共享' }}
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.card-item {
width: 18%;
border-radius: 6px;
overflow: hidden;
margin-right: 10px;
margin-bottom: 20px;
.card-item-top {
height: 170px;
position: relative;
&:hover {
.tool-pop-btn {
opacity: 1;
}
}
.cover-img {
width: 100%;
height: 100%;
background-size: cover;
}
.title {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
line-height: 37px;
background: rgba(0, 0, 0, 0.4);
font-size: 18px;
color: #ffffff;
padding-left: 16px;
box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.tool-pop-btn {
transition: all 0.5s;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
opacity: 0;
padding-top: 52px;
box-sizing: border-box;
.edit-btn {
width: 113px;
line-height: 32px;
background: #aa1941;
border-radius: 18px;
text-align: center;
font-size: 14px;
color: #ffffff;
margin: 0 auto 12px;
cursor: pointer;
}
.btn:hover {
background: none;
border: 1px solid #d3d3d3;
}
.view-btn {
width: 113px;
line-height: 32px;
background: #aa1941;
border-radius: 18px;
text-align: center;
font-size: 14px;
color: #ffffff;
margin: 0 auto;
cursor: pointer;
}
}
}
}
.card-item-bottom {
background: #fff;
padding: 20px 16px 30px;
.item-t {
display: flex;
align-items: center;
justify-content: space-between;
.text {
width: 150px;
font-size: 16px;
line-height: 100%;
color: #666666;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.tag {
width: 48px;
line-height: 22px;
background: #aa1941;
border-radius: 11px;
font-size: 14px;
color: #fff;
text-align: center;
&.green {
background: #1ab226;
}
}
}
.item-b {
margin-top: 18px;
display: flex;
align-items: center;
justify-content: space-between;
.time {
font-size: 14px;
line-height: 100%;
color: #999999;
}
.tag1 {
font-size: 16px;
line-height: 100%;
color: #999999;
word-break: keep-all;
}
}
}
</style>
<script setup lang="ts">
import { setDepartment, setPlatform, setStatus, setBelong } from '../api'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useProjectList } from '@/composables/useGetProjectList'
const router = useRouter()
const route = useRoute()
const props = defineProps(['data'])
// 详情id
const id = route.query.id as string
// 设置部门共享
const handleDepartment = () => {
ElMessageBox.confirm(
`
${
parseInt(props.data.department_public) === 0
? `该操作将会使本课程表资源在您所在的部门“${props.data.organ_id_name}”内部共享,管理者不变,其余人员只能共享使用该资源,确认部门共享吗?`
: `该操作将会取消本课程表资源在您所在的部门“${props.data.organ_id_name}”内部共享,部门其余人员将不能再看到该共享资源,确认取消部门共享吗?`
}
`,
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
setDepartment({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
router.go(0)
}, 500)
}
})
})
}
// 设置平台共享
const handlePlatform = () => {
ElMessageBox.confirm(
`
${
parseInt(props.data.platform_public) === 0
? '该操作将会使本课程表资源在e-SaaS平台中公开共享供所有老师使用,资源的管理者不变,其余人员只能共享使用该资源,确认公开该资源吗?'
: '该操作将会取消本课程表资源在e-SaaS平台中公开共享,平台所有人员将不能再看到该共享资源,确认取消平台共享吗?'
}
`,
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
setPlatform({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
router.go(0)
}, 500)
}
})
})
}
// 上下线设置
const handleStatus = () => {
ElMessageBox.confirm(
`
${
parseInt(props.data.status) === 1
? '已下线的资源将不能被关联到课程使用,确认下线该资源吗?'
: '确认再次上线该资源吗?'
}
`,
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
setStatus({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
router.go(0)
}, 500)
}
})
})
}
// 更该负责人
const form = reactive({
members: ''
})
const dialogFormVisible = ref(false)
// 人员列表
let members: any = ref([])
// 点击更改负责人按钮弹窗
const handleMembers = () => {
dialogFormVisible.value = true
members.value = useProjectList(props.data.organ_id).members
}
// 更改负责人确定
const handlesetBelong = () => {
setBelong({ id: id, belong_operator: form.members }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
dialogFormVisible.value = false
}, 500)
}
})
}
</script>
<template>
<div class="tool-btn-box" v-if="$route.query.id" style="margin-bottom: 20px">
<template v-if="props.data.auth_edit">
<router-link :to="`/resource/timetable/update?id=${id}`">
<div class="btn-item">编辑课程表信息</div>
</router-link>
<!-- <router-link :to="`/resource/video/edit-courseware?id=${id}`">
<div class="btn-item">编辑课程表课件</div>
</router-link> -->
</template>
<div v-if="props.data.auth_department" class="btn-item" @click="handleDepartment">
{{ props.data.department_public == 0 ? '部门共享' : '取消部门共享' }}
</div>
<div v-if="props.data.auth_platform" class="btn-item" @click="handlePlatform">
{{ props.data.platform_public == 0 ? '平台共享' : '取消平台共享' }}
</div>
<div
v-if="props.data.auth_status"
v-permission="'v1-resource-video-set-status'"
class="btn-item"
@click="handleStatus"
>
{{ props.data.status == 0 ? '资源上线' : '资源下线' }}
</div>
<div v-if="props.data.auth_belong" class="btn-item" @click="handleMembers">更改负责人</div>
</div>
<el-dialog v-model="dialogFormVisible" title="更改负责人" center>
<el-form :model="form">
<el-form-item>
<div style="width: 500px">
<el-row>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
资源创建人: {{ props.data.created_operator_name }}
</el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple-light" />
创建时间: {{ props.data.created_time }}
</el-col>
</el-row>
<el-row>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
资源负责人: {{ props.data.belong_operator_name }}
</el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple-light" />
更新时间: {{ props.data.updated_time }}
</el-col>
</el-row>
</div>
<el-select style="width: 500px; margin-top: 20px" v-model="form.members" placeholder="请选择新的资源负责人">
<el-option v-for="item in members.value" :label="item.name" :value="item.id" :key="item.id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="handlesetBelong">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<style lang="scss" scoped>
.tool-btn-box {
display: flex;
justify-content: right;
.btn-item {
width: 127px;
line-height: 36px;
background: #aa1941;
border-radius: 20px;
margin-right: 10px;
font-size: 14px;
color: #ffffff;
text-align: center;
cursor: pointer;
}
}
</style>
<script lang="ts" setup>
interface UploadInfo {
bucket: string
checkpoint: { file: File; name: string; fileSize: number; partSize: number; uploadId: string }
endpoint: string
file: File
fileHash: string
isImage: boolean
loaded: number
object: string
region: string
retry: boolean
ri: string
state: string
userData: string
videoId: string
videoInfo: any
progress: number
}
interface Props {
videoList: UploadInfo[]
modelValue: boolean
}
interface Emits {
(e: 'update:modelValue', modelValue: boolean): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const currentPage = ref(1)
const page = reactive({
size: 10,
currentPage: 1
})
const handleCancel = function () {
emit('update:modelValue', false)
}
const handleSizeChange = (val: any) => {
page.size = val
}
const handleCurrentChange = (val: any) => {
page.currentPage = val
}
function percentage(value: number) {
return parseFloat((value ? value * 100 : 0).toFixed(2))
}
</script>
<template>
<el-dialog :model-value="props.modelValue" title="更多视频文件" :before-close="handleCancel" width="30vw">
<div
v-for="(item, index) in props.videoList.slice((page.currentPage - 1) * page.size, page.currentPage * page.size)"
:key="index"
>
<div class="video-info">
<span class="name">{{ item.file?.name }}</span>
<el-progress style="width: 200px" :percentage="percentage(item.loaded)" class="view" />
</div>
</div>
<el-pagination
v-model:currentPage="currentPage"
:page-size="page.size"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[5, 10, 20, 30, 50]"
:total="props.videoList.length"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</el-dialog>
</template>
<style lang="scss" scoped>
.video-info {
display: flex;
margin-top: 10px;
.name {
color: #333333;
font-size: 14px;
line-height: 1.5;
}
.view {
font-size: 16px;
line-height: 100%;
margin-left: 20px;
cursor: pointer;
}
}
:deep(.el-pagination) {
margin-top: 20px;
}
</style>
<script setup lang="ts">
import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue'
const props = defineProps(['data'])
const videoOptions = $computed(() => {
return {
sources: [
{
src: props.data.play_auth.play_info_list.find((item: any) => {
return item.Definition === 'SD'
}).PlayURL
}
]
}
})
</script>
<template>
<div class="center-video-box">
<div class="right-statistics">
<div class="stat-item">
<img src="https://webapp-pub.ezijing.com/center_resource/view-vicon1.png" />
<div class="content">
<div class="unit">{{ data.project_count }}<span></span></div>
<div class="tag">使用项目/学校</div>
</div>
</div>
<div class="stat-item">
<img src="https://webapp-pub.ezijing.com/center_resource/view-vicon2.png" />
<div class="content">
<div class="unit">{{ data.course_count }}<span></span></div>
<div class="tag">使用课程</div>
</div>
</div>
<div class="stat-item">
<img src="https://webapp-pub.ezijing.com/center_resource/view-vicon3.png" />
<div class="content">
<div class="unit">{{ data.learn_count }}<span>人次</span></div>
<div class="tag">累计学习人次</div>
</div>
</div>
<div class="stat-item">
<img src="https://webapp-pub.ezijing.com/center_resource/view-vicon4.png" />
<div class="content">
<div class="unit">{{ data.learn_time_count }}<span>分钟</span></div>
<div class="tag">累计学习时长</div>
</div>
</div>
</div>
<div style="width: 100%; background: #000">
<div style="margin: 0 auto; width: 80%">
<img :src="data?.url" style="width: 100%;">
<!-- <AppVideoPlayer :isAdd="true" :options="videoOptions"></AppVideoPlayer> -->
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.center-video-box {
padding: 20px 0;
.right-statistics {
margin: 0 auto;
display: flex;
padding-top: 15px;
justify-content: space-between;
.stat-item {
width: 24%;
height: 85px;
background: #b41e47;
border-radius: 6px;
display: flex;
align-items: center;
// margin: 0 auto 20px;
margin-bottom: 20px;
&:nth-child(even) {
background: #bf9d6b;
}
img {
width: 52px;
height: 52px;
display: block;
margin-left: 18px;
}
.content {
margin-left: 18px;
.unit {
font-size: 26px;
line-height: 100%;
color: #ffffff;
span {
font-size: 12px;
line-height: 100%;
}
}
.tag {
font-size: 14px;
line-height: 100%;
color: #ffffff;
margin-top: 10px;
}
}
}
}
}
</style>
<script setup lang="ts">
import { PictureFilled } from '@element-plus/icons-vue'
const props = defineProps(['data'])
</script>
<template>
<div class="video-info">
<div class="video-img">
<div
v-if="props.data.url"
class="img"
:style="`width: 210px;height: 140px;background-size: cover;background-image: url(${props.data.url});`"
></div>
<el-icon v-else :size="200" color="#ccc">
<PictureFilled></PictureFilled>
</el-icon>
<!-- <img :src="props.data.cover"> -->
<!-- <div class="name">视频封面</div> -->
</div>
<div class="info-items">
<div class="flex-box">
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon5.png" class="icons" />
<div class="text-box">
<div class="name">课程表名称</div>
<div class="value">{{ props.data.name }}</div>
</div>
</div>
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon7.png" class="icons" />
<div class="text-box">
<div class="name">课程表分类</div>
<div class="value">{{ props.data.classification_name || '暂无' }}</div>
</div>
</div>
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon1.png" class="icons" />
<div class="text-box">
<div class="name">状态</div>
<div :class="props.data.status == '1' ? 'value active' : 'value inactive'">
{{ props.data.status_name }}
</div>
</div>
</div>
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon3.png" class="icons" />
<div class="text-box">
<div class="name">创建时间</div>
<div class="value">{{ props.data.created_time }}</div>
</div>
</div>
</div>
<div class="flex-box">
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon12.png" class="icons" />
<div class="text-box">
<div class="name">创建者</div>
<div class="value">{{ props.data.created_operator_name }}</div>
</div>
</div>
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/course-view-icon13.png" class="icons" />
<div class="text-box">
<div class="name">负责人</div>
<div class="value">{{ props.data.belong_operator_name }}</div>
</div>
</div>
<div class="i-items" style="align-items: flex-start">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon8.png" class="icons" />
<div class="text-box">
<div class="name">知识点</div>
<div class="value">{{ props.data?.knowledge_points || '暂无' }}</div>
</div>
</div>
<div class="i-items">
<img src="https://webapp-pub.ezijing.com/center_resource/video-view-icon4.png" class="icons" />
<div class="text-box">
<div class="name">更新时间</div>
<div class="value">{{ props.data.updated_time }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.video-info {
background: #f7f7f7;
margin-top: 20px;
padding: 20px;
display: flex;
.video-img {
width: 211px;
text-align: center;
img {
width: 211px;
display: block;
}
.name {
font-size: 16px;
line-height: 100%;
color: #666666;
margin-top: 12px;
text-align: center;
}
}
.info-items {
display: flex;
flex-direction: column;
flex: 1;
padding: 12px 0;
.flex-box {
display: flex;
justify-content: space-around;
}
.i-items:not(:last-child) {
border-right: 1px dashed rgb(191, 191, 191);
}
.i-items {
width: 25%;
display: flex;
align-items: center;
height: fit-content;
margin-bottom: 30px;
padding-left: 5%;
.textarea-box {
padding: 18px 18px 51px;
background: #ffffff;
border-radius: 4px;
font-size: 16px;
line-height: 24px;
color: #505050;
margin-top: 14px;
min-width: 500px;
}
.text-box {
margin-left: 16px;
.name {
font-size: 14px;
line-height: 100%;
color: #999;
}
.value {
font-size: 14px;
line-height: 100%;
color: #333;
font-family: Source Han Sans CN;
margin-top: 8px;
word-break: break-all;
&.active {
color: #1ab226;
}
&.inactive {
color: #aa1941;
}
}
}
}
}
}
</style>
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
export const routes: Array<RouteRecordRaw> = [
{
path: '/resource/timetable',
component: AppLayout,
children: [
{ path: '', component: () => import('./views/List.vue') },
{ path: 'update', component: () => import('./views/Update.vue') },
{ path: 'view', component: () => import('./views/View.vue') }
]
}
]
<script setup lang="ts">
import { getTimetableList, getSemestersList } from '../api'
import CardListItem from '../components/CardListItem.vue'
import { PictureFilled } from '@element-plus/icons-vue'
import { useGetCategoryList } from '@/composables/useGetCategoryList'
import { useProjectList } from '@/composables/useGetProjectList'
import { useMapStore } from '@/stores/map'
import { useUserStore } from '@/stores/user'
// import BatchImportVideo from '../components/BatchImportVideo.vue'
// 学期
let semestersList: any = $ref([])
getSemestersList().then((res: any) => {
semestersList = res.data
})
// 判断当前用户是不是超级管理员
const user = useUserStore().roles
const isAdmin = !!user.find((item: any) => item.name === '超级管理员')
const store = useMapStore()
// const router = useRouter()
// 筛选部门列表
const departmentList: any = useProjectList('', '79806610719731712').departmentList
// 筛选下拉选择tree 视频分类
let { list: selectTree }: any = useGetCategoryList()
const defaultProps = {
children: 'children',
label: 'category_name',
value: 'id'
}
// 资源出处 tab触发
const tabValue = ref('1')
const appList = ref()
// const isShowBatchImport = ref(false)
const tabChange = () => {
appList.value.refetch()
}
// 列表切换
let isCard = $ref(true)
// table 数据
const listOptions = $computed(() => {
const columns = [
{
label: '课程表标题',
prop: 'name',
align: 'center',
computed({ row }: any) {
return row.name?.length > 10 ? `${row.name.slice(0, 10)}...` : row.name
}
},
{ label: '课程表分类', prop: 'classification_name', align: 'center' },
{ label: '知识点', prop: 'knowledge_points', align: 'center' },
// { label: '封面', slots: 'table-cover', width: 150, align: 'center' },
{ label: '资源状态', prop: 'status_name', align: 'center' },
{ label: '审核状态', prop: 'audit_status_name', align: 'center' },
{ label: '更新人', prop: 'updated_operator_name', align: 'center' },
{ label: '更新人部门', prop: 'organ_id_name', align: 'center' },
{ label: '更新日期', prop: 'updated_time', align: 'center' },
{ label: '操作', slots: 'table-operate', align: 'center', width: 200, fixed: 'right' }
]
if (tabValue.value === '2') {
columns.splice(2, 0, { label: '部门共享', prop: 'department_public_name', align: 'center' })
}
if (tabValue.value === '3') {
columns.splice(2, 0, { label: '平台共享', prop: 'platform_public_name', align: 'center' })
}
return {
remote: {
httpRequest: getTimetableList,
params: { tab: tabValue, status: '', authorized: '', name: '', semesters_id: '' }
},
filters: [
{
type: 'select',
prop: 'status',
label: '状态:',
options: store.getMapValuesByKey('system_status')
},
{ prop: 'classification', label: '类别:', slots: 'filter-type' },
{ type: 'input', prop: 'name', label: '标题:' },
{
type: 'select',
prop: 'semesters_id',
label: '学期:',
options: semestersList,
valueKey: 'id',
labelKey: 'name'
},
{
prop: 'authorized',
slots: 'filter-department'
}
],
columns
}
})
const typeFilter = () => {
appList.value.refetch()
}
const changeCard = () => {
isCard = !isCard
}
// 批量上传
const handleBatchImport = () => {
window.open('/resource/video/upload')
// router.push('/resource/video/upload')
// isShowBatchImport.value = true
}
</script>
<template>
<AppCard>
<div class="video-head">
<el-tabs @tab-change="tabChange" v-model="tabValue">
<el-tab-pane label="我的资源" name="1"></el-tab-pane>
<el-tab-pane label="部门资源" name="2"></el-tab-pane>
<el-tab-pane label="公开资源" name="3"></el-tab-pane>
</el-tabs>
<div class="video-head-icon" @click="changeCard">
<img v-if="!isCard" src="https://webapp-pub.ezijing.com/center_resource/change_card.png" alt="" />
<img v-else src="https://webapp-pub.ezijing.com/center_resource/change_list.png" alt="" />
</div>
</div>
<div class="video-tool-btn">
<router-link v-if="tabValue === '1'" to="/resource/timetable/update">
<el-button type="primary" round>新建课程表资源</el-button>
</router-link>
<!-- <el-button v-if="tabValue === '1'" type="primary" round @click="handleBatchImport" style="margin-left: 20px"
>批量上传</el-button
> -->
</div>
<AppList v-bind="listOptions" ref="appList">
<template #header-aside></template>
<template #table-cover="{ row }">
<div style="background: #ccc">
<el-icon :size="50" color="#fff" v-if="row.cover == ''">
<PictureFilled></PictureFilled>
</el-icon>
<div
v-else
:style="`width: 150px;
display: block;
height: 83px;
background-size: cover;
background-image: url(${row.cover});`"
></div>
</div>
</template>
<template #filter-type="{ params }">
<el-tree-select
node-key="id"
@change="typeFilter"
clearable
:props="defaultProps"
v-model="params.classification"
:data="selectTree"
:default-expanded-keys="selectTree.length ? [selectTree[0]?.id] : []"
/>
</template>
<template v-if="tabValue == '3' && isAdmin" #filter-department="{ params }">
<div class="name" style="font-size: 14px; color: #606266; padding-right: 12px">部门:</div>
<el-select @change="typeFilter" clearable v-model="params.authorized">
<el-option v-for="item in departmentList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</template>
<template #table-operate="{ row }">
<el-space>
<router-link v-if="row.auth_edit" :to="`/resource/timetable/update?id=${row.id}`">
<el-button plain>编辑</el-button>
</router-link>
<router-link :to="`/resource/timetable/view?id=${row.id}`">
<el-button type="primary" plain>查看</el-button>
</router-link>
</el-space>
</template>
<!-- 卡片 -->
<template #body="{ data }" v-if="isCard">
<div class="card-list" v-if="data.length">
<CardListItem :tabIndex="tabValue" v-for="(item, index) in data" :data="item" :key="index"></CardListItem>
</div>
<!-- <el-empty v-else description="暂无数据" /> -->
</template>
</AppList>
</AppCard>
<!-- <BatchImportVideo v-model="isShowBatchImport" v-if="isShowBatchImport === true" @update="typeFilter" /> -->
</template>
<style lang="scss" scoped>
.card-list {
background: #fafafa;
display: flex;
flex-wrap: wrap;
padding: 20px 0;
}
.video-head {
position: relative;
.video-head-icon {
position: absolute;
top: 0;
right: 0;
font-size: 30px;
color: #666;
cursor: pointer;
}
}
.video-tool-btn {
padding: 10px 0 30px 0;
}
</style>
<script setup lang="ts">
import AppUpload from '@/components/base/AppUpload.vue'
import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { createTimetable, getTimetableDetails, updateTimetable, getSemestersList } from '../api'
import { useGetCategoryList } from '@/composables/useGetCategoryList'
import Protocol from '@/components/base/Protocol.vue'
// 路由
const router = useRouter()
const route = useRoute()
const id = route.query.id as string
// is 编辑 新建
const isUpdate = $computed(() => {
return !!route.query.id
})
// 编辑数据回显
if (isUpdate) {
getTimetableDetails({ id: id }).then((res: any) => {
form = res.data
form.file = [
{
name: res.data.name,
url: res.data.url
}
]
})
}
// 下拉选择tree 视频分类
let { list: selectTree }: any = useGetCategoryList()
const defaultProps = {
children: 'children',
label: 'category_name',
value: 'id'
}
// form表单
let form: any = $ref({
file: [],
name: '',
source: '2',
classification: '',
knowledge_points: '',
url: '',
type: '',
size: '',
semesters_id: []
})
// 监听文件上传
watch(
() => form.file,
value => {
if (value[0]?.name.indexOf('.') != -1) {
form.name = value[0]?.name.slice(0, value[0]?.name.lastIndexOf('.')) || ''
}
}
)
// 表单验证
const rules = {
name: [{ required: true, message: '请输入标题', trigger: 'blur' }],
semesters_id: [{ required: true, message: '请选择学期', trigger: 'change' }],
classification: [{ required: true, message: '请选择分类', trigger: 'change' }],
file: [{ required: true, message: '' }]
}
// 新建编辑表单提交
const ruleFormRef = ref<FormInstance>()
let submitFlag = true
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
if (!submitFlag) return
submitFlag = false
await formEl.validate(valid => {
if (valid) {
if (!protocol.value) {
submitFlag = true
ElMessage('请勾选用户协议')
return
}
const { url, size } = form.file[0]
const findString = url.lastIndexOf('.') + 1
form.url = url
form.type = url.substring(findString, url.length)
form.size = size
form.semesters_id = form.semesters_id.toString()
const params = Object.assign({}, form)
delete params.file
if (isUpdate) {
params.id = id
updateResources(params)
} else {
createResources(params)
}
} else {
submitFlag = true
ElMessage('请完善信息')
}
})
}
// 更新
const updateResources = (params: any) => {
updateTimetable(params)
.then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更新成功', type: 'success' })
setTimeout(() => {
router.push({
path: '/resource/timetable'
})
}, 1000)
} else {
submitFlag = true
}
})
.catch(() => {
submitFlag = true
})
}
// 新建
const createResources = (params: any) => {
createTimetable(params)
.then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '创建成功', type: 'success' })
setTimeout(() => {
router.push({
path: '/resource/timetable'
})
}, 1000)
} else {
submitFlag = true
}
})
.catch(() => {
submitFlag = true
})
}
// 协议
const protocol = ref(false)
const changeProtocol = (data: any) => {
protocol.value = data.value
}
let semestersList: any = $ref([])
getSemestersList().then((res: any) => {
semestersList = res.data
})
</script>
<template>
<AppCard :title="isUpdate ? '编辑课程表资源' : '新建课程表资源'">
<el-form ref="ruleFormRef" :model="form" :rules="rules" style="width: 70%">
<el-form-item label="课程表文件:" prop="file">
<div>
<div class="upload-video">
<div class="upload-box">
<AppUpload accept=".png,.jpeg,.jpg" :limit="1" v-model="form.file"></AppUpload>
<div class="upload-btn">本地文件</div>
</div>
</div>
<div class="tips">课程表支持格式包含:png jpeg jpg,大小不超过50M</div>
</div>
</el-form-item>
<el-form-item label="课程表名称:" prop="name">
<el-input maxlength="40" v-model="form.name" />
<div class="tips">课程表名称自动取值于文件名称,可以进行二次修改。</div>
</el-form-item>
<el-form-item label="课程表分类:" prop="classification">
<el-tree-select
:render-after-expand="false"
style="width: 100%"
:props="defaultProps"
v-model="form.classification"
:data="selectTree"
placeholder="请选择课程表分类"
node-key="id"
:default-expanded-keys="selectTree.length ? [selectTree[0]?.id] : []"
/>
</el-form-item>
<el-form-item label-width="105px" label="学期:" prop="semesters_id">
<el-select style="width: 100%" v-model="form.semesters_id" multiple placeholder="请选择学期">
<el-option v-for="item in semestersList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label-width="105px" label="知识点:">
<el-input
placeholder="请输入该课程表相关知识点,多个知识点请使用“,”分隔"
maxlength="100"
v-model="form.knowledge_points"
:rows="2"
type="textarea"
/>
</el-form-item>
</el-form>
<div class="protocol-box">
<Protocol @change="changeProtocol"></Protocol>
</div>
<div class="btn-box">
<div class="confirm" @click="submitForm(ruleFormRef)">保存</div>
<div class="cancel" @click="router.go(-1)">取消</div>
</div>
</AppCard>
</template>
<style lang="scss">
.tips {
font-size: 12px;
line-height: 100%;
color: #999999;
margin-top: 8px;
}
.upload-box {
// min-width: 400px;
.el-progress__text {
transform: translate(20px, -5px);
}
position: relative;
.app-upload-btn {
position: relative;
z-index: 99;
opacity: 0.0001 !important;
}
}
.tips {
font-size: 12px;
line-height: 100%;
color: #999999;
margin-top: 8px;
}
.tool-btn-box {
display: flex;
justify-content: right;
.btn-item {
width: 127px;
line-height: 36px;
background: #aa1941;
border-radius: 20px;
margin-right: 10px;
font-size: 14px;
color: #ffffff;
text-align: center;
}
}
.demo-progress {
width: 350px;
}
.video-cover {
display: flex;
.img-box {
width: 208px;
height: 130px;
border-radius: 4px;
background: #f7f7f7;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
display: block;
}
}
.video-cover_right {
margin-left: 20px;
.list {
.item {
display: flex;
align-items: center;
margin-top: 12px;
img {
width: 9px;
display: block;
margin-right: 5px;
}
.text {
font-size: 12px;
color: #999999;
line-height: 100%;
}
}
}
}
}
.swiper-box {
min-width: 660px;
margin-top: 20px;
padding: 0 40px;
position: relative;
.arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 30px;
color: #d5d5d5;
cursor: pointer;
&.left {
left: 0;
}
&.right {
right: 10px;
}
}
.cover-list {
display: flex;
flex-wrap: wrap;
.cover-list_item {
display: block;
width: 155px;
height: 96px;
margin: 0 10px 10px 0;
border-radius: 4px;
cursor: pointer;
}
}
}
:deep(.el-carousel__indicators--horizontal) {
display: none;
}
.protocol-box {
font-size: 14px;
line-height: 24px;
padding-left: 90px;
padding-top: 20px;
color: #666666;
}
.btn-box {
display: flex;
margin-top: 20px;
margin-left: 90px;
.confirm {
width: 94px;
background: #aa1941;
border-radius: 20px;
text-align: center;
line-height: 36px;
color: #fff;
margin-right: 26px;
cursor: pointer;
}
.cancel {
width: 94px;
line-height: 36px;
border: 1px solid #aa1941;
border-radius: 20px;
text-align: center;
color: #aa1941;
cursor: pointer;
}
}
.upload-video {
width: 500px;
display: flex;
align-items: center;
.upload-btn {
position: absolute;
top: 0;
left: 0;
width: 94px;
line-height: 36px;
background: rgba(250, 223, 230, 0.39);
border: 1px solid #aa1941;
border-radius: 20px;
font-size: 14px;
color: #aa1941;
margin-right: 30px;
text-align: center;
}
.video-info {
display: flex;
.name {
color: #333333;
font-size: 16px;
line-height: 100%;
}
.view {
font-size: 16px;
line-height: 100%;
color: #399ee8;
margin-left: 20px;
cursor: pointer;
}
}
}
</style>
<script setup lang="ts">
import ViewTop from '../components/ViewTop.vue'
import ViewCenter from '../components/ViewCenter.vue'
import Operation from '../components/Operation.vue'
import ResourceCourseList from '@/components/ResourceCourseList.vue'
import DataAccess from '@/components/DataAccess.vue'
import { getTimetableDetails } from '../api'
// 路由
const route = useRoute()
const id = route.query.id as string
let videoDetails: any = ref({})
// 获取详情
getTimetableDetails({ id: id }).then(res => {
videoDetails.value = res.data
})
</script>
<template>
<AppCard title="课程表资源信息">
<Operation v-if="Object.keys(videoDetails).length" :data="videoDetails"></Operation>
<ViewTop v-if="Object.keys(videoDetails).length" :data="videoDetails"></ViewTop>
<ViewCenter v-if="Object.keys(videoDetails).length" :data="videoDetails"></ViewCenter>
<!-- <DataAccess v-if="Object.keys(videoDetails).length" :data="videoDetails"></DataAccess> -->
</AppCard>
<ResourceCourseList title="课程表资源关联使用课程" :list="videoDetails.course_list"></ResourceCourseList>
</template>
import { createRouter, createWebHistory } from 'vue-router'
// import { useUserStore } from '@/stores/user'
import { useUserStore } from '@/stores/user'
const router = createRouter({
history: createWebHistory(),
......@@ -7,17 +7,17 @@ const router = createRouter({
})
router.beforeEach(async (to, from, next) => {
// const whiteList = ['/401']
// const user = useUserStore()
// if (!user.isLogin && !whiteList.includes(to.path)) {
// try {
// await user.getUser()
// } catch (e) {
// console.error(e)
// }
// user.isLogin ? next() : next('/401')
// return
// }
const whiteList = ['/401']
const user = useUserStore()
if (!user.isLogin && !whiteList.includes(to.path)) {
try {
await user.getUser()
} catch (e) {
console.error(e)
}
user.isLogin ? next() : next('/401')
return
}
next()
})
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论