feat(hooks): 添加媒体组件通用hook

- 创建 useMediaComponent hook 处理媒体组件公共逻辑
- 实现本地表单数据响应式拷贝与深度监听更新
- 提供表单字段更新方法并触发事件通知
- 定义文件基础URL常量便于统一管理
- 支持 dialogVideoForm 属性的双向绑定更新
- 集成 Vue Composition API 的 ref 和 watch 功能
This commit is contained in:
陈昱达
2025-11-24 16:07:26 +08:00
parent 7d18bc73ea
commit 1392148b77
4 changed files with 162 additions and 39 deletions

View File

@@ -1,10 +1,13 @@
<script setup>
import { reactive, onMounted, ref, h, watch } from "vue";
import { ElButton, ElInput, ElUpload } from "element-plus";
import { reactive, onMounted, ref, h } from "vue";
import { ElButton, ElInput, ElUpload, ElMessage } from "element-plus";
import BasicTable from "@/components/BasicElTable/BasicTable.vue";
import { getPageListByType } from "@/hooks/useCreateCourseMaps";
import { getType, getMapsItem } from "@/hooks/useCreateCourseMaps";
import { getPageListByType, getType, getMapsItem } from "@/hooks/useCreateCourseMaps";
import apiCourseFile from "@/api/modules/courseFile";
import { useRoute } from "vue-router";
import Cookies from "vue-cookies";
const route = useRoute();
const props = defineProps({
resType: {
type: Number,
@@ -12,25 +15,45 @@ const props = defineProps({
},
});
// 响应式数据
const tableData = ref([]);
const loading = ref(false);
const showDialog = ref(false);
const fileList = ref([]);
// 表单数据
const form = reactive({
name: "",
resType: props.resType,
});
let pagination = reactive({
// 分页配置
const pagination = reactive({
pageSize: 10,
current: 1,
total: 0,
});
// 视频表单数据
const dialogVideoForm = reactive({
name: "",
isDrag: false,
completeSetup: 0,
setupTage: "",
});
const loading = ref(false);
const showDialog = ref(false);
const emit = defineEmits(["chooseItem"]);
// 上传配置
const uploadData = reactive({
headers: {
"XBOE-Access-Token": Cookies.get("token"),
},
data: {
dir: "course",
},
actionUrl: process.env.VUE_APP_SYS_API + "/xboe/sys/xuploader/file/upload",
});
// 表格列配置
const columns = [
{
title: "序号",
@@ -66,13 +89,7 @@ const columns = [
type: "primary",
size: "small",
onClick: () => {
emit("chooseItem", {
...params.row,
isDrag: false,
completeSetup: 0,
setupTage: "",
resType: props.resType,
});
handleChooseItem(params.row);
},
},
"选择"
@@ -82,18 +99,36 @@ const columns = [
},
];
// 事件发射
const emit = defineEmits(["chooseItem"]);
// 处理选择项目
const handleChooseItem = (row) => {
emit("chooseItem", {
...row,
isDrag: false,
completeSetup: 0,
setupTage: "",
resType: props.resType,
});
};
// 分页改变处理
const changePagination = (PAGINATION) => {
pagination = PAGINATION;
Object.assign(pagination, PAGINATION);
getVideoList();
};
// 获取视频列表
const getVideoList = () => {
loading.value = true;
let paramsData = {
const paramsData = {
...form,
pageSize: pagination.pageSize,
pageIndex: pagination.current,
self: true,
};
getPageListByType(paramsData).then((res) => {
loading.value = false;
tableData.value = res.result.list;
@@ -101,7 +136,8 @@ const getVideoList = () => {
});
};
const chooseItem = (type) => {
// 选择项目处理
const chooseItem = () => {
showDialog.value = false;
emit("chooseItem", {
...dialogVideoForm,
@@ -109,29 +145,81 @@ const chooseItem = (type) => {
});
};
const uploadData = reactive({
headers: {
"XBOE-Access-Token": Cookies.get("token"),
},
data: {
dir: "course",
},
actionUrl: process.env.VUE_APP_SYS_API + "/xboe/sys/xuploader/file/upload",
});
const fileList = ref([]);
const uploadSuccess = (result) => {
if (result.status === 200) {
emit("chooseItem", {
...dialogVideoForm,
name: result.result.displayName,
// 上传成功处理
const handleUploadSuccess = (res, file) => {
if (res.status === 200) {
// 上传到课件库
const routeQuery = route.query;
const courseWare = {
fileName: res.result.displayName,
fileType: res.result.fileType,
filePath: res.result.filePath,
resType: props.resType,
...result.result,
orgId: routeQuery.orgId,
orgName: routeQuery.orgName,
duration: routeQuery.duration,
remark: "课程中直接上传",
};
apiCourseFile.saveUpload(courseWare).then((rs) => {
if (rs.status === 200) {
emit("chooseItem", {
...dialogVideoForm,
name: res.result.displayName,
resType: props.resType,
...rs.result,
});
fileList.value = [];
} else {
ElMessage.error(rs.message);
}
});
fileList.value = [];
}
};
// 上传前处理
const handleBeforeUpload = (file) => {
if (file.name.lastIndexOf(".") === -1) {
ElMessage({ message: `文件格式不正确!`, type: "error", offset: 100 });
return false;
}
let fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
fileExtension = fileExtension.toLowerCase();
// 校检文件类型
if (getMapsItem(props.resType).fileType.join(",").indexOf(fileExtension) === -1) {
ElMessage({
message: `文件格式不正确, 请上传正确格式的${getMapsItem(props.resType).name}文件!`,
type: "error",
offset: 100,
});
return false;
}
// 校检文件大小
if (getMapsItem(props.resType).uploadSize) {
const isLt = file.size / 1024 / 1024 < getMapsItem(props.resType).uploadSize;
if (!isLt) {
ElMessage({
message: `上传文件大小不能超过 ${getMapsItem(props.resType).uploadSize} !`,
type: "error",
offset: 100,
});
return false;
}
}
if (props.resType === 50) {
dialogVideoForm.dir = "scorm";
} else {
dialogVideoForm.dir = "course";
}
return true;
};
// 生命周期
onMounted(() => {
getVideoList();
});
@@ -145,8 +233,9 @@ onMounted(() => {
:action="uploadData.actionUrl"
:headers="uploadData.headers"
:data="uploadData.data"
:on-success="uploadSuccess"
:on-success="handleUploadSuccess"
:file-list="fileList"
:before-upload="handleBeforeUpload"
>
<el-button v-if="[10, 20, 40].includes(props.resType)" type="primary"
>上传新{{ getType(props.resType) }}</el-button
@@ -193,4 +282,4 @@ onMounted(() => {
}
}
}
</style>
</style>

View File

@@ -0,0 +1,34 @@
import { ref, watch } from "vue";
/**
* 通用媒体组件hook用于处理媒体组件的公共逻辑
* @param {Object} props - 组件props
* @param {Function} emit - 组件emit函数
*/
export function useMediaComponent(props, emit) {
// Create a reactive copy of the prop for local modifications
const localDialogVideoForm = ref({ ...props.dialogVideoForm });
// Watch for changes in the prop and update the local copy
watch(
() => props.dialogVideoForm,
(newVal) => {
Object.assign(localDialogVideoForm.value, newVal);
},
{ deep: true }
);
// Update form values and emit changes
const updateFormValue = (field, value) => {
localDialogVideoForm.value[field] = value;
emit("update:dialogVideoForm", { ...localDialogVideoForm.value });
};
const fileBaseUrl = "http://home.hzer.xyz:9960/upload";
return {
localDialogVideoForm,
updateFormValue,
fileBaseUrl,
};
}

View File

@@ -66,6 +66,7 @@ const executeCourseOperation = (operationName, data) => {
courseMetadata.chooseIndex = data;
courseMetadata.selectionIndex = null;
isPreview.value = false;
chooseItemData.value = {};
if (courseOperations[operationName]) {
courseOperations[operationName](data);
} else {

View File

@@ -149,7 +149,6 @@ const renderIndexColumn = () => {
// 渲染名称列
const renderNameColumn = () => {
return ({ record }) => {
console.log(record);
// 如果处于编辑状态,显示输入框和确认按钮
if (record.isEdit) {
return h(