Files
fe-manage/src/views/courselibrary/components/professionalmode.vue
陈昱达 688f0dff32 feat(cropper): 添加图片裁剪组件及依赖
- 新增 Cropper 组件,支持图片裁剪、缩放、旋转功能
- 集成 cropperjs 库及其相关元素组件
- 在专业模式页面中引入并使用裁剪组件
- 更新 package.json 和 lock 文件以包含新依赖
- 优化登录过期弹窗按钮文本显示
2025-12-05 10:10:15 +08:00

405 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref, defineOptions, onMounted } from "vue";
import { Plus, Loading } from "@element-plus/icons-vue";
import courseTag from "./courseTag.vue";
import { $message } from "@/utils/useMessage";
import {
ElForm,
ElFormItem,
ElRadio,
ElRadioGroup,
ElUpload,
ElButton,
ElInput,
ElCascader,
ElSelect,
ElSelectV2,
ElTreeSelect,
ElOption,
ElDialog,
} from "element-plus";
import FieldCloud from "@/components/FileCloud/index.vue";
import Cropper from "@/components/Cropper/Cropper.vue";
import { useUpload } from "@/hooks/useUpload";
import { useCourseForm } from "@/hooks/useCourseForm";
import { useRouter } from "vue-router";
const router = useRouter();
defineOptions({
name: "ProfessionalMode",
});
// 使用上传hook
const { fileList, loading, handleChange, beforeUpload } = useUpload();
// 使用表单hook
const { formRef, formState, visibilityOptions, resetForm } = useCourseForm();
import { useFetchCourseList } from "@/hooks/useFetchCourseList";
const {
fetchClassTree,
getUserGroupList,
teachersList,
sysTypeListMap,
courseTags,
loadOrgNode,
curCourseId,
userGroupList,
sysTypeList,
} = useFetchCourseList();
import { useMediaComponent } from "@/hooks/useMediaComponent";
const { fileBaseUrl } = useMediaComponent({});
const handleTagsChange = (tags) => {
console.log("父组件:", tags);
// 限制最多5个标签
if (tags.length > 5) {
this.$message.warning("最多只能选择5个标签");
// 强制限制为5个
tags = tags.slice(0, 5);
return;
}
let ids = "";
tags.forEach((tag) => {
console.log("父组件name : ", tag.tagName);
ids += tag.id + ",";
});
};
const onTagFocus = () => {};
// 文件选择对话框
const dlgFileShow = ref(false);
// 方法定义
const chooseFile = () => {
dlgFileShow.value = true;
};
const dlgCutShow = ref(false);
const changeCourseImage = (img) => {
if (!img.path) {
return;
}
dlgFileShow.value = false;
formState.coverImg = fileBaseUrl + img.path;
dlgCutShow.value = true;
};
const choseChoose = () => {
dlgFileShow.value = false;
};
const success = (e) => {
console.log(e);
formState.coverImg = e;
};
// 表单提交
const handleSubmit = () => {
// formRef.value
// .validate()
// .then(() => {
// console.log("Received values of form:", formState);
// ElMessage.success("表单提交成功");
// })
// .catch((error) => {
// console.log("Validate Failed:", error);
// ElMessage.error("请检查表单填写内容");
// });
console.log("Received values of form:", formState);
$message.success("表单提交成功");
};
// 表单重置
const handleReset = () => {
resetForm(fileList);
};
const next = () => {
// 注意这里的路由跳转需要正确引入和使用vue-router
router.push({
path: "/createcourse",
query: {
orgId: "1693922746301661185",
orgName: "'N'业务",
duration: "1",
},
});
};
// 调用接口
onMounted(() => {
fetchClassTree(1);
getUserGroupList({ keyword: "", page: 1, pageSize: 500 });
});
</script>
<template>
<div class="professional-mode">
<el-form
ref="formRef"
:model="formState"
:label-position="'right'"
label-width="80px"
class="default-form"
@submit.prevent="handleSubmit"
>
<div class="professional-mode-title">
<div class="professional-mode-title-text" style="padding-bottom: 20px">
基本信息
</div>
<div class="professional-mode-form">
<el-form-item
label="课程名称"
prop="name"
:rules="[{ required: true, message: '请输入课程名称' }]"
>
<el-input
size="large"
v-model="formState.name"
placeholder="请输入课程名称"
/>
</el-form-item>
<el-form-item
label="课程分类"
prop="courseCategory"
:rules="[{ required: true, message: '请选择课程分类' }]"
>
<el-cascader
size="large"
v-model="formState.courseCategory"
placeholder="请选择课程分类"
:options="sysTypeListMap"
:props="{
label: 'name',
value: 'id',
}"
/>
</el-form-item>
<el-form-item
label="资源归属"
prop="orgName"
:rules="[{ required: true, message: '请选择资源归属' }]"
>
<el-tree-select
size="large"
check-strictly
lazy
:load="loadOrgNode"
:render-after-expand="false"
v-model="formState.orgName"
:props="{ value: 'id', label: 'name', children: 'children' }"
placeholder="请选择资源归属"
/>
</el-form-item>
<el-form-item
label="授课教师"
prop="lecturer"
:rules="[{ required: true, message: '请选择授课教师' }]"
>
<el-select-v2
size="large"
filterable
multiple
v-model="formState.lecturer"
:options="teachersList"
placeholder="请选择授课教师"
>
</el-select-v2>
</el-form-item>
<el-form-item
label="目标人群"
prop="forUsers"
:rules="[{ required: true, message: '请输入目标人群' }]"
>
<el-input
size="large"
v-model="formState.forUsers"
placeholder="请输入目标人群"
/>
</el-form-item>
<el-form-item label="课程标签" prop="courseTags">
<courseTag
:courseId="curCourseId"
:sysTypeList="sysTypeList"
:initialTags="courseTags"
@change="handleTagsChange"
@focus="onTagFocus"
style="width: 100%"
></courseTag>
</el-form-item>
<el-form-item label="受众" prop="crowds">
<el-select
size="large"
v-model="formState.crowds"
placeholder="请选择受众"
multiple
value-key="id"
>
<el-option
v-for="item in userGroupList"
:key="item.id"
:label="item.name"
:value="item"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="观看设置" prop="device">
<el-radio-group v-model="formState.device">
<el-radio
v-for="item in visibilityOptions"
:key="item.value"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</div>
<div class="professional-mode-title-text" style="padding-top: 0">
课程介绍
</div>
<div class="professional-mode-form">
<el-form-item label="封面介绍" prop="coverIntro">
<div style="display: flex; align-items: flex-end">
<el-upload
v-model:file-list="fileList"
name="avatar"
list-type="picture-card"
class="avatar-uploader"
:show-file-list="false"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
:before-upload="beforeUpload"
:on-change="handleChange"
>
<img
v-if="formState.coverImg"
:src="formState.coverImg"
alt="avatar"
style="width: 100%"
/>
<div v-else class="text-center">
<Loading v-if="loading" class="w30" />
<Plus v-else class="w30" />
<div class="el-upload-text">上传图片</div>
</div>
</el-upload>
<el-button type="primary" link @click="chooseFile" class="ml10"
>选择封面</el-button
>
</div>
<div class="upload-hint">
建议上传800px*450px16:9的图片格式为.png或.jpg大小不超过2M
</div>
</el-form-item>
<el-form-item
label="课程价值"
prop="courseValue"
:rules="[{ required: true, message: '请输入课程价值' }]"
>
<el-input
type="textarea"
size="large"
v-model="formState.courseValue"
:rows="3"
placeholder="请输入课程价值"
/>
</el-form-item>
<el-form-item
label="课程简介"
prop="summary"
:rules="[{ required: true, message: '请输入课程简介' }]"
>
<el-input
type="textarea"
size="large"
v-model="formState.summary"
:rows="4"
placeholder="请输入课程简介"
/>
</el-form-item>
</div>
<div class="form-actions">
<el-button
style="margin-left: 80px; margin-right: 12px"
size="large"
@click="handleReset"
>存草稿</el-button
>
<el-button type="primary" @click="next">创建下一步</el-button>
</div>
</div>
</el-form>
<field-cloud
:show="dlgFileShow"
@choose="changeCourseImage"
@close="choseChoose"
></field-cloud>
<!-- 裁切-->
<Cropper
:img="formState.coverImg"
v-model:show="dlgCutShow"
@crop-success="success"
></Cropper>
</div>
</template>
<style scoped>
.professional-mode {
width: 70%;
.default-form {
width: 100%;
margin-bottom: 30px;
padding: 10px 15px;
}
.professional-mode-title {
width: 100%;
.professional-mode-title-text {
font-size: 15px;
padding: 10px 0;
font-weight: 600;
}
}
.professional-mode-form {
width: 100%;
.upload-hint {
margin-top: 8px;
font-size: 12px;
color: #999;
}
:deep(.el-form-item) {
margin-bottom: 22px;
}
}
.form-actions {
padding: 20px 0;
}
:deep(.el-upload--picture-card) {
width: 200px;
height: 112px;
}
:deep(.el-upload-list--picture-card) {
width: 200px;
}
:deep(.el-upload-list--picture-card .el-upload-list__item) {
width: 200px;
height: 112px;
}
}
</style>