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> <script setup>
import { reactive, onMounted, ref, h, watch } from "vue"; import { reactive, onMounted, ref, h } from "vue";
import { ElButton, ElInput, ElUpload } from "element-plus"; import { ElButton, ElInput, ElUpload, ElMessage } from "element-plus";
import BasicTable from "@/components/BasicElTable/BasicTable.vue"; import BasicTable from "@/components/BasicElTable/BasicTable.vue";
import { getPageListByType } from "@/hooks/useCreateCourseMaps"; import { getPageListByType, getType, getMapsItem } from "@/hooks/useCreateCourseMaps";
import { getType, getMapsItem } from "@/hooks/useCreateCourseMaps"; import apiCourseFile from "@/api/modules/courseFile";
import { useRoute } from "vue-router";
import Cookies from "vue-cookies"; import Cookies from "vue-cookies";
const route = useRoute();
const props = defineProps({ const props = defineProps({
resType: { resType: {
type: Number, type: Number,
@@ -12,25 +15,45 @@ const props = defineProps({
}, },
}); });
// 响应式数据
const tableData = ref([]); const tableData = ref([]);
const loading = ref(false);
const showDialog = ref(false);
const fileList = ref([]);
// 表单数据
const form = reactive({ const form = reactive({
name: "", name: "",
resType: props.resType, resType: props.resType,
}); });
let pagination = reactive({
// 分页配置
const pagination = reactive({
pageSize: 10, pageSize: 10,
current: 1, current: 1,
total: 0, total: 0,
}); });
// 视频表单数据
const dialogVideoForm = reactive({ const dialogVideoForm = reactive({
name: "", name: "",
isDrag: false, isDrag: false,
completeSetup: 0, completeSetup: 0,
setupTage: "", 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 = [ const columns = [
{ {
title: "序号", title: "序号",
@@ -66,13 +89,7 @@ const columns = [
type: "primary", type: "primary",
size: "small", size: "small",
onClick: () => { onClick: () => {
emit("chooseItem", { handleChooseItem(params.row);
...params.row,
isDrag: false,
completeSetup: 0,
setupTage: "",
resType: props.resType,
});
}, },
}, },
"选择" "选择"
@@ -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) => { const changePagination = (PAGINATION) => {
pagination = PAGINATION; Object.assign(pagination, PAGINATION);
getVideoList(); getVideoList();
}; };
// 获取视频列表
const getVideoList = () => { const getVideoList = () => {
loading.value = true; loading.value = true;
let paramsData = { const paramsData = {
...form, ...form,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
pageIndex: pagination.current, pageIndex: pagination.current,
self: true, self: true,
}; };
getPageListByType(paramsData).then((res) => { getPageListByType(paramsData).then((res) => {
loading.value = false; loading.value = false;
tableData.value = res.result.list; tableData.value = res.result.list;
@@ -101,7 +136,8 @@ const getVideoList = () => {
}); });
}; };
const chooseItem = (type) => { // 选择项目处理
const chooseItem = () => {
showDialog.value = false; showDialog.value = false;
emit("chooseItem", { emit("chooseItem", {
...dialogVideoForm, ...dialogVideoForm,
@@ -109,29 +145,81 @@ const chooseItem = (type) => {
}); });
}; };
const uploadData = reactive({ // 上传成功处理
headers: { const handleUploadSuccess = (res, file) => {
"XBOE-Access-Token": Cookies.get("token"), if (res.status === 200) {
}, // 上传到课件库
data: { const routeQuery = route.query;
dir: "course", const courseWare = {
}, fileName: res.result.displayName,
actionUrl: process.env.VUE_APP_SYS_API + "/xboe/sys/xuploader/file/upload", fileType: res.result.fileType,
}); filePath: res.result.filePath,
const fileList = ref([]);
const uploadSuccess = (result) => {
if (result.status === 200) {
emit("chooseItem", {
...dialogVideoForm,
name: result.result.displayName,
resType: props.resType, 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(() => { onMounted(() => {
getVideoList(); getVideoList();
}); });
@@ -145,8 +233,9 @@ onMounted(() => {
:action="uploadData.actionUrl" :action="uploadData.actionUrl"
:headers="uploadData.headers" :headers="uploadData.headers"
:data="uploadData.data" :data="uploadData.data"
:on-success="uploadSuccess" :on-success="handleUploadSuccess"
:file-list="fileList" :file-list="fileList"
:before-upload="handleBeforeUpload"
> >
<el-button v-if="[10, 20, 40].includes(props.resType)" type="primary" <el-button v-if="[10, 20, 40].includes(props.resType)" type="primary"
>上传新{{ getType(props.resType) }}</el-button >上传新{{ 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.chooseIndex = data;
courseMetadata.selectionIndex = null; courseMetadata.selectionIndex = null;
isPreview.value = false; isPreview.value = false;
chooseItemData.value = {};
if (courseOperations[operationName]) { if (courseOperations[operationName]) {
courseOperations[operationName](data); courseOperations[operationName](data);
} else { } else {

View File

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