houyaf 3 سال پیش
والد
کامیت
10440b2844

+ 54 - 0
src/api/paas/apiGatewayApi.ts

@@ -0,0 +1,54 @@
+import apiRequest from '@/network/http/apiRequest'
+
+/**
+ * 获取Atu列表
+ * @param data
+ * @returns
+ */
+export function getX(apiCode: string, data: any) {
+  return apiRequest({
+    url: `/x/${apiCode}`,
+    method: 'GET',
+    params: data,
+  })
+}
+
+/**
+ * 添加Atu
+ * @param data
+ * @returns
+ */
+export function createX(apiCode: string, data: any) {
+  return apiRequest({
+    url: `/x/${apiCode}`,
+    method: 'POST',
+    data,
+  })
+}
+
+/**
+ * 更新Atu
+ * @param data
+ * @returns
+ */
+export function updateX(apiCode: string, data: any) {
+  return apiRequest({
+    url: `/x/${apiCode}`,
+    method: 'PUT',
+    data,
+  })
+}
+
+/**
+ * 删除Atu
+ * @param apiId
+ * @param atuId
+ * @returns
+ */
+export function deleteX(apiCode: string, data: any) {
+  return apiRequest({
+    url: `/x/${apiCode}`,
+    method: 'DELETE',
+    data,
+  })
+}

+ 12 - 0
src/router/modules/paas.ts

@@ -24,6 +24,18 @@ const routes: RouteRecordRaw[] = [
           activeMenu: '/paas/api',
         },
       },
+      {
+        path: 'api/trans/:modId/:apiId',
+        name: 'apiTrans',
+        component: () => import('@/views/paas/atu/index.vue'),
+        meta: {
+          title: 'Api事务配置',
+          sidebar: false,
+          activeMenu: '/paas/api',
+          cache: true,
+          noCache: 'apiTrans',
+        },
+      },
     ],
   },
   {

+ 1 - 0
src/types/components.d.ts

@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' {
     ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default']
     ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']
     LayoutContainer: typeof import('./../components/LayoutContainer/index.vue')['default']
+    ObjectSelector: typeof import('./../components/ObjectSelector/index.vue')['default']
     PageHeader: typeof import('./../components/PageHeader/index.vue')['default']
     PageMain: typeof import('./../components/PageMain/index.vue')['default']
     PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default']

+ 1 - 0
src/views/model/model/index.vue

@@ -222,6 +222,7 @@ const onObjectDelete = (row: any) => {
                           </el-icon>
                         </template>
                       </el-button>
+
                       <el-button type="danger" plain size="small" @click.stop="onModelDelete(data)">
                         <template #icon>
                           <el-icon>

+ 28 - 4
src/views/paas/api/index.vue

@@ -9,6 +9,11 @@ import PSModule from './components/Module.vue'
 import PSApi from './components/Api.vue'
 import { deleteApiById, getApiByList, updateApiStatus } from '@/api/paas/apiApi'
 import { deleteModuleById, getModuleByList } from '@/api/paas/moduleApi'
+import useSettingsStore from '@/store/modules/settings'
+
+const settingsStore = useSettingsStore()
+const router = useRouter()
+const tabbar = useTabbar()
 
 interface TreeNode {
   label: string
@@ -128,10 +133,6 @@ const onApiAdd = () => {
   apiRef.value.showAddApi(state.curModuleId)
 }
 
-const onApiTrans = () => {
-
-}
-
 // 编辑API
 const onApiEdit = (data: { apiId: number; modId: number }) => {
   const apiId = data.apiId
@@ -151,6 +152,29 @@ const onApiDelete = (row: any) => {
     })
   })
 }
+
+// 编辑对象
+const onApiTrans = (row: { modId: number; apiId: number }) => {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.open({
+      name: 'apiTrans',
+      params: {
+        modId: row.modId,
+        apiId: row.apiId,
+      },
+    })
+  }
+  else {
+    router.push({
+      name: 'apiTrans',
+      params: {
+        modId: row.modId,
+        apiId: row.apiId,
+      },
+    })
+  }
+}
+
 async function onChangeStatus(row: { modId: number;apiId: number; status: number }) {
   const res = await updateApiStatus(row.modId, row.apiId, row.status)
   ElMessage.success({

+ 291 - 0
src/views/paas/atu/components/ModelObjectSelector.vue

@@ -0,0 +1,291 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup>
+import { getModelByList } from '@/api/model/modelApi'
+import { getObjectByList } from '@/api/model/objectApi'
+import { createAtu } from '@/api/paas/apiTransUnitApi'
+import useSettingsStore from '@/store/modules/settings'
+
+const settingsStore = useSettingsStore()
+const router = useRouter()
+const tabbar = useTabbar()
+
+interface TreeNode {
+  label: string
+  isLeaf: boolean
+  modelId: number
+  children?: TreeNode[]
+}
+
+interface Object {
+  modelId: number
+  objectId: number
+  objectTitle: string
+  objectCode: string
+  comment: string
+}
+
+const state = reactive({
+  loading: false,
+  keywords: '',
+  models: [] as TreeNode[],
+  curModelId: 0,
+  objectList: [] as Object[],
+  dialogState: 1,
+  search: {
+    keywords: '',
+  },
+})
+
+const leftTreeRef = ref()
+const modelRef = ref()
+const getObjectData = async () => {
+  const curModelId = state.curModelId
+  if (curModelId > 0) {
+    state.loading = true
+    const { data } = await getObjectByList({ modelId: curModelId })
+    state.objectList = data
+    state.loading = false
+  }
+}
+
+const getModelData = async () => {
+  const { data } = await getModelByList('')
+  const children: TreeNode[] = []
+  data.forEach((item: any) => {
+    return children.push({
+      label: item.modelTitle,
+      modelId: item.modelId,
+      isLeaf: true,
+    })
+  })
+  state.models = [
+    {
+      label: '模型库',
+      isLeaf: false,
+      modelId: -1,
+      children,
+    },
+  ]
+
+  if (data && data.length > 0) {
+    state.curModelId = data[0].modelId
+    nextTick(() => {
+      leftTreeRef.value.setCurrentKey(data[0].modelId)
+    })
+    await getObjectData()
+  }
+}
+
+onMounted(() => {
+  getModelData()
+})
+
+// 模型选择
+const onTreeNodeClick = (node: TreeNode) => {
+  if (node.isLeaf) {
+    state.curModelId = node.modelId
+    getObjectData()
+  }
+}
+
+const objectTableRef = ref()
+const objectRef = ref()
+
+// 检索对象
+const onSearch = () => {
+  getObjectData()
+}
+
+const onObjectSel = async (row: { modelId: any; objectId: any }) => {
+  const data = {
+    // apiId: apiId,
+    modelId: row.modelId,
+    objectId: row.objectId,
+  }
+  await createAtu(data)
+  getObjectData()
+}
+</script>
+
+<template>
+  <div class="absolute-container">
+    <tool-header title="数据模型">
+      <template #right>
+        <el-button plain @click="onModelAdd">
+          <template #icon>
+            <el-icon>
+              <svg-icon name="i-ep:plus" />
+            </el-icon>
+          </template>
+          新增模型
+        </el-button>
+      </template>
+    </tool-header>
+
+    <div class="page-main">
+      <LayoutContainer left-side-width="300px" hide-left-side-toggle>
+        <template #leftSide>
+          <el-scrollbar class="tree">
+            <ElTree ref="leftTreeRef" :data="state.models" default-expand-all node-key="modelId" @node-click="onTreeNodeClick">
+              <template #default="{ node, data }">
+                <div class="custom-tree-node">
+                  <div class="label" :title="node.label">
+                    <el-icon v-if="data.isLeaf" size="16px" style="margin-right: 5px;">
+                      <svg-icon name="uim:box" />
+                    </el-icon>
+                    <div class="text">
+                      {{ data.label }}
+                    </div>
+                  </div>
+                  <div v-if="data.isLeaf" class="actions">
+                    <el-button-group>
+                      <el-button type="info" plain size="small" @click.stop="onModelEdit(data)">
+                        <template #icon>
+                          <el-icon>
+                            <svg-icon name="i-ep:edit" />
+                          </el-icon>
+                        </template>
+                      </el-button>
+
+                      <el-button type="danger" plain size="small" @click.stop="onModelDelete(data)">
+                        <template #icon>
+                          <el-icon>
+                            <svg-icon name="i-ep:delete" />
+                          </el-icon>
+                        </template>
+                      </el-button>
+                    </el-button-group>
+                  </div>
+                </div>
+              </template>
+            </ElTree>
+          </el-scrollbar>
+        </template>
+
+        <div class="container">
+          <tool-bar>
+            <template #right>
+              <el-input v-model="state.search.keywords" placeholder="请输入名称,支持模糊查询" style="padding-right: 10px;" clearable />
+              <el-button type="primary" @click="onSearch">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:search" />
+                  </el-icon>
+                </template>
+              </el-button>
+            </template>
+          </tool-bar>
+
+          <el-table ref="objectTableRef" v-loading="state.loading" class="list-table" :data="state.objectList" border stripe highlight-current-row height="100%">
+            <el-table-column type="index" label="序号" header-align="center" align="center" width="70" />
+
+            <el-table-column prop="objectTitle" label="名称" header-align="center" align="center" width="200" />
+
+            <el-table-column prop="objectCode" label="代码" header-align="center" align="center" width="150" />
+
+            <el-table-column prop="objectType" label="类型" header-align="center" align="center" width="150">
+              <template #default="scope">
+                <span v-if="scope.row.objectType === 1">实体对象</span>
+                <span v-else>视图对象</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="comment" label="说明" header-align="center" align="left" />
+
+            <el-table-column label="操作" header-align="center" align="center" width="200">
+              <template #default="scope">
+                <el-button type="primary" size="small" plain @click="onObjectSel(scope.row)">
+                  选择
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </LayoutContainer>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.absolute-container {
+  :deep(.left-side) {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+
+    .btns {
+      display: inline-flex;
+      width: 100%;
+      margin-bottom: var(--container-padding);
+
+      .add {
+        width: 100%;
+      }
+    }
+
+    .tree {
+      flex: 1;
+      overflow-y: auto;
+
+      .el-tree {
+        .el-tree-node__content {
+          height: 40px;
+        }
+
+        .is-current > .el-tree-node__content {
+          background-color: var(--el-color-primary-light-9);
+        }
+
+        .custom-tree-node {
+          flex: 1;
+          position: relative;
+          width: 0;
+          height: 100%;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+
+          .label {
+            display: inline-flex;
+            align-items: center;
+            width: calc(100% - 10px);
+            color: var(--el-text-color-primary);
+
+            .text {
+              @include text-overflow;
+            }
+          }
+
+          &:hover {
+            .actions {
+              display: block;
+            }
+          }
+
+          .actions {
+            display: none;
+            position: absolute;
+            right: 10px;
+            top: 50%;
+            transform: translateY(-50%);
+
+            .el-button {
+              padding: 5px 8px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 265 - 0
src/views/paas/atu/index.vue

@@ -0,0 +1,265 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup name="PagesExampleTable">
+import type { FormInstance } from 'element-plus'
+import useSettingsStore from '@/store/modules/settings'
+import { getApiById } from '@/api/paas/apiApi'
+import { getAtuByList } from '@/api/paas/apiTransUnitApi'
+
+const settingsStore = useSettingsStore()
+const route = useRoute()
+const router = useRouter()
+const tabbar = useTabbar()
+
+const modId = parseInt(route.params.modId.toString())
+const apiId = parseInt(route.params.apiId.toString())
+
+const state = reactive({
+  loading: false,
+  apiTitle: '',
+  apiCode: '',
+  keywords: '',
+  atuList: [],
+  dialogState: 1,
+  search: {
+    apiId,
+    keywords: '',
+  },
+  data: '',
+})
+
+const getAtuData = async () => {
+  const { data } = await getAtuByList(state.search)
+  state.atuList = data
+  state.loading = false
+}
+
+// 查询数据
+const getData = async () => {
+  state.loading = true
+  const { data } = await getApiById(modId, apiId)
+  state.apiTitle = data.apiTitle
+  state.apiCode = data.apiCode
+  await getAtuData()
+}
+
+onMounted(() => {
+  getData()
+})
+
+const formRef = ref<FormInstance>()
+
+const onAdd = () => {}
+
+const onEdit = (data: { atuId: number }) => {
+}
+
+const onDelete = (data: { atuId: number }) => {
+}
+
+const onGenerateData = () => {
+
+}
+
+const onExecute = () => {
+
+}
+
+function onCancel() {
+  goBack()
+}
+
+// 返回列表页
+function goBack() {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.close({ name: 'paas_api' })
+  }
+  else {
+    router.push({ name: 'paas_api' })
+  }
+}
+</script>
+
+<template>
+  <div class="absolute-container">
+    <page-header :title="state.apiTitle">
+      <el-button size="default" round @click="goBack">
+        <template #icon>
+          <el-icon>
+            <svg-icon name="i-ep:arrow-left" />
+          </el-icon>
+        </template>
+        返回
+      </el-button>
+    </page-header>
+    <div class="page-main">
+      <LayoutContainer left-side-width="520px" hide-left-side-toggle>
+        <template #leftSide>
+          <el-space wrap>
+            <el-button type="primary" size="default" @click="onAdd">
+              <template #icon>
+                <el-icon>
+                  <svg-icon name="i-ep:plus" />
+                </el-icon>
+              </template>
+            </el-button>
+          </el-space>
+
+          <el-table v-loading="state.loading" class="list-table" :data="state.atuList" border stripe highlight-current-row height="100%">
+            <el-table-column prop="atuTitle" label="名称" min-width="150px" />
+            <el-table-column label="操作" width="140" align="center" fixed="right">
+              <template #default="scope">
+                <el-button-group>
+                  <el-button type="primary" size="small" plain @click="onEdit(scope.row)">
+                    <template #icon>
+                      <el-icon>
+                        <svg-icon name="i-ep:edit" />
+                      </el-icon>
+                    </template>
+                  </el-button>
+
+                  <el-button type="danger" size="small" plain @click="onDelete(scope.row)">
+                    <template #icon>
+                      <el-icon>
+                        <svg-icon name="i-ep:delete" />
+                      </el-icon>
+                    </template>
+                  </el-button>
+                </el-button-group>
+              </template>
+            </el-table-column>
+          </el-table>
+        </template>
+
+        <div class="form">
+          <el-form ref="formRef" label-width="100px" label-position="top">
+            <el-form-item label="数据参数" prop="code">
+              <textarea v-model="state.data" placeholder="数据" rows="10" />
+            </el-form-item>
+          </el-form>
+
+          <div class="action-bottom">
+            <el-button type="primary" @click="onGenerateData">
+              生成参数
+            </el-button>
+
+            <el-button type="primary" @click="onExecute">
+              立即执行
+            </el-button>
+          </div>
+        </div>
+      </LayoutContainer>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.absolute-container {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .page-header {
+    margin-bottom: 0;
+  }
+
+  .page-main {
+    // 让 page-main 的高度自适应
+    flex: 1;
+    overflow: auto;
+    display: flex;
+    flex-direction: column;
+
+    .flex-container {
+      position: static;
+
+      :deep(.left-side) {
+        overflow: auto;
+        display: flex;
+        flex-direction: column;
+      }
+
+      :deep(.main-container) {
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+
+        .empty {
+          flex: 1;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 32px;
+          color: var(--el-text-color-placeholder);
+        }
+      }
+    }
+
+    .form {
+      flex: 1;
+      min-height: 0;
+      display: flex;
+      flex-direction: column;
+
+      .el-form {
+        flex: 1;
+        min-height: 0;
+        display: flex;
+        flex-direction: column;
+
+        .columns-table {
+          flex: 1;
+          height: 0;
+          display: flex;
+          flex-direction: column;
+
+          :deep(.el-form-item__content) {
+            min-height: 0;
+
+            .el-table {
+              height: 100%;
+            }
+          }
+        }
+      }
+
+      .action-bottom {
+        padding-top: 2px;
+      }
+
+      .el-table {
+        .el-table__row {
+          .index {
+            display: inline-block;
+          }
+
+          .delete {
+            display: none;
+          }
+
+          &:hover {
+            .index {
+              display: none;
+            }
+
+            .delete {
+              display: inline-block;
+            }
+          }
+
+          .el-tag.sortable,
+          .el-tag.sortable .el-icon {
+            cursor: s-resize;
+          }
+        }
+      }
+    }
+
+  }
+}
+</style>