Parcourir la source

Merge branch 'master' of http://140.246.121.86:3000/hyf/xps-web-v5

zhaobao il y a 3 ans
Parent
commit
aeeaea2aeb

+ 0 - 93
src/api/model/entityApi.ts

@@ -1,93 +0,0 @@
-import apiRequest from '@/network/http/apiRequest'
-
-/**
- * 获取Entity分页
- * @param data
- * @returns
- */
-export function getEntityByPage(data: any) {
-  return apiRequest({
-    url: '/model/entity/page',
-    method: 'GET',
-    params: data,
-  })
-}
-
-/**
- * 获取Entity列表
- * @param data
- * @returns
- */
-export function getEntityByList(data: any) {
-  return apiRequest({
-    url: '/model/entity',
-    method: 'GET',
-    params: data,
-  })
-}
-
-/**
- * 根据Id获取Entity
- * @param modelId
- * @param entityId
- * @returns
- */
-export function getEntityById(modelId: number, entityId: number) {
-  return apiRequest({
-    url: `/model/entity/${modelId}/${entityId}`,
-    method: 'GET',
-  })
-}
-
-/**
- * 添加Entity
- * @param data
- * @returns
- */
-export function createEntity(data: any) {
-  return apiRequest({
-    url: '/model/entity/add',
-    method: 'POST',
-    data,
-  })
-}
-
-/**
- * 更新Entity
- * @param data
- * @returns
- */
-export function updateEntity(data: any) {
-  return apiRequest({
-    url: '/model/entity/update',
-    method: 'PUT',
-    data,
-  })
-}
-
-/**
- * 更新Entity
- * @param modelId
- * @param entityId
- * @param status
- * @returns
- */
-export function updateEntityStatus(modelId: number, entityId: number, status: number) {
-  return apiRequest({
-    url: `/model/entity/updateStatus/${modelId}/${entityId}/${status}`,
-    method: 'PUT',
-  })
-}
-
-/**
- * 删除Entity
- * @param modelId
- * @param entityId
- * @returns
- */
-export function deleteEntityById(modelId: number, entityId: number) {
-  return apiRequest({
-    url: `/model/entity/${modelId}/${entityId}`,
-    method: 'DELETE',
-  })
-}

+ 0 - 68
src/api/model/entityAttrApi.ts

@@ -1,68 +0,0 @@
-import apiRequest from '@/network/http/apiRequest'
-
-/**
- * 获取EntityAttr列表
- * @param data
- * @returns
- */
-export function getEntityAttrByList(data: any) {
-  return apiRequest({
-    url: '/model/entity/attribute',
-    method: 'GET',
-    params: data,
-  })
-}
-
-/**
- * 根据Id获取EntityAttr
- * @param modelId
- * @param entityId
- * @param attrId
- * @returns
- */
-export function getEntityAttrById(modelId: number, entityId: number, attrId: number) {
-  return apiRequest({
-    url: `/model/entity/attribute/${modelId}/${entityId}/${attrId}`,
-    method: 'GET',
-  })
-}
-
-/**
- * 添加EntityAttr
- * @param data
- * @returns
- */
-export function createEntityAttr(data: any) {
-  return apiRequest({
-    url: '/model/entity/attribute/add',
-    method: 'POST',
-    data,
-  })
-}
-
-/**
- * 更新EntityAttr
- * @param data
- * @returns
- */
-export function updateEntityAttr(data: any) {
-  return apiRequest({
-    url: '/model/entity/attribute/update',
-    method: 'PUT',
-    data,
-  })
-}
-
-/**
- * 删除EntityAttr
- * @param modelId
- * @param entityId
- * @param attrId
- * @returns
- */
-export function deleteEntityAttrById(modelId: number, entityId: number, attrId: number) {
-  return apiRequest({
-    url: `/model/entity/attribute/${modelId}/${entityId}/${attrId}`,
-    method: 'DELETE',
-  })
-}

+ 93 - 0
src/api/model/objectApi.ts

@@ -0,0 +1,93 @@
+import apiRequest from '@/network/http/apiRequest'
+
+/**
+ * 获取Object分页
+ * @param data
+ * @returns
+ */
+export function getObjectByPage(data: any) {
+  return apiRequest({
+    url: '/model/object/page',
+    method: 'GET',
+    params: data,
+  })
+}
+
+/**
+ * 获取Object列表
+ * @param data
+ * @returns
+ */
+export function getObjectByList(data: any) {
+  return apiRequest({
+    url: '/model/object',
+    method: 'GET',
+    params: data,
+  })
+}
+
+/**
+ * 根据Id获取Object
+ * @param modelId
+ * @param objectId
+ * @returns
+ */
+export function getObjectById(modelId: number, objectId: number) {
+  return apiRequest({
+    url: `/model/object/${modelId}/${objectId}`,
+    method: 'GET',
+  })
+}
+
+/**
+ * 添加Object
+ * @param data
+ * @returns
+ */
+export function createObject(data: any) {
+  return apiRequest({
+    url: '/model/object/add',
+    method: 'POST',
+    data,
+  })
+}
+
+/**
+ * 更新Object
+ * @param data
+ * @returns
+ */
+export function updateObject(data: any) {
+  return apiRequest({
+    url: '/model/object/update',
+    method: 'PUT',
+    data,
+  })
+}
+
+/**
+ * 更新Object
+ * @param modelId
+ * @param objectId
+ * @param status
+ * @returns
+ */
+export function updateObjectStatus(modelId: number, objectId: number, status: number) {
+  return apiRequest({
+    url: `/model/object/updateStatus/${modelId}/${objectId}/${status}`,
+    method: 'PUT',
+  })
+}
+
+/**
+ * 删除Object
+ * @param modelId
+ * @param objectId
+ * @returns
+ */
+export function deleteObjectById(modelId: number, objectId: number) {
+  return apiRequest({
+    url: `/model/object/${modelId}/${objectId}`,
+    method: 'DELETE',
+  })
+}

+ 68 - 0
src/api/model/objectAttrApi.ts

@@ -0,0 +1,68 @@
+import apiRequest from '@/network/http/apiRequest'
+
+/**
+ * 获取ObjectAttr列表
+ * @param data
+ * @returns
+ */
+export function getObjectAttrByList(data: any) {
+  return apiRequest({
+    url: '/model/object/attribute',
+    method: 'GET',
+    params: data,
+  })
+}
+
+/**
+ * 根据Id获取ObjectAttr
+ * @param modelId
+ * @param objectId
+ * @param attrId
+ * @returns
+ */
+export function getObjectAttrById(modelId: number, objectId: number, attrId: number) {
+  return apiRequest({
+    url: `/model/object/attribute/${modelId}/${objectId}/${attrId}`,
+    method: 'GET',
+  })
+}
+
+/**
+ * 添加ObjectAttr
+ * @param data
+ * @returns
+ */
+export function createObjectAttr(data: any) {
+  return apiRequest({
+    url: '/model/object/attribute/add',
+    method: 'POST',
+    data,
+  })
+}
+
+/**
+ * 更新ObjectAttr
+ * @param data
+ * @returns
+ */
+export function updateObjectAttr(data: any) {
+  return apiRequest({
+    url: '/model/object/attribute/update',
+    method: 'PUT',
+    data,
+  })
+}
+
+/**
+ * 删除ObjectAttr
+ * @param modelId
+ * @param objectId
+ * @param attrId
+ * @returns
+ */
+export function deleteObjectAttrById(modelId: number, objectId: number, attrId: number) {
+  return apiRequest({
+    url: `/model/object/attribute/${modelId}/${objectId}/${attrId}`,
+    method: 'DELETE',
+  })
+}

+ 26 - 3
src/router/modules/app.center.ts

@@ -9,7 +9,7 @@ const routes: RouteRecordRaw[] = [
     redirect: '/app-center/store',
     name: 'app_center',
     meta: {
-      title: '应用中心',
+      title: '应用商店',
       icon: 'ri:function-line',
     },
     children: [
@@ -54,7 +54,7 @@ const routes: RouteRecordRaw[] = [
     path: '/app-center/publish',
     component: Layout,
     redirect: '/app-center/publish/index',
-    name: 'daas_database',
+    name: 'appPublish',
     meta: {
       title: '应用发布',
       icon: 'bx:bxs-component',
@@ -62,7 +62,7 @@ const routes: RouteRecordRaw[] = [
     children: [
       {
         path: 'index',
-        name: 'daas_database_index',
+        name: 'appPublishIndex',
         component: () => import('@/views/app_center/publish/index.vue'),
         meta: {
           title: '应用发布',
@@ -73,5 +73,28 @@ const routes: RouteRecordRaw[] = [
       },
     ],
   },
+  {
+    path: '/app-center/removed',
+    component: Layout,
+    redirect: '/app-center/removed/index',
+    name: 'appRemoved',
+    meta: {
+      title: '已下架',
+      icon: 'ep:delete',
+    },
+    children: [
+      {
+        path: 'index',
+        name: 'appRemovedIndex',
+        component: () => import('@/views/app_center/removed/index.vue'),
+        meta: {
+          title: '已下架',
+          sidebar: false,
+          breadcrumb: false,
+          activeMenu: '/app-center/removed',
+        },
+      },
+    ],
+  },
 ]
 export default routes

+ 22 - 5
src/router/modules/daas.ts

@@ -7,21 +7,38 @@ const routes: RouteRecordRaw[] = [
     path: '/daas/model',
     component: Layout,
     redirect: '/daas/model/index',
-    name: 'daas_model',
+    name: 'daasModel',
     meta: {
       title: '数据模型',
-      icon: 'uim:box',
+      icon: 'ic:twotone-double-arrow',
     },
     children: [
       {
         path: 'index',
-        name: 'daas_model_index',
+        name: 'daasModelList',
         component: () => import('@/views/daas/model/index.vue'),
         meta: {
-          title: '数据模型',
+          title: '模型对象',
+        },
+      },
+      {
+        path: 'daas/object/:modelId/:ObjectId',
+        name: 'daasObjectEdit',
+        component: () => import('@/views/daas/model/index.vue'),
+        meta: {
+          title: '对象编辑',
           sidebar: false,
-          breadcrumb: false,
           activeMenu: '/daas/model',
+          cache: true,
+          noCache: 'daasObjectEdit',
+        },
+      },
+      {
+        path: 'behavior',
+        name: 'daasBehaviorList',
+        component: () => import('@/views/daas/behavior/index.vue'),
+        meta: {
+          title: '行为规则',
         },
       },
     ],

+ 34 - 1
src/views/app_center/app_store/index.vue

@@ -4,8 +4,9 @@ meta:
 </route>
 
 <script lang="ts" setup>
+import { ElMessage, ElMessageBox } from 'element-plus'
 import App from './components/App.vue'
-import { getAppByList } from '@/api/apps/appApi'
+import { getAppByList, updateApp } from '@/api/apps/appApi'
 const tabbar = useTabbar()
 
 const state = reactive({
@@ -42,6 +43,24 @@ const onAppPermit = (data: any) => {
     },
   })
 }
+
+const onAppPublish = () => {
+  tabbar.open({
+    name: 'appPublish',
+    params: { },
+  })
+}
+
+const onAppRemove = (data: any) => {
+  ElMessageBox.confirm(`确认下架「${data.appTitle}」吗?`, '确认信息').then(async () => {
+    await updateApp({ appId: data.appId, ifStore: 0 })
+    await getData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  }).catch(() => {})
+}
 </script>
 
 <template>
@@ -52,6 +71,17 @@ const onAppPermit = (data: any) => {
           便捷管理发布的应用软件或应用服务
         </div>
       </template>
+
+      <el-button-group>
+        <el-button type="primary" size="large" plain @click="onAppPublish">
+          <template #icon>
+            <el-icon>
+              <svg-icon name="ep:plus" />
+            </el-icon>
+          </template>
+          发布应用
+        </el-button>
+      </el-button-group>
     </page-header>
 
     <el-row :gutter="20" style="margin: 0 10px;">
@@ -77,6 +107,9 @@ const onAppPermit = (data: any) => {
             <el-button text @click="onAppPermit(item)">
               权限配置
             </el-button>
+            <el-button text @click="onAppRemove(item)">
+              下架
+            </el-button>
           </div>
         </el-card>
       </el-col>

+ 18 - 3
src/views/app_center/publish/index.vue

@@ -12,9 +12,11 @@ const formRef = ref<FormInstance>()
 const formRules = ref<FormRules>({
   appTitle: [{ required: true, message: '请输入appTitle', trigger: 'blur' }],
   appCatTitle: [{ required: true, message: '请输入appCatTitle', trigger: 'blur' }],
-  appUrl: [{ required: true, message: '请输入appUrl', trigger: 'blur' }],
-  appCode: [{ required: true, message: '请输入app编码', trigger: 'blur' }],
-  appDesc: [{ required: true, message: '请输入描述', trigger: 'blur' }],
+  appUrl: [{ required: false, message: '请输入appUrl', trigger: 'blur' }],
+  appCode: [{ required: false, message: '请输入app编码', trigger: 'blur' }],
+  appKey: [{ required: true, message: '请输入App Key', trigger: 'blur' }],
+  appSecret: [{ required: true, message: '请输入App Secret', trigger: 'blur' }],
+  appDesc: [{ required: false, message: '请输入描述', trigger: 'blur' }],
 })
 
 const state = reactive({
@@ -29,6 +31,8 @@ const state = reactive({
     appCatId: 0,
     appParam: '',
     appUrl: '',
+    appKey: '',
+    appSecret: '',
     appDesc: '',
     appLogo: '',
   },
@@ -50,6 +54,8 @@ function resetFormData() {
     appCatId: 0,
     appUrl: '',
     appParam: '',
+    appKey: '',
+    appSecret: '',
     appDesc: '',
     appLogo: '',
   }
@@ -101,10 +107,19 @@ function onSubmit() {
                 :preview-src-list="[state.formData.appLogo]"
               />
             </el-form-item>
+
             <el-form-item label="参数" prop="appParam">
               <el-input v-model="state.formData.appParam" />
             </el-form-item>
 
+            <el-form-item label="App Key" prop="appKey">
+              <el-input v-model="state.formData.appKey" />
+            </el-form-item>
+
+            <el-form-item label="App Secret" prop="appSecret">
+              <el-input v-model="state.formData.appSecret" />
+            </el-form-item>
+
             <el-form-item label="描述">
               <el-input v-model="state.formData.appDesc" type="textarea" />
             </el-form-item>

+ 116 - 0
src/views/app_center/removed/index.vue

@@ -0,0 +1,116 @@
+<script lang="ts" setup name="AppRemoved">
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { deleteAppById, getAppByList, updateApp } from '@/api/apps/appApi'
+
+const state = reactive({
+  loading: false,
+  // 表格是否自适应高度
+  tableAutoHeight: false,
+  // 搜索
+  search: {
+    ifStore: 0,
+    keywords: '',
+  },
+  // 列表数据
+  dataList: [],
+})
+
+const appRemovedTableRef = ref()
+const getDataList = async () => {
+  state.loading = true
+  const { data } = await getAppByList(state.search)
+  state.dataList = data
+  state.loading = false
+}
+
+onMounted(() => {
+  getDataList()
+})
+
+function onAppPublish(row: any) {
+  ElMessageBox.confirm(`确认上架「${row.appTitle}」吗?`, '确认信息').then(async () => {
+    await updateApp({ appId: row.appId, ifStore: 1 })
+    await getDataList()
+    ElMessage.success({
+      message: '上架成功',
+      center: true,
+    })
+  }).catch(() => {})
+}
+
+function onAppDel(row: any) {
+  ElMessageBox.confirm(`确认删除「${row.appTitle}」吗?`, '确认信息').then(async () => {
+    await deleteAppById(row.appId)
+    await getDataList()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  }).catch(() => {})
+}
+</script>
+
+<template>
+  <div :class="{ 'absolute-container': state.tableAutoHeight }">
+    <page-header title="已下架应用">
+      <template #content>
+        <div>
+          系统支持管理应用市场中已下架的应用
+        </div>
+      </template>
+    </page-header>
+
+    <page-main>
+      <el-table ref="appRemovedTableRef" v-loading="state.loading" class="list-table" :data="state.dataList" border stripe highlight-current-row height="100%">
+        <el-table-column type="index" label="序号" width="60" header-align="center" align="center" />
+
+        <el-table-column prop="appTitle" label="名称" header-align="center" align="center" width="200" />
+
+        <el-table-column prop="appCatTitle" label="分类" />
+
+        <el-table-column prop="appKey" label="app Key" />
+
+        <el-table-column prop="appSecret" label="app Secret" />
+
+        <el-table-column prop="appDesc" label="说明" />
+
+        <el-table-column label="操作" width="250" align="center" fixed="right">
+          <template #default="scope">
+            <el-button type="primary" size="small" plain @click="onAppPublish(scope.row)">
+              重新上架
+            </el-button>
+            <el-button v-if="!scope.row.isFixed" type="danger" size="small" plain @click="onAppDel(scope.row)">
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </page-main>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.absolute-container {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .page-header {
+    margin-bottom: 0;
+  }
+
+  .page-main {
+    // 让 page-main 的高度自适应
+    flex: 1;
+    overflow: auto;
+    display: flex;
+    flex-direction: column;
+
+    .search-container {
+      margin-bottom: 0;
+    }
+  }
+}
+</style>

+ 8 - 0
src/views/daas/behavior/index.vue

@@ -0,0 +1,8 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup name="behaviorList">
+
+</script>

+ 0 - 0
src/views/daas/model/components/model.vue → src/views/daas/model/components/Model.vue


+ 35 - 30
src/views/daas/model/components/entity.vue → src/views/daas/model/components/Object.vue

@@ -7,10 +7,10 @@ meta:
 import Sortable from 'sortablejs'
 import type { FormInstance, FormRules } from 'element-plus'
 import { ElMessage } from 'element-plus'
-import { createEntity, getEntityById, updateEntity } from '@/api/model/entityApi'
+import { createObject, getObjectById, updateObject } from '@/api/model/objectApi'
 const emit = defineEmits(['success'])
 
-interface EntityAttr {
+interface ObjectAttr {
   attrId: number
   attrTitle: string
   attrCode: string
@@ -34,31 +34,33 @@ const state = reactive({
   dialogVisible: false,
   loading: false,
   formData: {
-    modelId: -1,
-    entityId: -1,
-    entityCode: '',
-    entityTitle: '',
-    entityComment: '',
-    attrs: [] as EntityAttr[],
+    modelId: 0,
+    objectId: 0,
+    objectCode: '',
+    objectTitle: '',
+    comment: '',
+    attrs: [] as ObjectAttr[],
   },
   actionType: 1,
 })
 
 const formRef = ref<FormInstance>()
 const formRules = ref<FormRules>({
-  entityTitle: [
+  objectTitle: [
     { required: true, message: '请输入标题', trigger: 'blur' },
   ],
-  entityCode: [
+  objectCode: [
     { required: true, message: '请输入标题', trigger: 'blur' },
   ],
-  entityComment: [
+  comment: [
     { required: false, message: '请输入描述', trigger: 'blur' },
   ],
 })
-const getData = async (modelId: number, entityId: number) => {
+
+// 查询数据
+const getData = async (modelId: number, objectId: number) => {
   state.loading = true
-  const { data } = await getEntityById(modelId, entityId)
+  const { data } = await getObjectById(modelId, objectId)
   state.loading = false
   state.formData = data
 }
@@ -83,6 +85,8 @@ function onAttrAdd() {
     attrsTableRef.value.setScrollTop(state.formData.attrs.length * 50)
   })
 }
+
+// 删除属性
 function onAttrDelete(index: number) {
   state.formData.attrs.splice(index, 1)
 }
@@ -111,18 +115,19 @@ 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 createEntity(state.formData)
+        await createObject(state.formData)
         state.dialogVisible = false
         ElMessage.success('新增成功')
         emit('success')
       }
       else {
-        await updateEntity(state.formData)
+        await updateObject(state.formData)
         state.dialogVisible = false
         ElMessage.success('修改成功')
         emit('success')
@@ -137,11 +142,11 @@ function onCancel() {
 
 function resetFormData() {
   state.formData = {
-    modelId: -1,
-    entityId: -1,
-    entityCode: '',
-    entityTitle: '',
-    entityComment: '',
+    modelId: 0,
+    objectId: 0,
+    objectCode: '',
+    objectTitle: '',
+    comment: '',
     attrs: [],
   }
 }
@@ -149,17 +154,17 @@ function resetFormData() {
 defineExpose({
   showAddModel(modelId: number) {
     resetFormData()
-    state.title = '新增实体'
+    state.title = '新增对象'
     state.actionType = 1
     state.dialogVisible = true
     state.formData.modelId = modelId
   },
-  showEditModel(modelId: number, entityId: number) {
+  showEditModel(modelId: number, objectId: number) {
     resetFormData()
-    state.title = '编辑实体'
+    state.title = '编辑对象'
     state.actionType = 2
     state.dialogVisible = true
-    getData(modelId, entityId)
+    getData(modelId, objectId)
   },
 
 })
@@ -175,14 +180,14 @@ defineExpose({
             <el-scrollbar>
               <div class="form-content">
                 <el-form ref="formRef" :model="state.formData" :rules="formRules" label-position="top">
-                  <el-form-item label="实体名称" prop="entityTitle">
-                    <el-input v-model="state.formData.entityTitle" placeholder="请输入标题" />
+                  <el-form-item label="对象名称" prop="objectTitle">
+                    <el-input v-model="state.formData.objectTitle" placeholder="请输入标题" />
                   </el-form-item>
-                  <el-form-item label="实体代码" prop="entityCode">
-                    <el-input v-model="state.formData.entityCode" placeholder="请输入代码" />
+                  <el-form-item label="对象代码" prop="objectCode">
+                    <el-input v-model="state.formData.objectCode" placeholder="请输入代码" />
                   </el-form-item>
-                  <el-form-item label="简要说明" prop="entityComment">
-                    <el-input v-model="state.formData.entityComment" type="textarea" rows="6" placeholder="请输入标题" />
+                  <el-form-item label="简要说明" prop="comment">
+                    <el-input v-model="state.formData.comment" type="textarea" rows="10" placeholder="请输入标题" />
                   </el-form-item>
                 </el-form>
               </div>

+ 54 - 40
src/views/daas/model/index.vue

@@ -5,10 +5,11 @@ meta:
 
 <script lang="ts" setup>
 import { ElMessage, ElMessageBox } from 'element-plus'
-import DSModel from './components/model.vue'
-import DSEntity from './components/entity.vue'
+import DSModel from './components/Model.vue'
+import DSObject from './components/object.vue'
 import { deleteModelById, getModelByList } from '@/api/model/modelApi'
-import { deleteEntityById, getEntityByList } from '@/api/model/entityApi'
+import { deleteObjectById, getObjectByList } from '@/api/model/objectApi'
+const tabbar = useTabbar()
 
 interface TreeNode {
   label: string
@@ -17,12 +18,12 @@ interface TreeNode {
   children?: TreeNode[]
 }
 
-interface Entity {
+interface Object {
   modelId: number
-  entityId: number
-  entityTitle: string
-  entityCode: string
-  entityComment: string
+  objectId: number
+  objectTitle: string
+  objectCode: string
+  comment: string
 }
 
 const state = reactive({
@@ -30,7 +31,7 @@ const state = reactive({
   keywords: '',
   models: [] as TreeNode[],
   curModelId: 0,
-  entityList: [] as Entity[],
+  objectList: [] as Object[],
   dialogState: 1,
   search: {
     keywords: '',
@@ -39,20 +40,17 @@ const state = reactive({
 
 const leftTreeRef = ref()
 const modelRef = ref()
-const entityTableRef = ref()
-const entityRef = ref()
-
-const getEntityList = async () => {
+const getObjectData = async () => {
   const curModelId = state.curModelId
   if (curModelId > 0) {
     state.loading = true
-    const { data } = await getEntityByList({ modelId: curModelId })
-    state.entityList = data
+    const { data } = await getObjectByList({ modelId: curModelId })
+    state.objectList = data
     state.loading = false
   }
 }
 
-const getModelList = async () => {
+const getModelData = async () => {
   const { data } = await getModelByList('')
   const children: TreeNode[] = []
   data.forEach((item: any) => {
@@ -73,27 +71,30 @@ const getModelList = async () => {
 
   if (data && data.length > 0) {
     state.curModelId = data[0].modelId
-    await getEntityList()
+    await getObjectData()
   }
 }
 
 onMounted(() => {
-  getModelList()
+  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 getModelList()
+    await getModelData()
     ElMessage.success({
       message: '删除成功',
       center: true,
@@ -101,33 +102,46 @@ const onModelDelete = (data: any) => {
   })
 }
 
+// 模型选择
 const onTreeNodeClick = (node: TreeNode) => {
   if (node.isLeaf) {
     state.curModelId = node.modelId
-    getEntityList()
+    getObjectData()
   }
 }
 
+const objectTableRef = ref()
+const objectRef = ref()
+
+// 检索对象
 const onSearch = () => {
-  getEntityList()
+  getObjectData()
 }
 
-const onEntityAdd = () => {
+// 新增对象
+const onObjectAdd = () => {
   if (state.curModelId > 0) {
-    entityRef.value.showAddModel(state.curModelId)
+    objectRef.value.showAddModel(state.curModelId)
   }
 }
 
-const onEntityEdit = (row: any) => {
-  const { modelId, entityId } = row
-  entityRef.value.showEditModel(modelId, entityId)
+// 编辑对象
+const onObjectEdit = (row: any) => {
+  tabbar.open({
+    name: 'daasObjectEdit',
+    params: {
+      modelId: row.modelId,
+      objectId: row.objectId,
+    },
+  })
 }
 
-const onEntityDelete = (row: any) => {
-  const { modelId, entityId, entityTitle } = row
-  ElMessageBox.confirm(`确认删除「${entityTitle}」吗?`, '确认信息').then(async () => {
-    await deleteEntityById(modelId, entityId)
-    await getEntityList()
+// 删除对象
+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,
@@ -202,7 +216,7 @@ const onEntityDelete = (row: any) => {
                 </template>
               </el-button>
 
-              <el-button plain size="default" @click.stop="onEntityAdd">
+              <el-button plain size="default" @click.stop="onObjectAdd">
                 <template #icon>
                   <el-icon>
                     <svg-icon name="i-ep:plus" />
@@ -213,21 +227,21 @@ const onEntityDelete = (row: any) => {
             </template>
           </tool-bar>
 
-          <el-table ref="entityTableRef" v-loading="state.loading" class="list-table" :data="state.entityList" border stripe highlight-current-row height="100%">
+          <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="entityTitle" label="名称" header-align="center" align="center" width="200" />
+            <el-table-column prop="objectTitle" label="名称" header-align="center" align="center" width="200" />
 
-            <el-table-column prop="entityCode" label="编码" header-align="center" align="center" width="150" />
+            <el-table-column prop="objectCode" label="编码" header-align="center" align="center" width="150" />
 
-            <el-table-column prop="entityComment" label="说明" header-align="center" align="left" />
+            <el-table-column prop="comment" label="说明" header-align="center" align="left" />
 
             <el-table-column label="操作" header-align="center" align="center" width="200">
               <template #default="scope">
-                <el-button type="primary" size="small" plain @click="onEntityEdit(scope.row)">
+                <el-button type="primary" size="small" plain @click="onObjectEdit(scope.row)">
                   编辑
                 </el-button>
-                <el-button type="danger" size="small" plain @click="onEntityDelete(scope.row)">
+                <el-button type="danger" size="small" plain @click="onObjectDelete(scope.row)">
                   删除
                 </el-button>
               </template>
@@ -236,8 +250,8 @@ const onEntityDelete = (row: any) => {
         </div>
       </LayoutContainer>
     </div>
-    <DSEntity ref="entityRef" @success="getEntityList" />
-    <DSModel ref="modelRef" @success="getModelList" />
+    <DSModel ref="modelRef" @success="getModelData" />
+    <DSObject ref="objectRef" @success="getObjectData" />
   </div>
 </template>
 

+ 408 - 0
src/views/daas/object/index.vue

@@ -0,0 +1,408 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup>
+import Sortable from 'sortablejs'
+import type { FormInstance, FormRules } from 'element-plus'
+import { ElMessage } from 'element-plus'
+import { createObject, getObjectById, updateObject } from '@/api/model/objectApi'
+import useSettingsStore from '@/store/modules/settings'
+
+const emit = defineEmits(['success'])
+
+const settingsStore = useSettingsStore()
+
+const route = useRoute()
+const router = useRouter()
+const tabbar = useTabbar()
+
+interface ObjectAttr {
+  attrId: number
+  attrTitle: string
+  attrCode: string
+  attrType: number
+  attrLength: number
+  attrDecimals: number
+  isNotNull: number
+  isFixed: number
+  attrComment: string
+  isPKey: number
+  sortNo: number
+}
+
+const dataType = [
+  { dataType: 1, dataName: '整数' },
+  { dataType: 2, dataName: '字符串' },
+  { dataType: 3, dataName: '小数' }]
+
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  formData: {
+    modelId: 0,
+    objectId: 0,
+    objectCode: '',
+    objectTitle: '',
+    comment: '',
+    attrs: [] as ObjectAttr[],
+  },
+  actionType: 1,
+})
+
+const formRef = ref<FormInstance>()
+const formRules = ref<FormRules>({
+  objectTitle: [
+    { required: true, message: '请输入标题', trigger: 'blur' },
+  ],
+  objectCode: [
+    { required: true, message: '请输入标题', trigger: 'blur' },
+  ],
+  comment: [
+    { required: false, message: '请输入描述', trigger: 'blur' },
+  ],
+})
+
+// 查询数据
+const getData = async (modelId: number, objectId: number) => {
+  state.loading = true
+  const { data } = await getObjectById(modelId, objectId)
+  state.loading = false
+  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, // 主键
+    attrComment: '', // 注释
+    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()
+  })
+})
+
+// 保存
+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('新增成功')
+        emit('success')
+      }
+      else {
+        await updateObject(state.formData)
+        state.dialogVisible = false
+        ElMessage.success('修改成功')
+        emit('success')
+      }
+    }
+  })
+}
+
+function resetFormData() {
+  state.formData = {
+    modelId: 0,
+    objectId: 0,
+    objectCode: '',
+    objectTitle: '',
+    comment: '',
+    attrs: [],
+  }
+}
+
+defineExpose({
+  showAddModel(modelId: number) {
+    resetFormData()
+    state.title = '新增对象'
+    state.actionType = 1
+    state.dialogVisible = true
+    state.formData.modelId = modelId
+  },
+  showEditModel(modelId: number, objectId: number) {
+    resetFormData()
+    state.title = '编辑对象'
+    state.actionType = 2
+    state.dialogVisible = true
+    getData(modelId, objectId)
+  },
+
+})
+
+function onCancel() {
+  goBack()
+}
+
+// 返回列表页
+function goBack() {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.close({ name: 'daasModel' })
+  }
+  else {
+    router.push({ name: 'daasModel' })
+  }
+}
+</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>
+            <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>
+
+        <div class="container">
+          <tool-bar>
+            <template #right>
+              <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选字典项" clearable style="width: 200px;" />
+              <el-button @click="getPermitData">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:search" />
+                  </el-icon>
+                </template>
+              </el-button>
+
+              <el-button type="primary" @click="onPermitAdd">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:plus" />
+                  </el-icon>
+                </template>
+              </el-button>
+            </template>
+          </tool-bar>
+
+          <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.attrComment" />
+              </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>
+        <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>
+      </LayoutContainer>
+    </div>
+  </div>
+</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);
+  transition: background-color 0.3s;
+
+  :deep(.el-table) {
+    height: 100%;
+    margin-top: 15px;
+
+    .el-table__row {
+      .index {
+        display: inline-block;
+      }
+
+      .delete {
+        display: none;
+      }
+
+      &:hover {
+        .index {
+          display: none;
+        }
+
+        .delete {
+          display: inline-block;
+        }
+      }
+
+      .el-tag.sortable,
+      .el-tag.sortable .el-icon {
+        cursor: s-resize;
+      }
+    }
+  }
+}
+</style>