|
@@ -1,7 +1,71 @@
|
|
|
-<template></template>
|
|
|
+<template>
|
|
|
+ <div class="webhook-params">
|
|
|
+ <div class="params-header">
|
|
|
+ <h3>{{ title }}</h3>
|
|
|
+ <el-button type="primary" size="small" @click="addKeyValue" :disabled="readonly"> 添加参数 </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="params-content">
|
|
|
+ <div class="params-list">
|
|
|
+ <div v-for="(key, index) in keys" :key="key" class="param-item">
|
|
|
+ <div class="param-key">
|
|
|
+ <el-input v-model="editingKeys[index]" placeholder="参数名" :disabled="readonly" @blur="updateKey(index, key)" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="param-value">
|
|
|
+ <el-popover placement="bottom" :width="600" trigger="click">
|
|
|
+ <template #reference>
|
|
|
+ <el-input v-model="origin[key]" placeholder="参数值" :disabled="readonly" @input="updateOrigin" />
|
|
|
+ </template>
|
|
|
+ <div class="popover-content">
|
|
|
+ <div class="two-column-layout">
|
|
|
+ <!-- 第一列:flow_model_cate字典数据 -->
|
|
|
+ <div class="left-column">
|
|
|
+ <div class="column-title">表单类别</div>
|
|
|
+ <div class="category-list">
|
|
|
+ <div
|
|
|
+ v-for="item in flow_model_cate"
|
|
|
+ :key="item.value"
|
|
|
+ class="category-item"
|
|
|
+ :class="{ active: currentSelectCate === item.value }"
|
|
|
+ @click="selectCategory(item.value)"
|
|
|
+ >
|
|
|
+ {{ item.label }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 第二列:currentListSelectCate数据 -->
|
|
|
+ <div class="right-column">
|
|
|
+ <div class="column-title">字段列表</div>
|
|
|
+ <div class="field-list">
|
|
|
+ <div v-for="field in currentListSelectCate" :key="field.Key" class="field-item" @click="selectField(field.Key, key)">
|
|
|
+ <div class="field-name">{{ field.Key }}</div>
|
|
|
+ <div class="field-type">{{ field.Type }}</div>
|
|
|
+ <div class="field-desc">{{ field.Dc }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-popover>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="param-actions">
|
|
|
+ <el-button type="danger" size="small" :icon="Delete" @click="removeKeyValue(key)" :disabled="readonly" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { computed, getCurrentInstance } from 'vue'
|
|
|
+import { computed, getCurrentInstance, ref, watch } from 'vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { Delete } from '@element-plus/icons-vue'
|
|
|
+import { useAsyncState } from '@vueuse/core'
|
|
|
+import { getFlowFields } from '/@/api/flow/flowForm'
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as any
|
|
|
const {
|
|
@@ -46,6 +110,207 @@ const origin = computed<{ [key: string]: string }>({
|
|
|
emit('update:modelValue', JSON.stringify(value ?? {}))
|
|
|
},
|
|
|
})
|
|
|
+
|
|
|
+const keys = computed<string[]>(() => Object.keys(origin.value))
|
|
|
+
|
|
|
+// 用于编辑键名的响应式数组
|
|
|
+const editingKeys = ref<string[]>([])
|
|
|
+
|
|
|
+// 监听keys变化,同步editingKeys
|
|
|
+watch(
|
|
|
+ keys,
|
|
|
+ (newKeys) => {
|
|
|
+ editingKeys.value = [...newKeys]
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+)
|
|
|
+
|
|
|
+// 添加新的键值对
|
|
|
+const addKeyValue = () => {
|
|
|
+ const newKey = `params_${keys.value.length + 1}`
|
|
|
+ origin.value = { ...origin.value, [newKey]: '' }
|
|
|
+}
|
|
|
+
|
|
|
+// 删除键值对
|
|
|
+const removeKeyValue = (key: string) => {
|
|
|
+ const newOrigin = { ...origin.value }
|
|
|
+ delete newOrigin[key]
|
|
|
+ origin.value = newOrigin
|
|
|
+}
|
|
|
+
|
|
|
+// 更新键名
|
|
|
+const updateKey = (index: number, oldKey: string) => {
|
|
|
+ const newKey = editingKeys.value[index]
|
|
|
+
|
|
|
+ // 验证键名是否有效
|
|
|
+ if (!newKey || newKey.trim() === '') {
|
|
|
+ editingKeys.value[index] = oldKey
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查键名是否重复
|
|
|
+ if (newKey !== oldKey && keys.value.includes(newKey)) {
|
|
|
+ editingKeys.value[index] = oldKey
|
|
|
+ ElMessage.warning('参数名已存在')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新键名
|
|
|
+ if (newKey !== oldKey) {
|
|
|
+ const newOrigin = { ...origin.value }
|
|
|
+ const value = newOrigin[oldKey]
|
|
|
+ delete newOrigin[oldKey]
|
|
|
+ newOrigin[newKey] = value
|
|
|
+ origin.value = newOrigin
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 更新origin对象
|
|
|
+const updateOrigin = () => {
|
|
|
+ // 触发computed的setter
|
|
|
+ origin.value = { ...origin.value }
|
|
|
+}
|
|
|
+
|
|
|
+const currentSelectCate = ref('')
|
|
|
+
|
|
|
+// 选择表单类别
|
|
|
+const selectCategory = (categoryValue: string) => {
|
|
|
+ currentSelectCate.value = categoryValue
|
|
|
+}
|
|
|
+
|
|
|
+// 选择字段
|
|
|
+const selectField = (fieldKey: string, paramKey: string) => {
|
|
|
+ origin.value[paramKey] = `{{.${fieldKey}}`
|
|
|
+ updateOrigin()
|
|
|
+}
|
|
|
+
|
|
|
+watch(currentSelectCate, (newListSelectCate) => {
|
|
|
+ if (newListSelectCate === '') return
|
|
|
+ selectCate(100, newListSelectCate)
|
|
|
+})
|
|
|
+
|
|
|
+const { state: currentListSelectCate, execute: selectCate } = useAsyncState(
|
|
|
+ async (data) => {
|
|
|
+ return await getFlowFields(data)
|
|
|
+ .then((res: any) => (res as FlowFieldsInSql).infos)
|
|
|
+ .catch(() => [] as FlowFieldsInSql['infos'])
|
|
|
+ },
|
|
|
+ [] as FlowFieldsInSql['infos'],
|
|
|
+ { immediate: false }
|
|
|
+)
|
|
|
</script>
|
|
|
|
|
|
-<style scoped lang="scss"></style>
|
|
|
+<style scoped lang="scss">
|
|
|
+.webhook-params {
|
|
|
+ width: 100%;
|
|
|
+ .params-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .params-content {
|
|
|
+ .empty-state {
|
|
|
+ padding: 40px 0;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .params-list {
|
|
|
+ .param-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .param-key {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 150px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-value {
|
|
|
+ flex: 2;
|
|
|
+ min-width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-actions {
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.popover-content {
|
|
|
+ .two-column-layout {
|
|
|
+ display: flex;
|
|
|
+ gap: 16px;
|
|
|
+ height: 300px;
|
|
|
+
|
|
|
+ .left-column,
|
|
|
+ .right-column {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .column-title {
|
|
|
+ padding: 8px 12px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .category-list,
|
|
|
+ .field-list {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 4px 0;
|
|
|
+
|
|
|
+ .category-item,
|
|
|
+ .field-item {
|
|
|
+ padding: 8px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background-color: #409eff;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .field-item {
|
|
|
+ .field-name {
|
|
|
+ font-weight: 500;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .field-type {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .field-desc {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|