houyaf před 3 roky
rodič
revize
2d4e2bcc94

+ 14 - 0
src/api/model/modelObjectAttrApi.ts

@@ -28,6 +28,20 @@ export function getObjectAttrById(modelId: number, objectId: number, attrId: num
 }
 
 /**
+ * 根据Id获取ObjectAttr
+ * @param modelId
+ * @param objectId
+ * @returns
+ */
+export function getObjectAttrListById(modelId: number, objectId: number) {
+  return apiRequest({
+    url: `/model/object/attr/${modelId}/${objectId}`,
+    method: 'GET',
+  })
+}
+
+
+/**
  * 添加ObjectAttr
  * @param data
  * @returns

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

@@ -37,7 +37,7 @@ const routes: RouteRecordRaw[] = [
         },
       },
       {
-        path: '/object/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: '/biz1/rule',
+    path: '/biz/rule',
     component: Layout,
-    redirect: '/biz1/rule/index',
+    redirect: '/biz/rule/index',
     name: 'rule',
     meta: {
       title: '业务规则',

+ 6 - 6
src/router/modules/model.ts

@@ -15,8 +15,8 @@ const routes: RouteRecordRaw[] = [
     children: [
       {
         path: 'index',
-        name: 'modelList',
-        component: () => import('@/views/model/object/index.vue'),
+        name: 'model_object_list',
+        component: () => import('@/views/model/model/index.vue'),
         meta: {
           title: '对象定义',
           sidebar: false,
@@ -25,15 +25,15 @@ const routes: RouteRecordRaw[] = [
         },
       },
       {
-        path: 'object/attr/:modelId/:objectId',
-        name: 'modelObjectEdit',
-        component: () => import('@/views/model/attr/index.vue'),
+        path: 'object/:modelId/:objectId',
+        name: 'modelObjectDetails',
+        component: () => import('@/views/model/object/index.vue'),
         meta: {
           title: '数据对象',
           sidebar: false,
           activeMenu: '/model',
           cache: true,
-          noCache: 'modelObjectEdit',
+          noCache: 'modelObjectDetails',
         },
       },
       {

+ 2 - 20
src/views/biz/attr/index.vue

@@ -142,24 +142,6 @@ onMounted(() => {
   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>
@@ -167,7 +149,7 @@ function onSubmit() {
     <div class="container">
       <tool-bar>
         <template #left>
-          <h4>对象属性</h4>
+          <h5>对象属性</h5>
         </template>
         <template #right>
           <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选" clearable style="width: 200px;" />
@@ -334,7 +316,7 @@ function onSubmit() {
       }
     }
   }
-  h4{
+  h5{
     padding: 0;
     margin: 0;
   }

+ 1 - 1
src/views/biz/biz/index.vue

@@ -266,7 +266,7 @@ const onObjectDelete = (row: any) => {
             </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 ref="objectTableRef" v-loading="state.loading" class="list-table" :data="state.objectList" size="small" 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">

+ 2 - 2
src/views/biz/mapping/index.vue

@@ -67,7 +67,7 @@ onMounted(() => {
     <div class="container">
       <tool-bar>
         <template #left>
-          <h4>模型映射</h4>
+          <h5>模型映射</h5>
         </template>
         <template #right>
           <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选" clearable style="width: 200px;" />
@@ -157,7 +157,7 @@ onMounted(() => {
       }
     }
   }
-  h4{
+  h5{
     padding: 0;
     margin: 0;
   }

+ 6 - 1
src/views/biz/oam/index.vue

@@ -525,7 +525,7 @@ function goBack() {
               <div class="container">
                 <tool-bar>
                   <template #left>
-                    <h4>业务规则</h4>
+                    <h5>业务规则</h5>
                   </template>
 
                   <template #right>
@@ -717,4 +717,9 @@ function goBack() {
     }
   }
 }
+
+h5{
+  padding: 0;
+  margin: 0;
+}
 </style>

+ 2 - 2
src/views/biz/params/index.vue

@@ -133,7 +133,7 @@ onMounted(() => {
   <div class="container">
     <tool-bar>
       <template #left>
-        <h4>对象属性</h4>
+        <h5>对象属性</h5>
       </template>
       <template #right>
         <el-input placeholder="请输入关键词筛选" clearable style="width: 200px;" />
@@ -259,7 +259,7 @@ onMounted(() => {
       }
     }
   }
-  h4{
+  h5{
     padding: 0;
     margin: 0;
   }

+ 11 - 3
src/views/biz/rule/index.vue

@@ -30,12 +30,20 @@ onMounted(() => {
     <tool-header title="业务规则" />
 
     <page-main>
-      <el-table v-loading="state.loading" class="list-table" :data="state.dataList" border stripe highlight-current-row height="100%">
+      <el-table v-loading="state.loading" class="list-table" :data="state.dataList" size="small" border stripe highlight-current-row height="100%">
         <el-table-column type="index" label="序号" width="60" header-align="center" align="center" />
 
-        <el-table-column prop="ruleTitle" label="名称" header-align="center" align="center" width="200" />
+        <el-table-column prop="ruleTitle" label="名称" header-align="center" align="center" width="200">
+          <template #default="scope">
+            <span>{{ scope.row.ruleTitle }}</span>
+          </template>
+        </el-table-column>
 
-        <el-table-column prop="ruleDesc" label="说明" />
+        <el-table-column prop="ruleDesc" label="说明">
+          <template #default="scope">
+            <span>{{ scope.row.ruleDesc }}</span>
+          </template>
+        </el-table-column>
       </el-table>
     </page-main>
   </div>

+ 216 - 301
src/views/model/attr/index.vue

@@ -3,19 +3,25 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup name="modelObjectEdit">
-import Sortable from 'sortablejs'
+<script lang="ts" setup name="ModelObjectAttrList">
 import type { FormInstance, FormRules } from 'element-plus'
 import { ElMessage } from 'element-plus'
-import { createObject, getObjectById, updateObject } from '@/api/model/modelObjectApi'
-import useSettingsStore from '@/store/modules/settings'
-
-const settingsStore = useSettingsStore()
-const route = useRoute()
-const modelId = parseInt(route.params.modelId.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/model/modelObjectAttrApi'
+
+const props = withDefaults(defineProps<{
+  modelId: number
+  objectId: number
+}>(), {
+  modelId: 0,
+  objectId: 0,
+})
 
 interface ObjectAttr {
   attrId: number
@@ -29,30 +35,28 @@ interface ObjectAttr {
   comment: string
   isPKey: number
   sortNo: number
+  isEdit: boolean
 }
 
-const dataType = [
-  { dataType: 1, dataName: '整数' },
-  { dataType: 2, dataName: '字符串' },
-  { dataType: 3, dataName: '小数' }]
-
 // 数据
 const state = reactive({
   title: '',
   dialogVisible: false,
   loading: false,
   search: {
+    modelId: props.modelId,
+    objectId: props.objectId,
     keywords: '',
   },
   formData: {
-    modelId,
-    objectId,
+    modelId: 0,
+    objectId: 0,
     objectType: 1,
     objectCode: '',
     objectTitle: '',
     comment: '',
-    attrs: [] as ObjectAttr[],
   },
+  attrs: [] as ObjectAttr[],
   actionType: 1,
 })
 
@@ -75,15 +79,18 @@ const formRules = reactive<FormRules>({
 // 查询数据
 const getData = async () => {
   state.loading = true
-  const { data } = await getObjectById(modelId, objectId)
-  state.loading = false
-  state.formData = data
+  const { modelId, objectId } = state.search
+  const { data } = await getObjectAttrListById(modelId, objectId)
+  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({
     attrId: 0,
     attrTitle: '', // 名称
     attrCode: '', // 代码
@@ -95,322 +102,230 @@ function onAttrAdd() {
     isPKey: 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 { modelId, objectId } = state.search
+  const attrId = row.attrId
+  await deleteObjectAttrById(modelId, 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)
   }
-  else {
-    state.title = '编辑对象'
-    state.actionType = 2
-    getData()
-  }
-
-  nextTick(() => {
-    onAttrDrag()
-  })
-})
-
-// 保存
-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)
-        state.dialogVisible = false
-        ElMessage.success('新增成功')
-      }
-      else {
-        await updateObject(state.formData)
-        state.dialogVisible = false
-        ElMessage.success('修改成功')
-      }
-      goBack()
-    }
-  })
+  state.actionType = 0
 }
 
-function onCancel() {
-  goBack()
-}
-
-// 返回列表页
-function goBack() {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.close({ name: 'model' })
+async function onAttrSave(row: any) {
+  row.isEdit = false
+  if (state.actionType === 1) {
+    await createObjectAttr(row)
+    state.dialogVisible = false
+    ElMessage.success('新增成功')
   }
   else {
-    router.push({ name: 'model' })
+    await updateObjectAttr(row)
+    state.dialogVisible = false
+    ElMessage.success('修改成功')
   }
 }
+
+onMounted(() => {
+  getData()
+})
 </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>
+    <div class="container">
+      <tool-bar>
+        <template #left>
+          <h5>对象属性</h5>
         </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>
+        <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-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>
+          <el-button type="default" @click="onAttrAdd">
+            <template #icon>
+              <el-icon>
+                <svg-icon name="i-ep:plus" />
+              </el-icon>
+            </template>
+          </el-button>
         </template>
-
-        <div class="container">
-          <tool-bar>
-            <template #left>
-              <h2>属性</h2>
+      </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="名称" width="150">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.attrTitle" />
+            <span v-else>{{ scope.row.attrTitle }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="代码" width="120">
+          <template #default="scope">
+            <el-input v-if="scope.row.isEdit" v-model="scope.row.attrCode" />
+            <span v-else>{{ scope.row.attrCode }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="类型" 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="长度" width="130">
+          <template #default="scope">
+            <el-input-number
+              v-if="scope.row.isEdit"
+              v-model="scope.row.attrLength" style="width:100px"
+              :min="0"
+              controls-position="right"
+              size="large"
+            />
+            <span v-else>{{ scope.row.attrLength }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="小数位数" width="130">
+          <template #default="scope">
+            <el-input-number
+              v-if="scope.row.isEdit"
+              v-model="scope.row.attrDecimals" style="width:100px"
+              :min="0"
+              controls-position="right"
+              size="large"
+            />
+            <span v-else>{{ scope.row.attrDecimals }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="是否为空" 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="主键" width="70">
+          <template #default="scope">
+            <el-checkbox
+              v-if="scope.row.isEdit"
+              v-model="scope.row.isPKey"
+              :true-label="1"
+              :false-label="0"
+            >
+              {{ scope.row.isPKey === 1 ? '是' : '否' }}
+            </el-checkbox>
+            <span v-else>{{ scope.row.isPKey === 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" />
+            <span v-else>{{ scope.row.comment }}</span>
+          </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-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 #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>
+            <template v-else>
+              <el-button type="primary" plain size="small" @click="onAttrEdit(scope.row)">
+                编辑
               </el-button>
-
-              <el-button type="primary" @click="onAttrAdd">
-                <template #icon>
-                  <el-icon>
-                    <svg-icon name="i-ep:plus" />
-                  </el-icon>
+              <el-popconfirm title="是否要删除此行?" style="margin-left: 10px;" @confirm="onAttrDelete(scope.row)">
+                <template #reference>
+                  <el-button type="danger" plain size="small">
+                    删除
+                  </el-button>
                 </template>
-              </el-button>
+              </el-popconfirm>
             </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-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="主键" width="70">
-              <template #default="scope">
-                <el-checkbox v-model="scope.row.isPKey" :true-label="1" :false-label="0">
-                  {{ scope.row.isPKey === 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>
+        </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%;
-
-        .el-table__row {
-          .index {
-            display: inline-block;
-          }
+    height: 100%;
 
-          .delete {
-            display: none;
-          }
+    .el-table__row {
+      .index {
+        display: inline-block;
+      }
 
-          &:hover {
-            .index {
-              display: none;
-            }
+      .delete {
+        display: none;
+      }
 
-            .delete {
-              display: inline-block;
-            }
-          }
+      &:hover {
+        .index {
+          display: none;
+        }
 
-          .el-tag.sortable,
-          .el-tag.sortable .el-icon {
-            cursor: s-resize;
-          }
+        .delete {
+          display: inline-block;
         }
       }
-  h2{
+    }
+  }
+  h5{
     padding: 0;
     margin: 0;
   }

+ 15 - 18
src/views/model/object/components/Model.vue → src/views/model/model/components/Model.vue

@@ -3,7 +3,7 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup>
+<script lang="ts" setup name="Model">
 import type { FormInstance, FormRules } from 'element-plus'
 import { createModel, getModelById, updateModel } from '@/api/model/modelApi'
 const emit = defineEmits(['success'])
@@ -71,35 +71,32 @@ function resetFormData() {
 defineExpose({
   showAddModel() {
     resetFormData()
-    state.title = '新增模型'
+    state.title = '新增数据模型'
     state.actionType = 1
     state.dialogVisible = true
   },
   showEditModel(modelId: any) {
     resetFormData()
-    state.title = '编辑模型'
+    state.title = '编辑数据模型'
     state.actionType = 2
     state.dialogVisible = true
     getData(modelId)
   },
-
 })
 </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>
-      <div v-loading="state.loading">
-        <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">
-          <el-form-item label="模型名称" prop="modelTitle">
-            <el-input v-model="state.formData.modelTitle" placeholder="请输入标题" />
-          </el-form-item>
+  <el-drawer v-model="state.dialogVisible" :title="state.title" size="30%">
+    <el-scrollbar class="form-el-scrollbar">
+      <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">
+        <el-form-item label="模型名称" prop="modelTitle">
+          <el-input v-model="state.formData.modelTitle" placeholder="请输入标题" />
+        </el-form-item>
 
-          <el-form-item label="简要说明" prop="modelDesc">
-            <el-input v-model="state.formData.modelDesc" type="textarea" rows="10" placeholder="请输入标题" />
-          </el-form-item>
-        </el-form>
-      </div>
+        <el-form-item label="简要说明" prop="modelDesc">
+          <el-input v-model="state.formData.modelDesc" type="textarea" rows="10" placeholder="请输入标题" />
+        </el-form-item>
+      </el-form>
 
       <template #footer>
         <el-button size="large" @click="onCancel">
@@ -109,6 +106,6 @@ defineExpose({
           保存
         </el-button>
       </template>
-    </el-dialog>
-  </div>
+    </el-scrollbar>
+  </el-drawer>
 </template>

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

@@ -0,0 +1,396 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup>
+import { ElMessage, ElMessageBox } from 'element-plus'
+import ModelObjectComponent from '../object/components/ModelObject.vue'
+import ModelComponent from './components/Model.vue'
+
+import { deleteModelById, getModelByList } from '@/api/model/modelApi'
+import { deleteObjectById, getObjectByList } from '@/api/model/modelObjectApi'
+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: '',
+  treeData: [] as TreeNode[],
+  curModelId: 0,
+  objectList: [] as Object[],
+  dialogState: 1,
+  conditions: {
+    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.treeData = [
+    {
+      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 onModelAdd = () => {
+  modelRef.value.showAddModel()
+}
+
+// 模型编辑
+const onModelEdit = (data: { modelId: number }) => {
+  const modelId = data.modelId
+  modelRef.value.showEditModel(modelId)
+}
+
+// 模型删除
+const onModelDelete = (data: any) => {
+  ElMessageBox.confirm(`确认删除「${data.modelTitle}」吗?`, '确认信息').then(async () => {
+    await deleteModelById(data.modelId)
+    await getModelData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  })
+}
+
+// 模型选择
+const onTreeNodeClick = (node: TreeNode) => {
+  if (node.isLeaf) {
+    state.curModelId = node.modelId
+    getObjectData()
+  }
+}
+
+const modelObjectTableRef = ref()
+const modelObjectRef = ref()
+
+// 检索对象
+const onSearch = () => {
+  getObjectData()
+}
+
+// 新增对象
+const onObjectAdd = () => {
+  const modelId = state.curModelId
+  if (modelId > 0) {
+    modelObjectRef.value.showAddModel(modelId)
+  }
+}
+
+// 详情
+const onObjectDetails = (row: { modelId: any; objectId: any }) => {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.open({
+      name: 'modelObjectDetails',
+      params: {
+        modelId: row.modelId,
+        objectId: row.objectId,
+      },
+    })
+  }
+  else {
+    router.push({
+      name: 'modelObjectDetails',
+      params: {
+        modelId: row.modelId,
+        objectId: row.objectId,
+      },
+    })
+  }
+}
+
+const onObjectOAM = (row: any) => {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.open({
+      name: 'modelObjectOAM',
+      params: {
+        modelId: row.modelId,
+        objectId: row.objectId,
+      },
+    })
+  }
+  else {
+    router.push({
+      name: 'modelObjectOAM',
+      params: {
+        modelId: row.modelId,
+        objectId: row.objectId,
+      },
+    })
+  }
+}
+
+// 删除对象
+const onObjectDelete = (row: any) => {
+  const { modelId, objectId, objectTitle } = row
+  ElMessageBox.confirm(`确认删除「${objectTitle}」吗?`, '确认信息').then(async () => {
+    await deleteObjectById(modelId, 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="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="250px" hide-left-side-toggle>
+        <template #leftSide>
+          <el-scrollbar class="tree">
+            <ElTree ref="leftTreeRef" :data="state.treeData" 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="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="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.conditions.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 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="modelObjectTableRef" v-loading="state.loading" class="list-table" :data="state.objectList" size="small" border stripe highlight-current-row height="100%">
+            <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">
+                <el-icon>
+                  <svg-icon name="uim:box" />
+                </el-icon>
+                <span>{{ 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="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">
+              <template #default="scope">
+                <span>{{ scope.row.comment }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column label="操作" header-align="center" align="center" width="220">
+              <template #default="scope">
+                <el-button type="primary" size="small" plain @click="onObjectOAM(scope.row)">行为</el-button>
+                <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>
+    <ModelComponent ref="modelRef" @success="getModelData" />
+    <ModelObjectComponent ref="modelObjectRef" @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>

+ 29 - 214
src/views/model/object/components/ModelObject.vue

@@ -3,32 +3,12 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup>
-import Sortable from 'sortablejs'
+<script lang="ts" setup name="ModelObject">
 import type { FormInstance, FormRules } from 'element-plus'
 import { ElMessage } from 'element-plus'
 import { createObject, getObjectById, updateObject } from '@/api/model/modelObjectApi'
 const emit = defineEmits(['success'])
 
-interface ObjectAttr {
-  attrId: number
-  attrTitle: string
-  attrCode: string
-  attrType: number
-  attrLength: number
-  attrDecimals: number
-  isNotNull: number
-  isFixed: number
-  comment: string
-  isPKey: number
-  sortNo: number
-}
-
-const dataType = [
-  { dataType: 1, dataName: '整数' },
-  { dataType: 2, dataName: '字符串' },
-  { dataType: 3, dataName: '小数' }]
-
 const state = reactive({
   title: '',
   dialogVisible: false,
@@ -39,7 +19,6 @@ const state = reactive({
     objectCode: '',
     objectTitle: '',
     comment: '',
-    attrs: [] as ObjectAttr[],
   },
   actionType: 1,
 })
@@ -65,53 +44,8 @@ const getData = async (modelId: number, objectId: number) => {
   state.formData = data
 }
 
-const attrsTableRef = ref()
-const attrsTableKey = ref(0)
-function onAttrAdd() {
-  state.formData.attrs.push({
-    attrId: 0,
-    attrTitle: '', // 名称
-    attrCode: '', // 代码
-    attrType: 0, // 类型
-    attrLength: 0, // 长度
-    attrDecimals: 0, // 小数位数
-    isNotNull: 0, // 是否为空
-    isFixed: 0, // 是否锁定
-    isPKey: 0, // 主键
-    comment: '', // 注释
-    sortNo: 0,
-  })
-  nextTick(() => {
-    attrsTableRef.value.setScrollTop(state.formData.attrs.length * 50)
-  })
-}
-
-// 删除属性
-function onAttrDelete(index: number) {
-  state.formData.attrs.splice(index, 1)
-}
-
-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())
-    },
-  })
-}
-
 onMounted(() => {
   nextTick(() => {
-    onAttrDrag()
   })
 })
 
@@ -171,157 +105,38 @@ defineExpose({
 </script>
 
 <template>
-  <div v-show="state.dialogVisible">
-    <el-drawer :model-value="true" :title="state.title" size="80%" direction="ltr">
-      <template #default>
-        <el-row :gutter="20" class="container">
-          <el-col :span="7" :xs="24" class="form-left-side">
-            <h2>基本信息</h2>
-            <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="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>
-          </el-col>
-          <el-col :span="17" :xs="24" class="form-right-side">
-            <h2>属性</h2>
-            <el-table ref="attrsTableRef" :key="attrsTableKey" :data="state.formData.attrs" 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-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="90">
-                <template #default="scope">
-                  <el-input v-model="scope.row.attrLength" />
-                </template>
-              </el-table-column>
-
-              <el-table-column label="小数位数" width="90">
-                <template #default="scope">
-                  <el-input v-model="scope.row.attrDecimals" />
-                </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="主键" width="70">
-                <template #default="scope">
-                  <el-checkbox v-model="scope.row.isPKey" :true-label="1" :false-label="0">
-                    {{ scope.row.isPKey === 1 ? '是' : '否' }}
-                  </el-checkbox>
-                </template>
-              </el-table-column>
-
-              <el-table-column label="锁定" width="70">
-                <template #default="scope">
-                  <el-checkbox v-model="scope.row.isFixed" :true-label="1" :false-label="0">
-                    {{ scope.row.isFixed === 1 ? '是' : '否' }}
-                  </el-checkbox>
-                </template>
-              </el-table-column>
-
-              <el-table-column label="注释" width="120">
-                <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>
-          </el-col>
-        </el-row>
-      </template>
-
-      <template #footer>
-        <div style="flex: auto">
-          <el-button type="primary" size="large" @click="onSubmit">
-            提交
-          </el-button>
-          <el-button size="large" @click="onCancel">
-            取消
-          </el-button>
-        </div>
-      </template>
-    </el-drawer>
-  </div>
+  <el-drawer v-model="state.dialogVisible" :title="state.title" size="80%" direction="ltr">
+    <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="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>
+      <div style="flex: auto">
+        <el-button type="primary" size="large" @click="onSubmit">
+          提交
+        </el-button>
+        <el-button size="large" @click="onCancel">
+          取消
+        </el-button>
+      </div>
+    </template>
+  </el-drawer>
 </template>
 
 <style lang="scss" scoped>
     .form-right-side {
-      // display: flex;
-      // flex-direction: column;
-      // width: 500px;
       padding: var(--container-padding);
       border-left: 1px dashed var(--el-disabled-border-color);
       background-color: var(--g-app-bg);

+ 96 - 353
src/views/model/object/index.vue

@@ -3,406 +3,149 @@ meta:
   enabled: false
 </route>
 
-<script lang="ts" setup>
-import { ElMessage, ElMessageBox } from 'element-plus'
-import Model from './components/Model.vue'
-import ModelObject from './components/ModelObject.vue'
-import { deleteModelById, getModelByList } from '@/api/model/modelApi'
-import { deleteObjectById, getObjectByList } from '@/api/model/modelObjectApi'
+<script lang="ts" setup name="modelObjectDetails">
+import type { TabsPaneContext } from 'element-plus'
+import ModelObjectAttrListComponent from '../attr/index.vue'
+import ModelObjectComponent from './components/ModelObject.vue'
+import { getObjectById } from '@/api/model/modelObjectApi'
 import useSettingsStore from '@/store/modules/settings'
 
 const settingsStore = useSettingsStore()
+
 const router = useRouter()
 const tabbar = useTabbar()
+const route = useRoute()
 
-interface TreeNode {
-  label: string
-  isLeaf: boolean
-  modelId: number
-  children?: TreeNode[]
-}
-
-interface Object {
-  modelId: number
-  objectId: number
-  objectTitle: string
-  objectCode: string
-  comment: string
-}
+const modelId = parseInt(route.params.modelId.toString())
+const objectId = parseInt(route.params.objectId.toString())
 
 const state = reactive({
   loading: false,
-  keywords: '',
-  treeData: [] as TreeNode[],
   curModelId: 0,
-  objectList: [] as Object[],
-  dialogState: 1,
-  search: {
-    keywords: '',
+  objectData: {
+    objectTitle: '',
+    objectCode: '',
+    objectType: 0,
+    comment: '',
   },
+  dialogState: 1,
 })
 
-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.treeData = [
-    {
-      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()
-  }
+  state.loading = true
+  const { data } = await getObjectById(modelId, objectId)
+  state.objectData.objectTitle = data.objectTitle
+  state.objectData.objectType = data.objectType
+  state.objectData.objectCode = data.objectCode
+  state.objectData.comment = data.comment
+  state.loading = false
 }
 
 onMounted(() => {
-  getModelData()
-})
-
-// 模型新增
-const onModelAdd = () => {
-  modelRef.value.showAddModel()
-}
-
-// 模型编辑
-const onModelEdit = (data: { modelId: number }) => {
-  const modelId = data.modelId
-  modelRef.value.showEditModel(modelId)
-}
+  getObjectData()
+  nextTick(() => {
 
-// 模型删除
-const onModelDelete = (data: any) => {
-  ElMessageBox.confirm(`确认删除「${data.modelTitle}」吗?`, '确认信息').then(async () => {
-    await deleteModelById(data.modelId)
-    await getModelData()
-    ElMessage.success({
-      message: '删除成功',
-      center: true,
-    })
   })
-}
+})
 
-// 模型选择
-const onTreeNodeClick = (node: TreeNode) => {
-  if (node.isLeaf) {
-    state.curModelId = node.modelId
-    getObjectData()
-  }
+const activeTab = ref('first')
+
+const handleClick = (tab: TabsPaneContext, event: Event) => {
+  // console.log(tab, event)
 }
 
-const objectTableRef = ref()
-const objectRef = ref()
+const modelObjectRef = ref()
 
 // 检索对象
 const onSearch = () => {
   getObjectData()
 }
 
-// 新增对象
-const onObjectAdd = () => {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.open({
-      name: 'modelObjectEdit',
-      params: {
-        modelId: state.curModelId,
-        objectId: 0,
-      },
-    })
-  }
-  else {
-    router.push({
-      name: 'modelObjectEdit',
-      params: {
-        modelId: state.curModelId,
-        objectId: 0,
-      },
-    })
-  }
-}
-
 // 编辑对象
-const onObjectEdit = (row: { modelId: any; objectId: any }) => {
-  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.open({
-      name: 'modelObjectEdit',
-      params: {
-        modelId: row.modelId,
-        objectId: row.objectId,
-      },
-    })
-  }
-  else {
-    router.push({
-      name: 'modelObjectEdit',
-      params: {
-        modelId: row.modelId,
-        objectId: row.objectId,
-      },
-    })
-  }
+const onObjectEdit = (row: { modelId: number; objectId: number }) => {
+  modelObjectRef.value.showEditModel(modelId, objectId)
 }
 
-const onObjectOAM = (row: any) => {
+const size = ref('')
+const modelObjectAttrList = ref()
+
+// 返回列表页
+function goBack() {
   if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
-    tabbar.open({
-      name: 'modelObjectOAM',
-      params: {
-        modelId: row.modelId,
-        objectId: row.objectId,
-      },
-    })
+    tabbar.close({ name: 'model_object_list' })
   }
   else {
-    router.push({
-      name: 'modelObjectOAM',
-      params: {
-        modelId: row.modelId,
-        objectId: row.objectId,
-      },
-    })
+    router.push({ name: 'model_object_list' })
   }
 }
-
-// 删除对象
-const onObjectDelete = (row: any) => {
-  const { modelId, objectId, objectTitle } = row
-  ElMessageBox.confirm(`确认删除「${objectTitle}」吗?`, '确认信息').then(async () => {
-    await deleteObjectById(modelId, 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="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="250px" hide-left-side-toggle>
-        <template #leftSide>
-          <el-scrollbar class="tree">
-            <ElTree ref="leftTreeRef" :data="state.treeData" 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="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="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>
+    <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 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="60" />
-
-            <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>{{ 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="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">
-              <template #default="scope">
-                <span>{{ scope.row.comment }}</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="small"
+              border
+            >
+              <template #extra>
+                <el-button type="primary" @click="onObjectEdit">
+                  编辑
+                </el-button>
               </template>
-            </el-table-column>
 
-            <el-table-column label="操作" header-align="center" align="center" width="170">
-              <template #default="scope">
-                <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>
-    <Model ref="modelRef" @success="getModelData" />
-    <ModelObject ref="objectRef" @success="getObjectData" />
+              <el-descriptions-item
+                label="代码"
+                label-class-name="el-description_label"
+                width="150px"
+                label-align="right"
+              >
+                {{ state.objectData.objectCode }}
+              </el-descriptions-item>
+
+              <el-descriptions-item
+                label-align="right"
+                label-class-name="el-description_label"
+                width="150px"
+                label="说明"
+              >
+                {{ state.objectData.comment }}
+              </el-descriptions-item>
+            </el-descriptions>
+          </el-tab-pane>
+
+          <el-tab-pane label="对象属性" name="second">
+            <ModelObjectAttrListComponent ref="modelObjectAttrList" :model-id="modelId" :object-id="objectId" />
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </page-main>
+    <ModelObjectComponent ref="modelObjectRef" @success="getObjectData" />
   </div>
 </template>
 
-<style lang="scss" scoped>
-.container {
-  height: 100%;
-  display: flex;
-  flex-direction: column;
+<style scoped>
+.el-descriptions {
+  margin: 0 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>