|
@@ -0,0 +1,395 @@
|
|
|
+<template>
|
|
|
+ <VideoLineGraph
|
|
|
+ ref="G6GraphRef"
|
|
|
+ :graphData="props.graphData"
|
|
|
+ :ifSendMsg="ifSendMsg"
|
|
|
+ :pipData="pipData"
|
|
|
+ @onEdit="handleEdit"
|
|
|
+ />
|
|
|
+ <el-drawer
|
|
|
+ v-model="poltDrawerRef"
|
|
|
+ class="editor-drawer"
|
|
|
+ direction="rtl"
|
|
|
+ :size="380"
|
|
|
+ :show-close="false"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ >
|
|
|
+ <template #header="{ titleId }">
|
|
|
+ <h2 :id="titleId" class="drawer-title">剧情模块</h2>
|
|
|
+ <el-button text @click="handleCancel"> 取消更改 </el-button>
|
|
|
+ <el-button type="primary" @click="handleSave"> 保存 </el-button>
|
|
|
+ </template>
|
|
|
+ <div class="plot-module-box">
|
|
|
+ <h3 class="drawer-title">剧情模块设置</h3>
|
|
|
+ <el-form
|
|
|
+ ref="moduleForm"
|
|
|
+ :model="moduleFormRef"
|
|
|
+ :rules="moduleFormRuleRef"
|
|
|
+ label-width="auto"
|
|
|
+ status-icon
|
|
|
+ >
|
|
|
+ <el-form-item
|
|
|
+ label="剧情名称"
|
|
|
+ prop="plotName"
|
|
|
+ placeholder="请输入剧情名称"
|
|
|
+ >
|
|
|
+ <el-input v-model="moduleFormRef.plotName" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="剧情视频素材" prop="plotVideoSource">
|
|
|
+ <el-select
|
|
|
+ v-model="moduleFormRef.plotVideoSource"
|
|
|
+ placeholder="请选择剧情视频素材"
|
|
|
+ @change="
|
|
|
+ (sourceId) =>
|
|
|
+ handleChangePlotVideoSource(sourceId, props.videoSources)
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in props.videoSources"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="剧情封面素材" prop="plotImageSource">
|
|
|
+ <el-select
|
|
|
+ v-model="moduleFormRef.plotImageSource"
|
|
|
+ placeholder="请选择剧情封面素材"
|
|
|
+ @change="
|
|
|
+ (sourceId) =>
|
|
|
+ handleChangePlotImageSource(sourceId, props.imageSources)
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in props.imageSources"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="plot-options-box"
|
|
|
+ v-if="poltDataRef.videoId && poltDataRef.imageId"
|
|
|
+ >
|
|
|
+ <div class="plot-options-title">
|
|
|
+ <h3 class="drawer-title">剧情分支设置</h3>
|
|
|
+ <!-- <el-button text size="small" type="primary" @click="handleReview"> 预览 </el-button> -->
|
|
|
+ </div>
|
|
|
+ <div class="view-box">
|
|
|
+ <img style="width: 100%" :src="poltDataRef.imageUrl" alt="" />
|
|
|
+ <div v-if="questionRef" class="quesetion">问:{{ questionRef }}</div>
|
|
|
+ <div
|
|
|
+ class="option-btn-box"
|
|
|
+ :style="{ flexWrap: optionsRef.length >= 4 ? 'wrap' : 'nowrap' }"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="option-btn-item"
|
|
|
+ v-for="(option, index) in optionsRef"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <div class="option-btn">
|
|
|
+ {{ option.optionType }}:{{ option.optionName }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="question-wrapper">
|
|
|
+ <h4 class="drawer-title">问题内容</h4>
|
|
|
+ <div class="question-content">
|
|
|
+ <el-input
|
|
|
+ v-model="questionRef"
|
|
|
+ rows="3"
|
|
|
+ resize="none"
|
|
|
+ maxlength="50"
|
|
|
+ placeholder="请输入问题内容"
|
|
|
+ show-word-limit
|
|
|
+ clearable
|
|
|
+ type="textarea"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="options-wrapper" v-if="optionsRef.length > 0">
|
|
|
+ <div class="options-title">
|
|
|
+ <h4 class="drawer-title">分支选项</h4>
|
|
|
+ <!-- 这里不能新增,节点类型不知道 -->
|
|
|
+ <!-- <el-button text size="small" type="primary" @click="handleAddOptions">
|
|
|
+ 添加分支
|
|
|
+ </el-button> -->
|
|
|
+ </div>
|
|
|
+ <div class="options-content">
|
|
|
+ <div
|
|
|
+ class="options-card"
|
|
|
+ v-for="(item, index) in optionsRef"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <div class="option-name">
|
|
|
+ 选项{{ item.optionType }} > 剧情模块:{{ item.optionName }}
|
|
|
+ </div>
|
|
|
+ <div class="option-input">
|
|
|
+ <el-input
|
|
|
+ v-model="item.optionName"
|
|
|
+ rows="3"
|
|
|
+ resize="none"
|
|
|
+ maxlength="16"
|
|
|
+ placeholder="请输入选项名称"
|
|
|
+ show-word-limit
|
|
|
+ clearable
|
|
|
+ type="textarea"
|
|
|
+ @change="(value) => handleCheckSameName(value, index)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <el-drawer
|
|
|
+ v-model="jumpDrawerRef"
|
|
|
+ class="editor-drawer"
|
|
|
+ direction="rtl"
|
|
|
+ :size="380"
|
|
|
+ :show-close="false"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ >
|
|
|
+ <template #header="{ titleId }">
|
|
|
+ <h2 :id="titleId" class="drawer-title">跳转模块</h2>
|
|
|
+ <el-button text @click="handleJumpCancel"> 取消更改 </el-button>
|
|
|
+ <el-button type="primary" @click="handleJumpSave"> 保存 </el-button>
|
|
|
+ </template>
|
|
|
+ <div class="jump-module-box">
|
|
|
+ <h3 class="drawer-title">跳转模块设置</h3>
|
|
|
+ <el-form
|
|
|
+ ref="jumpForm"
|
|
|
+ :model="jumpFormRef"
|
|
|
+ :rules="jumpFormRuleRef"
|
|
|
+ label-width="auto"
|
|
|
+ status-icon
|
|
|
+ >
|
|
|
+ <el-form-item label="剧情素材" prop="jumpNodeId">
|
|
|
+ <el-select
|
|
|
+ v-model="jumpFormRef.jumpNodeId"
|
|
|
+ placeholder="请选择剧情素材"
|
|
|
+ @change="handleChangeJumpTarget"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in canJumpNodeListRef"
|
|
|
+ :key="item.jumpNodeId"
|
|
|
+ :label="item.jumpNodeName"
|
|
|
+ :value="item.jumpNodeId"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: "VideoLineEditor",
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref } from "vue";
|
|
|
+import VideoLineGraph from "../VideoLineGraph/index.vue";
|
|
|
+import usePoltEditorDrawer from "./hooks/usePoltEditorDrawer.js";
|
|
|
+import useModuleForm from "./hooks/useModuleForm.js";
|
|
|
+import useJumpEditorDrawer from "./hooks/useJumpEditorDrawer.js";
|
|
|
+const props = defineProps({
|
|
|
+ // 初始化图表数据
|
|
|
+ graphData: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
+ // 视频资源列表
|
|
|
+ videoSources: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ // 图片资源列表
|
|
|
+ imageSources: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ // 是否需要对外发送信息
|
|
|
+ ifSendMsg: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ // 外部调用者传递的数据, ifSendMsg 为 false时,说明没有外部调用,此参数就无意义
|
|
|
+ pipData: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+// 当前选中的节点
|
|
|
+const curNodeRef = ref(null);
|
|
|
+// 画布
|
|
|
+const graphRef = ref(null);
|
|
|
+// 编辑器组件实例
|
|
|
+const G6GraphRef = ref(null);
|
|
|
+
|
|
|
+const { moduleFormRef, moduleFormRuleRef } = useModuleForm();
|
|
|
+
|
|
|
+const {
|
|
|
+ poltDrawerRef,
|
|
|
+ poltDataRef,
|
|
|
+ optionsRef,
|
|
|
+ questionRef,
|
|
|
+ handleChangePlotVideoSource,
|
|
|
+ handleChangePlotImageSource,
|
|
|
+ // handleAddOptions,
|
|
|
+ handleCheckSameName,
|
|
|
+ handleSave,
|
|
|
+ handleCancel,
|
|
|
+} = usePoltEditorDrawer(curNodeRef, graphRef, moduleFormRef, props);
|
|
|
+
|
|
|
+const {
|
|
|
+ jumpDrawerRef,
|
|
|
+ jumpFormRef,
|
|
|
+ jumpFormRuleRef,
|
|
|
+ canJumpNodeListRef,
|
|
|
+ updataCanJumpNodeList,
|
|
|
+ handleChangeJumpTarget,
|
|
|
+ handleJumpCancel,
|
|
|
+ handleJumpSave,
|
|
|
+} = useJumpEditorDrawer(curNodeRef, graphRef);
|
|
|
+
|
|
|
+// 触发编辑,打开弹窗
|
|
|
+const handleEdit = (node, graph) => {
|
|
|
+ graphRef.value = graph;
|
|
|
+ curNodeRef.value = node;
|
|
|
+ const model = node.getModel();
|
|
|
+ switch (model.type) {
|
|
|
+ case "root-node":
|
|
|
+ case "plot-node":
|
|
|
+ poltDrawerRef.value = true;
|
|
|
+ break;
|
|
|
+ case "jump-node":
|
|
|
+ updataCanJumpNodeList();
|
|
|
+ jumpDrawerRef.value = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const getGraphData = () => {
|
|
|
+ return G6GraphRef.value.getGraphData();
|
|
|
+};
|
|
|
+
|
|
|
+defineExpose({
|
|
|
+ getGraphData,
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.drawer-title {
|
|
|
+ line-height: 25px;
|
|
|
+ color: #212121;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+.plot-module-box,
|
|
|
+.plot-options-box,
|
|
|
+.jump-module-box {
|
|
|
+ width: 100%;
|
|
|
+ padding: 0 16px;
|
|
|
+ border-bottom: 1px solid #e7e7e7;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ h3 {
|
|
|
+ padding: 12px 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+.plot-options-box {
|
|
|
+ .plot-options-title {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
+ .view-box {
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ .quesetion {
|
|
|
+ width: 100%;
|
|
|
+ position: absolute;
|
|
|
+ padding: 8px 16px;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ background-color: rgba($color: #000000, $alpha: 0.6);
|
|
|
+ color: #fff;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ .option-btn-box {
|
|
|
+ width: 100%;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 10px;
|
|
|
+ left: 0;
|
|
|
+ display: flex;
|
|
|
+ .option-btn-item {
|
|
|
+ width: 50%;
|
|
|
+ padding: 2px;
|
|
|
+ .option-btn {
|
|
|
+ border: 1px solid #000;
|
|
|
+ background-color: rgba($color: #ffffff, $alpha: 0.6);
|
|
|
+ color: #000000;
|
|
|
+ font-size: 8px;
|
|
|
+ width: 100%;
|
|
|
+ height: 28px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-start;
|
|
|
+ padding: 0 2px;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .question-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ padding-top: 12px;
|
|
|
+ .question-content {
|
|
|
+ background-color: #f5f6f7;
|
|
|
+ padding: 12px 10px;
|
|
|
+ margin-top: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .options-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ padding: 12px 0;
|
|
|
+ .options-title {
|
|
|
+ widows: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
+ .options-content {
|
|
|
+ width: 100%;
|
|
|
+ .options-card {
|
|
|
+ width: 100%;
|
|
|
+ background-color: #f5f6f7;
|
|
|
+ padding: 12px 10px;
|
|
|
+ margin-top: 8px;
|
|
|
+ .option-name {
|
|
|
+ width: 100%;
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+ .option-input {
|
|
|
+ width: 100%;
|
|
|
+ padding-top: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|