dataQuery.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <template>
  2. <el-row class="app-container" style="width: 100%;height: 100%;">
  3. <el-col :span="4" style="height: 100%">
  4. <el-card style="height: 100%;overflow: auto;overflow-x: hidden;" shadow="never">
  5. <template #header>
  6. <div class="card-header" style="display: flex;align-items: center;justify-content: space-between;">
  7. <span>目录:</span>
  8. <div>
  9. <el-icon @click="addDir" style="cursor: pointer;color: #409EFF;">
  10. <Plus/>
  11. </el-icon>
  12. </div>
  13. </div>
  14. </template>
  15. <el-tree :expand-on-click-node="false" ref="treeRef"
  16. :current-node-key="currentNodeKey" class="treeLeft"
  17. :data="data" @node-click="handleNodeClick" node-key="id"
  18. style="margin-left: -10%;margin-top: 0;width: 120%;background-color: transparent;" default-expand-all>
  19. <template #default="{ node, data }">
  20. <span style="justify-content: space-between;display: flex;width: 20%;align-items: center;margin-left: 1%;">
  21. <div class="custom-tree-node" style="align-items: center;line-height: 1.5;">
  22. <svg-icon icon-class="model" v-if="data.nodeType=='SET'"/>
  23. <svg-icon icon-class="model2" v-if="data.nodeType=='TREE'"/>
  24. </div>
  25. <span style="font-size: 1rem;">{{ node.label }}</span>
  26. <!-- <div style="margin-left: auto;position: absolute;left:90%;">-->
  27. <!-- <el-dropdown trigger="hover" @click.stop v-if="currentNodeKey === data.id">-->
  28. <!-- &lt;!&ndash; <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
  29. <!-- <plus />-->
  30. <!-- </el-icon> &ndash;&gt;-->
  31. <!-- <svg-icon icon-class="zhankai"/>-->
  32. <!-- <template #dropdown>-->
  33. <!-- <el-dropdown-menu>-->
  34. <!-- <el-dropdown-item style="display: flex;" @click="add1Level">-->
  35. <!-- <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
  36. <!-- <CirclePlus/>-->
  37. <!-- </el-icon>-->
  38. <!-- <div>-->
  39. <!-- 添加同级-->
  40. <!-- </div>-->
  41. <!-- </el-dropdown-item>-->
  42. <!-- <el-dropdown-item style="display: flex;" @click="addNextLevel">-->
  43. <!-- <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
  44. <!-- <Connection/>-->
  45. <!-- </el-icon>-->
  46. <!-- <div>-->
  47. <!-- 新建下级-->
  48. <!-- </div>-->
  49. <!-- </el-dropdown-item>-->
  50. <!-- <divider/>-->
  51. <!-- <el-dropdown-item style="display: flex;" @click="delAll" divided>-->
  52. <!-- <el-icon class="el-icon&#45;&#45;right" style="color: black;">-->
  53. <!-- <CircleClose/>-->
  54. <!-- </el-icon>-->
  55. <!-- <div>-->
  56. <!-- 删除节点-->
  57. <!-- </div>-->
  58. <!-- </el-dropdown-item>-->
  59. <!-- </el-dropdown-menu>-->
  60. <!-- </template>-->
  61. <!-- </el-dropdown>-->
  62. <!-- </div>-->
  63. </span>
  64. </template>
  65. </el-tree>
  66. </el-card>
  67. </el-col>
  68. <el-col :span="20" style="height: 100%">
  69. <div style="height: 40px;display: flex;justify-content: space-between;align-items: center;">
  70. <div style="display: flex;align-items: center;">
  71. <template v-for="(param, index) in params" :key="index">
  72. <div style="margin-right: 5px;display: flex;align-items: center;">
  73. <div style="margin-right: 5px;">{{ param.key }}</div>
  74. <el-input v-model="param.value"></el-input>
  75. </div>
  76. </template>
  77. <el-button type="primary" @click="handleQuery">搜索</el-button>
  78. </div>
  79. <el-button @click="handleEdit">编辑配置</el-button>
  80. </div>
  81. <div style="height: calc(100% - 40px);">
  82. <biz-display ref="bizDisplayRef" source="DATA_SET" :config="dataSetConfig"></biz-display>
  83. </div>
  84. </el-col>
  85. <el-dialog v-model="dialogVisible" :title="dialogTitle" destroy-on-close>
  86. <el-form :model="form" label-position="right" ref="formRef" label-width="120px" :rules="rules">
  87. <el-form-item label="数据集中文名:" prop="dcName" style="">
  88. <el-input v-model="form.dcName" style="width: 75%;" placeholder="" resize="none"/>
  89. </el-form-item>
  90. <el-form-item label="数据集说明:" prop="dcNote" style="">
  91. <el-input v-model="form.dcNote" style="width: 75%;" placeholder="" type="textarea" :row="2" resize="none"/>
  92. </el-form-item>
  93. <el-form-item label="数据集类型:" prop="dcType" style="">
  94. <el-select
  95. v-model="form.dcType"
  96. class="noBorSel"
  97. placeholder=""
  98. style="width: 75%;margin-left: 0%;"
  99. >
  100. <el-option
  101. v-for="item in optionsShuLei"
  102. :key="item.value"
  103. :label="item.label"
  104. :value="item.value"
  105. />
  106. </el-select>
  107. </el-form-item>
  108. <el-form-item label="排序:" prop="dcSort">
  109. <el-input-number v-model="form.dcSort" :min="1" style="width: 50%;"/>
  110. </el-form-item>
  111. <el-form-item label="请求数据" style="max-height: 300px;">
  112. <div style="width: 100%;height: 100%;background-color: #FAFAFA;overflow: auto;border-radius: 10px;">
  113. <biz-data-show-config-api v-model="form.queryOptions"></biz-data-show-config-api>
  114. </div>
  115. </el-form-item>
  116. <el-form-item label="展示类型">
  117. <div
  118. style="width:100%;max-height: 300px;overflow: auto;background-color: #FAFAFA;border-radius: 10px;padding: 10px;">
  119. <biz-data-show-config-list :columns="columns"
  120. v-model="form.renderingOptions"></biz-data-show-config-list>
  121. </div>
  122. </el-form-item>
  123. </el-form>
  124. <template #footer>
  125. <el-button @click="close">取消</el-button>
  126. <el-button type="primary" @click="submit">提交</el-button>
  127. </template>
  128. </el-dialog>
  129. </el-row>
  130. </template>
  131. <script setup>
  132. import {nextTick, onMounted, ref} from 'vue'
  133. import {modelTreeSelect} from "@/api/service/info";
  134. import {dataSetTree, editSet, getDataSourceList, getSetById} from "@/api/register/regCom.js";
  135. import {cloneDeep} from 'lodash'
  136. import BizDataShowConfigApi from "@/views/standardization/bizDataShowConfig/api/index.vue";
  137. import BizDataShowConfigList from "@/views/standardization/bizDataShowConfig/list/index.vue";
  138. import BizDisplay from "@/views/standardization/bizDataShowConfig/show/index.vue";
  139. const total = ref(0)
  140. const dialogVisible = ref(false)
  141. const dialogTitle = ref('')
  142. const formRef = ref()
  143. const form = ref({})
  144. const rules = ref({})
  145. const columns = ref([])
  146. const bizDisplayRef = ref(null)
  147. const dataSetConfig = ref(null)
  148. const params = ref({})
  149. const optionsShuLei = ref([
  150. {
  151. label: "SQL数据集",
  152. value: 'SQL'
  153. },
  154. {
  155. label: "API数据集",
  156. value: 'API'
  157. },
  158. {
  159. label: "文件数据集",
  160. value: 'FILE'
  161. },
  162. ])
  163. const valueNode = ref('')
  164. const {proxy} = getCurrentInstance();
  165. const data = ref([])
  166. const dialogVisibleData = ref(false)
  167. const parTree = ref(null)
  168. const byNode = ref(false)
  169. const optionsDs = ref([])
  170. const currentNodeKey = ref('')
  171. const dataSel = ref([])
  172. const parAr = ref([])
  173. function handleEdit() {
  174. getSetById(parTree.value.id).then(res => {
  175. form.value = res.data.dataSet
  176. })
  177. dialogTitle.value = `编辑【${parTree.value.label}】`
  178. dialogVisible.value = true
  179. }
  180. function submit() {
  181. proxy.$refs.formRef.validate(valid => {
  182. if (valid) {
  183. editSet(form.value).then(response => {
  184. proxy.$modal.msgSuccess("修改成功");
  185. });
  186. }
  187. });
  188. }
  189. function close() {
  190. form.value = {}
  191. dialogTitle.value = ''
  192. dialogVisible.value = false
  193. }
  194. function addDir() {
  195. byNode.value = true
  196. valueNode.value = data.value[0].value
  197. dialogVisibleData.value = true
  198. }
  199. async function getTreeSel() {
  200. await modelTreeSelect().then(res => {
  201. dataSel.value = renameTreeProperties(res.data)
  202. console.log(dataSel.value)
  203. })
  204. }
  205. function getOptionsDs() {
  206. getDataSourceList().then(res => {
  207. optionsDs.value = res.rows
  208. optionsDs.value.forEach(item => {
  209. item.label = item.dsDbname
  210. item.value = item.dsCode
  211. })
  212. total.value = res.total
  213. })
  214. }
  215. function addValueField(nodes) {
  216. if (!Array.isArray(nodes)) return;
  217. for (const node of nodes) {
  218. // 添加value字段,其值等于id
  219. node.value = node.id;
  220. // 如果存在子节点,递归处理
  221. if (node.children && Array.isArray(node.children)) {
  222. addValueField(node.children);
  223. }
  224. }
  225. }
  226. const flattenTree = (nodes) => {
  227. const result = [];
  228. const stack = [...nodes];
  229. while (stack.length) {
  230. const node = stack.pop();
  231. result.push({id: node.id, pid: node.pid}); // 只提取 id 和 pid
  232. if (node.children && node.children.length) {
  233. stack.push(...node.children);
  234. }
  235. }
  236. return result;
  237. };
  238. function getTree() {
  239. dataSetTree().then(res => {
  240. data.value = res.data
  241. data.value.forEach(item => {
  242. item.value = item.id
  243. item.id = item.id
  244. })
  245. addValueField(data.value)
  246. parAr.value = flattenTree(data.value);
  247. })
  248. }
  249. function handleNodeClick(node, data) {
  250. currentNodeKey.value = data.data.id
  251. parTree.value = data.data
  252. getSetById(data.data.id).then(res => {
  253. nextTick(() => {
  254. let data = res.data.dataSet
  255. data.id = data.dcCode
  256. dataSetConfig.value = data
  257. })
  258. })
  259. }
  260. function renameTreeProperties(tree) {
  261. // 深拷贝避免修改原始数据(可选,根据需求)
  262. const newTree = cloneDeep(tree); // 使用 lodash
  263. // 或:const newTree = JSON.parse(JSON.stringify(tree)); // 原生方法(不兼容函数等)
  264. // 递归处理函数
  265. const processNode = (node) => {
  266. // 重命名属性
  267. if ('cateName' in node) {
  268. node.label = node.cateName;
  269. delete node.cateName;
  270. }
  271. if ('id' in node) {
  272. node.value = node.id;
  273. delete node.id;
  274. }
  275. // 递归处理子节点
  276. if (node.children && node.children.length > 0) {
  277. node.children.forEach(child => processNode(child));
  278. }
  279. };
  280. // 遍历根节点
  281. newTree.forEach(rootNode => processNode(rootNode));
  282. return newTree;
  283. }
  284. function handleQuery() {
  285. }
  286. onMounted(() => {
  287. getTree()
  288. getOptionsDs()
  289. getTreeSel()
  290. // getTable()
  291. });
  292. watch(() => form.value.queryOptions, queryOptions => {
  293. if (queryOptions) {
  294. const api = JSON.parse(queryOptions)
  295. columns.value = api.response
  296. }
  297. }, {immediate: true, deep: true})
  298. watch(() => dataSetConfig.value, config => {
  299. const queryOptions = JSON.parse(config.queryOptions)
  300. const param = [...queryOptions.params, ...queryOptions.body]
  301. debugger
  302. params.value = param
  303. }, {deep: true})
  304. </script>
  305. <style scoped>
  306. :deep(.svg-icon) {
  307. outline: none;
  308. }
  309. :deep(.svg-icon svg) {
  310. stroke: none;
  311. }
  312. :deep(.el-tree-node__expand-icon) {
  313. margin-left: 5%; /* 向右推动图标 */
  314. }
  315. :deep(.el-table__body tr:hover > td) {
  316. background-color: #eaf7ff !important;
  317. }
  318. :deep(.treeLeft) .el-tree-node__content {
  319. width: 100%;
  320. display: flex !important;
  321. height: 30px !important;
  322. align-items: center;
  323. padding-top: 0 !important;
  324. }
  325. :deep(.treeLeft) .el-tree-node__content:hover {
  326. background-color: #ecf5ff;
  327. }
  328. :deep(.treeLeft) .el-tree-node__content:active {
  329. background-color: #d9ecff !important;
  330. }
  331. /* 选中态(Active) */
  332. :deep(.treeLeft) .el-tree-node.is-current > .el-tree-node__content {
  333. background-color: #c6e2ff !important;
  334. }
  335. .drag-handle {
  336. cursor: move;
  337. }
  338. .ghost {
  339. opacity: 0.5;
  340. background: #c8ebfb;
  341. }
  342. /* 防止文字选中 */
  343. :deep(.el-table__row) {
  344. user-select: none;
  345. -webkit-user-select: none;
  346. }
  347. </style>
  348. <style scoped lang="scss">
  349. .el-table .el-table__row td {
  350. height: 60px !important; /* 行高 */
  351. }
  352. .custom-tree-node {
  353. display: flex; /* 启用 Flex 布局 */
  354. align-items: center; /* 垂直居中 */
  355. gap: 6px; /* 图标与文字间距 */
  356. }
  357. .custom-tree-node {
  358. flex: 1;
  359. display: flex;
  360. align-items: center;
  361. justify-content: space-between;
  362. font-size: 14px;
  363. padding-right: 8px;
  364. }
  365. </style>