| | |
| | | </template> |
| | | </titleBox> |
| | | |
| | | <div class="grid grid-cols-2 gap-2 h-full flex-auto"> |
| | | <div class="h-full overflow-auto"> |
| | | <Splitpanes class="default-theme flex-auto" :horizontal="false"> |
| | | <Pane :size="40" class="flex-column"> |
| | | <div class="flex-0 flex justify-between items-center mb-1 ml-1 h-[36px]"> |
| | | <span class="font-bold">配置项</span> |
| | | </div> |
| | | <el-table |
| | | ref="rsTableRef" |
| | | :data="configList" |
| | | row-class-name="cursor-pointer" |
| | | class="h-full" |
| | | class="flex-auto" |
| | | highlight-current-row |
| | | @current-change="dockRowChange" |
| | | > |
| | | <el-table-column prop="asyncId" label="查询 id" /> |
| | | |
| | | <el-table-column prop="path" label="配置路径" /> |
| | | |
| | | <el-table-column prop="recordId" label="SQL id" /> |
| | | <!-- <el-table-column prop="url" label="请求地址" /> --> |
| | | </el-table> |
| | | <!-- <codemirror |
| | | v-model="dockCode" |
| | | :style="{ height: '100%' }" |
| | | :autofocus="true" |
| | | :indent-with-tab="true" |
| | | :tab-size="2" |
| | | :extensions="dockEditorExtensions" |
| | | @change="log('change', $event)" |
| | | @focus="log('focus', $event)" |
| | | @blur="log('blur', $event)" |
| | | /> --> |
| | | </div> |
| | | <div class="h-full overflow-auto" v-if="currentSql"> |
| | | <codemirror |
| | | class="h-full overflow-auto" |
| | | v-model="currentSql.sql" |
| | | :style="{ height: '100%' }" |
| | | :autofocus="true" |
| | | :indent-with-tab="true" |
| | | :tab-size="2" |
| | | :extensions="sqlEditorExtensions" |
| | | @change="sqlCodeChange" |
| | | @focus="log('focus', $event)" |
| | | @blur="log('blur', $event)" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <!-- <el-table-column label="请求地址" ></el-table-column> --> |
| | | </el-table></Pane |
| | | > |
| | | <Pane :size="60"> |
| | | <Splitpanes class="default-theme h100" :horizontal="true"> |
| | | <Pane :size="35" class="flex-col flex"> |
| | | <div class="flex-0 flex justify-between items-center mb-1 ml-1 h-[36px]"> |
| | | <span class="font-bold">参数</span> |
| | | <el-button type="primary" @click="addArg">添加</el-button> |
| | | </div> |
| | | <el-table class="flex-auto" :data="args" border> |
| | | <el-table-column prop="name" width="150" label="名称" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.name" @input="argsInput"></el-input> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="prompt" width="450" label="提示词" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input type="textarea" :rows="2" v-model="scope.row.prompt" @input="argsInput"></el-input> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="check" label="缺省值" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.check" @input="argsInput"></el-input> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="" width="55" fixed="right" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tooltip effect="dark" content="删除" placement="top"> |
| | | <i class="ywifont ywicon-shanchu !text-[17px] text-red-400 cursor-pointer" @click="deleteArg(scope.$index)"></i> |
| | | </el-tooltip> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </Pane> |
| | | <Pane :size="65"> |
| | | <div class="h-full"> |
| | | <template v-if="currentDockConfig"> |
| | | <div class="flex justify-between items-center my-1 ml-1 h-[36px]"> |
| | | <el-select class="w-52 font-bold" v-model="currentDockConfig.type"> |
| | | <el-option |
| | | v-for="item in Object.keys(amisDockTypeMap)" |
| | | :key="item" |
| | | :value="item" |
| | | :label="amisDockTypeMap[item]" |
| | | ></el-option> |
| | | </el-select> |
| | | |
| | | <el-select class="w-40" v-model="currentDockConfig.database" @change="databaseSelectChange"> |
| | | <el-option v-for="item in databaseList" :key="item.id" :value="item.id" :label="item.title"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <codemirror |
| | | class="overflow-auto" |
| | | style="height: calc(100% - 36px)" |
| | | v-model="currentDockConfig.sql" |
| | | :autofocus="true" |
| | | :indent-with-tab="true" |
| | | :tab-size="2" |
| | | :extensions="sqlEditorExtensions" |
| | | @change="sqlCodeChange" |
| | | @focus="log('focus', $event)" |
| | | @blur="log('blur', $event)" |
| | | /> |
| | | </template> |
| | | </div> |
| | | </Pane> |
| | | </Splitpanes> |
| | | </Pane> |
| | | </Splitpanes> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { sql } from '@codemirror/lang-sql'; |
| | | import { xml } from '@codemirror/lang-xml'; |
| | | import { vscodeDark } from '@uiw/codemirror-theme-vscode'; |
| | | import _, { debounce } from 'lodash'; |
| | | import { onMounted, ref } from 'vue'; |
| | | import { Codemirror } from 'vue-codemirror'; |
| | | import * as codeExample from './testData'; |
| | | import titleBox from '/@/components/titleBox.vue'; |
| | | |
| | | import type { TableInstance } from 'element-plus'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import _, { debounce } from 'lodash'; |
| | | import { Pane, Splitpanes } from 'splitpanes'; |
| | | import 'splitpanes/dist/splitpanes.css'; |
| | | import { v4 as uuid } from 'uuid'; |
| | | import { onMounted, ref, watch } from 'vue'; |
| | | import { Codemirror } from 'vue-codemirror'; |
| | | import { SupervisorPublished } from '../types'; |
| | | import * as codeExample from './testData'; |
| | | import { amisDockTypeMap } from './types'; |
| | | import * as supervisorApi from '/@/api/supervisorAdmin'; |
| | | import { updateSqlApi } from '/@/api/supervisorAdmin'; |
| | | import titleBox from '/@/components/titleBox.vue'; |
| | | import { useCompRef } from '/@/utils/types'; |
| | | |
| | | const props = defineProps(['supervisor']); |
| | | const emit = defineEmits(['backLastPage']); |
| | | const emit = defineEmits(['backLastPage', 'updatePublished']); |
| | | const log = console.log; |
| | | const jsonCode = ref(codeExample.jsonCode); |
| | | const dockCode = ref(codeExample.dockCode); |
| | |
| | | configList.value = []; |
| | | sqlCode.value = ''; |
| | | }; |
| | | |
| | | let defaultSelectDatabase = null; |
| | | const databaseSelectChange = (val) => { |
| | | defaultSelectDatabase = val; |
| | | if (currentDockConfig.value) { |
| | | currentDockConfig.value.database = val; |
| | | } |
| | | updateSqlAndRs(currentDockConfig.value.id, currentDockConfig.value); |
| | | }; |
| | | const backLastPage = () => { |
| | | // setTimeout(() => { |
| | | // resetStatus(); |
| | |
| | | emit('backLastPage'); |
| | | }; |
| | | const currentRs = ref<AmisDockConfig>(null); |
| | | |
| | | const getWithTemplateDataCommentSql = (sql: string, data: any) => { |
| | | if (!Array.isArray(data)) { |
| | | return sql; |
| | | } |
| | | const first = data[0]; |
| | | if (!_.isObjectLike(first)) { |
| | | return sql; |
| | | } |
| | | const firstKeyList = Object.keys(first); |
| | | // 值为对象 |
| | | if (_.isObjectLike(first[firstKeyList[0]])) { |
| | | return sql; |
| | | } |
| | | |
| | | |
| | | |
| | | let comment = ''; |
| | | firstKeyList.map((key, index, array) => { |
| | | const value = JSON.stringify(first[key]); |
| | | comment += `-- "${key}": ${value}\n`; |
| | | }); |
| | | |
| | | const reg = new RegExp(`^\\s*(\\s*--\\s*".*"\\s*:.*\\n)+`) |
| | | // 已经存在,一定要替换成最新的 |
| | | if (reg.test(sql)) { |
| | | const replaceStr = sql.replace(reg,comment); |
| | | return replaceStr; |
| | | } |
| | | |
| | | const result = comment+sql; |
| | | return result; |
| | | }; |
| | | const dockRowChange = (row) => { |
| | | currentRs.value = row; |
| | | currentSql.value = sqlList.value.find((item) => item.id === currentRs.value.recordId) ?? { |
| | | currentDockConfig.value = dockConfigList.value.find((item) => item.id === currentRs.value.recordId) ?? { |
| | | id: row.recordId, |
| | | type: AmisDockType.Sql, |
| | | sql: '', |
| | | database: defaultSelectDatabase ?? databaseList.value[0]?.id ?? null, |
| | | }; |
| | | const templateData = extraInfoMap.value.get(row.recordId).templateData; |
| | | |
| | | currentDockConfig.value.sql = getWithTemplateDataCommentSql(currentDockConfig.value.sql,templateData) |
| | | }; |
| | | const currentSql = ref(null); |
| | | const currentDockConfig = ref(null); |
| | | /** @description 路径分隔符 */ |
| | | const PATH_SEPARATOR = '/'; |
| | | /** @description 1 退出所有循环 -1退出当次循环 0或其他值继续 */ |
| | | const travelObj = (obj, callBack?: (key?: string, value?: any, path?: string) => 1 | -1 | undefined | void | 0, path = '') => { |
| | | const travelObj = ( |
| | | obj, |
| | | callBack?: (key?: string, value?: any, path?: string, item?: any) => 1 | -1 | undefined | void | 0, |
| | | path = '' |
| | | ) => { |
| | | let entry = Object.entries(obj); |
| | | |
| | | let res; |
| | |
| | | iterate: for (const item of entry) { |
| | | const [key, value] = item; |
| | | const currentPath = path ? path + PATH_SEPARATOR + key : key; |
| | | res = callBack?.(key, value, currentPath); |
| | | res = callBack?.(key, value, currentPath, obj); |
| | | switch (res) { |
| | | case 1: |
| | | break iterate; |
| | |
| | | }; |
| | | |
| | | const enum AmisDockType { |
| | | Api, |
| | | Sql = 'SQL', |
| | | } |
| | | type AmisDockApi = string; |
| | | type AmisDockValue = AmisDockApi; |
| | |
| | | path: string; |
| | | asyncId: string; |
| | | recordId: string; |
| | | // url: string; |
| | | }; |
| | | |
| | | const configList = ref<AmisDockConfig[]>([]); |
| | | |
| | | type ExtraInfo = { |
| | | url: string; |
| | | templateData: Array<any>; |
| | | }; |
| | | const extraInfoMap = ref(new Map<string, ExtraInfo>()); |
| | | |
| | | const getAmisData = (data) => { |
| | | if (!data) return null; |
| | | |
| | | const dataKeyList = Object.keys(data); |
| | | if (dataKeyList.length === 0) return null; |
| | | if (dataKeyList.length === 1) return data.items ?? data.rows ?? data; |
| | | |
| | | return data; |
| | | }; |
| | | const parseJSONData = (obj: any, apiDataList) => { |
| | | // 先清空 |
| | | |
| | | const jsonConfigList = []; |
| | | travelObj(obj, (key, value, path) => { |
| | | extraInfoMap.value.clear(); |
| | | travelObj(obj, (key, value, path, item) => { |
| | | if (key === 'api') { |
| | | // const url = value.url; |
| | | const urlPath = path + PATH_SEPARATOR + 'url'; |
| | | const randomStr = 'query_' + uuid().slice(0, 12); |
| | | const randomStr = 'q_' + value.url + '_' + uuid().slice(0, 12); |
| | | |
| | | const foundItem = apiDataList.find((item) => item.path === urlPath); |
| | | const asyncId = foundItem?.asyncId ?? randomStr; |
| | | const recordId = foundItem?.recordId ?? randomStr; |
| | | |
| | | extraInfoMap.value.set(recordId, { |
| | | url: value.url, |
| | | templateData: getAmisData(item.data), |
| | | }); |
| | | // const recordId = uniqueId() |
| | | jsonConfigList.push({ |
| | | asyncId: asyncId, |
| | | recordId: recordId, |
| | | type: AmisDockType.Api, |
| | | type: AmisDockType.Sql, |
| | | path: urlPath, |
| | | }); |
| | | } |
| | |
| | | return jsonConfigList; |
| | | }; |
| | | |
| | | const updateSqlAndRs = (id?, sqlValue?: string) => { |
| | | const apiConfig = configList.value.filter((item) => item.type === AmisDockType.Api); |
| | | if (apiConfig.length === 0) return; |
| | | const checkValid = (showTip: boolean, tip?: string) => { |
| | | if (!dockConfigList.value || dockConfigList.value.length === 0) return true; |
| | | const foundWithNoDatabase = dockConfigList.value?.find((item) => !item.database); |
| | | if (foundWithNoDatabase && showTip) { |
| | | ElMessage.warning(tip ?? `【${foundWithNoDatabase.id}】未选择数据库`); |
| | | } |
| | | return !foundWithNoDatabase; |
| | | }; |
| | | |
| | | const updateSqlAndRs = (id?, sqlValue?: { database?: string; sql?: string; type?: AmisDockType }) => { |
| | | const apiConfig = configList.value.filter((item) => item.type === AmisDockType.Sql); |
| | | |
| | | if (!checkValid(true)) { |
| | | return; |
| | | } |
| | | |
| | | const recordIds = apiConfig?.map((item) => item.recordId) ?? []; |
| | | // 过滤出 apiConfig 中有的record; |
| | | dockConfigList.value = dockConfigList.value?.filter((item) => recordIds.includes(item.id)) ?? []; |
| | | const asyncRsList = apiConfig.map((item) => ({ |
| | | amis_path: item.path, |
| | | async_id: item.asyncId, |
| | | rec_id: item.recordId, |
| | | })); |
| | | |
| | | if (id) { |
| | | const found = sqlList.value?.find((item) => item.id === id); |
| | | if (found) { |
| | | found.sql = sqlValue; |
| | | if (id && sqlValue) { |
| | | const foundIndex = dockConfigList.value?.findIndex((item) => item.id === id); |
| | | if (foundIndex > -1) { |
| | | dockConfigList.value[foundIndex] = { |
| | | ...dockConfigList.value[foundIndex], |
| | | ...sqlValue, |
| | | }; |
| | | } else { |
| | | if (!sqlList.value || sqlList.value.length === 0) { |
| | | sqlList.value = [ |
| | | { |
| | | id: id, |
| | | sql: sqlValue, |
| | | }, |
| | | ]; |
| | | if (!sqlValue.database) { |
| | | ElMessage.warning('请先选择数据库'); |
| | | return; |
| | | } |
| | | const newSqlValue = { |
| | | id: id, |
| | | database: sqlValue.database ?? null, |
| | | sql: sqlValue.sql ?? null, |
| | | type: sqlValue.type ?? AmisDockType.Sql, |
| | | }; |
| | | if (!dockConfigList.value || dockConfigList.value.length === 0) { |
| | | dockConfigList.value = [newSqlValue]; |
| | | } else { |
| | | sqlList.value.push({ |
| | | id: id, |
| | | sql: sqlValue, |
| | | }); |
| | | dockConfigList.value.push(newSqlValue); |
| | | } |
| | | } |
| | | } |
| | |
| | | updateSqlApi( |
| | | { |
| | | id: props.supervisor.id, |
| | | sql_json: sqlList.value.length === 0 ? null : JSON.stringify(sqlList.value), |
| | | def_rs_json: dockConfigList.value.length === 0 ? null : JSON.stringify(dockConfigList.value), |
| | | async_rs_json: asyncRsList.length === 0 ? null : JSON.stringify(asyncRsList), |
| | | args: args.value.length === 0 ? null : JSON.stringify(args.value), |
| | | }, |
| | | { |
| | | loading: false, |
| | | } |
| | | ); |
| | | ).then(() => { |
| | | emit('updatePublished', props.supervisor.id, SupervisorPublished.N); |
| | | }); |
| | | }; |
| | | |
| | | const sqlCodeChange = debounce((val) => { |
| | | if (!currentRs.value.recordId) return; |
| | | updateSqlAndRs(currentRs.value.recordId, val); |
| | | if (currentDockConfig.value) { |
| | | if (!currentDockConfig.value.database) { |
| | | ElMessage.warning('请先选择数据库'); |
| | | return; |
| | | } |
| | | } |
| | | updateSqlAndRs(currentDockConfig.value.id, currentDockConfig.value); |
| | | }, 1000); |
| | | const sqlList = ref([]); |
| | | const dockConfigList = ref([]); |
| | | const rsTableRef = ref<TableInstance>(null); |
| | | let xmlJson = null; |
| | | |
| | |
| | | return Object.keys(currentItem).some((item) => originItem[item] !== currentItem[item]); |
| | | }); |
| | | }; |
| | | |
| | | //#region ====================== 可编辑表格 ====================== |
| | | const args = ref([]); |
| | | const debounceUpdateInput = debounce(() => { |
| | | updateSqlAndRs(); |
| | | }, 400); |
| | | const argsInput = (val) => { |
| | | debounceUpdateInput(); |
| | | }; |
| | | const addArg = () => { |
| | | const initData = { |
| | | name: '', |
| | | prompt: '', |
| | | check: '', |
| | | }; |
| | | if (!args.value || args.value.length === 0) { |
| | | args.value = [initData]; |
| | | } else { |
| | | args.value.push(initData); |
| | | } |
| | | |
| | | updateSqlAndRs(); |
| | | }; |
| | | |
| | | const deleteArg = (index) => { |
| | | args.value.splice(index, 1); |
| | | updateSqlAndRs(); |
| | | }; |
| | | |
| | | const databaseList = ref([]); |
| | | //#endregion |
| | | onMounted(async () => { |
| | | xmlJson = await supervisorApi.getLowCodeJson({ |
| | | id: props.supervisor.id, |
| | | }); |
| | | if (!xmlJson?.amis_json) { |
| | | ElMessage.warning('暂无SQL配置'); |
| | | return; |
| | | } |
| | | |
| | | const originConfig = |
| | | xmlJson.async_rs?.map( |
| | | (item) => |
| | | ({ |
| | | type: AmisDockType.Api, |
| | | type: AmisDockType.Sql, |
| | | asyncId: item.async_id, |
| | | recordId: item.rec_id, |
| | | path: item.amis_path, |
| | | } as AmisDockConfig) |
| | | ) ?? []; |
| | | sqlList.value = xmlJson.sql ?? null; |
| | | dockConfigList.value = xmlJson.def_rs_json ?? null; |
| | | args.value = xmlJson.args ?? []; |
| | | |
| | | configList.value = parseJSONData(xmlJson.amis_json, originConfig); |
| | | |
| | | const res = await supervisorApi.getAmisDatabaseList(); |
| | | databaseList.value = res.values ?? []; |
| | | if (configList.value.length > 0) { |
| | | rsTableRef.value.setCurrentRow(configList.value[0]); |
| | | // dockRowChange(configList.value[0]); |
| | | } |
| | | |
| | | if (!configList || configList.value.length === 0) { |
| | | ElMessage.warning('暂无对接配置'); |
| | | } |
| | | |
| | | |
| | | if (checkRsUpdate(originConfig, configList.value)) { |
| | | const isRsUpdate = checkRsUpdate(originConfig, configList.value); |
| | | if (isRsUpdate) { |
| | | // 自动更新Rs |
| | | updateSqlAndRs(); |
| | | } |
| | | }); |
| | | |
| | | // watch(() => sqlCodemirrorRef.value, (codeMirrorRef) => { |
| | | // codeMirrorRef. |
| | | // }) |
| | | </script> |