middleground_code_v2/src/views/LinkUp/compoment/addDialogChunk.vue

2960 lines
83 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.

<template>
<div class="addDialogChunk">
<base-right-dialog
@handleClose="examineHandleClose"
:dialogVisible="examineOperateDialog"
size="100%"
:appendBody="true"
:loading="sceneLoading"
:footerShow="true"
:submitShow="true"
@handleConfirmClick="handleConfirmClick"
:submitTitle="'发布'"
:submitIcon="'el-icon-upload2'"
:title="sceneName"
>
<template #operateTitle>
<img
style="width: 15px; height: 15px; margin-left: 10px; cursor: pointer"
src="../images/edit.svg"
@click="editSceneName"
/>
</template>
<div
class="rightDialogClass_main drawContent"
v-if="drawShowList.length > 0"
style="background: #fff"
>
<!-- 步骤画布 -->
<div class="drawCanvas" v-show="!representation">
<div
class="drawItem"
v-for="(ele, index) in drawShowList"
:key="ele.value"
>
<div class="title">{{ ele.title }}</div>
<div
@click="selectDrawItem(index)"
class="drawBox"
:class="{ active: index == drawSelectIndex }"
>
<template v-if="index == 0">
<div class="img">
<img src="../images/pingtai.svg" v-if="triggerMode == 2" />
<img
src="../images/naozhong.svg"
v-else-if="triggerMode == 1"
/>
<img src="../images/api.svg" v-else-if="triggerMode == 4" />
<img src="../images/point.svg" v-else-if="triggerMode == 3" />
</div>
<div style="width: calc(100% - 60px)">
<div class="actionName">
{{ index + 1 + "." + ele.actionName }}
</div>
<div
class="content DescribeContent"
v-if="!ele.options.taskType"
>
{{ ele.content }}
</div>
<div class="content" v-else>
{{ getTimeDivide(ele.options.taskType) }}
</div>
</div>
</template>
<template v-else>
<div
:class="
ele.options && ele.options.iconUrl ? 'iconStyle' : 'img'
"
>
<img
v-if="ele.options && ele.options.iconUrl"
:src="
ele.options.iconUrl
? ele.options.iconUrl.split('/wwwroot')[1]
: ''
"
/>
<img v-else src="../images/huaO.svg" />
</div>
<div style="width: calc(100% - 60px)">
<div class="actionName" v-if="!ele.options.appName">
{{ index + 1 + "." + ele.actionName }}
</div>
<div
class="actionName"
style="display: flex; align-items: center"
v-else
>
{{ index + 1 + "." + ele.options.appName }}
</div>
<div
class="content DescribeContent"
v-if="ele.options.stepDescribe"
>
{{ ele.options.stepDescribe }}
</div>
<div class="content" v-else>{{ ele.content }}</div>
</div>
</template>
<div
@click.stop="delDrawItem(ele, index)"
v-if="index != 0"
class="del"
>
<img src="../images/close.svg" />
</div>
</div>
<div class="line-wrapper" v-if="index != drawShowList.length - 1">
<div class="line"></div>
<img
src="../images/addBtn.svg"
@click.stop="insertDrawItem(ele, index)"
/>
</div>
</div>
<div @click="addDrwaItem" class="drawItem drawItemBtn">
添加新步骤
</div>
</div>
<!-- 动态内容及其表达式 -->
<div
class="drawCanvas"
style="align-items: flex-start; padding: 0"
v-show="representation"
>
<div class="repContainer">
<el-tabs v-model="representationActiveName">
<el-tab-pane label="动态内容" name="first"></el-tab-pane>
<el-tab-pane label="表达式" name="second"></el-tab-pane>
</el-tabs>
<div class="closeRepContainer" @click="closeRepContainer">
<i class="el-icon-close"></i> 关闭
</div>
<div class="treeNodeBox" v-if="representationActiveName == 'first'">
<tree-node
v-for="(value, key) in treeData"
:key="key"
:node-key="key"
:node-value="value"
:path="key"
:selected-node="selectedNode"
@node-selected="handleNodeSelected"
/>
</div>
<div
class="treeNodeBox"
v-if="representationActiveName == 'second'"
>
<el-tabs tab-position="left" style="height: 100%">
<el-tab-pane
v-for="(item, index) in representationData"
:label="item.label"
>
<template v-for="(row, rowIndex) in item.children">
<div class="repItem" @click="handleTagSelected(row.label)">
<el-tag size="small">
{{ row.label }}
</el-tag>
<span class="repItemValue">{{ row.value }}</span>
</div>
</template>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
<!-- 操作区 -->
<!-- 第一步的时候单独处理 -->
<div class="drawAction" v-if="drawSelectIndex == 0">
<el-tabs v-model="activeTabName">
<el-tab-pane label="1. 选择操作" name="选择操作">
<div
class="currentDrawBox"
style="padding: 10px"
v-if="drawShowList.length > 0"
>
<div class="drawimg">
<img src="../images/pingtai.svg" v-if="triggerMode == 2" />
<img
src="../images/naozhong.svg"
v-else-if="triggerMode == 1"
/>
<img src="../images/api.svg" v-else-if="triggerMode == 4" />
<img src="../images/point.svg" v-else-if="triggerMode == 3" />
</div>
<div style="width: calc(100% - 60px)">
<div class="actionName">
{{ 1 + "." + drawShowList[0].actionName }}
</div>
<div class="content" v-if="!drawShowList[0].options.taskType">
{{ drawShowList[0].content }}
</div>
<div class="content" v-else>
{{ getTimeDivide(drawShowList[0].options.taskType) }}
</div>
</div>
</div>
<div class="timeWrap">
<template v-for="(item, index) in timeDivide">
<div
@click="handleTimeItem(item, index)"
:class="{ activTimeItem: activTimeIndex == item.id }"
class="timeItem"
>
<div class="imgBox">
<img src="../images/shandian.svg" />
</div>
<div>
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
</div>
</div>
</template>
</div>
</el-tab-pane>
<el-tab-pane
label="2. 配置"
name="配置"
:disabled="!this.activTimeIndex"
>
<div
class="currentDrawBox"
style="padding: 10px"
v-if="drawShowList.length > 0"
>
<div class="drawimg">
<img src="../images/pingtai.svg" v-if="triggerMode == 2" />
<img
src="../images/naozhong.svg"
v-else-if="triggerMode == 1"
/>
<img src="../images/api.svg" v-else-if="triggerMode == 4" />
<img src="../images/point.svg" v-else-if="triggerMode == 3" />
</div>
<div style="width: calc(100% - 60px)">
<div class="actionName">
{{ 1 + "." + drawShowList[0].actionName }}
</div>
<div class="content" v-if="!drawShowList[0].options.taskType">
{{ drawShowList[0].content }}
</div>
<div class="content" v-else>
{{ getTimeDivide(drawShowList[0].options.taskType) }}
</div>
</div>
</div>
<div class="crontabBox">
<div class="crontabBoxTitle">cron表达式</div>
<FishCrontab
ref="fishCrontab"
class="crontab"
@fill="crontabFill"
:expression="expression"
:fiveTimes="false"
></FishCrontab>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 其他步骤 -->
<div class="drawAction" v-else v-loading="drawMask">
<el-tabs
v-model="activeOtherTabName"
@tab-click="handleOtherTabClick"
>
<el-tab-pane label="1. 选择应用" name="选择应用">
<!-- 选中的操作步骤 -->
<div class="drawItemBox">
<div class="currentDrawBox" style="width: 100%">
<div class="img">
<img class="appIcon" src="../images/applicotion.svg" />
</div>
<div style="width: calc(100% - 64px)">
<!-- 应用名称 -->
<div class="actionName" v-if="CurrentAppRow.appName">
{{ CurrentAppRow.appName }}
</div>
<!-- 步骤描述 -->
<div class="content" v-if="CurrentAppRow.description">
{{ CurrentAppRow.description }}
</div>
</div>
</div>
</div>
<div class="search">
<el-input
class="input"
v-model="appCodeOrName"
placeholder="请输入"
clearable
style="width: 210px"
@keydown.enter.native="searchApp"
></el-input>
<el-button
type="primary"
style="margin-right: 5px"
size="small"
@click="searchApp"
>查询</el-button
>
</div>
<div
class="app"
style="max-height: calc(-345px + 100vh); overflow: auto"
>
<div class="applist">
<div
v-for="(app, index) in appList"
class="appItem"
@click="hangleAppItem(app, index)"
:class="{ active: appActivIndex == app.id }"
>
<img class="appIcon" src="../images/applicotion.svg" />
<div class="name">{{ app.name }}</div>
</div>
</div>
</div>
<div class="DescribeDataBox">
<span style="font-size: 14px">步骤描述</span>
<el-input
class="input"
v-model="description"
placeholder="请输入步骤描述"
clearable
@blur="changeDescription"
type="textarea"
></el-input>
</div>
</el-tab-pane>
<el-tab-pane
label="2. 选择操作"
name="选择操作"
:disabled="!this.appActivIndex"
>
<!-- 选中的操作步骤 -->
<div class="drawItemBox">
<div class="currentDrawBox" style="width: 100%">
<div class="img">
<img class="appIcon" src="../images/applicotion.svg" />
</div>
<div style="width: calc(100% - 64px)">
<!-- 应用名称 -->
<div class="actionName" v-if="CurrentAppRow.appName">
{{ CurrentAppRow.appName }}
</div>
<!-- 步骤描述 -->
<div class="content" v-if="CurrentAppRow.description">
{{ CurrentAppRow.description }}
</div>
</div>
</div>
</div>
<div class="search">
<el-input
class="input"
v-model="operateCodeOrName"
placeholder="请输入"
style="width: 210px"
clearable
@keydown.enter.native="searchApiPlugin"
></el-input>
<el-button type="primary" size="small" @click="searchApiPlugin"
>查询</el-button
>
</div>
<el-radio-group
v-model="activeApiPliginTabName"
@change="ApiPliginTabChangeEvent"
>
<el-radio-button label="API接口">API接口</el-radio-button>
<el-radio-button label="插件">插件</el-radio-button>
</el-radio-group>
<div class="ApiPliginBox app">
<div
class="applist"
v-show="activeApiPliginTabName == 'API接口'"
>
<div
v-for="(item, index) in apiList"
class="appItem"
@click="hangleApiClickEvent(item, index)"
:class="{ active: apiIdActiv == item.id }"
>
<div class="imgBox">
<img src="../images/shandian.svg" />
</div>
<div>
<div class="name">{{ item.apiName }}</div>
<div class="name">{{ item.apiPath }}</div>
</div>
</div>
</div>
<div class="applist" v-show="activeApiPliginTabName == '插件'">
<div
v-for="(item, index) in pluginList"
class="appItem"
@click="hanglePluginClickEvent(item, index)"
:class="{ active: pluginActiv == item.id }"
>
<div class="imgBox">
<img src="../images/shandian.svg" />
</div>
<div>
<div class="name">{{ item.plugName }}</div>
<div class="name">{{ item.plugDescribe }}</div>
</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane
label="3. 选择账号"
name="选择账号"
:disabled="
!this.appActivIndex || !(this.apiIdActiv || this.pluginActiv)
"
>
<!-- 选中的操作步骤 -->
<div class="drawItemBox">
<div class="currentDrawBox" style="width: 100%">
<div class="img">
<img class="appIcon" src="../images/applicotion.svg" />
</div>
<div style="width: calc(100% - 64px)">
<!-- 应用名称 -->
<div class="actionName" v-if="CurrentAppRow.appName">
{{ CurrentAppRow.appName }}
</div>
<!-- 步骤描述 -->
<div class="content" v-if="CurrentAppRow.description">
{{ CurrentAppRow.description }}
</div>
</div>
</div>
</div>
<div class="userList">
<div @click="addAccount" class="userItem userBtn">
<i
class="el-icon-plus"
style="font-size: 12px; margin-right: 5px"
></i>
添加账户
</div>
<div class="userContentBox">
<div
v-for="(item, index) in userList"
class="userItem"
style="justify-content: flex-start"
@click="handleUserClickEvent(item, index)"
:class="{ active: userActivId == item.id }"
>
<div class="status">
<i
class="el-icon-success"
style="color: #7ec050"
v-if="item.status == 1"
></i>
<i
class="el-icon-error"
style="color: #f56c6c"
v-else-if="item.status == 0"
></i>
<i
class="el-icon-remove"
style="color: #919398"
v-else
></i>
</div>
<div>
<div class="userName">{{ item.name }}</div>
<div class="content" style="margin-top: 5px">
最新编辑时间:{{ item.modify_time }}
</div>
</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane
label="4. 配置"
name="配置应用"
:disabled="
!this.appActivIndex ||
!(this.apiIdActiv || this.pluginActiv) ||
!this.userActivId
"
>
<!-- 选中的操作步骤 -->
<div class="drawItemBox">
<div class="currentDrawBox" style="width: 100%">
<div class="img">
<img class="appIcon" src="../images/applicotion.svg" />
</div>
<div style="width: calc(100% - 64px)">
<!-- 应用名称 -->
<div class="actionName" v-if="CurrentAppRow.appName">
{{ CurrentAppRow.appName }}
</div>
<!-- 步骤描述 -->
<div class="content" v-if="CurrentAppRow.description">
{{ CurrentAppRow.description }}
</div>
</div>
</div>
</div>
<div class="outsideContent">
<!-- 选择表 -->
<el-form
ref="outsideForm"
:model="outsideFormData"
:rules="outsideRules"
label-width="80px"
label-position="top"
style="margin-bottom: 10px"
>
<el-form-item label="选择表">
<el-select
v-model="outsideFormData.tableName"
placeholder="请选择表"
@change="outsideSelectChange"
style="width: 100%"
>
<el-option
v-for="(el, index) in outsideOptions"
:key="index"
:label="el.table_name"
:value="el.table_name"
></el-option>
</el-select>
</el-form-item>
</el-form>
<!-- 表字段 -->
<el-collapse
v-model="activeNames"
v-if="outsideColumns.length > 0"
>
<el-collapse-item title="查询条件配置" name="1">
<el-form
ref="ColumnsForm"
:model="ColumnsFormData"
:rules="ColumnsRules"
label-width="80px"
label-position="top"
style="margin: 0 10px"
>
<div ref="editorContainer">
<div
v-for="(row, rowIndex) in outsideColumns"
:key="rowIndex"
>
<el-form-item :label="row.column_name">
<div class="editor-container">
<div
class="content-editor"
:ref="
'contentEditor' + row.column_name + rowIndex
"
contenteditable="true"
@keydown="
handleKeyDown(
$event,
row.column_name,
rowIndex
)
"
@input="
handleInput($event, row.column_name, rowIndex)
"
@click="
handleEditorClick(
$event,
row.column_name,
rowIndex
)
"
@paste="
handlePaste($event, row.column_name, rowIndex)
"
@focus="
handleEditorFocus(
$event,
row.column_name,
rowIndex
)
"
@blur="
handleEditorBlur(
$event,
row.column_name,
rowIndex
)
"
></div>
<div
class="editorIcon"
@click.stop="
handleAddNodeToEditor(
row.column_name,
rowIndex
)
"
>
<i class="el-icon-circle-plus"></i>
</div>
<div
class="clearIcon"
@click.stop="
handleClearNodeToEditor(
row.column_name,
rowIndex
)
"
>
<i class="el-icon-circle-close"></i>
</div>
</div>
</el-form-item>
</div>
</div>
</el-form>
</el-collapse-item>
</el-collapse>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</base-right-dialog>
<editSence ref="editSence" @handleConfirmClick="updateTitle"></editSence>
<addAccount
ref="addAccount"
@handleConfirmClick="addAccountConfirmClick"
></addAccount>
</div>
</template>
<script>
// 导入编辑场景组件
import baseRightDialog from "@/components/base/baseRightDialog/index.vue";
import editSence from "./editSence.vue";
import { authApi } from "@/api/apis/auth";
// 导入步骤添加和使用的常量
import { stepAdd, stepUse, timeDivide, representationData } from "./constant";
// 导入 cron 表达式组件
import FishCrontab from "fish-crontab";
// 导入添加账号组件
import addAccount from "./addAccount.vue";
import baseForm from "@/components/base/baseNewForm";
import treeNode from "./TreeNode";
import IconsDialog from "../../tool/build/IconsDialog.vue";
export default {
components: {
editSence,
baseRightDialog,
FishCrontab,
addAccount,
baseForm,
treeNode,
IconsDialog,
},
data() {
return {
examineOperateDialog: false, //判断是都打开弹窗
sceneLoading: false, //遮照
sceneName: "", //场景名称
sceneID: "", //场景ID
// 画布列表,对应右侧操作区域
drawShowList: [],
drawSelectIndex: 0, // 当前选中的画布索引
//当步骤为1时
timeDivide: timeDivide,
activeTabName: "选择操作", // 显示的操作栏
activTimeIndex: "", // 当前选择操作的类型
expression: "", //cron表达式
// 其他步骤
activeOtherTabName: "",
CurrentAppRow: {}, // 当前选择的应用数据
appCodeOrName: "", //应用搜索条件
drawMask: false,
// 应用列表
appList: [],
appCodeOrName: "", // 应用搜索关键字
appActivIndex: "", // 当前选中的应用 ID
description: "", //应用描述
operateCodeOrName: "", //api 或者 插件的搜索
activeApiPliginTabName: "API接口", // 当前操作是 API 还是插件
// API 插件列表
pluginList: [],
// API 接口列表
apiList: [],
apiIdActiv: "", // 选择的 API ID
pluginActiv: "", // 选择的插件 ID
// 用户列表
userList: [],
userActivId: "", // 选择的用户 ID
outsideFormRow: [
{
elCol: [
{
label: "选择表",
prop: "tableName",
tag: "elSelect",
span: 24,
},
],
},
],
outsideRules: {
tableName: [
{
required: true,
message: "请选择表",
trigger: "change, blur",
},
],
},
outsideFormData: {
tableName: "",
},
outsideOptions: [],
outsideColumns: [], //表字段
activeNames: "1", //展开
ColumnsFormData: {},
ColumnsRules: {},
representation: false,
selectedNode: "",
representationActiveName: "first",
treeData: {
user: {
id: 1001,
profile: {
name: "李四",
email: "lisi@example.com",
settings: {
theme: "dark",
notifications: {
email: true,
sms: false,
push: true,
},
},
},
permissions: ["read", "write", "admin"],
metadata: {
created_at: "2023-01-15T10:30:00Z",
updated_at: "2024-03-20T15:45:30Z",
version: 2.1,
},
},
config: {
database: {
host: "localhost",
port: 5432,
ssl: false,
},
features: {
experimental: ["ai_assist", "real_time_sync"],
stable: ["user_management", "data_export"],
},
},
},
currenrActiveNodeRef: "",
lastSelectedTokenIndex: -1,
tokenCounter: 0,
representationData: representationData,
};
},
methods: {
/**
* 打开弹窗
* @param {string} sceneID 场景 ID
* @param {string} sceneName 场景名称
* @param {string} type 操作类型
* @param {number} triggerMode 触发模式
*/
openDialog(sceneID, sceneName, type, triggerMode) {
// 更新场景 ID
this.sceneID = sceneID;
// 更新场景类型
this.sceneType = type;
// 更新场景名称
this.sceneName = sceneName;
// 更新触发模式
this.triggerMode = triggerMode;
// 显示审核操作弹窗
this.examineOperateDialog = true;
// 显示场景加载状态
this.sceneLoading = true;
// 5 秒后隐藏场景加载状态
setTimeout(() => {
this.sceneLoading = false;
}, 5000);
if (type === "add") {
// 新增时生成第一步
let params = {
flowId: this.sceneID,
step: 1,
stepType: 2,
};
this.SaveSceneStepData(params, "first");
}
if (type === "edit") {
// 编辑时获取场景步骤列表
this.GetSceneStepList();
}
},
/**
* 获取场景步骤列表
*/
async GetSceneStepList() {
// 清空画布列表
this.drawShowList = [];
// 获取场景步骤列表的参数
let params = {
flowId: this.sceneID,
};
let res = await authApi(
"sysFlowStepService",
"",
"queryList",
"",
params
);
// 隐藏场景加载状态
this.sceneLoading = false;
if (res.status == "200") {
if (res.attribute.length > 0) {
res.attribute.forEach((item, index) => {
if (index === 0) {
let obj = {
...stepAdd,
options: {
...item,
stepID: item.id,
taskType: item.apiId,
},
};
if (this.triggerMode === 2) {
obj.actionName = "定时触发";
} else if (this.triggerMode === 1) {
obj.actionName = "应用程序触发";
} else if (this.triggerMode === 4) {
obj.actionName = "Webhook触发";
} else if (this.triggerMode === 3) {
obj.actionName = "手动触发";
}
// 将第一个步骤添加到画布列表
this.drawShowList.push(obj);
} else {
// 将其他步骤添加到画布列表
this.drawShowList.push({
...stepUse,
options: {
...item,
stepID: item.id,
stepDescribe: item.apiName || item.plugName,
},
});
}
});
// 在下一个 DOM 更新周期后执行操作
this.$nextTick(() => {
// 选中第一个步骤
this.drawSelectIndex = 0;
this.selectDrawItem(this.drawSelectIndex);
});
} else {
// 新增时生成第一步
let params = {
flowId: this.sceneID,
step: 1,
stepType: 2,
};
this.SaveSceneStepData(params, "first");
}
}
},
/**
* 新增、实时修改场景步骤基本信息
* @param {Object} params 请求参数
* @param {string} type 操作类型
* @param {string} insertIndex 用于插入
*/
async SaveSceneStepData(params, type, insertIndex) {
let res = await authApi(
"sysFlowStepService",
"",
"saveOrUpdateFlowStep",
"",
params
);
// 隐藏场景加载状态
this.sceneLoading = false;
if (res.status == "200") {
// 第一次打开弹窗
if (type === "first") {
let obj = [
{
...stepAdd,
options: {
// 步骤 id
stepID: res.attribute.id,
step: 1,
stepType: 2, // 步骤类型1、触发方式 2、应用 3、 数据集 4、条件
},
},
];
if (this.triggerMode === 2) {
obj[0].actionName = "定时触发";
} else if (this.triggerMode === 1) {
obj[0].actionName = "应用程序触发";
} else if (this.triggerMode === 4) {
obj[0].actionName = "Webhook触发";
} else if (this.triggerMode === 3) {
obj[0].actionName = "手动触发";
}
// 更新画布列表
this.drawShowList = obj;
// 在下一个 DOM 更新周期后执行操作
this.$nextTick(() => {
// 选中第一个步骤
this.drawSelectIndex = 0;
this.selectDrawItem(this.drawSelectIndex);
// 获取应用列表
this.getAppList();
});
}
// 手动新增时
if (type === "add") {
// 将新步骤添加到画布列表
this.drawShowList.push({
...stepUse,
options: {
// 步骤 id
stepID: res.attribute.id,
},
});
// 在下一个 DOM 更新周期后执行操作
this.$nextTick(() => {
// 选中当前新增的步骤
this.drawSelectIndex = this.drawShowList.length - 1;
this.selectDrawItem(this.drawSelectIndex);
});
}
// 插入时
if (type == "insert") {
this.drawShowList.splice(insertIndex + 1, 0, {
...stepUse,
options: {
// 步骤 id
stepID: res.attribute.id,
},
});
// 在下一个 DOM 更新周期后执行操作
this.$nextTick(() => {
// 选中当前新增的步骤
this.drawSelectIndex = insertIndex + 1;
this.selectDrawItem(this.drawSelectIndex);
});
}
if (type === "time") {
this.activeTabName = "配置";
}
// cron表达式保存后
if (type === "expression") {
if (this.drawShowList.length === 1) {
// 自动添加一个步骤
this.addDrwaItem();
} else {
// 在下一个 DOM 更新周期后执行操作
this.$nextTick(() => {
// 选中第二个步骤
this.drawSelectIndex = 1;
this.selectDrawItem(this.drawSelectIndex);
});
}
}
}
},
/**
* 手动新增场景步骤
*/
addDrwaItem() {
// 新增步骤的参数
let params = {
flowId: this.sceneID,
step: this.drawShowList.length + 1,
stepType: 2,
};
// 保存场景步骤数据
this.SaveSceneStepData(params, "add");
},
/**
* 切换步骤时
* @param {number} index 步骤索引
*/
async selectDrawItem(index) {
// 更新当前选中的步骤索引
this.drawSelectIndex = index;
// 先清空
this.pluginActiv = "";
this.apiIDActiv = "";
this.userActivId = "";
this.CurrentAppRow = {};
if (index === 0) {
this.activeTabName = "选择操作";
} else {
this.activeOtherTabName = "选择应用";
// 清空应用搜索关键字
this.appCodeOrName = "";
// 获取应用列表
await this.getAppList();
// 获取场景步骤数据
await this.GetSceneStepData();
}
},
/**
* 获取场景步骤数据
*/
async GetSceneStepData() {
// 获取场景步骤数据的参数
let params = {
id: this.drawShowList[this.drawSelectIndex].options.stepID,
};
let res = await authApi(
"sysFlowStepService",
"",
"getFlowStep",
"",
params
);
if (res.status == "200") {
let obj = {
...this.drawShowList[this.drawSelectIndex].options,
...res.attribute,
};
// 更新当前步骤的选项
obj.stepDescribe = obj.apiName || obj.plugName;
this.drawShowList[this.drawSelectIndex].options = obj;
// 更新当前选择的应用步骤描述
this.CurrentAppRow.description = obj.stepDescribe;
if (this.drawSelectIndex == 0) {
this.expression = obj.taskCorn;
this.activTimeIndex = obj.apiId;
} else {
this.activeApiPliginTabName = obj.actionType || "API接口";
// 清空 API 或插件搜索关键字
this.operateCodeOrName = "";
// 清空 API 列表
this.apiList = [];
// 清空插件列表
this.pluginList = [];
// 清空用户列表
this.userList = [];
if (this.drawShowList[this.drawSelectIndex].options.appId) {
// 更新当前选中的应用 ID
this.appActivIndex =
this.drawShowList[this.drawSelectIndex].options.appId;
// 更新当前选择的应用名称
this.CurrentAppRow.appName =
this.drawShowList[this.drawSelectIndex].options.appName;
// 获取当前步骤的 api、插件、账号
await this.GetApiList(this.appActivIndex);
// this.getPluginList(this.appActivIndex);
await this.GetAccountListAPI(this.appActivIndex);
} else {
// 重置当前选中的应用 ID
this.appActivIndex = "";
// 重置当前选择的应用数据
this.CurrentAppRow = {};
}
this.$nextTick(() => {
if (this.drawShowList[this.drawSelectIndex].options.description) {
// 更新步骤描述
this.description =
this.drawShowList[this.drawSelectIndex].options.description;
} else {
// 重置步骤描述
this.description = "";
}
if (this.drawShowList[this.drawSelectIndex].options.apiId) {
// 更新选择的 API ID
this.apiIdActiv =
this.drawShowList[this.drawSelectIndex].options.apiId;
} else {
// 重置选择的 API ID
this.apiIdActiv = "";
}
// 插件
if (this.drawShowList[this.drawSelectIndex].options.plugId) {
// 更新选择的插件 ID
this.pluginActiv =
this.drawShowList[this.drawSelectIndex].options.plugId;
} else {
// 重置选择的插件 ID
this.pluginActiv = "";
}
// 账号
if (this.drawShowList[this.drawSelectIndex].options.step_acc_id) {
// 更新选择的用户 ID
this.userActivId =
this.drawShowList[this.drawSelectIndex].options.step_acc_id;
// 更新当前选择的应用账号名称
this.CurrentAppRow.step_acc_name =
this.drawShowList[this.drawSelectIndex].options.step_acc_name;
} else {
// 重置选择的用户 ID
this.userActivId = "";
// 重置当前选择的应用账号名称
this.CurrentAppRow.step_acc_name = "";
}
});
}
}
},
/**
* 删除场景步骤
* @param {Object} ele 步骤数据
* @param {number} index 步骤索引
*/
delDrawItem(ele, index) {
// 获取步骤 ID
let stepID = ele.options.stepID || null;
if (!stepID) {
this.$vmNews("删除失败步骤ID丢失请联系管理员处理");
return;
}
// 弹出确认删除对话框
this.$confirm("确认删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
if (stepID) {
let res = await authApi(
"sysFlowStepService",
"",
"saveFlowStep",
"",
{
id: stepID,
}
);
if (res.status == "200") {
// 提示删除成功
this.$vmNews("删除成功", "success");
// 从画布列表中删除该步骤
this.drawShowList.splice(index, 1);
if (this.drawSelectIndex === index) {
// 选中第一个步骤
this.drawSelectIndex = 0;
this.selectDrawItem(this.drawSelectIndex);
} else if (this.drawSelectIndex >= this.drawShowList.length) {
// 选中最后一个步骤
this.drawSelectIndex = this.drawShowList.length - 1;
this.selectDrawItem(this.drawSelectIndex);
}
}
} else {
// 提示删除成功
this.$vmNews("删除成功", "success");
// 从画布列表中删除该步骤
this.drawShowList.splice(index, 1);
if (this.drawSelectIndex === index) {
// 选中第一个步骤
this.drawSelectIndex = 0;
this.selectDrawItem(this.drawSelectIndex);
} else if (this.drawSelectIndex >= this.drawShowList.length) {
// 选中最后一个步骤
this.drawSelectIndex = this.drawShowList.length - 1;
this.selectDrawItem(this.drawSelectIndex);
}
}
})
.catch(() => {});
},
/**
* 插入场景步骤
* @param {any} value 标签页值
*/
insertDrawItem(ele, index) {
let params = {
flowId: this.sceneID,
step: index + 1,
stepType: 2,
};
// 保存场景步骤数据
this.SaveSceneStepData(params, "insert", index);
},
// ----------------------------------------------步骤一相关操作
/**
* 处理时间项点击事件
* @param {Object} item 时间项数据
* @param {number} index 时间项索引
*/
handleTimeItem(item, index) {
// 更新当前选择操作的类型
this.activTimeIndex = item.id;
// 更新当前步骤的任务类型
this.drawShowList[this.drawSelectIndex].options.taskType = item.id;
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
apiId: item.id,
apiName: this.getTimeDivide(item.id),
};
// 保存场景步骤数据
this.SaveSceneStepData(params, "time");
},
/**
* cron 表达式点击确定时时间插件回调
* @param {string} value cron 表达式的值
*/
crontabFill(value) {
// 更新 cron 表达式
this.expression = value;
// 更新当前步骤的任务值
this.drawShowList[this.drawSelectIndex].options.taskValue = value;
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
taskCorn: value,
};
// 保存场景步骤数据
this.SaveSceneStepData(params, "expression");
},
// ----------------------------------------------其他步骤--相关操作
/**
* 处理 其他步骤的tab点击事件
* @param {any} value 标签页值
*/
handleOtherTabClick(value) {},
/**
* 应用搜索
*/
searchApp() {
// 显示搜索遮罩
this.drawMask = true;
// 5 秒后隐藏搜索遮罩
setTimeout(() => {
this.drawMask = false;
}, 5000);
// 获取应用列表
this.getAppList();
},
/**
* 获取应用列表
*/
async getAppList() {
// 获取应用列表的参数
let params = {
name: this.appCodeOrName,
};
// 调用获取应用列表的 API
let res = await authApi(
"sysApplicationService",
"",
"queryEntity",
"",
params
);
// 隐藏搜索遮罩
this.drawMask = false;
if (res.status == "200") {
// 更新应用列表
this.appList = res.attribute;
}
},
/**
* 步骤描述变化时保存数据
*/
changeDescription() {
// 更新当前步骤的步骤描述
this.drawShowList[this.drawSelectIndex].options.description =
this.description;
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
description: this.description,
};
// 保存场景步骤数据
this.SaveSceneStepData(params);
},
/**
* 选择应用时触发的方法
* @param {Object} row 应用数据
* @param {number} index 应用索引
*/
async hangleAppItem(row, index) {
if (!this.appActivIndex) {
// 保存应用信息
this.matchAppData(row);
} else if (this.appActivIndex !== row.appId) {
this.$confirm(
"切换应用会清空操作、账号及配置内的所有数据,确定切换吗?",
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(async () => {
// 重置当前选择的应用插件名称
this.CurrentAppRow.plugName = "";
// 重置当前选择的应用账号名称
this.CurrentAppRow.step_acc_name = "";
this.CurrentAppRow.description = "";
// 重置当前步骤的资源类型
this.drawShowList[this.drawSelectIndex].options.actionType = "";
// 重置当前步骤的 API ID
this.drawShowList[this.drawSelectIndex].options.apiId = "";
this.drawShowList[this.drawSelectIndex].options.apiName = "";
// 重置当前步骤的插件 ID
this.drawShowList[this.drawSelectIndex].options.plugId = "";
// 重置当前步骤的插件名称
this.drawShowList[this.drawSelectIndex].options.plugName = "";
// 重置当前步骤的账号 ID
this.drawShowList[this.drawSelectIndex].options.step_acc_id = "";
// 重置当前步骤的账号名称
this.drawShowList[this.drawSelectIndex].options.step_acc_name = "";
this.drawShowList[this.drawSelectIndex].options.stepDescribe = "";
this.apiIdActiv = "";
this.apiList = [];
this.pluginActiv = "";
this.pluginList = [];
this.userActivId = "";
this.userList = [];
// 保存应用信息
this.matchAppData(row);
// 清空步骤配置
await this.clearFlowStep();
})
.catch(() => {});
}
},
async matchAppData(row) {
// 更新当前选中的应用 ID
this.appActivIndex = row.id;
// 更新当前选择的应用 ID
this.CurrentAppRow.appId = row.id;
// 更新当前选择的应用名称
this.CurrentAppRow.appName = row.name;
// 更新当前步骤的应用 ID
this.drawShowList[this.drawSelectIndex].options.appId = row.id;
// 更新当前步骤的应用名称
this.drawShowList[this.drawSelectIndex].options.appName = row.name;
// 切换到选择操作标签页
this.activeOtherTabName = "选择操作";
// 切换到 API 接口标签页
this.activeApiPliginTabName = "API接口";
// 清空 API 或插件搜索关键字
this.operateCodeOrName = "";
// 更新当前步骤的资源类型
this.drawShowList[this.drawSelectIndex].options.actionType = "API接口";
// 实时保存配置
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
appId: row.id,
appName: row.name,
actionType: "",
plugId: "",
plugName: "",
apiId: "",
apiName: "",
step_acc_name: "",
step_acc_id: "",
};
// 保存场景步骤数据
await this.SaveSceneStepData(params);
// 获取 API 列表
await this.GetApiList(row.id);
// 获取插件列表
// await this.getPluginList(row.id);
// // 获取用户列表
await this.GetAccountListAPI(row.id);
},
async clearFlowStep() {
let params = {
id: this.drawShowList[this.drawSelectIndex].options.stepID,
};
let res = await authApi(
"sysFlowStepService",
"",
"clearFlowStep",
"",
params
);
},
/**
* API 或者插件的搜索
*/
searchApiPlugin() {
// 检查是否选择了应用
if (!this.drawShowList[this.drawSelectIndex].options.appId) {
this.$vmNews("请先选择应用");
return;
}
if (this.activeApiPliginTabName === "API接口") {
// 显示搜索遮罩
this.drawMask = true;
// 5 秒后隐藏搜索遮罩
setTimeout(() => {
this.drawMask = false;
}, 5000);
// 获取 API 列表
this.GetApiList(this.drawShowList[this.drawSelectIndex].options.appId);
} else if (this.activeApiPliginTabName === "插件") {
// 显示搜索遮罩
this.drawMask = true;
// 5 秒后隐藏搜索遮罩
setTimeout(() => {
this.drawMask = false;
}, 5000);
// 获取插件列表
this.getPluginList(
this.drawShowList[this.drawSelectIndex].options.appId
);
}
},
/**
* 切换 API 插件标签页
* @param {string} e 标签页值
*/
ApiPliginTabChangeEvent(e) {
// 更新当前操作是 API 还是插件
this.activeApiPliginTabName = e;
},
// 选择API
hangleApiClickEvent(item) {
this.pluginActiv = "";
this.drawShowList[this.drawSelectIndex].options.plugId = "";
this.drawShowList[this.drawSelectIndex].options.plugName = "";
// 更新选择的 API ID
this.apiIdActiv = item.id;
// 更新当前步骤的 API ID
this.drawShowList[this.drawSelectIndex].options.apiId = item.id;
this.drawShowList[this.drawSelectIndex].options.apiName = item.apiName;
// 更新当前选择的应用步骤描述
this.CurrentAppRow.description = item.apiName;
this.drawShowList[this.drawSelectIndex].options.stepDescribe =
item.apiName;
// 更新当前步骤的资源类型
this.drawShowList[this.drawSelectIndex].options.actionType =
this.activeApiPliginTabName;
// 切换到选择账号标签页
this.activeOtherTabName = "选择账号";
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
actionType: this.activeApiPliginTabName,
plugId: "",
plugName: "",
apiId: item.id,
apiName: item.apiName,
};
// 保存场景步骤数据
this.SaveSceneStepData(params);
},
// 选择插件
hanglePluginClickEvent(item) {
// 更新选择的 API ID
this.apiIdActiv = "";
// 更新当前步骤的 API ID
this.drawShowList[this.drawSelectIndex].options.apiId = "";
this.drawShowList[this.drawSelectIndex].options.apiName = "";
this.pluginActiv = item.id;
this.drawShowList[this.drawSelectIndex].options.plugId = item.id;
this.drawShowList[this.drawSelectIndex].options.plugName = item.plugName;
// 更新当前选择的应用步骤描述
this.CurrentAppRow.description = item.plugName;
this.drawShowList[this.drawSelectIndex].options.stepDescribe =
item.plugName;
// 更新当前步骤的资源类型
this.drawShowList[this.drawSelectIndex].options.actionType =
this.activeApiPliginTabName;
// 切换到选择账号标签页
this.activeOtherTabName = "选择账号";
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
actionType: this.activeApiPliginTabName,
plugId: item.id,
plugName: item.plugName,
apiId: "",
apiName: "",
};
// 保存场景步骤数据
this.SaveSceneStepData(params);
},
/**
* 选择账号 --》 添加账号
*/
addAccount() {
if (!this.appActivIndex) {
this.$vmNews("请选择应用");
return;
}
// 打开添加账号弹窗
this.$refs.addAccount.openDialog(
this.appActivIndex,
this.sceneID,
this.drawShowList[this.drawSelectIndex].options.stepID
);
},
/**
* 重新加载用户列表
*/
addAccountConfirmClick() {
// 获取用户列表
this.GetAccountListAPI(this.appActivIndex);
},
/**
* 当前选中的用户
* @param {Object} item 用户数据
*/
async handleUserClickEvent(item) {
// 更新选择的用户 ID
this.userActivId = item.id;
// 更新当前选择的应用账号名称
this.CurrentAppRow.step_acc_name = item.name;
// 更新当前步骤的用户 ID
this.drawShowList[this.drawSelectIndex].options.step_acc_id = item.id;
// 更新当前步骤的用户账号名称
this.drawShowList[this.drawSelectIndex].options.step_acc_name = item.name;
this.outsideFormData = {
tableName: "",
};
this.ColumnsFormData = {};
this.outsideOptions = [];
this.outsideColumns = [];
let params = {
flowId: this.sceneID,
id: this.drawShowList[this.drawSelectIndex].options.stepID,
step_acc_name: item.name,
step_acc_id: item.id,
};
// 保存场景步骤数据
await this.SaveSceneStepData(params);
// 切换到配置应用
this.activeOtherTabName = "配置应用";
// 获取表数据
await this.queryTables(item.id);
this.$nextTick(() => {
this.resetForm("outsideForm");
});
},
/**
* 重置表单
* @param {string} formName 表单的ref
*/
resetForm(formName) {
this.$refs[formName].resetFields();
},
/**
* 获取表列表
* @param {string} stepAccountId 账号ID
*/
async queryTables(stepAccountId) {
let params = {
stepAccountId: stepAccountId,
};
let res = await authApi(
"sysFlowDataBaseExtServiceImpl",
"",
"queryTables",
"",
params
);
this.drawMask = false;
if (res.status == "200") {
this.outsideOptions = res.attribute;
}
},
// 切换表时
outsideSelectChange(val) {
if (val) {
this.outsideColumns = [];
this.queryColumns(val);
}
},
// 获取表字段
async queryColumns(tableName) {
let params = {
stepAccountId: this.userActivId, //账户id
tableName: tableName,
};
let res = await authApi(
"sysFlowDataBaseExtServiceImpl",
"",
"queryColumns",
"",
params
);
this.drawMask = false;
if (res.status == "200") {
this.outsideColumns = res.attribute.cloums || [];
this.ColumnsRules = res.attribute.rules || {};
this.$nextTick(() => {
this.ColumnsFormData = {};
this.resetForm("ColumnsForm");
});
}
},
/**
* 获取插件列表
* @param {string} appId 应用 ID
*/
async getPluginList(appId) {
let params = {
appId: appId,
name: this.operateCodeOrName,
};
let res = await authApi(
"sysApplicationApiService",
"",
"queryEntity",
"",
params
);
this.drawMask = false;
if (res.status == "200") {
this.pluginList = res.attribute;
}
},
/**
* 获取 api 列表
* @param {string} appId 应用 ID
*/
async GetApiList(appId) {
let params = {
appId: appId,
name: this.operateCodeOrName,
};
let res = await authApi(
"sysApplicationApiService",
"",
"queryEntity",
"",
params
);
this.drawMask = false;
if (res.status == "200") {
this.apiList = res.attribute;
}
},
/**
* 获取用户列表
* @param {string} appId 应用 ID
*/
async GetAccountListAPI(appId) {
let params = {
appId: appId,
};
let res = await authApi(
"sysApplicationAccountService",
"",
"queryAccountList",
"",
params
);
this.drawMask = false;
if (res.status == "200") {
this.userList = res.attribute;
}
},
/**
* 根据任务类型获取时间划分的标题
* @param {number} taskType 任务类型
* @returns {string} 时间划分的标题
*/
getTimeDivide(taskType) {
if (taskType === 1) {
return "秒级";
} else if (taskType === 2) {
return "分钟级";
} else if (taskType === 3) {
return "小时级";
} else if (taskType === 4) {
return "天级";
}
},
/**
* 编辑场景名称
*/
editSceneName() {
// 编辑场景名称的参数
let row = {
flowId: this.sceneID,
};
// 在下一个 DOM 更新周期后打开编辑场景名称的弹窗
this.$nextTick(() => {
this.$refs.editSence.openDialog(row, "edit");
});
},
/**
* 更新场景名称
* @param {string} sceneID 场景 ID
* @param {Object} res 响应数据
*/
updateTitle(sceneID, res) {
// 更新场景名称
this.sceneName = res.sceneName;
this.title = res.sceneName;
// 更新触发模式
this.triggerMode = res.triggerMode;
if (this.triggerMode === 2) {
this.drawShowList[this.drawSelectIndex].actionName = "定时触发";
} else if (this.triggerMode === 1) {
this.drawShowList[this.drawSelectIndex].actionName = "应用程序触发";
} else if (this.triggerMode === 4) {
this.drawShowList[this.drawSelectIndex].actionName = "Webhook触发";
} else if (this.triggerMode === 3) {
this.drawShowList[this.drawSelectIndex].actionName = "手动触发";
}
},
// 场景步骤---【确定】
handleConfirmClick() {
this.examineHandleClose();
},
// 关闭场景步骤弹窗
examineHandleClose() {
// 触发关闭事件
this.$emit("examineHandleClose");
this.examineOperateDialog = false;
this.sceneName = "";
// 清空画布列表
this.drawShowList = [];
// 重置当前选中的步骤索引
this.drawSelectIndex = "";
// 重置当前选择操作的类型
this.activTimeIndex = "";
// 重置 cron 表达式
this.expression = "";
// 清空应用搜索关键字
this.appCodeOrName = "";
// 重置当前选中的应用 ID
this.appActivIndex = "";
// 清空 API 或插件搜索关键字
this.operateCodeOrName = "";
// 重置当前操作是 API 还是插件
this.activeApiPliginTabName = "API接口";
// 重置选择的 API ID
this.apiIdActiv = "";
// 清空 API 列表
this.apiList = [];
// 重置选择的插件 ID
this.pluginActiv = "";
// 清空插件列表
this.pluginList = [];
// 重置选择的用户 ID
this.userActivId = "";
// 清空用户列表
this.userList = [];
this.representation = false;
},
handleKeyDown(e, columnName, rowIndex) {
const editor = this.$refs["contentEditor" + columnName + rowIndex][0];
if (e.key === "Backspace") {
// 检查是否在光标前有token需要删除
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
// 如果光标在文本开头或者前一个兄弟节点是token
if (range.startOffset === 0) {
const prevSibling = range.startContainer.previousSibling;
if (
prevSibling &&
prevSibling.classList &&
prevSibling.classList.contains("content-token")
) {
e.preventDefault();
prevSibling.remove();
this.$message.success("已删除元素");
return;
}
}
}
}
// 清除token选中状态除了特殊键
if (
![
"Backspace",
"Delete",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
"ArrowDown",
].includes(e.key)
) {
this.clearTokenSelection(columnName, rowIndex);
}
},
// 清除token选中状态
clearTokenSelection(columnName, rowIndex) {
const editor = this.$refs["contentEditor" + columnName + rowIndex][0];
this.lastSelectedTokenIndex = -1;
},
// 处理输入事件
handleInput(e, columnName, rowIndex) {
// 清除token选中状态
this.clearTokenSelection(columnName, rowIndex);
// 强制更新computed属性
this.$forceUpdate();
},
// 处理编辑器点击
handleEditorClick(e, columnName, rowIndex) {
const editor = this.$refs["contentEditor" + columnName + rowIndex][0];
this.currenrActiveNodeRef = "contentEditor" + columnName + rowIndex;
// 如果点击的不是token清除选中状态
if (
!e.target.classList.contains("content-token") &&
!e.target.closest(".content-token")
) {
this.clearTokenSelection(columnName, rowIndex);
}
// 确保编辑器获得焦点
if (e.target === editor || editor.contains(e.target)) {
editor.focus();
}
},
// 处理粘贴事件
handlePaste(e, columnName, rowIndex) {
// 清除token选中状态
this.clearTokenSelection(columnName, rowIndex);
},
// 处理编辑器获得焦点
handleEditorFocus(e, columnName, rowIndex) {
const editor = this.$refs["contentEditor" + columnName + rowIndex][0];
// 如果编辑器为空,设置光标到开始位置
if (this.isEmpty(columnName, rowIndex)) {
setTimeout(() => {
const range = document.createRange();
const selection = window.getSelection();
range.setStart(editor, 0);
range.setEnd(editor, 0);
selection.removeAllRanges();
selection.addRange(range);
}, 0);
}
},
// 判断当前编辑器是否为空
isEmpty(columnName, rowIndex) {
const editor = this.$refs["contentEditor" + columnName + rowIndex][0];
if (!editor) return true;
const content = editor.textContent.trim();
const hasTokens = editor.querySelectorAll(".content-token").length > 0;
return content === "" && !hasTokens;
},
// 处理编辑器失去焦点
handleEditorBlur(e, columnName, rowIndex) {
// 可以在这里处理失去焦点的逻辑
},
handleClearNodeToEditor(columnName, rowIndex) {
const editor = this.$refs["contentEditor" + columnName + rowIndex][0];
if (!editor) return;
let nodeRef = "contentEditor" + columnName + rowIndex;
this.currenrActiveNodeRef = nodeRef;
editor.innerHTML = ""; // 先清空内容
this.$forceUpdate();
this.$nextTick(() => {
// 创建 range 和 selection
const range = document.createRange();
const selection = window.getSelection();
// 将 range 定位到 editor 的最后一个子节点
range.selectNodeContents(editor);
range.collapse(false); // 折叠到末尾
// 清空现有选区并设置新 range
selection.removeAllRanges();
selection.addRange(range);
// 确保编辑器有焦点
editor.focus();
});
},
//添加节点
handleAddNodeToEditor(columnName, rowIndex) {
let nodeRef = "contentEditor" + columnName + rowIndex;
this.currenrActiveNodeRef = nodeRef;
this.representation = true;
this.representationActiveName = "first";
// 这段代码的目的是 让光标停留在最后一个span标签后面
const editor = this.$refs[this.currenrActiveNodeRef][0];
if (!editor) return;
// 创建 range 和 selection
const range = document.createRange();
const selection = window.getSelection();
// 将 range 定位到 editor 的最后一个子节点
range.selectNodeContents(editor);
range.collapse(false); // 折叠到末尾
// 清空现有选区并设置新 range
selection.removeAllRanges();
selection.addRange(range);
// 确保编辑器有焦点
editor.focus();
},
handleNodeSelected(path) {
this.selectedNode = path;
this.insertToken(path, "node-reference");
},
handleTagSelected(label){
},
// 插入token到编辑器
insertToken(text, type) {
if (!this.currenrActiveNodeRef) return;
const editor = this.$refs[this.currenrActiveNodeRef][0];
if (!editor) return;
// 确保编辑器有焦点
editor.focus();
const token = this.createTokenElement(text, type);
// 获取当前光标位置
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
range.deleteContents();
// 插入token
range.insertNode(token);
// 在token后添加一个空格
const space = document.createTextNode(" ");
range.setStartAfter(token);
range.insertNode(space);
// 使用setTimeout确保DOM更新完成后设置光标
setTimeout(() => {
if (!this.setCursorAfter(space)) {
// 如果设置失败,则设置到编辑器末尾
this.setCursorToEnd();
}
}, 10); // 增加延迟时间确保DOM完全更新
} else {
// 如果没有选中区域,直接添加到末尾
editor.appendChild(token);
const space = document.createTextNode(" ");
editor.appendChild(space);
// 使用setTimeout确保DOM更新完成后设置光标
setTimeout(() => {
if (!this.setCursorAfter(space)) {
this.setCursorToEnd();
}
}, 10);
}
// 强制更新
this.$forceUpdate();
},
// 设置光标到末尾
setCursorToEnd() {
if (!this.currenrActiveNodeRef) return;
const editor = this.$refs[this.currenrActiveNodeRef][0];
if (!editor) return;
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(editor);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
},
// 设置光标到指定节点后面
setCursorAfter(node) {
try {
const selection = window.getSelection();
const range = document.createRange();
range.setStartAfter(node);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
return true;
} catch (error) {
return false;
}
},
// 创建token元素
createTokenElement(text, type) {
const token = document.createElement("span");
token.className = `content-token token-function`;
token.contentEditable = false;
token.setAttribute("data-token-id", ++this.tokenCounter);
token.setAttribute("data-token-type", type);
token.setAttribute("data-token-text", text);
if (type === "node-reference") {
token.innerHTML = `<i class="el-icon-connection"></i> ${text}`;
}
return token;
},
// 设置光标到末尾
setCursorToEnd() {
// 清除之前的选中状态
if (!this.currenrActiveNodeRef) return;
const editor = this.$refs[this.currenrActiveNodeRef][0];
if (!editor) return;
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(editor);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
},
closeRepContainer() {
this.representationActiveName = "first";
this.representation = false;
this.selectedNode = "";
this.currenrActiveNodeRef = "";
},
},
};
</script>
<style scoped lang="scss">
::v-deep .el-radio-group {
background: #fff;
padding: 2px;
margin: 0 10px 0 10px;
border-radius: 4px;
}
::v-deep .el-radio-button__inner {
border: 0 !important;
background-color: #fff;
color: #999999;
font-weight: 400;
font-size: 14px;
padding: 7px 15px;
}
::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner {
background-color: #f5f5f5;
color: #333333;
box-shadow: none;
border-radius: 4px;
}
.drawContent {
width: 100%;
height: calc(100vh - 100px);
display: flex;
.drawCanvas {
width: 40%;
height: 100%;
background: url("../images/background.png") no-repeat;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-direction: column;
flex-direction: column;
-ms-flex-pack: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
overflow-y: auto;
padding: 50px 0;
.drawItem {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.title {
font-weight: 400;
font-size: 14px;
color: #333333;
margin-bottom: 5px;
}
.drawBox {
width: 300px;
height: 58px;
background: #ffffff;
border-radius: 4px;
border: 1px solid #dbdde1;
font-weight: 400;
font-size: 14px;
color: #333333;
display: flex;
// justify-content: center;
align-items: center;
position: relative;
.img {
margin: 0 10px;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #f18709;
img {
width: 60%;
height: 60%;
}
}
.iconStyle {
margin: 0 10px;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.actionName {
font-weight: 400;
font-size: 14px;
color: #333333;
}
.content {
font-weight: 500;
font-size: 14px;
color: #333333;
margin-top: 5px;
}
}
.drawBox:hover {
background: #f2fcff;
border: 1px solid #1478f5;
}
.del {
position: absolute;
right: -5px;
top: -3px;
cursor: pointer;
width: 16px;
height: 16px;
background: #fff;
img {
width: 100%;
height: 100%;
}
}
.active {
background: #f2fcff;
border: 1px solid #1478f5;
}
.line-wrapper {
width: 100%;
display: flex;
justify-content: center;
position: relative;
img {
position: absolute;
cursor: pointer;
width: 20px;
height: 20px;
top: 33px;
left: 140px;
display: none;
}
}
.line-wrapper:hover img {
display: block;
}
.line {
width: 1px;
height: 60px;
border: 1px solid #dbdde1;
margin: 10px 0;
}
}
}
.drawAction {
width: 60%;
height: 100%;
background-color: #f5f5f5;
position: relative;
.nextButton {
position: absolute;
right: 15px;
top: 8px;
color: #1477f3;
cursor: pointer;
font-size: 14px;
}
::v-deep .el-tabs__header {
margin: 0;
background: #ffffff;
.el-tabs__nav {
margin-left: 10px;
}
}
}
}
// cron表达式样式
.crontabBox {
padding: 15px;
.crontabBoxTitle {
margin-bottom: 15px;
color: #333333;
}
::v-deep .el-tabs--border-card {
border-radius: 8px;
box-shadow: unset;
border: 1px solid #dadce0;
}
::v-deep .popup-main {
border-radius: 8px;
border: 1px solid #dadce0;
font-size: 14px;
}
::v-deep .popup-result {
border: unset;
margin: 0 auto;
padding: 15px 10px 5px 15px;
.title {
width: unset;
margin-left: unset;
background: transparent;
color: #333;
}
}
::v-deep .el-tabs__header {
border-radius: 8px 8px 0 0;
margin: 0;
background: #f5f7fa !important;
.el-tabs__nav {
margin-left: 0 !important;
}
}
::v-deep .el-tabs__header .el-tabs__item:first-child {
border-radius: 8px 0 0 0;
margin-left: -2px;
margin-top: 0px;
}
::v-deep .pop_btn {
text-align: center;
margin-top: 10px;
margin-bottom: 10px;
.el-button:last-child {
display: none;
}
}
}
.drawItemBox {
display: flex;
background: #ffffff;
padding: 10px;
.title {
font-weight: 400;
font-size: 14px;
color: #333333;
}
.drawBox {
background: #ffffff;
font-weight: 400;
font-size: 14px;
color: #333333;
display: flex;
position: relative;
align-items: center;
.img {
margin-right: 20px;
}
.actionName {
font-weight: 400;
font-size: 14px;
color: #333333;
margin-bottom: 5px;
}
.content {
font-weight: 500;
font-size: 14px;
color: #333333;
margin-top: 5px;
}
}
.del {
position: absolute;
right: 5px;
top: 2px;
cursor: pointer;
}
.active {
background: #f2fcff;
border: 1px solid #1478f5;
}
.line {
width: 1px;
height: 60px;
border: 1px solid #dbdde1;
margin: 10px 0;
}
}
// 选择操作
.timeWrap {
padding: 15px;
box-sizing: border-box;
.timeItem {
width: 100%;
height: 58px;
background: #ffffff;
border-radius: 4px;
border: 1px solid #dbdde1;
padding: 10px 14px;
box-sizing: border-box;
margin-bottom: 10px;
display: flex;
.imgBox {
width: 15px;
height: 20px;
margin-right: 3px;
img {
width: 100%;
height: 100%;
}
}
.title {
font-weight: 500;
font-size: 14px;
color: #333333;
}
.content {
font-weight: 400;
font-size: 14px;
color: #333333;
}
}
.timeItem:hover {
background: #f2fcff;
border: 1px solid #1478f5;
}
.activTimeItem {
background: #f2fcff;
border-radius: 4px;
border: 1px solid #1478f6;
}
}
.app {
.applist {
display: flex;
padding: 10px;
flex-wrap: wrap;
.appItem {
width: 202px;
height: 58px;
background: #ffffff;
border-radius: 4px;
padding: 0 10px;
display: flex;
flex-direction: row;
align-items: center;
margin-right: 10px;
border: 1px solid #fff;
margin-bottom: 10px;
cursor: pointer;
.imgBox {
width: 15px;
height: 22px;
margin-right: 3px;
img {
width: 100%;
height: 100%;
}
}
.appIcon {
width: 44px;
height: 44px;
margin-right: 10px;
border-radius: 50%;
}
.name {
font-weight: 400;
font-size: 14px;
color: #333333;
flex: 1;
}
}
.appItem:hover {
background: #f2fcff;
border: 1px solid #1478f5;
}
.active {
background: #f2fcff;
border: 1px solid #1478f5;
}
}
}
.DescribeDataBox {
display: flex;
flex-direction: column;
padding: 10px 15px 10px 10px;
font-size: 15px;
color: #333;
span {
margin-bottom: 10px;
}
}
.search {
display: flex;
margin: 10px;
.input {
margin-right: 10px;
}
}
.ApiPliginBox {
height: calc(100vh - 270px);
overflow: auto;
padding-right: 5px;
.appItem {
height: unset !important;
display: flex;
flex-direction: column;
width: 100% !important;
padding: 10px !important;
align-items: flex-start !important;
margin-right: 0 !important;
color: #333333 !important;
font-size: 14px !important;
.name:first-child {
font-weight: 500;
color: #333333;
}
}
}
.userList {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 10px 15px 10px 10px;
font-size: 14px;
.userItem {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background: #ffffff;
border-radius: 4px;
border: 1px solid #dbdde1;
margin-bottom: 10px;
padding: 15px;
cursor: pointer;
}
.status {
margin-right: 10px;
font-size: 18px !important;
}
.userAccountIcon {
width: 15px;
height: 15px;
margin-right: 10px;
img {
width: 100%;
height: 100%;
}
}
.userItem:hover {
background: #f2fcff;
border: 1px solid #1478f5;
}
.userBtn {
padding: 15px 10px;
border: 1px dashed #dbdde1;
}
.userBtn:hover {
background: #f2fcff;
border: 1px dashed #1478f6;
}
}
.userContentBox {
width: 100%;
height: calc(100vh - 200px);
overflow: auto;
.active {
background: #f2fcff;
border: 1px solid #1478f5;
}
}
.redBox {
background: #fff1eb;
border-radius: 4px;
border: 1px solid #ffb999;
color: #ff5100;
font-size: 12px;
text-align: center;
padding: 2px 8px;
margin-left: 5px;
}
.successBox {
background: #f6ffed;
border-radius: 4px;
border: 1px solid #b7eb8f;
color: #52c41a;
font-size: 12px;
text-align: center;
padding: 2px 8px;
margin-left: 5px;
}
.settingBox {
padding: 10px;
.title {
margin-bottom: 10px;
color: #333333;
font-weight: 500;
font-size: 14px;
}
}
.settingbtn {
width: 20px;
height: 20px;
font-size: 16px;
color: #1478f6;
text-align: center;
line-height: 20px;
cursor: pointer;
margin: 0 auto;
position: absolute;
top: 90px;
left: 19px;
z-index: 999;
padding: 6px;
}
.settingTabs {
width: 100%;
padding: 10px 15px 10px 10px;
.tabs {
display: flex;
align-items: center;
height: 32px;
background: #fff;
border-radius: 4px;
padding: 2px;
width: fit-content;
.tabsItem {
height: 28px;
line-height: 28px;
background: #fff;
border-radius: 3px;
font-size: 14px;
color: #999;
padding: 0 10px;
cursor: pointer;
}
.active {
background: #f5f5f5;
color: #333;
}
}
::v-deep .el-switch {
height: 23px !important;
line-height: 23px !important;
}
}
.currentDrawBox {
background: #ffffff;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
.img {
width: 44px;
height: 44px;
border-radius: 50%;
margin-right: 10px;
img {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.actionName {
font-weight: 400;
color: #333333;
margin-bottom: 5px;
}
.content {
font-weight: 500;
color: #333333;
margin-bottom: 5px;
}
.drawimg {
margin-right: 10px;
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #f18709;
img {
width: 60%;
height: 60%;
}
}
}
.drawItemBtn {
background: #ffffff;
padding: 10px 50px;
font-size: 14px;
color: #1478f6;
border: 1px dashed #dbdde1;
border-radius: 4px;
cursor: pointer;
margin-top: 50px;
}
.drawItemBtn:hover {
background: #f2fcff;
border: 1px dashed #1478f6;
}
.DescribeContent {
display: -webkit-box;
/*作为弹性伸缩盒子模型显示*/
-webkit-box-orient: vertical;
/*作为弹性伸缩盒子模型显示*/
-webkit-line-clamp: 1; //*显示的行*/
overflow: hidden;
/*溢出隐藏*/
/* 对于连字情况或者纯字母可以解决 */
word-break: break-all;
// max-width: 100px;
}
.InterfaceConfiguration {
background: #fff;
margin: 10px;
padding: 10px;
border-radius: 4px;
.InterCigTitle {
font-size: 14px;
margin-bottom: 10px;
}
}
::v-deep .operatingClass {
display: flex;
align-items: center;
.el-radio-group {
background: unset !important;
}
}
::v-deep .CodeMirror {
height: 100% !important;
}
.toolbar {
background: #fff;
padding: 8px;
font-size: 14px;
border-radius: 4px 4px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
select {
width: 100px;
margin-left: 10px;
padding: 2px;
}
}
.fullScreen {
display: flex;
align-items: center;
font-size: 14px;
margin-left: 10px;
cursor: pointer;
> img {
width: 15px;
height: 15px;
margin-right: 5px;
}
}
.acionImg {
width: 15px;
height: 15px;
margin-left: 5px;
}
::v-deep .el-form-item {
margin-bottom: 0 !important;
}
::v-deep .el-form-item__label {
padding-bottom: 0 !important;
font-weight: bold;
}
.outsideContent {
height: calc(100vh - 220px);
overflow: auto;
padding: 0 10px 10px 10px;
}
::v-deep .el-collapse-item__header.is-active {
padding-left: 10px !important;
}
// ------------------------
.editor-container {
flex: 1;
overflow: hidden;
position: relative;
}
.editorIcon {
position: absolute;
right: 10px;
top: 0;
font-size: 20px;
color: #999999;
}
.clearIcon {
display: none;
position: absolute;
top: 0;
right: 35px;
font-size: 20px;
color: #999999;
}
.editor-container:hover .clearIcon {
display: block;
}
/* hover 时显示 close-icon */
.editor-container:hover .close-icon {
display: inline-flex; /* 或 block根据你的布局 */
}
.content-editor {
border: 1px solid #e5e7eb;
border-radius: 4px;
min-height: 36px;
padding: 0 50px 0 15px;
background: #fff;
position: relative;
font-size: 14px;
// line-height: 1.6;
overflow-y: auto;
outline: none;
word-wrap: break-word;
}
.content-editor:focus {
border-color: #409eff;
}
.content-editor:empty:before {
content: attr(data-placeholder);
color: #9ca3af;
font-style: italic;
pointer-events: none;
}
.content-editor:focus:empty:before {
content: "";
}
/* 确保空编辑器也能显示光标 */
.content-editor:empty {
min-height: 20px;
}
.content-editor:focus {
outline: none;
}
// --------------------------------
::v-deep .content-token {
display: inline-table;
padding: 0 10px;
border-radius: 4px;
margin: 2px;
cursor: pointer;
transition: all 0.2s ease;
vertical-align: middle;
font-size: 13px;
font-weight: 500;
user-select: none;
}
::v-deep .token-function {
background: #e0e7ff;
color: #5b21b6;
border: 1px solid #8b5cf6;
}
::v-deep .repContainer {
width: 100%;
height: 100%;
background: #f5f5f5;
padding: 10px;
position: relative;
}
.treeNodeBox {
height: calc(100% - 50px);
overflow-y: auto;
}
.closeRepContainer {
position: absolute;
top: 20px;
right: 12px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
i {
margin-right: 5px;
}
}
.closeRepContainer:hover {
color: #409eff;
}
.repItem {
display: flex;
align-items: center;
margin-bottom: 8px;
cursor: pointer;
}
.repItem:hover .repItemValue{
color: #409eff;
}
.repItemValue {
color: #6b7280;
font-size: 13px;
margin-left: 10px;
}
</style>