feat(course): implement video selection and management features

- Added AddVideo component for video selection with preview and settings
- Implemented BasicTable component for displaying paginated data with custom rendering
- Created course file API module for managing course materials
- Enhanced dragTable component with edit, preview, and delete functionalities
- Added comprehensive styling utilities for margins, paddings, and dimensions
- Integrated video selection dialog in course creation workflow
- Added support for video drag-and-drop and completion rule configuration
- Implemented reusable course data management hook with icon support
- Added chapter and section management capabilities
- Enhanced course operation mapping for various content types
This commit is contained in:
陈昱达
2025-11-20 17:28:23 +08:00
parent 740ad58897
commit 8ebca12470
8 changed files with 1018 additions and 322 deletions

View File

@@ -0,0 +1,92 @@
/**
* 课件文件内容的相关处理,当前只是业务管理员和教师功能使用<br/>
* 下载课件,一期是直接在新的窗口打开文件的地址就可以了。不采用流的方式下载
*/
import ajax from "./xajax.js";
/**
* 分页查询,课件的管理列表
* @param {Object} data
* {
name:查询的名称的关键字
self:true/false,默认是true,是否只查询自己上传的课件
resOwner1:资源归属一级的id
resOwner2:资源归属二级的id
resOwner3:资源归属三级的id
resType: 文件类型,10视频20音频30图片 40 文档41表图文50表scrom包,90表其它
}
*/
const pageList = function (data) {
return ajax.post("/systemapi/xboe/m/course/file/pagelist", data);
};
/**
* 选择课件的查询,这里也是分页查询,只是返回的内容,字段会很少,用于课件制作那选择已有课件内容。
*
* @param {Object} data
* 查询参数如上面pageList方法
*/
const findList = function (data) {
//一期返回内容就对象,之后优化只是返回需要的几字段,不是全部
return ajax.post("/systemapi/xboe/m/course/file/pagelist", data);
};
/**
* 上传保存,上传课件成功后,再调用此接口,保存上传的文件信息。
* 接口返回课件记录json对象,直接追加到列表中。
* @param {Object} data
* {
fileName:上传文件的名称
fileType: 文件的后缀名,比如:.mp4
filePath: 文件的保存路径
resOwner1:资源归属一级的id
resOwner2:资源归属二级的id
resOwner3:资源归属三级的id
resType: 文件类型,10视频20音频30图片 40 文档50表图文60表scrom包,90表其它
remark: 备注说明,可以为空,不填
}
*/
const saveUpload = function (data) {
return ajax.post("/systemapi/xboe/m/course/file/upload/save", data, {
timeout: 60000,
});
};
/**
* 批量更新,上传时,一个一个上传,下面的列表的保存,使用此方法
* @param {Object} data list集合
*saveUpload方法返回的对象
*/
const batchUpdate = function (data) {
return ajax.postJson("/systemapi/xboe/m/course/file/batch/update", data);
};
/**
* 删除一个课件文件
* @param {Object} data
* {
id:删除记录的id,
filePath: 文件的路径
flag: true/false, 是否是物理删除,在上传那是物理删除,在列表中是逻辑删除
}
*/
const delFile = function (data) {
return ajax.post("/systemapi/xboe/m/course/file/delete", data);
};
/**
* 根据id获取详细信息用于显示课件内容时播放时
* @param {Object} id
*/
const detail = function (id) {
return ajax.get("/systemapi/xboe/m/course/file/detail?id=" + id);
};
export default {
pageList,
saveUpload,
batchUpdate,
detail,
delFile,
};

View File

@@ -766,3 +766,53 @@ textarea {
// } // }
// } // }
//} //}
//循环 生成 mt mb 从 5 10 20 30
@for $i from 1 through 100 {
.mt#{$i} {
margin-top: $i * 1px;
}
.mb#{$i} {
margin-bottom: $i * 1px;
}
.ml#{$i} {
margin-left: $i * 1px;
}
.mr#{$i} {
margin-right: $i * 1px;
}
.mv#{$i} {
margin-top: $i * 1px;
margin-bottom: $i * 1px;
}
.mh#{$i} {
margin-left: $i * 1px;
margin-right: $i * 1px;
}
.fs#{$i} {
font-size: $i * 1px;
}
.pt#{$i} {
padding-top: $i * 1px;
}
.pb#{$i} {
padding-bottom: $i * 1px;
}
.pl#{$i} {
padding-left: $i * 1px;
}
.pr#{$i} {
padding-right: $i * 1px;
}
.pv#{$i} {
padding-top: $i * 1px;
padding-bottom: $i * 1px;
}
.ph#{$i} {
padding-left: $i * 1px;
padding-right: $i * 1px;
}
.w#{$i} {
width: $i * 1px;
}
}

View File

@@ -0,0 +1,87 @@
<script setup>
// import { ElTable, ElTableColumn } from "element-plus";
import { computed } from "vue";
const props = defineProps({
data: {
type: Array,
default: () => [],
},
columns: {
type: Array,
default: () => [],
},
pagination: {
type: Object,
default: () => {
return {
pageSize: 10,
total: 0,
pageIndex: 1,
};
},
},
loading: {
type: Boolean,
default: false,
},
});
console.log(props);
const paginationCopy = computed(() => {
return {
pageSize: props.pagination.pageSize,
total: props.pagination.total,
current: props.pagination.pageIndex,
pageSizeOptions: ["10", "20", "50", "100"],
};
});
const emit = defineEmits(["change"]);
const change = (tablePage) => {
emit("changePage", tablePage);
};
</script>
<template>
<a-table
:data-source="data"
:columns="columns"
:pagination="paginationCopy.pageSize ? paginationCopy : false"
class="tableBox"
@change="change"
align="center"
:loading="loading"
bordered
>
<template #bodyCell="{ text, record, index, column }">
<div v-if="column.render" style="text-align: center">
<component :is="column.render({ row: record, index })"></component>
</div>
<div v-else style="text-align: center">
{{ record[column.key] }}
</div>
</template>
</a-table>
<!-- <el-table :data="data">-->
<!-- <el-table-column-->
<!-- v-for="(column, index) in columns"-->
<!-- :key="index"-->
<!-- :label="column.title"-->
<!-- :width="column.width"-->
<!-- >-->
<!-- <template #default="{ row }">-->
<!-- <div v-if="column.render">-->
<!-- <component :is="column.render({ row })"></component>-->
<!-- </div>-->
<!-- <div v-else>-->
<!-- {{ row[column.key] }}-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </el-table>-->
</template>
<style scoped lang="scss">
// 样式已移除,使用项目中全局定义的 .tableBox 样式
</style>

View File

@@ -0,0 +1,225 @@
<script setup>
import { reactive, onMounted, ref, h } from "vue";
import {
ElButton,
ElInput,
ElDialog,
ElForm,
ElFormItem,
ElRadioGroup,
ElRadio,
ElInputNumber,
} from "element-plus";
import BasicTable from "@/components/BasicElTable/BasicTable.vue";
import { getPageListByType } from "@/hooks/useCreateCourseMaps";
const tableData = ref([]);
const form = reactive({
name: "",
resType: 10,
});
let pagination = reactive({
pageSize: 10,
current: 1,
total: 0,
});
const dialogVideoForm = ref({
name: "",
isDrag: false,
completeSetup: 0,
setupTage: "",
});
const loading = ref(false);
const showDialog = ref(false);
const emit = defineEmits(["saveContent"]);
const columns = [
{
title: "序号",
render: (params) => {
return h("span", {}, params.index + 1);
},
},
{
title: "名称",
key: "name",
dataIndex: "name",
},
{
title: "创建人",
key: "sysCreateBy",
dataIndex: "sysCreateBy",
},
{
title: "创建时间",
key: "sysCreateTime",
dataIndex: "sysCreateTime",
},
{
title: "操作",
key: "action",
dataIndex: "action",
width: 150,
render: (params) => {
return h("div", [
h(
ElButton,
{
type: "primary",
size: "small",
onClick: () => {
console.log(params);
showDialog.value = true;
dialogVideoForm.value = {
...dialogVideoForm.value,
...params.row,
};
},
},
"选择"
),
]);
},
},
];
const changePagination = (PAGINATION) => {
pagination = PAGINATION;
getVideoList();
};
const getVideoList = () => {
loading.value = true;
let paramsData = {
...form,
pageSize: pagination.pageSize,
pageIndex: pagination.current,
self: true,
};
getPageListByType(paramsData).then((res) => {
loading.value = false;
tableData.value = res.result.list;
pagination.total = res.result.count;
});
};
const saveContent = (type) => {
showDialog.value = false;
emit("saveContent", {
...dialogVideoForm,
type: 10,
});
// postData.content=this.cware.content;
// this.cwareChange.content = deepClone(this.cware.content)
// if(this.cware.content.contentType==52){
// if(this.cware.linkInfo.url==''){
// this.$message.error("请填写外连URL地址");
// return;
// }
// postData.content.content=JSON.stringify(this.cware.linkInfo);
// this.cwareChange.linkInfo = deepClone(this.cware.linkInfo)
// }else if(this.cware.content.contentType==10 || this.cware.content.contentType==20){
// if(this.cware.curriculumData.url==''){
// this.$message.error("请选择课件");
// return;
// }
// postData.content.content=JSON.stringify(this.cware.curriculumData);
// this.cwareChange.curriculumData = deepClone(this.cware.curriculumData)
// }
};
onMounted(() => {
getVideoList();
});
</script>
<template>
<div class="add-video">
<div class="add-vide-header">
<div>
<el-button>上传新视频</el-button>
<span class="desc ml10">文件大小限制1G,支持的文件类型:mp4 </span>
</div>
<div>
<el-input
style="width: 150px"
placeholder="请输入视频名称"
v-model="form.name"
clearable
></el-input>
<el-button class="ml10" @click="getVideoList">查询</el-button>
</div>
</div>
<div class="mt10">
<BasicTable
:columns="columns"
:data="tableData"
:pagination="pagination"
@changePage="changePagination"
:loading="loading"
></BasicTable>
</div>
</div>
<el-dialog v-model="showDialog" title="视频">
<el-form>
<el-form-item label="视频名称">
<el-input v-model="dialogVideoForm.name"></el-input>
</el-form-item>
<video
controls
style="width: 100%; max-height: 400px"
class="mb10"
v-if="showDialog"
>
<source
:src="'http://home.hzer.xyz:9960/upload/' + dialogVideoForm.filePath"
type="video/mp4"
/>
您的浏览器不支持video
</video>
<el-form-item label="是否允许拖拽">
<el-radio-group v-model="dialogVideoForm.isDrag" size="small">
<el-radio :label="true" border></el-radio>
<el-radio :label="false" border></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="完成规则设置">
<el-radio-group v-model="dialogVideoForm.completeSetup">
<el-radio :label="0">默认(系统自动控制)</el-radio>
<el-radio :label="1"
>按进度
<el-input-number
:disabled="dialogVideoForm.completeSetup === 0"
v-model="dialogVideoForm.setupTage"
size="mini"
:min="0"
:max="100"
label="描述文字"
controls-position="right"
></el-input-number>
%</el-radio
>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveContent(1)"> 保存 </el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped lang="scss">
.add-video {
.add-vide-header {
display: flex;
justify-content: space-between;
.desc {
font-size: 12px;
color: #999;
}
}
}
</style>

View File

@@ -1,53 +1,271 @@
import { ref, reactive } from "vue"; import { ref, reactive } from "vue";
import { message } from "ant-design-vue"; // import { message } from "ant-design-vue";
import {
SettingOutlined,
EyeOutlined,
DeleteOutlined,
MenuOutlined,
VideoCameraOutlined,
AudioOutlined,
FileTextOutlined,
PictureOutlined,
LinkOutlined,
FolderOpenOutlined,
BankOutlined,
EditOutlined,
} from "@ant-design/icons-vue";
import { createVNode, h } from "vue";
import { getType } from "@/hooks/useCreateCourseMaps";
/** /**
* 课程数据管理hook * 课程数据管理hook
* @returns * @returns
*/ */
export function useCourseData() { export function useCourseData() {
// 课程元数据 // 课程元数据
const courseMetadata = reactive({ const courseMetadata = reactive({
courseName: "", courseName: "",
createTime: "", createTime: "",
chooseIndex: "",
sectionIndex: "",
}); });
const tableColumns = [
{
title: "序号",
key: "index",
width: 100,
customRender: ({ index }) => {
return h(
"span",
{
style: {
display: "flex",
justifyContent: "space-between",
},
},
[
h("span", { class: "drag-handle" }, [
createVNode(MenuOutlined, {
style: { fontSize: "14px", color: "#666" },
}),
]),
h("span", {}, index + 1),
]
);
},
},
{
title: "节名称",
key: "name",
dataIndex: "name",
customRender: ({ record }) => {
// 检查当前行是否处于编辑状态
if (record._value.isEdit) {
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
h("input", {
value: record._value.copyName,
onInput: (e) => {
record._value.copyName = e.target.value;
},
style: {
border: "1px solid #d9d9d9",
borderRadius: "4px",
padding: "4px 11px",
width: "200px",
},
}),
h(
"a",
{
href: "javascript:void(0)",
style: { fontSize: "16px", color: "#52c41a" },
onClick: () => {
record._value.name = record._value.copyName;
record._value.copyName = null;
record._value.isEdit = false;
},
},
"✓"
),
]
);
}
// 否则显示正常文本和编辑图标
const getIconComponent = (type) => {
switch (type) {
case "视频":
return VideoCameraOutlined;
case "音频":
return AudioOutlined;
case "文档":
return FileTextOutlined;
case "图文":
return PictureOutlined;
case "链接":
return LinkOutlined;
case "SCORM":
return FolderOpenOutlined;
case "考试":
return BankOutlined;
case "作业":
return EditOutlined;
case "评估":
return BankOutlined;
default:
return FileTextOutlined;
}
};
const Icon = getIconComponent(getType(record._value.resType));
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
createVNode(Icon, { style: { color: "#1890ff" } }),
h("span", {}, record._value.name),
h(
"a",
{
href: "javascript:void(0)",
style: { marginLeft: "4px", fontSize: "12px" },
onClick: () => {
record._value.copyName = record._value.name;
record._value.isEdit = true;
},
},
"✎"
),
]
);
},
},
{
title: "类型",
key: "resType",
dataIndex: "resType",
align: "center",
customRender: ({ record }) => {
return h(
"span",
{
style: {
display: "block",
textAlign: "center",
},
},
getType(record._value.resType)
);
},
},
{
title: "操作",
key: "action",
width: 220,
align: "center",
customRender: ({ record, index, data }) => {
return h(
"span",
{
style: { display: "flex", justifyContent: "center", gap: "12px" },
},
[
// 设置
h(
"a",
{
href: "javascript:void(0)",
onClick: () => {
console.log("设置:", record);
// 在这里添加设置功能,比如打开设置弹窗
},
},
[
createVNode(SettingOutlined, {
style: {
fontSize: "14px",
color: "#1890ff",
paddingRight: "10px",
},
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"设置"
),
]
),
// 预览
h(
"a",
{
href: "javascript:void(0)",
onClick: () => {
console.log("预览:", record);
// 在这里添加预览功能
},
},
[
createVNode(EyeOutlined, {
style: { fontSize: "14px", color: "#1890ff" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"预览"
),
]
),
// 删除
h(
"a",
{
href: "javascript:void(0)",
onClick: () => {
console.log("删除:", record, "索引:", index);
// 执行删除操作
data.splice(index, 1);
},
},
[
createVNode(DeleteOutlined, {
style: { fontSize: "14px", color: "red" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"删除"
),
]
),
]
);
},
},
];
// 课程列表数据 // 课程列表数据
const courseList = ref([ const courseList = ref([
{ {
title: "课程1", title: "课程1",
data: [ data: [],
{ key: "1-1", name: "视频课件名称", type: "视频" },
{ key: "1-2", name: "音频课件名称", type: "音频" },
{ key: "1-3", name: "文档课件名称", type: "文档" },
],
}, },
{ {
title: "课程2", title: "课程2",
data: [ data: [],
{ key: "2-1", name: "图文课件名称", type: "图文" },
{ key: "2-2", name: "外部链接", type: "链接" },
],
}, },
{ {
title: "课程3", title: "课程3",
data: [ data: [],
{ key: "3-1", name: "SCORM", type: "SCORM" },
{ key: "3-2", name: "考试名称", type: "考试" },
{ key: "3-3", name: "自定义考试名称", type: "考试" },
{ key: "3-4", name: "作业名称", type: "作业" },
],
}, },
]); ]);
// 课程操作映射 // 课程操作映射
const courseOperations = { const courseOperations = {
addVideo: (index) => { addVideo: (index) => {
message.error("功能开发中"); courseMetadata.chooseIndex = index;
console.log("添加视频功能调用,索引:", index);
if (index !== undefined && courseList.value[index]) {
courseList.value[index].title = "课程4";
}
}, },
addAudio: () => { addAudio: () => {
console.log("添加音频功能调用"); console.log("添加音频功能调用");
@@ -137,6 +355,7 @@ export function useCourseData() {
courseMetadata, courseMetadata,
courseList, courseList,
courseActionButtons, courseActionButtons,
executeCourseOperation executeCourseOperation,
tableColumns,
}; };
} }

View File

@@ -0,0 +1,36 @@
import apiCourseFile from "@/api/modules/courseFile";
const contentTypeMaps = {
10: "视频",
20: "音频",
30: "图片",
40: "文档",
41: "图文",
50: "scorm",
52: "外链",
60: "作业",
61: "考试",
62: "评估",
90: "其它",
};
export function getType(type) {
return contentTypeMaps[type];
}
// 根据不同type 类型查询 媒体 视频等列表
export function getPageListByType(params) {
return new Promise((resolve, reject) => {
apiCourseFile
.pageList(params)
.then((res) => {
if (res.status === 200) {
resolve(res);
} else {
reject(res.message);
}
})
.catch((err) => {
reject(err);
});
});
}

View File

@@ -1,44 +1,94 @@
<script setup> <script setup>
import dragCollapse from "./dragCollapse.vue"; import dragCollapse from "./dragCollapse.vue";
import { ElButton, ElCheckbox } from "element-plus"; import { ElButton, ElCheckbox, ElDialog } from "element-plus";
import dragTable from "./dragTable.vue"; import dragTable from "./dragTable.vue";
import { message } from "ant-design-vue"; import { ref } from "vue";
defineOptions({ defineOptions({
name: "CreateCourse", name: "CreateCourse",
}); });
import { ref, reactive, watch, toRaw, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useCourseData } from "@/hooks/useCourseData"; import { useCourseData } from "@/hooks/useCourseData";
import AddVideoComp from "@/components/CreatedCourse/AddVideo.vue";
// 使用课程数据hook // 使用课程数据hook
const { courseMetadata, courseList, courseActionButtons, executeCourseOperation } = useCourseData(); const { courseMetadata, courseList, courseActionButtons } = useCourseData();
// 定义表格列 // 定义表格列
const tableColumns = [
{ // 添加章
title: "序号", const addChapter = () => {
key: "index", courseList.value.push({
width: 80, title: "视频课程名称",
align: "center", data: [],
});
};
const showDialog = ref(false);
// 课程操作映射
const courseOperations = {
addVideo: (index) => {
showDialog.value = true;
}, },
{ addAudio: () => {
title: "节名称", console.log("添加音频功能调用");
key: "name",
dataIndex: "name",
}, },
{ addDocument: () => {
title: "类型", console.log("添加文档功能调用");
key: "type",
dataIndex: "type",
align: "center",
}, },
{ addImageText: () => {
title: "操作", console.log("添加图文功能调用");
key: "action",
width: 220,
align: "center",
}, },
]; addExternalLink: () => {
console.log("添加外部链接功能调用");
},
addScorm: () => {
console.log("添加SCORM功能调用");
},
addExam: () => {
console.log("添加考试功能调用");
},
addHomework: () => {
console.log("添加作业功能调用");
},
addAssessment: () => {
console.log("添加评估功能调用");
},
};
// 执行课程操作
const executeCourseOperation = (operationName, data) => {
courseMetadata.chooseIndex = data;
courseMetadata.selectionIndex = null;
if (courseOperations[operationName]) {
courseOperations[operationName](data);
} else {
console.warn(`未找到操作: ${operationName}`);
}
};
const isSetting = ref(false);
const saveVideo = (data) => {
showDialog.value = false;
if (isSetting.value) {
} else {
courseList.value[courseMetadata.chooseIndex].data.push(data);
}
};
const deleteRow = (data) => {
console.log(data);
courseMetadata.chooseIndex = data.index;
courseMetadata.selectionIndex = data.selectionIndex;
};
const settingRow = (data) => {
courseMetadata.chooseIndex = data.index;
courseMetadata.selectionIndex = data.selectionIndex;
};
const previewRow = (data) => {
courseMetadata.chooseIndex = data.index;
courseMetadata.selectionIndex = data.selectionIndex;
};
</script> </script>
<template> <template>
@@ -49,7 +99,7 @@ const tableColumns = [
</div> </div>
<div class="course-content"> <div class="course-content">
<div style="padding: 10px"> <div style="padding: 10px">
<el-button>添加章</el-button> <el-button @click="addChapter">添加章</el-button>
<el-checkbox style="margin-left: 10px">顺序学习</el-checkbox> <el-checkbox style="margin-left: 10px">顺序学习</el-checkbox>
</div> </div>
@@ -74,15 +124,23 @@ const tableColumns = [
<!-- 修改添加 groupId tableId 属性以支持跨表格拖拽 --> <!-- 修改添加 groupId tableId 属性以支持跨表格拖拽 -->
<dragTable <dragTable
:data="course.data" :data="course.data"
:columns="tableColumns"
:group-id="'course-chapters'" :group-id="'course-chapters'"
:table-id="'chapter-' + index" :table-id="'chapter-' + index"
:index="index"
@delete="deleteRow"
@setting="settingRow"
@preview="previewRow"
></dragTable> ></dragTable>
</div> </div>
</template> </template>
</dragCollapse> </dragCollapse>
</div> </div>
</div> </div>
<!-- 课程按钮弹窗-->
<el-dialog v-model="showDialog" title="请选择操作">
<AddVideoComp @saveContent="saveVideo"></AddVideoComp>
</el-dialog>
</div> </div>
</template> </template>
@@ -93,6 +151,10 @@ const tableColumns = [
.course-header { .course-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.title {
font-size: 16px;
font-weight: 600;
}
} }
.drag-course-btn-content { .drag-course-btn-content {

View File

@@ -29,7 +29,7 @@ import {
EyeOutlined, EyeOutlined,
SettingOutlined, SettingOutlined,
} from "@ant-design/icons-vue"; } from "@ant-design/icons-vue";
import { getType } from "@/hooks/useCreateCourseMaps";
// 定义 props // 定义 props
const props = defineProps({ const props = defineProps({
data: { data: {
@@ -60,8 +60,14 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
index: {
type: Number,
default: 0,
},
}); });
// 设置 预览 删除 三个按钮emit 事件
const emit = defineEmits(["setting", "preview", "delete"]);
// 根据类型返回对应图标组件 // 根据类型返回对应图标组件
const getIconComponent = (type) => { const getIconComponent = (type) => {
switch (type) { switch (type) {
@@ -95,24 +101,191 @@ const editValue = ref("");
// 开始编辑 // 开始编辑
const startEdit = (record) => { const startEdit = (record) => {
editingKey.value = record.key; record._value.copyName = record._value.name;
editValue.value = record.name; record._value.isEdit = true;
}; };
// 保存编辑 // 保存编辑
const saveEdit = (record) => { const saveEdit = (record) => {
record.name = editValue.value; record._value.name = record._value.copyName;
editingKey.value = ""; record._value.copyName = null;
editValue.value = ""; record._value.isEdit = false;
}; };
// 删除处理函数 // 删除处理函数
const handleDelete = (key) => { const handleDelete = (index) => {
const index = props.data.findIndex((item) => item.key === key); emit("delete", { index: props.index, selectionIndex: index });
if (index > -1) { };
props.data.splice(index, 1); const handleSetting = (index) => {
console.log("删除了:", key); emit("setting", { index: props.index, selectionIndex: index });
} };
const handlePreview = (index) => {
emit("preview", { index: props.index, selectionIndex: index });
};
// 渲染序号列
const renderIndexColumn = () => {
return ({ index }) => {
return h(
"span",
{
style: {
display: "flex",
justifyContent: "space-between",
},
},
[
h("span", { class: "drag-handle" }, [
createVNode(MenuOutlined, {
style: { fontSize: "14px", color: "#666" },
}),
]),
h("span", {}, index + 1),
]
);
};
};
// 渲染名称列
const renderNameColumn = () => {
return ({ record }) => {
// 如果处于编辑状态,显示输入框和确认按钮
if (record._value.isEdit) {
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
h("input", {
value: record._value.copyName,
onInput: (e) => {
record._value.copyName = e.target.value;
},
style: {
border: "1px solid #d9d9d9",
borderRadius: "4px",
padding: "4px 11px",
width: "200px",
},
}),
h(
"a",
{
href: "javascript:void(0)",
style: { fontSize: "16px", color: "#52c41a" },
onClick: () => {
saveEdit(record);
},
},
"✓"
),
]
);
}
// 否则显示正常文本和编辑图标
const Icon = getIconComponent(getType(record._value.resType));
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
createVNode(Icon, { style: { color: "#1890ff" } }),
h("span", {}, record._value.name),
h(
"a",
{
href: "javascript:void(0)",
style: { marginLeft: "4px", fontSize: "12px" },
onClick: () => {
startEdit(record);
},
},
"✎"
),
]
);
};
};
// 渲染资源类型列
const renderResTypeColumn = () => {
return ({ record }) => {
return h(
"span",
{
style: {
display: "block",
textAlign: "center",
},
},
getType(record._value.resType)
);
};
};
// 渲染操作列
const renderActionColumn = () => {
return ({ record, index }) => {
return h(
"span",
{
style: { display: "flex", justifyContent: "center", gap: "12px" },
},
[
// 设置
h(
"a",
{ href: "javascript:void(0)", onClick: () => handleSetting(index) },
[
createVNode(SettingOutlined, {
style: {
fontSize: "14px",
color: "#1890ff",
paddingRight: "10px",
},
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"设置"
),
]
),
// 预览
h(
"a",
{ href: "javascript:void(0)", onClick: () => handlePreview(index) },
[
createVNode(EyeOutlined, {
style: { fontSize: "14px", color: "#1890ff" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"预览"
),
]
),
// 删除
h(
"a",
{
href: "javascript:void(0)",
onClick: () => handleDelete(index),
},
[
createVNode(DeleteOutlined, {
style: { fontSize: "14px", color: "red" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"删除"
),
]
),
]
);
};
}; };
// 处理列定义,添加自定义渲染逻辑 // 处理列定义,添加自定义渲染逻辑
@@ -125,142 +298,21 @@ const processedColumns = computed(() => {
// 为序号列添加自定义渲染 // 为序号列添加自定义渲染
if (col.key === "index") { if (col.key === "index") {
processedCol.customRender = ({ index }) => { processedCol.customRender = renderIndexColumn();
return h(
"span",
{
style: {
display: "flex",
justifyContent: "space-between",
},
},
[
h("span", { class: "drag-handle" }, [
createVNode(MenuOutlined, {
style: { fontSize: "14px", color: "#666" },
}),
]),
h("span", {}, index + 1),
]
);
};
} }
// 为节名称列添加自定义渲染 // 为节名称列添加自定义渲染
if (col.key === "name") { if (col.key === "name") {
processedCol.customRender = ({ record }) => { processedCol.customRender = renderNameColumn();
// 检查当前行是否处于编辑状态 }
const isEditing = record.key === editingKey.value;
// 如果处于编辑状态,显示输入框和确认按钮 if (col.key === "resType") {
if (isEditing) { processedCol.customRender = renderResTypeColumn();
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
h("input", {
value: editValue.value,
onInput: (e) => {
editValue.value = e.target.value;
},
style: {
border: "1px solid #d9d9d9",
borderRadius: "4px",
padding: "4px 11px",
width: "200px",
},
}),
h(
"a",
{
href: "javascript:void(0)",
style: { fontSize: "16px", color: "#52c41a" },
onClick: () => saveEdit(record),
},
"✓"
),
]
);
}
// 否则显示正常文本和编辑图标
const Icon = getIconComponent(record.type);
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
createVNode(Icon, { style: { color: "#1890ff" } }),
h("span", {}, record.name),
h(
"a",
{
href: "javascript:void(0)",
style: { marginLeft: "4px", fontSize: "12px" },
onClick: () => startEdit(record),
},
"✎"
),
]
);
};
} }
// 为操作列添加自定义渲染 // 为操作列添加自定义渲染
if (col.key === "action") { if (col.key === "action") {
processedCol.customRender = ({ record }) => { processedCol.customRender = renderActionColumn();
return h(
"span",
{
style: { display: "flex", justifyContent: "center", gap: "12px" },
},
[
// 设置
h("a", { href: "javascript:void(0)" }, [
createVNode(SettingOutlined, {
style: {
fontSize: "14px",
color: "#1890ff",
paddingRight: "10px",
},
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"设置"
),
]),
// 预览
h("a", { href: "javascript:void(0)" }, [
createVNode(EyeOutlined, {
style: { fontSize: "14px", color: "#1890ff" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"预览"
),
]),
// 删除
h(
"a",
{
href: "javascript:void(0)",
onClick: () => handleDelete(record.key),
},
[
createVNode(DeleteOutlined, {
style: { fontSize: "14px", color: "red" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"删除"
),
]
),
]
);
};
} }
return processedCol; return processedCol;
@@ -274,85 +326,13 @@ const processedColumns = computed(() => {
key: "index", key: "index",
width: 80, width: 80,
align: "center", align: "center",
customRender: ({ index }) => { customRender: renderIndexColumn(),
return h(
"span",
{
style: {
display: "flex",
justifyContent: "space-between",
},
},
[
h("span", { class: "drag-handle" }, [
createVNode(MenuOutlined, {
style: { fontSize: "14px", color: "#666" },
}),
]),
h("span", {}, index + 1),
]
);
},
}, },
{ {
title: "节名称", title: "节名称",
key: "name", key: "name",
dataIndex: "name", dataIndex: "name",
customRender: ({ record }) => { customRender: renderNameColumn(),
// 检查当前行是否处于编辑状态
const isEditing = record.key === editingKey.value;
// 如果处于编辑状态,显示输入框和确认按钮
if (isEditing) {
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
h("input", {
value: editValue.value,
onInput: (e) => {
editValue.value = e.target.value;
},
style: {
border: "1px solid #d9d9d9",
borderRadius: "4px",
padding: "4px 11px",
width: "200px",
},
}),
h(
"a",
{
href: "javascript:void(0)",
style: { fontSize: "16px", color: "#52c41a" },
onClick: () => saveEdit(record),
},
"✓"
),
]
);
}
// 否则显示正常文本和编辑图标
const Icon = getIconComponent(record.type);
return h(
"span",
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
[
createVNode(Icon, { style: { color: "#1890ff" } }),
h("span", {}, record.name),
h(
"a",
{
href: "javascript:void(0)",
style: { marginLeft: "4px", fontSize: "12px" },
onClick: () => startEdit(record),
},
"✎"
),
]
);
},
}, },
{ {
title: "类型", title: "类型",
@@ -365,58 +345,7 @@ const processedColumns = computed(() => {
key: "action", key: "action",
width: 220, width: 220,
align: "center", align: "center",
customRender: ({ record }) => { customRender: renderActionColumn(),
return h(
"span",
{ style: { display: "flex", justifyContent: "center", gap: "12px" } },
[
// 设置
h("a", { href: "javascript:void(0)" }, [
createVNode(SettingOutlined, {
style: {
fontSize: "14px",
color: "#1890ff",
paddingRight: "10px",
},
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"设置"
),
]),
// 预览
h("a", { href: "javascript:void(0)" }, [
createVNode(EyeOutlined, {
style: { fontSize: "14px", color: "#1890ff" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"预览"
),
]),
// 删除
h(
"a",
{
href: "javascript:void(0)",
onClick: () => handleDelete(record.key),
},
[
createVNode(DeleteOutlined, {
style: { fontSize: "14px", color: "red" },
}),
h(
"span",
{ style: { marginLeft: "4px", fontSize: "12px" } },
"删除"
),
]
),
]
);
},
}, },
]; ];
}); });
@@ -440,10 +369,6 @@ const components = {
pull: props.allowDragOut, pull: props.allowDragOut,
put: props.allowDragIn, put: props.allowDragIn,
}, },
onStart: () => {},
onEnd: (evt) => {
console.log("拖拽结束,新顺序:", props.data);
},
}, },
{ {
item: ({ element, index }) => item: ({ element, index }) =>