提交 90a5baaf authored 作者: lhh's avatar lhh

新增知识图库页面

上级 2b786ee9
...@@ -13,15 +13,19 @@ const props = defineProps({ ...@@ -13,15 +13,19 @@ const props = defineProps({
}, },
course_id: { course_id: {
type: String, type: String,
required: true required: false
}, },
chapterName: { chapterName: {
type: String, type: String,
required: true required: false
}, },
chapterID: { chapterID: {
type: String, type: String,
required: true required: false
},
drawerName: {
type: String,
required: false
} }
}) })
interface Emits { interface Emits {
...@@ -72,12 +76,18 @@ const handleGl = function (val: any) { ...@@ -72,12 +76,18 @@ const handleGl = function (val: any) {
</script> </script>
<template> <template>
<el-drawer :model-value="isShowTextbookDialog" draggable :before-close="handleCancel" size="60%" title="添加数字教材"> <el-drawer
:model-value="isShowTextbookDialog"
draggable
:before-close="handleCancel"
size="60%"
:title="drawerName || '添加数字教材'"
>
<el-tree :data="chapterList" node-key="id" :accordion="true" :expand-on-click-node="false" style="min-width: 100%"> <el-tree :data="chapterList" node-key="id" :accordion="true" :expand-on-click-node="false" style="min-width: 100%">
<template #default="{ data }"> <template #default="{ data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<span class="node_title">{{ data.name }}</span> <span class="node_title">{{ data.name }}</span>
<div> <div v-if="course_id">
<el-button class="btn_operate" v-if="data.index === '1'" @click="handleView(data.id)">查阅 </el-button> <el-button class="btn_operate" v-if="data.index === '1'" @click="handleView(data.id)">查阅 </el-button>
<el-button class="btn_operate" v-if="data.index === '1'" @click="handleGl(data)"> <el-button class="btn_operate" v-if="data.index === '1'" @click="handleGl(data)">
<el-icon><Plus /></el-icon> <el-icon><Plus /></el-icon>
......
<script setup lang="ts">
import { getChapter, createCharacter } from '../../api'
import { Plus } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
const route = useRoute()
const emit = defineEmits<Emits>()
const props = defineProps({
isShowTextbookDialog2: {
type: Boolean,
required: true
},
course_id: {
type: String,
required: false
},
chapterName: {
type: String,
required: false
},
chapterID: {
type: String,
required: false
}
})
interface Emits {
(e: 'update:isShowTextbookDialog2', isShowVideoDialog2: boolean): void
(e: 'create', val: any): void
}
// 取消
const handleCancel = () => {
emit('update:isShowTextbookDialog2', false)
}
const chapterList = ref([])
onMounted(() => {
getChapter({ book_id: route.query?.bid as string }).then(res => {
chapterList.value = res.data.reduce((a: any, b: any) => {
b.index = '0'
if (b.children) {
b.children.map((m: any) => {
m.index = '1'
})
}
a.push(b)
return a
}, [])
console.log(chapterList.value, 'chapterList.value')
})
})
// emit('update:isShowTextbookDialog', false)
const handleView = function (id: any) {
window.open(`https://zijingebook.ezijing.com/student/book?book_id=${route.query.bid}&chapter_id=${id}`)
}
const handleGl = function (val: any) {
const params: any = {
name: val.name,
course_id: props.course_id,
resource_type: '13',
parent_id: props.chapterID,
resource_id: val.id
}
createCharacter(params).then(() => {
emit('update:isShowTextbookDialog2', false)
emit('create', val)
ElMessage.success('添加成功')
})
}
</script>
<template>
<el-drawer
:model-value="isShowTextbookDialog2"
draggable
:before-close="handleCancel"
size="60%"
title="关联知识图谱"
>
<el-tree :data="chapterList" node-key="id" :accordion="true" :expand-on-click-node="false" style="min-width: 100%">
<template #default="{ data }">
<span class="custom-tree-node">
<span class="node_title">{{ data.name }}</span>
<div v-if="course_id">
<!-- <el-button class="btn_operate" v-if="data.index === '1'" @click="handleView(data.id)">查阅 </el-button> -->
<el-button class="btn_operate" v-if="data.index === '1'" @click="handleGl(data)">
<el-icon><Plus /></el-icon>
&nbsp; 关联
</el-button>
</div>
</span>
</template>
</el-tree>
</el-drawer>
</template>
<style lang="scss" scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.btn_operate {
background-color: rgba(248, 241, 229, 0.39);
font-weight: 400;
color: #b2833d;
border: none;
border-radius: 16px;
margin: 3px;
}
.card-list {
display: flex;
flex-direction: column;
}
.card-list-con {
background: #fafafa;
padding: 20px;
display: flex;
flex-wrap: wrap;
}
.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;
}
.video-head-icon {
position: absolute;
top: 0;
right: 0;
font-size: 30px;
color: #666;
cursor: pointer;
}
</style>
...@@ -39,7 +39,10 @@ const listOptions = computed(() => { ...@@ -39,7 +39,10 @@ const listOptions = computed(() => {
}, },
{ label: '作者', prop: 'authors' }, { label: '作者', prop: 'authors' },
{ label: '发布者', prop: 'user.real_name' }, { label: '发布者', prop: 'user.real_name' },
{ label: '审核状态', prop: 'audit_status', computed(row: any){ {
label: '审核状态',
prop: 'audit_status',
computed(row: any) {
const j: any = { const j: any = {
'1': '待发布', '1': '待发布',
'2': '审核中', '2': '审核中',
...@@ -47,7 +50,8 @@ const listOptions = computed(() => { ...@@ -47,7 +50,8 @@ const listOptions = computed(() => {
'4': '审核通过' '4': '审核通过'
} }
return j[row?.row?.audit_status] return j[row?.row?.audit_status]
} }, }
},
// { label: '版号', prop: 'name' }, // { label: '版号', prop: 'name' },
{ label: '操作', slots: 'table-operate', align: 'center', width: 200, fixed: 'right' } { label: '操作', slots: 'table-operate', align: 'center', width: 200, fixed: 'right' }
] ]
......
...@@ -13,6 +13,7 @@ import AddExamDialog from '../components/stepTwoComponents/AddExamDialog.vue' ...@@ -13,6 +13,7 @@ import AddExamDialog from '../components/stepTwoComponents/AddExamDialog.vue'
import VideoPlayDialog from '../components/stepTwoComponents/VideoPlayDialog.vue' import VideoPlayDialog from '../components/stepTwoComponents/VideoPlayDialog.vue'
import AddChapterDialog from '../components/stepTwoComponents/AddChapterDialog.vue' import AddChapterDialog from '../components/stepTwoComponents/AddChapterDialog.vue'
import AddTextbookDialog from '../components/stepTwoComponents/AddTextbookDialog.vue' import AddTextbookDialog from '../components/stepTwoComponents/AddTextbookDialog.vue'
import AddTextbookDialog2 from '../components/stepTwoComponents/AddTextbookDialog2.vue'
import OpenRules from '../components/stepTwoComponents/OpenRules.vue' import OpenRules from '../components/stepTwoComponents/OpenRules.vue'
const route = useRoute() const route = useRoute()
...@@ -27,6 +28,7 @@ const isShowSectionDialog = ref(false) ...@@ -27,6 +28,7 @@ const isShowSectionDialog = ref(false)
const isEditResourcesNameDialog = ref(false) const isEditResourcesNameDialog = ref(false)
// 资源id // 资源id
const isShowTextbookDialog = ref(false) const isShowTextbookDialog = ref(false)
const isShowTextbookDialog2 = ref(false)
const resourceId = ref('') const resourceId = ref('')
const isShowLiveDialog = ref(false) const isShowLiveDialog = ref(false)
const isShowAddDialog = ref(false) const isShowAddDialog = ref(false)
...@@ -74,6 +76,10 @@ const btnList = [ ...@@ -74,6 +76,10 @@ const btnList = [
{ {
btn_name: '关联数字教材', btn_name: '关联数字教材',
resource_type: '13' resource_type: '13'
},
{
btn_name: '关联知识图谱',
resource_type: '14'
} }
] ]
const defaultProps = { const defaultProps = {
...@@ -174,6 +180,8 @@ const handleAddDialog = (node: any, item: any, data: any) => { ...@@ -174,6 +180,8 @@ const handleAddDialog = (node: any, item: any, data: any) => {
paper_use_list.value = [2] paper_use_list.value = [2]
} else if (item.resource_type === '13') { } else if (item.resource_type === '13') {
isShowTextbookDialog.value = true isShowTextbookDialog.value = true
} else if (item.resource_type === '14') {
isShowTextbookDialog2.value = true
} }
} }
//查阅 //查阅
...@@ -297,6 +305,8 @@ const nodeType = (node: any) => { ...@@ -297,6 +305,8 @@ const nodeType = (node: any) => {
return '教案' return '教案'
} else if (node.data.resource_type === '13') { } else if (node.data.resource_type === '13') {
return '数字教材' return '数字教材'
} else if (node.data.resource_type === '14') {
return '知识图谱'
} }
} }
const handleNodeExpand = (data: any) => { const handleNodeExpand = (data: any) => {
...@@ -388,9 +398,16 @@ const handleChangeStatus = (node: any, data: any) => { ...@@ -388,9 +398,16 @@ const handleChangeStatus = (node: any, data: any) => {
<span class="node_title">{{ node.label.length > 20 ? node.label.slice(0, 20) + '...' : node.label }}</span> <span class="node_title">{{ node.label.length > 20 ? node.label.slice(0, 20) + '...' : node.label }}</span>
<el-link <el-link
class="btn_edit" class="btn_edit"
v-if="(data.depth === '3' && node.data.resource_type === '2') || node.data.resource_type === '13'" v-if="
(data.depth === '3' && node.data.resource_type === '2') ||
node.data.resource_type === '13' ||
node.data.resource_type === '14'
"
@click="handleConsult(node)" @click="handleConsult(node)"
:disabled="node.data.resource?.can_view !== true && node.data.resource_type !== '13'" :disabled="
(node.data.resource?.can_view !== true && node.data.resource_type !== '13') ||
node.data.resource_type === '14'
"
style="margin-left: 35px" style="margin-left: 35px"
>查阅</el-link >查阅</el-link
> >
...@@ -402,7 +419,8 @@ const handleChangeStatus = (node: any, data: any) => { ...@@ -402,7 +419,8 @@ const handleChangeStatus = (node: any, data: any) => {
node.data.resource_type !== '6' && node.data.resource_type !== '6' &&
node.data.resource_type !== '3' && node.data.resource_type !== '3' &&
node.data.resource_type !== '9' && node.data.resource_type !== '9' &&
node.data.resource_type !== '13' node.data.resource_type !== '13' &&
node.data.resource_type !== '14'
" "
@click="handleDownload(node)" @click="handleDownload(node)"
:disabled="node.data.resource?.can_view !== true" :disabled="node.data.resource?.can_view !== true"
...@@ -411,7 +429,7 @@ const handleChangeStatus = (node: any, data: any) => { ...@@ -411,7 +429,7 @@ const handleChangeStatus = (node: any, data: any) => {
> >
<!-- data.depth !== '3' 资源--> <!-- data.depth !== '3' 资源-->
<el-link <el-link
v-if="node.data.resource_type !== '13'" v-if="node.data.resource_type !== '13' || node.data.resource_type !== '14'"
class="btn_edit" class="btn_edit"
@click="handleEdit(node, data.depth)" @click="handleEdit(node, data.depth)"
style="margin-left: 35px" style="margin-left: 35px"
...@@ -459,7 +477,8 @@ const handleChangeStatus = (node: any, data: any) => { ...@@ -459,7 +477,8 @@ const handleChangeStatus = (node: any, data: any) => {
node.data.resource_type !== '6' && node.data.resource_type !== '6' &&
node.data.resource_type !== '3' && node.data.resource_type !== '3' &&
node.data.resource_type !== '9' && node.data.resource_type !== '9' &&
node.data.resource_type !== '13' node.data.resource_type !== '13' &&
node.data.resource_type !== '14'
" "
> >
<span class="btn_operate">学生下载控制:</span> <span class="btn_operate">学生下载控制:</span>
...@@ -581,6 +600,14 @@ const handleChangeStatus = (node: any, data: any) => { ...@@ -581,6 +600,14 @@ const handleChangeStatus = (node: any, data: any) => {
:chapterID="chapterID" :chapterID="chapterID"
:course_id="id" :course_id="id"
/> />
<AddTextbookDialog2
v-if="isShowTextbookDialog2 === true"
v-model:isShowTextbookDialog2="isShowTextbookDialog2"
@create="handleFresh"
:chapterName="chapterName"
:chapterID="chapterID"
:course_id="id"
/>
</template> </template>
<style> <style>
......
...@@ -124,6 +124,11 @@ const handleMembers = () => { ...@@ -124,6 +124,11 @@ const handleMembers = () => {
members.value = useProjectList(props.data.organ_id).members members.value = useProjectList(props.data.organ_id).members
} }
// 点击知识图谱
const handleChart = () => {
router.push({ path: '/course/my/graph', query: { n: props.data?.name, bid: 19 } })
}
// 更改负责人确定 // 更改负责人确定
const handleSetBelong = () => { const handleSetBelong = () => {
setBelong({ id: id, belong_operator: form.members }).then((res: any) => { setBelong({ id: id, belong_operator: form.members }).then((res: any) => {
...@@ -223,6 +228,7 @@ watch( ...@@ -223,6 +228,7 @@ watch(
{{ props.data.status == 0 ? '课程上线' : '课程下线' }} {{ props.data.status == 0 ? '课程上线' : '课程下线' }}
</div> </div>
<div v-if="props.data.auth_belong" class="btn-item" @click="handleMembers">更改负责人</div> <div v-if="props.data.auth_belong" class="btn-item" @click="handleMembers">更改负责人</div>
<div class="btn-item" @click="handleChart">知识图谱</div>
</div> </div>
<el-dialog v-if="props.data.auth_belong" v-model="dialogFormVisible" title="更改负责人" center> <el-dialog v-if="props.data.auth_belong" v-model="dialogFormVisible" title="更改负责人" center>
<el-form :model="form"> <el-form :model="form">
......
...@@ -9,7 +9,8 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -9,7 +9,8 @@ export const routes: Array<RouteRecordRaw> = [
{ path: '', component: () => import('./views/List.vue') }, { path: '', component: () => import('./views/List.vue') },
{ path: '/course/my/view', component: () => import('./views/View.vue') }, { path: '/course/my/view', component: () => import('./views/View.vue') },
// id: 章节id courseId课程id // id: 章节id courseId课程id
{ path: '/course/my/view/:id/:courseId', component: () => import('./views/ViewDetails.vue') } { path: '/course/my/view/:id/:courseId', component: () => import('./views/ViewDetails.vue') },
{ path: '/course/my/graph', component: () => import('./views/Graph.vue') }
] ]
} }
] ]
<script setup lang="ts">
import AddTextbookDialog from '../../create/components/stepTwoComponents/AddTextbookDialog.vue'
const route = useRoute()
const tableData = ref([
{
id: 1,
points: '项目一:初识商务数据分析',
update: '管理员',
time: '2024-09-12 12:00:23',
children: [
{
id: 2,
points: '任务一:人人都需要商务数据分析',
update: '管理员',
time: '2024-09-12 12:00:23',
children: [
{
id: 3,
points: '商务数据分析就业情况',
update: '管理员',
time: '2024-09-12 12:00:23',
children: [
{
id: 4,
points: '商务数据分析师岗位',
update: '管理员',
time: '2024-09-12 12:00:23'
},
{
id: 5,
points: '商务数据分析师的市场优势',
update: '管理员',
time: '2024-09-12 12:00:23'
},
{
id: 6,
points: '商务数据分析师的工作职责',
update: '管理员',
time: '2024-09-12 12:00:23'
}
]
}
]
}
]
}
])
// 弹窗输入框
const pName: any = ref('')
const dialogVisible = ref(false)
// 点击添加同级的id
const addSameId = ref<any>('')
const stu = ref<number>(0)
const addChild = function (scope: any, n: number) {
if (scope !== 1) {
addSameId.value = scope?.row.id
} else {
addSameId.value = 1
}
stu.value = n
dialogVisible.value = true
}
const handleConfirm = () => {
// 查找 ID 为 4 的所有层级下标
// 同级增加
// for (let i = 0; i < fIndex.length - 1; i++){
// // tableData[]
// tableData.value[fIndex[i]]
// }
const getRandomInt = function (min: any, max: any) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
const data: any = {
id: getRandomInt(100, 100000),
points: pName.value,
update: '管理员',
time: '2024-09-12 12:00:23'
}
if (stu.value === 0) {
manageById(tableData.value, addSameId.value, data)
}
if (stu.value === 1) {
manageById(tableData.value, addSameId.value, data, true)
}
if (stu.value === 2) {
// manageById(tableData.value, addSameId.value, null, false, pName.value)
// manageById(tableData.value, addSameId.value, null, false, 'Updated Child 2 Points')
// manageById(tableData.value, 3, null, false, 'Updated Child 2')
}
// if (stu.value === 3) {
// manageById(tableData.value, addSameId.value, null, false, null, true)
// }
pName.value = ''
dialogVisible.value = false
}
function manageById(
array: any,
targetId: any,
newElement = null,
insertAsChild = false,
newPoints = null,
deleteTarget = false
) {
for (let i = 0; i < array.length; i++) {
const item = array[i]
// 检查当前项的 ID 是否匹配目标 ID
if (item.id === targetId) {
if (deleteTarget) {
// 删除目标对象
array.splice(i, 1)
return true // 删除成功
}
// 如果需要更新 points,则修改
if (newPoints !== null) {
item.points = newPoints
}
if (insertAsChild) {
// 如果插入为子级,初始化 children 如果不存在
if (!item.children) {
item.children = []
}
// 在 children 中插入新元素
item.children.push(newElement)
} else {
// 如果不作为子级,插入在当前项后面
array.splice(i + 1, 0, newElement)
}
return true // 插入或更新成功
}
// 如果当前项有 children,递归查找
if (Array.isArray(item.children)) {
const result = manageById(item.children, targetId, newElement, insertAsChild, newPoints, deleteTarget)
if (result) {
return true // 插入、更新或删除成功
}
}
}
return false // 未找到目标 ID
}
const handleDelete = (scope: any) => {
addSameId.value = scope?.row.id
manageById(tableData.value, addSameId.value, null, false, null, true)
}
const isShowTextbookDialog = ref(false)
const bookName = ref('关联课程章节')
const handleView = function (n: any) {
bookName.value = n
isShowTextbookDialog.value = true
}
</script>
<template>
<AppCard title="知识图库">
<div style="margin-bottom: 15px">课程名称:{{ route.query?.n }}</div>
<el-button type="primary" @click="addChild(1, 1)">添加知识点</el-button>
<el-table
:data="tableData"
style="width: 100%; margin: 20px 0"
row-key="id"
highlight-current-row
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
align="center"
>
<!-- <el-table-column type="index"></el-table-column> -->
<el-table-column prop="points" label="知识点"> </el-table-column>
<el-table-column prop="update" label="更新人" width="180"> </el-table-column>
<el-table-column prop="time" label="更新时间" width="180"> </el-table-column>
<el-table-column fixed="right" label="操作">
<template #default="scope">
<el-button link type="primary" size="small" @click="addChild(scope, 0)"> 添加同级 </el-button>
<el-button link type="primary" size="small" @click="addChild(scope, 1)">添加子级</el-button>
<el-button link type="primary" size="small" @click="addChild(scope, 2)">编辑知识点</el-button>
<el-button link type="primary" size="small" @click="handleDelete(scope)">删除</el-button>
<el-button link type="primary" size="small" @click="handleView('关联课程章节')">查看关联章节</el-button>
<el-button link type="primary" size="small" @click="handleView('关联数字教材章节')"
>查看关联数字教材</el-button
>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="dialogVisible" title="提示" width="500px">
<!-- <span>This is a message</span> -->
<el-input v-model="pName" placeholder="请输入知识点名称" />
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm"> 确认 </el-button>
</div>
</template>
</el-dialog>
<!-- <Operation :data="courseDetails" @update="handleFresh"></Operation>
<div class="course-view">
<ViewCourseInfoTop :data="courseDetails"></ViewCourseInfoTop>
<div class="course-left_info">
<ViewCourseInfo :data="courseDetails" class="info_bottom" :id="id"></ViewCourseInfo>
<ViewCourseChapter
v-if="Object.keys(courseDetails).length"
:data="chapters"
class="info_chapter"
></ViewCourseChapter>
</div>
</div> -->
</AppCard>
<AddTextbookDialog
:drawerName="bookName"
v-if="isShowTextbookDialog === true"
v-model:isShowTextbookDialog="isShowTextbookDialog"
/>
</template>
<style lang="scss">
.course-view {
border: 1px solid #bfbfbf;
border-radius: 10px;
display: flex;
flex-direction: column;
.course-left_info {
display: flex;
.info_bottom {
width: 67%;
}
.info_chapter {
width: 25%;
}
}
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.course_tip {
font-size: 12px;
font-weight: 400;
color: #999999;
margin-left: 16px;
line-height: 36px;
height: 36px;
}
.el-tree-node__content {
height: 42px;
margin-bottom: 15px;
background: #f4f4f4;
}
.btn_operate {
background-color: rgba(248, 241, 229, 0.39);
font-weight: 400;
color: #b2833d;
border: none;
border-radius: 16px;
margin: 3px;
}
.btn_control {
font-weight: 400;
color: #b2833d;
}
.btn_edit {
color: #b2833d;
margin-right: 10px;
}
.node_type {
background: #bf9d6b;
border-radius: 20px;
line-height: 1;
font-size: 12px;
font-weight: 400;
color: #ffffff;
text-align: center;
padding: 4px 8px;
margin-right: 14px;
}
.node_title {
font-size: 14px;
font-weight: 400;
color: #666666;
line-height: 1;
text-align: center;
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论