houyaf hace 3 años
padre
commit
3696d857db

+ 1 - 1
src/components/AppCatSelector/index.vue

@@ -8,7 +8,7 @@ const props = withDefaults(defineProps<{
 const emit = defineEmits(['update:modelValue'])
 interface AppCat {
   appCatId: number
-  appCatTitle: number
+  appCatTitle: string
 }
 const appCatId = computed({
   get: () => {

+ 52 - 0
src/components/DataTypeSelector/index.vue

@@ -0,0 +1,52 @@
+<script lang="ts" setup name="DataTypeSelector">
+const props = withDefaults(defineProps<{
+  modelValue: number
+}>(), {
+  modelValue: 0,
+})
+const emit = defineEmits(['update:modelValue'])
+interface DataType {
+  dataTypeId: number
+  dataTypeTitle: string
+}
+const dataTypeId = computed({
+  get: () => {
+    return props.modelValue
+  },
+  set: (val) => {
+    emit('update:modelValue', val)
+  },
+})
+
+const dataType = [
+  { dataType: 0, dataName: '请选择' },
+  { dataType: 1, dataName: '整数' },
+  { dataType: 2, dataName: '字符串' },
+  { dataType: 3, dataName: '浮点小数' },
+  { dataType: 4, dataName: '日期' },
+  { dataType: 5, dataName: '日期时间' },
+]
+
+const state = reactive({
+  loading: false,
+  dataList: dataType,
+})
+
+const getData = async () => {
+}
+onMounted(() => {
+  getData ()
+})
+</script>
+
+<template>
+  <el-select v-model="dataTypeId" size="small" v-loading="state.loading" style="width: 100%" filterable placeholder="请选择数据类型" value-key="dataType">
+    <el-option key="0" label="请选择" :value="0" disabled />
+    <el-option
+      v-for="item in dataType"
+      :key="item.dataType"
+      :label="item.dataName"
+      :value="item.dataType"
+    />
+  </el-select>
+</template>

+ 15 - 15
src/router/modules/biz.ts

@@ -4,19 +4,19 @@ const Layout = () => import('@/layouts/index.vue')
 
 const routes: RouteRecordRaw[] = [
   {
-    path: '/biz/object',
+    path: '/biz',
     component: Layout,
-    redirect: '/biz/object/index',
-    name: 'biz_object',
+    redirect: '/biz/index',
+    name: 'biz_index',
     meta: {
       title: '业务模型',
-      icon: 'ep:element-plus',
+      icon: 'bx:bxs-component',
     },
     children: [
       {
         path: 'index',
-        name: 'biz_object_index',
-        component: () => import('@/views/biz/object/index.vue'),
+        name: 'biz_object_list',
+        component: () => import('@/views/biz/biz/index.vue'),
         meta: {
           title: '业务对象',
           sidebar: false,
@@ -25,19 +25,19 @@ const routes: RouteRecordRaw[] = [
         },
       },
       {
-        path: 'attr/:bizId/:objectId',
-        name: 'bizObjectEdit',
-        component: () => import('@/views/biz/attr/index.vue'),
+        path: 'object/:bizId/:objectId',
+        name: 'bizObjectDetails',
+        component: () => import('@/views/biz/object/index.vue'),
         meta: {
-          title: '对象编辑',
+          title: '对象详情',
           sidebar: false,
           activeMenu: '/biz/object',
           cache: true,
-          noCache: 'bizObjectEdit',
+          noCache: 'bizObjectDetails',
         },
       },
       {
-        path: '/oam/:bizId/:objectId',
+        path: '/object/oam/:bizId/:objectId',
         name: 'bizObjectOAM',
         component: () => import('@/views/biz/oam/index.vue'),
         meta: {
@@ -51,9 +51,9 @@ const routes: RouteRecordRaw[] = [
     ],
   },
   {
-    path: '/biz/rule',
+    path: '/biz1/rule',
     component: Layout,
-    redirect: '/biz/rule/index',
+    redirect: '/biz1/rule/index',
     name: 'rule',
     meta: {
       title: '业务规则',
@@ -68,7 +68,7 @@ const routes: RouteRecordRaw[] = [
           title: '业务规则',
           sidebar: false,
           breadcrumb: false,
-          activeMenu: '/biz/rule',
+          activeMenu: '/biz1/rule',
         },
       },
     ],

+ 1 - 1
src/router/routes.ts

@@ -99,7 +99,7 @@ const asyncRoutes: Route.recordMainRaw[] = [
   {
     meta: {
       title: '模型',
-      icon: 'uim:box',
+      icon: 'bx:bxs-component',
     },
     children: [
       ...ModelEntries,

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

@@ -16,6 +16,7 @@ declare module '@vue/runtime-core' {
     Chip: typeof import('./../components/Chip/index.vue')['default']
     ColorfulCard: typeof import('./../components/ColorfulCard/index.vue')['default']
     Copyright: typeof import('./../components/Copyright/index.vue')['default']
+    DataTypeSelector: typeof import('./../components/DataTypeSelector/index.vue')['default']
     Editor: typeof import('./../components/Editor/index.vue')['default']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
     FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default']

+ 12 - 1
src/utils/tool.ts

@@ -759,7 +759,6 @@ export function oamType(i: string | number) {
   return strs[i as unknown as keyof typeof strs]
 }
 
-
 export function sqlType(i: string | number) {
   const strs = [
     '未知',
@@ -770,3 +769,15 @@ export function sqlType(i: string | number) {
   ]
   return strs[i as unknown as keyof typeof strs]
 }
+
+export function dataType(i: string | number) {
+  const dataType = [
+    '请选择',
+    '整数',
+    '字符串',
+    '浮点小数',
+    '日期',
+    '日期时间',
+  ]
+  return dataType[i as unknown as keyof typeof dataType]
+}

+ 345 - 0
src/views/biz/attr/components/AttrList.vue

@@ -0,0 +1,345 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup name="BizObjectAttrList">
+import type { FormInstance, FormRules } from 'element-plus'
+import { ElMessage } from 'element-plus'
+import { dataType } from '@/utils/tool'
+import {
+  createObjectAttr,
+  deleteObjectAttrById,
+  getObjectAttrListById,
+  updateObjectAttr
+} from '@/api/biz/bizObjectAttrApi'
+const props = withDefaults(defineProps<{
+  bizId: number
+  objectId: number
+}>(), {
+  bizId: 0,
+  objectId: 0,
+})
+
+interface ObjectAttr {
+  bizId: number
+  objectId: number
+  attrId: number
+  attrTitle: string
+  attrCode: string
+  mappingCode: string
+  attrType: number
+  attrLength: number
+  attrDecimals: number
+  isNotNull: number
+  comment: string
+  sortNo: number
+  isEdit: boolean
+}
+
+// 数据
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  search: {
+    keywords: '',
+  },
+  conditions: {
+    bizId: props.bizId,
+    objectId: props.objectId,
+  },
+  attrs: [] as ObjectAttr[],
+  actionType: 1,
+})
+
+const formRef = ref<FormInstance>()
+const formRules = reactive<FormRules>({
+  objectTitle: [
+    { required: true, message: '请输入对象标题', trigger: 'blur' },
+  ],
+  objectType: [
+    { required: true, message: '请输入对象类型', trigger: 'blur' },
+  ],
+  objectCode: [
+    { required: true, message: '请输入对像代码', trigger: 'blur' },
+  ],
+  comment: [
+    { required: false, message: '请输入简要描述', trigger: 'blur' },
+  ],
+})
+
+// 查询数据
+const getData = async () => {
+  state.loading = true
+  const { bizId, objectId } = state.conditions
+  const { data } = await getObjectAttrListById(bizId, objectId)
+  state.loading = false
+  state.attrs = data
+  data.forEach((attr: ObjectAttr) => {
+    attr.isEdit = false
+  })
+}
+
+const attrsTableRef = ref()
+const attrsTableKey = ref(0)
+
+function onAttrAdd() {
+  state.attrs.push({
+    bizId: props.bizId,
+    objectId: props.objectId,
+    attrId: 0,
+    attrTitle: '', // 名称
+    attrCode: '', // 代码
+    mappingCode: '',
+    attrType: 0, // 类型
+    attrLength: 0, // 长度
+    attrDecimals: 0, // 小数位数
+    isNotNull: 0, // 是否为空
+    comment: '', // 注释
+    sortNo: 0,
+    isEdit: true,
+  })
+  state.actionType = 1
+}
+
+function onAttrEdit(row: any) {
+  row.isEdit = true
+  state.actionType = 2
+}
+
+async function onAttrDelete(row: any) {
+  const { bizId, objectId } = state.conditions
+  const attrId = row.attrId
+  await deleteObjectAttrById(bizId, objectId, attrId)
+  ElMessage.success('删除成功')
+}
+
+function onAttrCancel(row: any, index: number) {
+  row.isEdit = false
+  if (state.actionType === 1) {
+    state.attrs.splice(index, 1)
+  }
+  state.actionType = 0
+}
+
+async function onAttrSave(row: any) {
+  row.isEdit = false
+  if (state.actionType === 1) {
+    await createObjectAttr(row)
+    state.dialogVisible = false
+    ElMessage.success('新增成功')
+  }
+  else {
+    await updateObjectAttr(row)
+    state.dialogVisible = false
+    ElMessage.success('修改成功')
+  }
+}
+
+onMounted(() => {
+  getData()
+  nextTick(() => {
+  })
+})
+
+// 保存
+function onSubmit() {
+  formRef.value && formRef.value.validate(async (valid) => {
+    if (valid) {
+      if (state.actionType === 1) {
+        await createObjectAttr(state.attrs)
+        state.dialogVisible = false
+        ElMessage.success('新增成功')
+      }
+      else {
+        await updateObjectAttr(state.attrs)
+        state.dialogVisible = false
+        ElMessage.success('修改成功')
+      }
+    }
+  })
+}
+</script>
+
+<template>
+  <div class="absolute-container">
+    <div class="container">
+      <tool-bar>
+        <template #left>
+          <h4>对象属性</h4>
+        </template>
+        <template #right>
+          <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选" clearable style="width: 200px;" />
+          <el-button style="margin-left:10px" @click="getData">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:search" />
+              </el-icon>
+            </template>
+          </el-button>
+
+          <el-button type="default" @click="onAttrAdd">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:plus" />
+              </el-icon>
+            </template>
+          </el-button>
+        </template>
+      </tool-bar>
+
+      <el-table ref="attrsTableRef" :key="attrsTableKey" :data="state.attrs" class="list-table" size="small" border stripe highlight-current-row>
+        <el-table-column label="名称" header-align="center" align="center" width="150">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.attrTitle" size="small" />
+            <span v-else>{{ scope.row.attrTitle }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="代码" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.attrCode" size="small" />
+            <span v-else>{{ scope.row.attrCode }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="映射代码" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.mappingCode" size="small" />
+            <span v-else>{{ scope.row.mappingCode }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="类型" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <data-type-selector v-if="scope.row.isEdit" v-model="scope.row.attrType" />
+            <span v-else>{{ dataType(scope.row.attrType) }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="长度" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input-number
+              v-if="scope.row.isEdit"
+              v-model="scope.row.attrLength" style="width:90px"
+              :min="0"
+              controls-position="right"
+              size="small"
+            />
+            <span v-else>{{ scope.row.attrLength }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="小数位数" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input-number
+              v-if="scope.row.isEdit"
+              v-model="scope.row.attrDecimals" style="width:90px"
+              :min="0"
+              controls-position="right"
+              size="small"
+            />
+            <span v-else>{{ scope.row.attrDecimals }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="是否为空" header-align="center" align="center" width="90">
+          <template #default="scope">
+            <el-checkbox
+              v-if="scope.row.isEdit"
+              v-model="scope.row.isNotNull"
+              :true-label="1"
+              :false-label="0"
+            >
+              {{ scope.row.isNotNull === 1 ? '是' : '否' }}
+            </el-checkbox>
+            <span v-else>{{ scope.row.isNotNull === 1 ? '是' : '否' }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="注释">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.comment" size="small" />
+            <span v-else>{{ scope.row.comment }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column header-align="center" align="center" width="70">
+          <template #header>
+            排序
+          </template>
+          <template #default>
+            <el-tag type="info" class="sortable">
+              <el-icon>
+                <svg-icon name="i-ep:d-caret" />
+              </el-icon>
+            </el-tag>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" width="140" align="center">
+          <template #default="scope">
+            <template v-if="scope.row.isEdit">
+              <el-button type="primary" plain size="small" @click="onAttrSave(scope.row)">
+                保存
+              </el-button>
+              <el-button type="primary" plain size="small" @click="onAttrCancel(scope.row, scope.$index)">
+                取消
+              </el-button>
+            </template>
+            <template v-else>
+              <el-button type="primary" plain size="small" @click="onAttrEdit(scope.row)">
+                编辑
+              </el-button>
+              <el-popconfirm title="是否要删除此行?" style="margin-left: 10px;" @confirm="onAttrDelete(scope.row)">
+                <template #reference>
+                  <el-button type="danger" plain size="small">
+                    删除
+                  </el-button>
+                </template>
+              </el-popconfirm>
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.page-main {
+  :deep(.el-table) {
+    height: 100%;
+
+    .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;
+      }
+    }
+  }
+  h4{
+    padding: 0;
+    margin: 0;
+  }
+}
+</style>

+ 200 - 268
src/views/biz/attr/index.vue

@@ -3,21 +3,27 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup name="bizObjectEdit">
-import Sortable from 'sortablejs'
+<script lang="ts" setup name="BizObjectAttrList">
 import type { FormInstance, FormRules } from 'element-plus'
 import { ElMessage } from 'element-plus'
-import { createObject, getObjectById, updateObject } from '@/api/biz/bizObjectApi'
-import useSettingsStore from '@/store/modules/settings'
-
-const settingsStore = useSettingsStore()
-const route = useRoute()
-const bizId = parseInt(route.params.bizId.toString())
-const objectId = parseInt(route.params.objectId.toString())
-const router = useRouter()
-const tabbar = useTabbar()
+import { dataType } from '@/utils/tool'
+import {
+  createObjectAttr,
+  deleteObjectAttrById,
+  getObjectAttrListById,
+  updateObjectAttr,
+} from '@/api/biz/bizObjectAttrApi'
+const props = withDefaults(defineProps<{
+  bizId: number
+  objectId: number
+}>(), {
+  bizId: 0,
+  objectId: 0,
+})
 
 interface ObjectAttr {
+  bizId: number
+  objectId: number
   attrId: number
   attrTitle: string
   attrCode: string
@@ -28,16 +34,9 @@ interface ObjectAttr {
   isNotNull: number
   comment: string
   sortNo: number
+  isEdit: boolean
 }
 
-const dataType = [
-  { dataType: 1, dataName: '整数' },
-  { dataType: 2, dataName: '字符串' },
-  { dataType: 3, dataName: '浮点小数' },
-  { dataType: 4, dataName: '日期' },
-  { dataType: 5, dataName: '日期时间' },
-]
-
 // 数据
 const state = reactive({
   title: '',
@@ -46,15 +45,11 @@ const state = reactive({
   search: {
     keywords: '',
   },
-  formData: {
-    bizId,
-    objectId,
-    objectType: 1,
-    objectCode: '',
-    objectTitle: '',
-    comment: '',
-    attrs: [] as ObjectAttr[],
+  conditions: {
+    bizId: props.bizId,
+    objectId: props.objectId,
   },
+  attrs: [] as ObjectAttr[],
   actionType: 1,
 })
 
@@ -77,15 +72,22 @@ const formRules = reactive<FormRules>({
 // 查询数据
 const getData = async () => {
   state.loading = true
-  const { data } = await getObjectById(bizId, objectId)
+  const { bizId, objectId } = state.conditions
+  const { data } = await getObjectAttrListById(bizId, objectId)
   state.loading = false
-  state.formData = data
+  state.attrs = data
+  data.forEach((attr: ObjectAttr) => {
+    attr.isEdit = false
+  })
 }
 
 const attrsTableRef = ref()
 const attrsTableKey = ref(0)
+
 function onAttrAdd() {
-  state.formData.attrs.push({
+  state.attrs.push({
+    bizId: props.bizId,
+    objectId: props.objectId,
     attrId: 0,
     attrTitle: '', // 名称
     attrCode: '', // 代码
@@ -96,48 +98,48 @@ function onAttrAdd() {
     isNotNull: 0, // 是否为空
     comment: '', // 注释
     sortNo: 0,
+    isEdit: true,
   })
-  nextTick(() => {
-    attrsTableRef.value.setScrollTop(state.formData.attrs.length * 50)
-  })
+  state.actionType = 1
 }
 
-// 删除属性
-function onAttrDelete(index: number) {
-  state.formData.attrs.splice(index, 1)
+function onAttrEdit(row: any) {
+  row.isEdit = true
+  state.actionType = 2
 }
 
-function onAttrDrag() {
-  const tbody = attrsTableRef.value.$el.querySelector('.el-table__body-wrapper tbody')
-  Sortable.create(tbody, {
-    handle: '.sortable',
-    animation: 300,
-    ghostClass: 'ghost',
-    onEnd: ({ newIndex, oldIndex }) => {
-      if (newIndex === undefined || oldIndex === undefined) {
-        return
-      }
-      const currRow = state.formData.attrs.splice(oldIndex, 1)[0]
-      state.formData.attrs.splice(newIndex, 0, currRow)
-      attrsTableKey.value += 1
-      nextTick(() => onAttrDrag())
-    },
-  })
+async function onAttrDelete(row: any) {
+  const { bizId, objectId } = state.conditions
+  const attrId = row.attrId
+  await deleteObjectAttrById(bizId, objectId, attrId)
+  ElMessage.success('删除成功')
 }
 
-onMounted(() => {
-  if (objectId <= 0) {
-    state.title = '新增对象'
-    state.actionType = 1
+function onAttrCancel(row: any, index: number) {
+  row.isEdit = false
+  if (state.actionType === 1) {
+    state.attrs.splice(index, 1)
+  }
+  state.actionType = 0
+}
+
+async function onAttrSave(row: any) {
+  row.isEdit = false
+  if (state.actionType === 1) {
+    await createObjectAttr(row)
+    state.dialogVisible = false
+    ElMessage.success('新增成功')
   }
   else {
-    state.title = '编辑对象'
-    state.actionType = 2
-    getData()
+    await updateObjectAttr(row)
+    state.dialogVisible = false
+    ElMessage.success('修改成功')
   }
+}
 
+onMounted(() => {
+  getData()
   nextTick(() => {
-    onAttrDrag()
   })
 })
 
@@ -145,240 +147,170 @@ onMounted(() => {
 function onSubmit() {
   formRef.value && formRef.value.validate(async (valid) => {
     if (valid) {
-      state.formData.attrs.forEach((item, index) => item.sortNo = index)
       if (state.actionType === 1) {
-        await createObject(state.formData)
+        await createObjectAttr(state.attrs)
         state.dialogVisible = false
         ElMessage.success('新增成功')
       }
       else {
-        await updateObject(state.formData)
+        await updateObjectAttr(state.attrs)
         state.dialogVisible = false
         ElMessage.success('修改成功')
       }
-      goBack()
     }
   })
 }
-
-function onCancel() {
-  goBack()
-}
-
-// 返回列表页
-function goBack() {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.close({ name: 'biz_object_index' })
-  }
-  else {
-    router.push({ name: 'biz_object_index' })
-  }
-}
 </script>
 
 <template>
   <div class="absolute-container">
-    <page-header title="对象" content="创建元数据对象。">
-      <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 hide-left-side-toggle>
-        <template #leftSide>
-          <h2>基本信息</h2>
-          <el-scrollbar class="form-el-scrollbar">
-            <div class="form-content">
-              <el-form ref="formRef" :model="state.formData" :rules="formRules" label-position="top">
-                <el-form-item label="对象名称" prop="objectTitle">
-                  <el-input v-model="state.formData.objectTitle" placeholder="请输入标题" />
-                </el-form-item>
-
-                <el-form-item label="类型" prop="objectType">
-                  <el-radio-group v-model="state.formData.objectType">
-                    <el-radio :label="1">
-                      实体对象
-                    </el-radio>
-                    <el-radio :label="2">
-                      视图对象
-                    </el-radio>
-                  </el-radio-group>
-                </el-form-item>
-
-                <el-form-item label="对象代码" prop="objectCode">
-                  <el-input v-model="state.formData.objectCode" placeholder="请输入代码" />
-                </el-form-item>
-
-                <el-form-item label="简要说明" prop="comment">
-                  <el-input v-model="state.formData.comment" type="textarea" rows="10" placeholder="请输入标题" />
-                </el-form-item>
-              </el-form>
-            </div>
-          </el-scrollbar>
+    <div class="container">
+      <tool-bar>
+        <template #left>
+          <h4>对象属性</h4>
         </template>
+        <template #right>
+          <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选" clearable style="width: 200px;" />
+          <el-button style="margin-left:10px" @click="getData">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:search" />
+              </el-icon>
+            </template>
+          </el-button>
 
-        <div class="container">
-          <tool-bar>
-            <template #left>
-              <h2>属性</h2>
+          <el-button type="default" @click="onAttrAdd">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:plus" />
+              </el-icon>
             </template>
-            <template #right>
-              <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选字典项" clearable style="width: 200px;" />
-              <el-button style="margin-left:10px" @click="getData">
-                <template #icon>
-                  <el-icon>
-                    <svg-icon name="i-ep:search" />
-                  </el-icon>
-                </template>
+          </el-button>
+        </template>
+      </tool-bar>
+
+      <el-table ref="attrsTableRef" :key="attrsTableKey" :data="state.attrs" class="list-table" size="small" border stripe highlight-current-row>
+        <el-table-column type="index" label="序号" header-align="center" align="center" width="60" />
+
+        <el-table-column label="名称" header-align="center" align="center" width="150">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.attrTitle" size="small" />
+            <span v-else>{{ scope.row.attrTitle }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="代码" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.attrCode" size="small" />
+            <span v-else>{{ scope.row.attrCode }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="映射代码" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.mappingCode" size="small" />
+            <span v-else>{{ scope.row.mappingCode }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="类型" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <data-type-selector v-if="scope.row.isEdit" v-model="scope.row.attrType" />
+            <span v-else>{{ dataType(scope.row.attrType) }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="长度" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input-number
+              v-if="scope.row.isEdit"
+              v-model="scope.row.attrLength" style="width:90px"
+              :min="0"
+              controls-position="right"
+              size="small"
+            />
+            <span v-else>{{ scope.row.attrLength }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="小数位数" header-align="center" align="center" width="120">
+          <template #default="scope">
+            <el-input-number
+              v-if="scope.row.isEdit"
+              v-model="scope.row.attrDecimals" style="width:90px"
+              :min="0"
+              controls-position="right"
+              size="small"
+            />
+            <span v-else>{{ scope.row.attrDecimals }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="是否为空" header-align="center" align="center" width="90">
+          <template #default="scope">
+            <el-checkbox
+              v-if="scope.row.isEdit"
+              v-model="scope.row.isNotNull"
+              :true-label="1"
+              :false-label="0"
+            >
+              {{ scope.row.isNotNull === 1 ? '是' : '否' }}
+            </el-checkbox>
+            <span v-else>{{ scope.row.isNotNull === 1 ? '是' : '否' }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="注释">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.comment" size="small" />
+            <span v-else>{{ scope.row.comment }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column header-align="center" align="center" width="70">
+          <template #header>
+            排序
+          </template>
+          <template #default>
+            <el-tag type="info" class="sortable">
+              <el-icon>
+                <svg-icon name="i-ep:d-caret" />
+              </el-icon>
+            </el-tag>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" width="140" align="center">
+          <template #default="scope">
+            <template v-if="scope.row.isEdit">
+              <el-button type="primary" plain size="small" @click="onAttrSave(scope.row)">
+                保存
               </el-button>
-
-              <el-button type="primary" @click="onAttrAdd">
-                <template #icon>
-                  <el-icon>
-                    <svg-icon name="i-ep:plus" />
-                  </el-icon>
-                </template>
+              <el-button type="primary" plain size="small" @click="onAttrCancel(scope.row, scope.$index)">
+                取消
               </el-button>
             </template>
-          </tool-bar>
-
-          <el-table ref="attrsTableRef" :key="attrsTableKey" :data="state.formData.attrs" class="list-table" border stripe highlight-current-row>
-            <el-table-column width="60" align="center" fixed>
-              <template #header>
-                <el-button type="primary" size="small" plain circle @click="onAttrAdd">
-                  <template #icon>
-                    <el-icon>
-                      <svg-icon name="i-ep:plus" />
-                    </el-icon>
-                  </template>
-                </el-button>
-              </template>
-
-              <template #default="scope">
-                <span class="index">{{ scope.$index + 1 }}</span>
-                <el-button type="danger" size="small" plain circle class="delete" @click="onAttrDelete(scope.$index)">
-                  <template #icon>
-                    <el-icon>
-                      <svg-icon name="i-ep:delete" />
-                    </el-icon>
-                  </template>
-                </el-button>
-              </template>
-            </el-table-column>
-
-            <el-table-column label="名称" width="150">
-              <template #default="scope">
-                <el-input v-model="scope.row.attrTitle" />
-              </template>
-            </el-table-column>
-
-            <el-table-column label="代码" width="120">
-              <template #default="scope">
-                <el-input v-model="scope.row.attrCode" />
-              </template>
-            </el-table-column>
-
-            <el-table-column label="模型代码" width="120">
-              <template #default="scope">
-                <el-input v-model="scope.row.mappingCode" />
-              </template>
-            </el-table-column>
-
-            <el-table-column label="类型" width="120">
-              <template #default="scope">
-                <el-select v-model="scope.row.attrType">
-                  <el-option
-                    v-for="item in dataType"
-                    :key="item.dataType"
-                    :value="item.dataType"
-                    :label="item.dataName"
-                  />
-                </el-select>
-              </template>
-            </el-table-column>
-
-            <el-table-column label="长度" width="130">
-              <template #default="scope">
-                <el-input-number
-                  v-model="scope.row.attrLength" style="width:100px"
-                  :min="0"
-                  controls-position="right"
-                  size="large"
-                />
-              </template>
-            </el-table-column>
-            <el-table-column label="长度" width="130">
-              <template #default="scope">
-                <el-input-number
-                  v-model="scope.row.attrDecimals" style="width:100px"
-                  :min="0"
-                  controls-position="right"
-                  size="large"
-                />
-              </template>
-            </el-table-column>
-            <el-table-column label="是否为空" width="90">
-              <template #default="scope">
-                <el-checkbox v-model="scope.row.isNotNull" :true-label="1" :false-label="0">
-                  {{ scope.row.isNotNull === 1 ? '是' : '否' }}
-                </el-checkbox>
-              </template>
-            </el-table-column>
-
-            <el-table-column label="注释">
-              <template #default="scope">
-                <el-input v-model="scope.row.comment" />
-              </template>
-            </el-table-column>
-
-            <el-table-column width="80" align="center">
-              <template #header>
-                排序
-              </template>
-              <template #default>
-                <el-tag type="info" class="sortable">
-                  <el-icon>
-                    <svg-icon name="i-ep:d-caret" />
-                  </el-icon>
-                </el-tag>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-      </LayoutContainer>
+            <template v-else>
+              <el-button type="primary" plain size="small" @click="onAttrEdit(scope.row)">
+                编辑
+              </el-button>
+              <el-popconfirm title="是否要删除此行?" style="margin-left: 10px;" @confirm="onAttrDelete(scope.row)">
+                <template #reference>
+                  <el-button type="danger" plain size="small">
+                    删除
+                  </el-button>
+                </template>
+              </el-popconfirm>
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
     </div>
-
-    <fixed-action-bar>
-      <el-button type="primary" size="large" @click="onSubmit">
-        保存
-      </el-button>
-      <el-button size="large" @click="onCancel">
-        取消
-      </el-button>
-    </fixed-action-bar>
   </div>
 </template>
 
 <style lang="scss" scoped>
-.page-header {
-  margin-bottom: 0;
-}
-
 .page-main {
-  :deep(.left-side) {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    .form-el-scrollbar{
-      flex: 1;
-    }
-  }
   :deep(.el-table) {
     height: 100%;
 
@@ -407,7 +339,7 @@ function goBack() {
       }
     }
   }
-  h2{
+  h4{
     padding: 0;
     margin: 0;
   }

+ 10 - 10
src/views/biz/object/components/Biz.vue → src/views/biz/biz/components/Biz.vue

@@ -3,7 +3,7 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup>
+<script lang="ts" setup name="biz">
 import type { FormInstance, FormRules } from 'element-plus'
 import { createBiz, getBizById, updateBiz } from '@/api/biz/bizApi'
 const emit = defineEmits(['success'])
@@ -11,7 +11,7 @@ const emit = defineEmits(['success'])
 const formRef = ref<FormInstance>()
 const formRules = ref<FormRules>({
   bizTitle: [
-    { required: true, message: '请输入标题', trigger: 'blur' },
+    { required: true, message: '请输入业务模型名称', trigger: 'blur' },
   ],
   bizDesc: [
     { required: false, message: '请输入描述', trigger: 'blur' },
@@ -71,13 +71,13 @@ function resetFormData() {
 defineExpose({
   showAddModel() {
     resetFormData()
-    state.title = '新增业务'
+    state.title = '新增业务模型'
     state.actionType = 1
     state.dialogVisible = true
   },
   showEditModel(bizId: any) {
     resetFormData()
-    state.title = '编辑业务'
+    state.title = '编辑业务模型'
     state.actionType = 2
     state.dialogVisible = true
     getData(bizId)
@@ -87,12 +87,12 @@ defineExpose({
 </script>
 
 <template>
-  <div>
-    <el-dialog v-model="state.dialogVisible" :title="state.title" width="600px" :close-on-click-modal="false" append-to-body destroy-on-close>
+  <el-drawer v-model="state.dialogVisible" :title="state.title" size="30%">
+    <el-scrollbar class="form-el-scrollbar">
       <div v-loading="state.loading">
         <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">
-          <el-form-item label="业务名称" prop="bizTitle">
-            <el-input v-model="state.formData.bizTitle" placeholder="请输入标题" />
+          <el-form-item label="模型名称" prop="bizTitle">
+            <el-input v-model="state.formData.bizTitle" placeholder="业务模型名称" />
           </el-form-item>
 
           <el-form-item label="简要说明" prop="bizDesc">
@@ -109,6 +109,6 @@ defineExpose({
           保存
         </el-button>
       </template>
-    </el-dialog>
-  </div>
+    </el-scrollbar>
+  </el-drawer>
 </template>

+ 0 - 0
src/views/biz/object/components/BizObject.vue → src/views/biz/biz/components/BizObject.vue


+ 386 - 0
src/views/biz/biz/index.vue

@@ -0,0 +1,386 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup>
+import { ElMessage, ElMessageBox } from 'element-plus'
+import BizObjectComponent from '../object/compnents/Object.vue'
+import BizComponent from './components/Biz.vue'
+import { deleteBizById, getBizByList } from '@/api/biz/bizApi'
+import { deleteObjectById, getObjectByList } from '@/api/biz/bizObjectApi'
+import useSettingsStore from '@/store/modules/settings'
+
+const settingsStore = useSettingsStore()
+const router = useRouter()
+const tabbar = useTabbar()
+
+interface TreeNode {
+  label: string
+  isLeaf: boolean
+  bizId: number
+  children?: TreeNode[]
+}
+
+interface Object {
+  bizId: number
+  objectId: number
+  objectTitle: string
+  objectCode: string
+  comment: string
+}
+
+const state = reactive({
+  loading: false,
+  keywords: '',
+  treeData: [] as TreeNode[],
+  curBizId: 0,
+  objectList: [] as Object[],
+  dialogState: 1,
+  search: {
+    keywords: '',
+  },
+})
+
+const leftTreeRef = ref()
+const bizRef = ref()
+const getObjectData = async () => {
+  const curBizId = state.curBizId
+  if (curBizId > 0) {
+    state.loading = true
+    const { data } = await getObjectByList({ bizId: curBizId })
+    state.objectList = data
+    state.loading = false
+  }
+}
+
+const getBizData = async () => {
+  const { data } = await getBizByList('')
+  const children: TreeNode[] = []
+  data.forEach((item: any) => {
+    return children.push({
+      label: item.bizTitle,
+      bizId: item.bizId,
+      isLeaf: true,
+    })
+  })
+  state.treeData = [
+    {
+      label: '业务模型库',
+      isLeaf: false,
+      bizId: -1,
+      children,
+    },
+  ]
+
+  if (data && data.length > 0) {
+    state.curBizId = data[0].bizId
+    nextTick(() => {
+      leftTreeRef.value.setCurrentKey(data[0].bizId)
+    })
+    await getObjectData()
+  }
+}
+
+onMounted(() => {
+  getBizData()
+})
+
+// 业务新增
+const onBizAdd = () => {
+  bizRef.value.showAddModel()
+}
+
+// 业务编辑
+const onBizEdit = (data: { bizId: number }) => {
+  const bizId = data.bizId
+  bizRef.value.showEditModel(bizId)
+}
+
+// 业务删除
+const onBizDelete = (data: any) => {
+  ElMessageBox.confirm(`确认删除「${data.bizTitle}」吗?`, '确认信息').then(async () => {
+    await deleteBizById(data.bizId)
+    await getBizData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  })
+}
+
+// 业务选择
+const onTreeNodeClick = (node: TreeNode) => {
+  if (node.isLeaf) {
+    state.curBizId = node.bizId
+    getObjectData()
+  }
+}
+
+const bizObjectRef = ref()
+const objectTableRef = ref()
+
+// 检索对象
+const onSearch = () => {
+  getObjectData()
+}
+
+// 新增对象
+const onObjectAdd = () => {
+  const bizId = state.curBizId
+  bizObjectRef.value.showAddModel(bizId)
+}
+
+// 编辑对象
+const onObjectDetails = (row: { bizId: any; objectId: any }) => {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.open({
+      name: 'bizObjectDetails',
+      params: {
+        bizId: row.bizId,
+        objectId: row.objectId,
+      },
+    })
+  }
+  else {
+    router.push({
+      name: 'bizObjectDetails',
+      params: {
+        bizId: row.bizId,
+        objectId: row.objectId,
+      },
+    })
+  }
+}
+
+const onObjectOAM = (row: any) => {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.open({
+      name: 'bizObjectOAM',
+      params: {
+        bizId: row.bizId,
+        objectId: row.objectId,
+      },
+    })
+  }
+  else {
+    router.push({
+      name: 'bizObjectOAM',
+      params: {
+        bizId: row.bizId,
+        objectId: row.objectId,
+      },
+    })
+  }
+}
+
+// 删除对象
+const onObjectDelete = (row: any) => {
+  const { bizId, objectId, objectTitle } = row
+  ElMessageBox.confirm(`确认删除「${objectTitle}」吗?`, '确认信息').then(async () => {
+    await deleteObjectById(bizId, objectId)
+    await getObjectData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  })
+}
+</script>
+
+<template>
+  <div class="absolute-container">
+    <tool-header title="业务模型">
+      <template #right>
+        <el-button v-waves.float type="primary" @click="onBizAdd">
+          <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="250px" hide-left-side-toggle>
+        <template #leftSide>
+          <el-scrollbar class="tree">
+            <ElTree ref="leftTreeRef" :data="state.treeData" default-expand-all node-key="bizId" @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="bx:bxs-component" />
+                    </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="onBizEdit(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="onBizDelete(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>
+
+              <el-button type="primary" size="default" @click.stop="onObjectAdd">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:plus" />
+                  </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">
+              <template #default="scope">
+                <el-icon>
+                  <svg-icon name="uim:box" />
+                </el-icon>
+                <span> &nbsp;&nbsp;{{ scope.row.objectTitle }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="objectCode" label="代码" header-align="center" align="center" width="150">
+              <template #default="scope">
+                <span>{{ scope.row.objectCode }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="comment" label="说明" header-align="center" align="left">
+              <template #default="scope">
+                <span>{{ scope.row.comment }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column label="操作" header-align="center" align="center" width="230">
+              <template #default="scope">
+                <el-button type="warning" size="small" plain @click="onObjectDetails(scope.row)">详情</el-button>
+                <el-button type="danger" size="small" plain @click="onObjectDelete(scope.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </LayoutContainer>
+    </div>
+    <BizComponent ref="bizRef" @success="getBizData" />
+    <BizObjectComponent ref="bizObjectRef" @success="getObjectData" />
+  </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>

+ 0 - 0
src/views/biz/mapping/components/ModelObjectSelector.vue


+ 0 - 0
src/views/biz/mapping/components/ObjectMapping.vue


+ 165 - 0
src/views/biz/mapping/index.vue

@@ -0,0 +1,165 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup name="BizObjectMappingList">
+import { ElMessage } from 'element-plus'
+import {
+  deleteObjectMappingById,
+  getObjectAttrListById,
+} from '@/api/biz/bizObjectMappingApi'
+const props = withDefaults(defineProps<{
+  bizId: number
+  objectId: number
+}>(), {
+  bizId: 0,
+  objectId: 0,
+})
+
+// 数据
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  search: {
+    keywords: '',
+  },
+  conditions: {
+    bizId: props.bizId,
+    objectId: props.objectId,
+  },
+  dataList: [],
+  actionType: 1,
+})
+
+// 查询数据
+const getData = async () => {
+  state.loading = true
+  const { bizId, objectId } = state.conditions
+  const { data } = await getObjectAttrListById(bizId, objectId)
+  state.loading = false
+  state.dataList = data
+}
+
+const objectMappingTableRef = ref()
+
+function onObjectMappingAdd() {
+  state.actionType = 2
+}
+
+async function onObjectMappingDelete(row: any) {
+  const { bizId, objectId } = state.conditions
+  const mappingId = row.mappingId
+  await deleteObjectMappingById(bizId, objectId, mappingId)
+  ElMessage.success('删除成功')
+}
+
+onMounted(() => {
+  getData()
+  nextTick(() => {
+  })
+})
+</script>
+
+<template>
+  <div class="absolute-container">
+    <div class="container">
+      <tool-bar>
+        <template #left>
+          <h4>模型映射</h4>
+        </template>
+        <template #right>
+          <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选" clearable style="width: 200px;" />
+          <el-button style="margin-left:10px" @click="getData">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:search" />
+              </el-icon>
+            </template>
+          </el-button>
+
+          <el-button type="default" @click="onObjectMappingAdd">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:plus" />
+              </el-icon>
+            </template>
+          </el-button>
+        </template>
+      </tool-bar>
+
+      <el-table ref="objectMappingTableRef" :data="state.dataList" class="list-table" size="small" border stripe highlight-current-row>
+        <el-table-column type="index" label="序号" header-align="center" align="center" width="70" />
+
+        <el-table-column label="模型" header-align="center" align="center" width="300">
+          <template #default="scope">
+            <span>{{ scope.row.mappingModelTitle }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="对象" header-align="center" align="center" width="300">
+          <template #default="scope">
+            <span>{{ scope.row.mappingObjectTitle }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="映射代码" header-align="center" align="center">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.mappingCode" size="small" />
+            <span v-else>{{ scope.row.mappingCode }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" align="center">
+          <template #default="scope">
+            <el-popconfirm title="是否要删除此行?" style="margin-left: 10px;" @confirm="onObjectMappingDelete(scope.row)">
+              <template #reference>
+                <el-button type="danger" plain size="small">
+                  删除
+                </el-button>
+              </template>
+            </el-popconfirm>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.page-main {
+  :deep(.el-table) {
+    height: 100%;
+
+    .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;
+      }
+    }
+  }
+  h4{
+    padding: 0;
+    margin: 0;
+  }
+}
+</style>

+ 157 - 182
src/views/biz/oam/index.vue

@@ -3,24 +3,20 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup name="PagesExampleTable">
-import type { TabsPaneContext } from 'element-plus'
+<script lang="ts" setup name="BizObjectOAMListComponent">
 import { ElMessage, ElMessageBox } from 'element-plus'
 import ObjectOAMComponent from './components/ObjectOAM.vue'
 import ObjectOAMRuleComponent from './components/ObjectOAMRule.vue'
-import useSettingsStore from '@/store/modules/settings'
-import { getObjectById } from '@/api/biz/bizObjectApi'
 import { deleteObjectOAMById, getObjectOAMById, getObjectOAMByList } from '@/api/biz/bizObjectOAMApi'
-import { getObjectOAMRuleListByOamId, deleteObjectOAMRuleById } from '@/api/biz/bizObjectOAMRuleApi'
+import { deleteObjectOAMRuleById, getObjectOAMRuleListByOamId } from '@/api/biz/bizObjectOAMRuleApi'
 import { oamType, sqlType } from '@/utils/tool'
-
-const settingsStore = useSettingsStore()
-const route = useRoute()
-const router = useRouter()
-const tabbar = useTabbar()
-
-const bizId = parseInt(route.params.bizId.toString())
-const objectId = parseInt(route.params.objectId.toString())
+const props = withDefaults(defineProps<{
+  bizId: number
+  objectId: number
+}>(), {
+  bizId: 0,
+  objectId: 0,
+})
 
 interface TreeNode {
   label: string
@@ -52,16 +48,15 @@ const state = reactive({
   objectTitle: '',
   objectCode: '',
   objectType: 0,
-  keywords: '',
   curOamId: 0,
   oamList: [],
   objectOamRuleList: [],
   treeData: [] as TreeNode[],
   oam: {} as ObjectOAM,
   dialogState: 1,
-  search: {
-    bizId,
-    objectId,
+  conditions: {
+    bizId: props.bizId,
+    objectId: props.objectId,
     keywords: '',
   },
   params: '',
@@ -70,19 +65,11 @@ const state = reactive({
 
 const leftTreeRef = ref()
 
-const getObjectData = async () => {
-  state.loading = true
-  const { data } = await getObjectById(bizId, objectId)
-  state.objectTitle = data.objectTitle
-  state.objectCode = data.objectCode
-  state.objectType = data.objectType
-  state.loading = false
-}
-
 const getObjectOAMData = async () => {
   const curOamId = state.curOamId
   if (curOamId > 0) {
     state.loading = true
+    const { bizId, objectId } = state.conditions
     const { data } = await getObjectOAMById(bizId, objectId, curOamId)
     state.oam = data
     state.loading = false
@@ -93,6 +80,7 @@ const getObjectOAMRuleList = async () => {
   const curOamId = state.curOamId
   if (curOamId > 0) {
     state.loading = true
+    const { bizId, objectId } = state.conditions
     const { data } = await getObjectOAMRuleListByOamId(bizId, objectId, curOamId)
     state.objectOamRuleList = data
     state.loading = false
@@ -100,17 +88,17 @@ const getObjectOAMRuleList = async () => {
 }
 
 const getOAMTree = async () => {
-  const { data } = await getObjectOAMByList(state.search)
+  const { data } = await getObjectOAMByList(state.conditions)
   state.treeData = []
-  data.forEach((item: any) => {
-    state.treeData.push({
-      label: item.oamTitle,
-      oamId: item.oamId,
-      isLeaf: true,
+  if (data && data.length > 0){
+    data.forEach((item: any) => {
+      state.treeData.push({
+        label: item.oamTitle,
+        oamId: item.oamId,
+        isLeaf: true,
+      })
     })
-  })
 
-  if (data && data.length > 0) {
     state.curOamId = data[0].oamId
     nextTick(() => {
       leftTreeRef.value.setCurrentKey(data[0].oamId)
@@ -121,7 +109,6 @@ const getOAMTree = async () => {
 }
 
 onMounted(() => {
-  getObjectData()
   getOAMTree()
 })
 
@@ -136,10 +123,12 @@ const onTreeNodeClick = (node: TreeNode) => {
 
 const objectOAMRef = ref()
 const onAddOAM = () => {
+  const { bizId, objectId } = state.conditions
   objectOAMRef.value.showAddModel(bizId, objectId)
 }
 
 const onEditOAM = () => {
+  const { bizId, objectId } = state.conditions
   objectOAMRef.value.showEditModel(bizId, objectId, state.curOamId)
 }
 
@@ -157,14 +146,10 @@ const onDeleteOAM = (data: { bizId: number; objectId: number; oamId: number; oam
 
 // 选择对象模型回调
 const onSuccess = () => {
-  getOAMTree()
+  // getOAMTree()
 }
 
-const activeName = ref('first')
-
-const handleClick = (tab: TabsPaneContext, event: Event) => {
-  // console.log(tab, event)
-}
+const tabIndex = ref(1)
 
 const objectOAMRuleRef = ref()
 const onObjectOAMRuleSearch = () => {
@@ -172,10 +157,12 @@ const onObjectOAMRuleSearch = () => {
 }
 
 const onObjectOAMRuleAdd = () => {
+  const { bizId, objectId } = state.conditions
   objectOAMRuleRef.value.showAddModel(bizId, objectId, state.curOamId)
 }
 
 const onObjectOAMRuleEdit = (row: { orId: number }) => {
+  const { bizId, objectId } = state.conditions
   objectOAMRuleRef.value.showEditModel(bizId, objectId, state.curOamId, row.orId)
 }
 
@@ -190,156 +177,144 @@ const onObjectOAMRuleDelete = (row: { bizId: number; objectId: number; oamId: nu
     })
   })
 }
-// 返回列表页
-function goBack() {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.close({ name: 'biz_object_index' })
-  }
-  else {
-    router.push({ name: 'biz_object_index' })
-  }
-}
 </script>
 
 <template>
-  <div class="absolute-container">
-    <page-header :title="state.objectTitle" :content="state.objectCode ">
-      <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="250px" hide-left-side-toggle>
-        <template #leftSide>
-          <el-button-group class="btns">
-            <el-button type="primary" class="add" @click="onAddOAM()">
-              新增对象行为
-            </el-button>
-            <el-button @click="getObjectOAMData">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:refresh" />
-                </el-icon>
-              </template>
-            </el-button>
-          </el-button-group>
-
-          <el-scrollbar class="tree">
-            <ElTree ref="leftTreeRef" :data="state.treeData" default-expand-all node-key="oamId" @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="ep:key" />
-                    </el-icon>
-                    <div class="text">
-                      {{ data.label }}
-                    </div>
-                    <div class="code">
-                      {{ oamType(data.oamType) }}
-                    </div>
+  <div class="page-main">
+    <LayoutContainer left-side-width="250px" hide-left-side-toggle>
+      <template #leftSide>
+        <el-button-group class="btns">
+          <el-button type="primary" class="add" @click="onAddOAM()">
+            新增对象行为
+          </el-button>
+          <el-button @click="getObjectOAMData">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:refresh" />
+              </el-icon>
+            </template>
+          </el-button>
+        </el-button-group>
+
+        <el-scrollbar class="tree">
+          <ElTree ref="leftTreeRef" :data="state.treeData" default-expand-all node-key="oamId" @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="ep:key" />
+                  </el-icon>
+                  <div class="text">
+                    {{ data.label }}
+                  </div>
+                  <div class="code">
+                    {{ oamType(data.oamType) }}
                   </div>
                 </div>
+              </div>
+            </template>
+          </ElTree>
+        </el-scrollbar>
+      </template>
+
+      <div class="container">
+        <page-main title="基础用法" class="demo">
+          <el-radio v-model="tabIndex" label="1">
+            行为定义
+          </el-radio>
+          <el-radio v-model="tabIndex" label="2">
+            业务规则
+          </el-radio>
+        </page-main>
+
+        <page-main v-show="tabIndex === 1">
+          <div class="form">
+            <tool-header>
+              <template #right>
+                <el-button type="primary" @click="onEditOAM">
+                  编辑
+                </el-button>
               </template>
-            </ElTree>
-          </el-scrollbar>
-        </template>
-        <div class="container">
-          <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
-            <el-tab-pane label="行为定义" name="first">
-              <div class="form">
-                <tool-header>
-                  <template #right>
-                    <el-button type="primary" @click="onEditOAM">
-                      编辑
-                    </el-button>
-                  </template>
-                </tool-header>
-                <page-main :title="state.oam.oamTitle">
-                  <div class="question">
-                    <ol class="answer">
-                      <li><span>编码:</span><br>{{ state.oam.oamCode }}</li>
-                      <li><span>行为类型:</span><br>{{ oamType(state.oam.oamType) }}</li>
-                      <li><span>操作类型:</span><br>{{ sqlType(state.oam.sqlType) }}</li>
-                      <li v-if="state.oam.sqlType === 1">
-                        <span>Sql:</span><br>{{ state.oam.sqlStatement }}
-                      </li>
-                      <li v-if="state.oam.oamType === 3">
-                        <span>Count</span><br>{{ state.oam.sqlCount }}
-                      </li>
-                      <li><span>说明</span><br>{{ state.oam.oamDesc }}</li>
-                    </ol>
-                  </div>
-                </page-main>
+            </tool-header>
+            <page-main v-if="state.oam" :title="state.oam.oamTitle">
+              <div class="question">
+                <ol class="answer">
+                  <li><span>编码:</span><br>{{ state.oam.oamCode }}</li>
+                  <li><span>行为类型:</span><br>{{ oamType(state.oam.oamType) }}</li>
+                  <li><span>操作类型:</span><br>{{ sqlType(state.oam.sqlType) }}</li>
+                  <li v-if="state.oam.sqlType === 1">
+                    <span>Sql:</span><br>{{ state.oam.sqlStatement }}
+                  </li>
+                  <li v-if="state.oam.oamType === 3">
+                    <span>Count</span><br>{{ state.oam.sqlCount }}
+                  </li>
+                  <li><span>说明</span><br>{{ state.oam.oamDesc }}</li>
+                </ol>
               </div>
-            </el-tab-pane>
-            <el-tab-pane label="业务规则" name="second">
-              <tool-bar>
-                <template #right>
-                  <el-input v-model="state.search.keywords" placeholder="请输入名称,支持模糊查询" style="padding-right: 10px;" clearable />
-                  <el-button type="primary" @click="onObjectOAMRuleSearch">
-                    <template #icon>
-                      <el-icon>
-                        <svg-icon name="i-ep:search" />
-                      </el-icon>
-                    </template>
-                  </el-button>
-
-                  <el-button type="primary" size="default" @click.stop="onObjectOAMRuleAdd">
-                    <template #icon>
-                      <el-icon>
-                        <svg-icon name="i-ep:plus" />
-                      </el-icon>
-                    </template>
-                    业务规则
-                  </el-button>
+            </page-main>
+          </div>
+        </page-main>
+
+        <page-main v-show="tabIndex === 2">
+          <tool-bar>
+            <template #right>
+              <el-input v-model="state.conditions.keywords" placeholder="请输入名称,支持模糊查询" style="padding-right: 10px;" clearable />
+              <el-button type="primary" @click="onObjectOAMRuleSearch">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:search" />
+                  </el-icon>
+                </template>
+              </el-button>
+
+              <el-button type="primary" size="default" @click.stop="onObjectOAMRuleAdd">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:plus" />
+                  </el-icon>
                 </template>
-              </tool-bar>
-
-              <el-table ref="objectRuleTableRef" v-loading="state.loading" class="list-table" :data="state.objectOamRuleList" 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">
-                  <template #default="scope">
-                    <el-icon>
-                      <svg-icon name="lucide:clipboard-check" />
-                    </el-icon>
-                    <span> &nbsp;&nbsp;{{ scope.row.objectTitle }}</span>
-                  </template>
-                </el-table-column>
-
-                <el-table-column prop="attrTitle" label="属性" header-align="center" align="center" width="250">
-                  <template #default="scope">
-                    <span> {{ scope.row.targetAttrTitle }}-{{ scope.row.targetAttrCode }}</span>
-                  </template>
-                </el-table-column>
-
-                <el-table-column prop="ruleTitle" label="规则" header-align="center" align="center" width="150" />
-
-                <el-table-column label="操作" header-align="center" align="center" width="230">
-                  <template #default="scope">
-                    <el-button type="warning" size="small" plain @click="onObjectOAMRuleEdit(scope.row)">
-                      编辑
-                    </el-button>
-                    <el-button type="danger" size="small" plain @click="onObjectOAMRuleDelete(scope.row)">
-                      删除
-                    </el-button>
-                  </template>
-                </el-table-column>
-              </el-table>
-            </el-tab-pane>
-          </el-tabs>
-        </div>
-      </LayoutContainer>
-    </div>
-    <ObjectOAMComponent ref="objectOAMRef" @success="getOAMTree" />
-    <ObjectOAMRuleComponent ref="objectOAMRuleRef" @success="getObjectOAMRuleList" />
+                业务规则
+              </el-button>
+            </template>
+          </tool-bar>
+
+          <el-table ref="objectRuleTableRef" v-loading="state.loading" class="list-table" :data="state.objectOamRuleList" 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">
+              <template #default="scope">
+                <el-icon>
+                  <svg-icon name="lucide:clipboard-check" />
+                </el-icon>
+                <span> &nbsp;&nbsp;{{ scope.row.objectTitle }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="attrTitle" label="属性" header-align="center" align="center" width="250">
+              <template #default="scope">
+                <span> {{ scope.row.targetAttrTitle }}-{{ scope.row.targetAttrCode }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="ruleTitle" label="规则" header-align="center" align="center" width="150" />
+
+            <el-table-column label="操作" header-align="center" align="center" width="230">
+              <template #default="scope">
+                <el-button type="warning" size="small" plain @click="onObjectOAMRuleEdit(scope.row)">
+                  编辑
+                </el-button>
+                <el-button type="danger" size="small" plain @click="onObjectOAMRuleDelete(scope.row)">
+                  删除
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </page-main>
+      </div>
+    </LayoutContainer>
   </div>
+  <ObjectOAMComponent ref="objectOAMRef" @success="getOAMTree" />
+  <ObjectOAMRuleComponent ref="objectOAMRuleRef" @success="getObjectOAMRuleList" />
 </template>
 
 <style lang="scss" scoped>

+ 149 - 0
src/views/biz/object/compnents/Object.vue

@@ -0,0 +1,149 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup name="bizObject">
+import type { FormInstance, FormRules } from 'element-plus'
+import { createObject, getObjectById, updateObject } from '@/api/biz/bizObjectApi'
+const emit = defineEmits(['success'])
+
+// 数据
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  search: {
+    keywords: '',
+  },
+  formData: {
+    bizId: 0,
+    objectId: 0,
+    objectType: 1,
+    objectCode: '',
+    objectTitle: '',
+    comment: '',
+  },
+  actionType: 1,
+})
+
+onMounted(() => {})
+
+const getData = async () => {
+  const { bizId, objectId } = state.formData
+  state.loading = true
+  const { data } = await getObjectById(bizId, objectId)
+  state.loading = false
+  state.formData = data
+}
+
+const formRef = ref<FormInstance>()
+const formRules = reactive<FormRules>({
+  objectTitle: [
+    { required: true, message: '请输入对象标题', trigger: 'blur' },
+  ],
+  objectType: [
+    { required: true, message: '请输入对象类型', trigger: 'blur' },
+  ],
+  objectCode: [
+    { required: true, message: '请输入对像代码', trigger: 'blur' },
+  ],
+  comment: [
+    { required: false, message: '请输入简要描述', trigger: 'blur' },
+  ],
+})
+
+function onCancel() {
+  state.dialogVisible = false
+}
+
+function onSubmit() {
+  formRef.value && formRef.value.validate(async (valid) => {
+    if (valid) {
+      if (state.actionType === 1) {
+        await createObject(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+      else {
+        await updateObject(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+    }
+  })
+}
+
+function resetFormData() {
+  state.formData = {
+    bizId: 0,
+    objectId: 0,
+    objectType: 0,
+    objectCode: '',
+    objectTitle: '',
+    comment: '',
+  }
+}
+
+defineExpose({
+  showAddModel(bizId: number) {
+    resetFormData()
+    state.title = '新增业务对象'
+    state.actionType = 1
+    state.formData.bizId = bizId
+    state.dialogVisible = true
+  },
+
+  showEditModel(bizId: number, objectId: number) {
+    resetFormData()
+    state.title = '编辑业务对象'
+    state.actionType = 2
+    state.formData.bizId = bizId
+    state.formData.objectId = objectId
+    state.dialogVisible = true
+    getData()
+  },
+})
+</script>
+
+<template>
+  <el-drawer v-model="state.dialogVisible" :title="state.title" size="30%">
+    <el-scrollbar class="form-el-scrollbar">
+      <div class="form-content">
+        <el-form ref="formRef" :model="state.formData" :rules="formRules" label-position="top">
+          <el-form-item label="对象名称" prop="objectTitle">
+            <el-input v-model="state.formData.objectTitle" placeholder="请输入标题" />
+          </el-form-item>
+
+          <el-form-item label="类型" prop="objectType">
+            <el-radio-group v-model="state.formData.objectType">
+              <el-radio :label="1">
+                实体对象
+              </el-radio>
+              <el-radio :label="2">
+                视图对象
+              </el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+          <el-form-item label="对象代码" prop="objectCode">
+            <el-input v-model="state.formData.objectCode" placeholder="请输入代码" />
+          </el-form-item>
+
+          <el-form-item label="简要说明" prop="comment">
+            <el-input v-model="state.formData.comment" type="textarea" rows="10" placeholder="请输入标题" />
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-scrollbar>
+
+    <template #footer>
+      <el-button size="large" @click="onCancel">
+        取消
+      </el-button>
+      <el-button type="primary" size="large" @click="onSubmit">
+        保存
+      </el-button>
+    </template>
+  </el-drawer>
+</template>

+ 112 - 350
src/views/biz/object/index.vue

@@ -3,399 +3,161 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup>
-import { ElMessage, ElMessageBox } from 'element-plus'
-import Biz from './components/Biz.vue'
-import BizObject from './components/BizObject.vue'
-import { deleteBizById, getBizByList } from '@/api/biz/bizApi'
-import { deleteObjectById, getObjectByList } from '@/api/biz/bizObjectApi'
+<script lang="ts" setup name="bizObjectDetails">
+import type { TabsPaneContext } from 'element-plus'
+import BizObjectComponent from '../object/compnents/Object.vue'
+import BizObjectOAMListComponent from '../oam/index.vue'
+import BizObjectAttrListComponent from '../attr/index.vue'
+import BizObjectMappingListComponent from '../mapping/index.vue'
+import { getObjectById } from '@/api/biz/bizObjectApi'
 import useSettingsStore from '@/store/modules/settings'
 
 const settingsStore = useSettingsStore()
+
 const router = useRouter()
 const tabbar = useTabbar()
+const route = useRoute()
 
-interface TreeNode {
-  label: string
-  isLeaf: boolean
-  bizId: number
-  children?: TreeNode[]
-}
-
-interface Object {
-  bizId: number
-  objectId: number
-  objectTitle: string
-  objectCode: string
-  comment: string
-}
+const bizId = parseInt(route.params.bizId.toString())
+const objectId = parseInt(route.params.objectId.toString())
 
 const state = reactive({
   loading: false,
+  objectData: {
+    objectTitle: '',
+    objectCode: '',
+    objectType: 0,
+    comment: '',
+  },
   keywords: '',
-  treeData: [] as TreeNode[],
-  curBizId: 0,
-  objectList: [] as Object[],
   dialogState: 1,
   search: {
+    bizId,
+    objectId,
     keywords: '',
   },
+  params: '',
+  result: '',
 })
 
-const leftTreeRef = ref()
-const bizRef = ref()
+const objectRef = ref()
+// 查询数据
 const getObjectData = async () => {
-  const curBizId = state.curBizId
-  if (curBizId > 0) {
-    state.loading = true
-    const { data } = await getObjectByList({ bizId: curBizId })
-    state.objectList = data
-    state.loading = false
-  }
-}
-
-const getBizData = async () => {
-  const { data } = await getBizByList('')
-  const children: TreeNode[] = []
-  data.forEach((item: any) => {
-    return children.push({
-      label: item.bizTitle,
-      bizId: item.bizId,
-      isLeaf: true,
-    })
-  })
-  state.treeData = [
-    {
-      label: '业务模型库',
-      isLeaf: false,
-      bizId: -1,
-      children,
-    },
-  ]
-
-  if (data && data.length > 0) {
-    state.curBizId = data[0].bizId
-    nextTick(() => {
-      leftTreeRef.value.setCurrentKey(data[0].bizId)
-    })
-    await getObjectData()
-  }
+  state.loading = true
+  const { data } = await getObjectById(bizId, objectId)
+  state.loading = false
+  state.objectData.objectTitle = data.objectTitle
+  state.objectData.objectType = data.objectType
+  state.objectData.objectCode = data.objectCode
+  state.objectData.comment = data.comment
 }
 
 onMounted(() => {
-  getBizData()
-})
-
-// 业务新增
-const onBizAdd = () => {
-  bizRef.value.showAddModel()
-}
-
-// 业务编辑
-const onBizEdit = (data: { bizId: number }) => {
-  const bizId = data.bizId
-  bizRef.value.showEditModel(bizId)
-}
+  getObjectData()
+  nextTick(() => {
 
-// 业务删除
-const onBizDelete = (data: any) => {
-  ElMessageBox.confirm(`确认删除「${data.bizTitle}」吗?`, '确认信息').then(async () => {
-    await deleteBizById(data.bizId)
-    await getBizData()
-    ElMessage.success({
-      message: '删除成功',
-      center: true,
-    })
   })
-}
-
-// 业务选择
-const onTreeNodeClick = (node: TreeNode) => {
-  if (node.isLeaf) {
-    state.curBizId = node.bizId
-    getObjectData()
-  }
-}
+})
 
-const bizObjectRef = ref()
-const objectTableRef = ref()
+const activeTab = ref('first')
 
-// 检索对象
-const onSearch = () => {
-  getObjectData()
+const handleClick = (tab: TabsPaneContext, event: Event) => {
+  // console.log(tab, event)
 }
 
-// 新增对象
-const onObjectAdd = () => {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.open({
-      name: 'bizObjectEdit',
-      params: {
-        bizId: state.curBizId,
-        objectId: 0,
-      },
-    })
-  }
-  else {
-    router.push({
-      name: 'bizObjectEdit',
-      params: {
-        bizId: state.curBizId,
-        objectId: 0,
-      },
-    })
-  }
+const onObjectEdit = () => {
+  objectRef.value.showEditModel(bizId, objectId)
 }
 
-// 编辑对象
-const onObjectEdit = (row: { bizId: any; objectId: any }) => {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.open({
-      name: 'bizObjectEdit',
-      params: {
-        bizId: row.bizId,
-        objectId: row.objectId,
-      },
-    })
-  }
-  else {
-    router.push({
-      name: 'bizObjectEdit',
-      params: {
-        bizId: row.bizId,
-        objectId: row.objectId,
-      },
-    })
-  }
-}
+const size = ref('')
+const bizObjectAttrList = ref()
+const bizObjectOAMList = ref()
+const bizObjectMappingList = ref()
 
-const onObjectOAM = (row: any) => {
+// 返回列表页
+function goBack() {
   if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.open({
-      name: 'bizObjectOAM',
-      params: {
-        bizId: row.bizId,
-        objectId: row.objectId,
-      },
-    })
+    tabbar.close({ name: 'biz_object_list' })
   }
   else {
-    router.push({
-      name: 'bizObjectOAM',
-      params: {
-        bizId: row.bizId,
-        objectId: row.objectId,
-      },
-    })
+    router.push({ name: 'biz_object_list' })
   }
 }
-
-// 删除对象
-const onObjectDelete = (row: any) => {
-  const { bizId, objectId, objectTitle } = row
-  ElMessageBox.confirm(`确认删除「${objectTitle}」吗?`, '确认信息').then(async () => {
-    await deleteObjectById(bizId, objectId)
-    await getObjectData()
-    ElMessage.success({
-      message: '删除成功',
-      center: true,
-    })
-  })
-}
 </script>
-
 <template>
   <div class="absolute-container">
-    <tool-header title="业务模型">
-      <template #right>
-        <el-button v-waves.float type="primary" @click="onBizAdd">
-          <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="250px" hide-left-side-toggle>
-        <template #leftSide>
-          <el-scrollbar class="tree">
-            <ElTree ref="leftTreeRef" :data="state.treeData" default-expand-all node-key="bizId" @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="bx:bxs-component" />
-                    </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="onBizEdit(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="onBizDelete(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>
+    <page-header :title="state.objectData.objectTitle">
+      <el-button size="default" round @click="goBack">
+        <template #icon>
+          <el-icon>
+            <svg-icon name="i-ep:arrow-left" />
+          </el-icon>
         </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>
-
-              <el-button type="primary" size="default" @click.stop="onObjectAdd">
-                <template #icon>
-                  <el-icon>
-                    <svg-icon name="i-ep:plus" />
-                  </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">
-              <template #default="scope">
-                <el-icon>
-                  <svg-icon name="lucide:clipboard-check" />
-                </el-icon>
-                <span> &nbsp;&nbsp;{{ scope.row.objectTitle }}</span>
+        返回
+      </el-button>
+    </page-header>
+    <page-main>
+      <div class="container">
+        <el-tabs v-model="activeTab" class="demo-tabs" @tab-click="handleClick">
+          <el-tab-pane label="基本信息" name="first">
+            <el-descriptions
+              :title="state.objectData.objectTitle"
+              :column="1"
+              :size="size"
+              border>
+              <template #extra>
+                <el-button type="primary" @click="onObjectEdit">编辑</el-button>
               </template>
-            </el-table-column>
 
-            <el-table-column prop="objectCode" label="代码" header-align="center" align="center" width="150" />
-
-            <el-table-column prop="comment" label="说明" header-align="center" align="left" />
-
-            <el-table-column label="操作" header-align="center" align="center" width="230">
-              <template #default="scope">
-                <el-button type="primary" size="small" plain @click="onObjectOAM(scope.row)">
-                  行为
-                </el-button>
-                <el-button type="warning" size="small" plain @click="onObjectEdit(scope.row)">
-                  编辑
-                </el-button>
-                <el-button type="danger" size="small" plain @click="onObjectDelete(scope.row)">
-                  删除
-                </el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-      </LayoutContainer>
-    </div>
-    <Biz ref="bizRef" @success="getBizData" />
-    <BizObject ref="bizObjectRef" @success="getObjectData" />
+              <el-descriptions-item
+                label-align="right"
+                label="对象类型"
+                width="150px">
+                {{ state.objectData.objectType }}
+              </el-descriptions-item>
+
+              <el-descriptions-item
+                label="代码"
+                width="150px"
+                label-align="right">
+                {{ state.objectData.objectCode }}
+              </el-descriptions-item>
+
+              <el-descriptions-item
+                label-align="right"
+                width="150px"
+                label="说明">
+                {{ state.objectData.comment }}
+              </el-descriptions-item>
+
+            </el-descriptions>
+          </el-tab-pane>
+
+          <el-tab-pane label="对象属性" name="second">
+            <BizObjectAttrListComponent :bizId="bizId" :objectId="objectId" ref="bizObjectAttrList" />
+          </el-tab-pane>
+
+          <el-tab-pane label="对象行为" name="three">
+            <BizObjectOAMListComponent :bizId="bizId" :objectId="objectId" ref="bizObjectOAMList" />
+          </el-tab-pane>
+
+          <el-tab-pane label="模型映射" name="four">
+            <BizObjectMappingListComponent :bizId="bizId" :objectId="objectId" ref="bizObjectMappingList" />
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </page-main>
+
+    <BizObjectComponent ref="objectRef" @success="getObjectData" />
   </div>
 </template>
 
-<style lang="scss" scoped>
-.container {
-  height: 100%;
-  display: flex;
-  flex-direction: column;
+<style scoped>
+.el-descriptions {
+  margin: 10px;
 }
-
-.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;
-            }
-          }
-        }
-      }
-    }
-  }
+.cell-item {
+  display: flex;
+  align-items: center;
 }
 </style>

+ 12 - 4
src/views/model/object/index.vue

@@ -270,7 +270,7 @@ const onObjectDelete = (row: any) => {
                 </template>
               </el-button>
 
-              <el-button size="primary" @click.stop="onObjectAdd">
+              <el-button size="default" @click.stop="onObjectAdd">
                 <template #icon>
                   <el-icon>
                     <svg-icon name="i-ep:plus" />
@@ -282,7 +282,7 @@ const onObjectDelete = (row: any) => {
           </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 type="index" label="序号" header-align="center" align="center" width="60" />
 
             <el-table-column prop="objectTitle" label="名称" header-align="center" align="center" width="200">
               <template #default="scope">
@@ -293,7 +293,11 @@ const onObjectDelete = (row: any) => {
               </template>
             </el-table-column>
 
-            <el-table-column prop="objectCode" label="代码" header-align="center" align="center" width="150" />
+            <el-table-column prop="objectCode" label="代码" header-align="center" align="center" width="150">
+              <template #default="scope">
+                <span>{{ scope.row.objectCode }}</span>
+              </template>
+            </el-table-column>
 
             <el-table-column prop="objectType" label="类型" header-align="center" align="center" width="150">
               <template #default="scope">
@@ -302,7 +306,11 @@ const onObjectDelete = (row: any) => {
               </template>
             </el-table-column>
 
-            <el-table-column prop="comment" label="说明" header-align="center" align="left" />
+            <el-table-column prop="comment" label="说明" header-align="center" align="left">
+              <template #default="scope">
+                <span>{{ scope.row.comment }}</span>
+              </template>
+            </el-table-column>
 
             <el-table-column label="操作" header-align="center" align="center" width="170">
               <template #default="scope">