From 4f2d13e6de729a08bf96a391fdd21f1cc8f48453 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期五, 20 九月 2024 13:03:28 +0800 Subject: [PATCH] 自动加注释 --- src/views/project/yw/lowCode/sqlAmis/edit/SqlAmisEdit.vue | 461 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 376 insertions(+), 85 deletions(-) diff --git a/src/views/project/yw/lowCode/sqlAmis/edit/SqlAmisEdit.vue b/src/views/project/yw/lowCode/sqlAmis/edit/SqlAmisEdit.vue index dbc68a5..08b1351 100644 --- a/src/views/project/yw/lowCode/sqlAmis/edit/SqlAmisEdit.vue +++ b/src/views/project/yw/lowCode/sqlAmis/edit/SqlAmisEdit.vue @@ -4,7 +4,7 @@ <template v-slot:left> <el-button icon="ele-ArrowLeft" - text + link style="margin-right: 10px; margin-left: 10px; width: 40px" size="small" @click="backLastPage" @@ -13,47 +13,92 @@ </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="queryId" label="鏌ヨ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"> - <codemirror - class="h-full overflow-auto" - v-model="sqlCode" - :style="{ height: '100%' }" - :autofocus="true" - :indent-with-tab="true" - :tab-size="2" - :extensions="sqlEditorExtensions" - @change="log('change', $event)" - @focus="log('focus', $event)" - @blur="log('blur', $event)" - /> - </div> - </div> + <el-table-column prop="recordId" label="SQL id" /> + <!-- <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> @@ -62,20 +107,24 @@ import { sql } from '@codemirror/lang-sql'; import { xml } from '@codemirror/lang-xml'; import { vscodeDark } from '@uiw/codemirror-theme-vscode'; -import _ from 'lodash'; -import { onMounted, ref } from 'vue'; -import { Codemirror } from 'vue-codemirror'; -import * as codeExample from './testData'; -import titleBox from '/@/components/titleBox.vue'; - -import { v4 as uuid } from 'uuid'; -import { getAmisXml } from '/@/api/supervisorAdmin'; -import { useCompRef } from '/@/utils/types'; -import { XMLParser, XMLBuilder, XMLValidator } from 'fast-xml-parser'; +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); @@ -89,17 +138,76 @@ 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(); // }, 300); emit('backLastPage'); }; -const dockRowChange = (row) => {}; +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; + 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 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; @@ -107,7 +215,7 @@ 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; @@ -135,61 +243,244 @@ }; const enum AmisDockType { - Api, + Sql = 'SQL', } type AmisDockApi = string; type AmisDockValue = AmisDockApi; type AmisDockConfig = { - type: AmisDockType; + type?: AmisDockType; path: string; - value: AmisDockValue; -}; - -type AmisDockSQLConfig = AmisDockConfig & { - queryId: string; + asyncId: string; recordId: string; + // url: string; }; const configList = ref<AmisDockConfig[]>([]); -const parseJSONData = (json: string) => { - if (!json) return; - const obj = JSON.parse(json); - travelObj(obj, (key, value, path) => { - if (key === 'api') { - const url = value.url; - const urlPath = path + PATH_SEPARATOR + 'url'; - const queryId = 'query_' + uuid().slice(0, 12); +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 = []; + extraInfoMap.value.clear(); + travelObj(obj, (key, value, path, item) => { + if (key === 'api') { + // const url = value.url; + const urlPath = path + PATH_SEPARATOR + 'url'; + 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() - configList.value.push({ - queryId: queryId, - recordId: queryId, - type: AmisDockType.Api, + jsonConfigList.push({ + asyncId: asyncId, + recordId: recordId, + type: AmisDockType.Sql, path: urlPath, - value: url, - url: url, - } as any); + }); } + }); + + return jsonConfigList; +}; + +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 涓湁鐨剅ecord; + 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 && sqlValue) { + const foundIndex = dockConfigList.value?.findIndex((item) => item.id === id); + if (foundIndex > -1) { + dockConfigList.value[foundIndex] = { + ...dockConfigList.value[foundIndex], + ...sqlValue, + }; + } else { + 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 { + dockConfigList.value.push(newSqlValue); + } + } + } + + updateSqlApi( + { + id: props.supervisor.id, + 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; + if (currentDockConfig.value) { + if (!currentDockConfig.value.database) { + ElMessage.warning('璇峰厛閫夋嫨鏁版嵁搴�'); + return; + } + } + updateSqlAndRs(currentDockConfig.value.id, currentDockConfig.value); +}, 1000); +const dockConfigList = ref([]); +const rsTableRef = ref<TableInstance>(null); +let xmlJson = null; -const xmlParserInstance = new XMLParser(); -const xmlBuilderInstance = new XMLBuilder(); -let xmlConfig = null; -onMounted(async () => { - xmlConfig = await getAmisXml({ - agent_id: props.supervisor.id, +// 妫�鏌ユ槸鍚﹂渶瑕佹洿鏂� rs +const checkRsUpdate = (originData: AmisDockConfig[], currentData: AmisDockConfig[]) => { + // 閮戒负绌猴紝涓嶉渶瑕佹洿鏂� + if ((!originData || originData.length === 0) && (!currentData || currentData.length === 0)) { + return false; + } + + // 瀛樺湪涓�鏂逛负绌猴紝闇�瑕佹洿鏂� + if (!originData || !currentData) { + return true; + } + + // 闀垮害涓嶄竴鑷达紝闇�瑕佹洿鏂� + if (originData.length !== currentData.length) { + return true; + } + + return originData.some((originItem) => { + const id = originItem.asyncId; + const currentItem = currentData.find((item) => item.asyncId === id); + // 娌℃壘鍒板搴旈」锛岄渶瑕佹洿鏂� + if (!currentItem) { + return true; + } + + // 瀵硅薄椤瑰�间笉鐩哥瓑锛岄渶瑕佹洿鏂� + return Object.keys(currentItem).some((item) => originItem[item] !== currentItem[item]); }); - if (!xmlConfig?.amis_xml) { - // ElMessage.warning('鏆傛棤鏁版嵁'); - return; +}; + +//#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); } - const jObj = xmlParserInstance.parse(xmlConfig.amis_xml); - if (!jObj?.AMIS_JSON) { - return; + + updateSqlAndRs(); +}; + +const deleteArg = (index) => { + args.value.splice(index, 1); + updateSqlAndRs(); +}; + +const databaseList = ref([]); +//#endregion +onMounted(async () => { + xmlJson = await supervisorApi.getLowCodeJson({ + id: props.supervisor.id, + }); + + const originConfig = + xmlJson.async_rs?.map( + (item) => + ({ + type: AmisDockType.Sql, + asyncId: item.async_id, + recordId: item.rec_id, + path: item.amis_path, + } as AmisDockConfig) + ) ?? []; + 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]); } - const amisJSON = jObj.AMIS_JSON; - parseJSONData(amisJSON); + + if (!configList || configList.value.length === 0) { + ElMessage.warning('鏆傛棤瀵规帴閰嶇疆'); + } + + const isRsUpdate = checkRsUpdate(originConfig, configList.value); + if (isRsUpdate) { + // 鑷姩鏇存柊Rs + updateSqlAndRs(); + } }); + +// watch(() => sqlCodemirrorRef.value, (codeMirrorRef) => { +// codeMirrorRef. +// }) </script> -- Gitblit v1.9.3