houyaf пре 3 година
родитељ
комит
20c867052a

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
     "@vueuse/integrations": "^9.13.0",
     "axios": "^1.3.3",
     "dayjs": "^1.11.7",
+    "echarts": "^5.4.2",
     "element-plus": "^2.2.32",
     "eruda": "^2.11.2",
     "hotkeys-js": "^3.10.1",

+ 27 - 8
pnpm-lock.yaml

@@ -21,6 +21,7 @@ specifiers:
   bumpp: ^8.2.1
   cz-git: ^1.5.0
   dayjs: ^1.11.7
+  echarts: ^5.4.2
   element-plus: ^2.2.32
   eruda: ^2.11.2
   eslint: ^8.34.0
@@ -84,6 +85,7 @@ dependencies:
   '@vueuse/integrations': 9.13.0_2ejpgh2trzxqqykcalkxmj34uu
   axios: 1.3.3
   dayjs: 1.11.7
+  echarts: 5.4.2
   element-plus: 2.2.32_vue@3.2.47
   eruda: 2.11.2
   hotkeys-js: 3.10.1
@@ -2035,8 +2037,8 @@ packages:
       vue-i18n:
         optional: true
     dependencies:
-      '@intlify/message-compiler': 9.3.0-beta.16
-      '@intlify/shared': 9.3.0-beta.16
+      '@intlify/message-compiler': 9.3.0-beta.17
+      '@intlify/shared': 9.3.0-beta.17
       jsonc-eslint-parser: 1.4.1
       source-map: 0.6.1
       vue-i18n: 9.2.2_vue@3.2.47
@@ -2065,11 +2067,11 @@ packages:
       '@intlify/shared': 9.2.2
       source-map: 0.6.1
 
-  /@intlify/message-compiler/9.3.0-beta.16:
-    resolution: {integrity: sha512-CGQI3xRcs1ET75eDQ0DUy3MRYOqTauRIIgaMoISKiF83gqRWg93FqN8lGMKcpBqaF4tI0JhsfosCaGiBL9+dnw==}
+  /@intlify/message-compiler/9.3.0-beta.17:
+    resolution: {integrity: sha512-i7hvVIRk1Ax2uKa9xLRJCT57to08OhFMhFXXjWN07rmx5pWQYQ23MfX1xgggv9drnWTNhqEiD+u4EJeHoS5+Ww==}
     engines: {node: '>= 14'}
     dependencies:
-      '@intlify/shared': 9.3.0-beta.16
+      '@intlify/shared': 9.3.0-beta.17
       source-map: 0.6.1
     dev: true
 
@@ -2077,8 +2079,8 @@ packages:
     resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==}
     engines: {node: '>= 14'}
 
-  /@intlify/shared/9.3.0-beta.16:
-    resolution: {integrity: sha512-kXbm4svALe3lX+EjdJxfnabOphqS4yQ1Ge/iIlR8tvUiYRCoNz3hig1M4336iY++Dfx5ytEQJPNjIcknNIuvig==}
+  /@intlify/shared/9.3.0-beta.17:
+    resolution: {integrity: sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==}
     engines: {node: '>= 14'}
     dev: true
 
@@ -2098,7 +2100,7 @@ packages:
         optional: true
     dependencies:
       '@intlify/bundle-utils': 4.0.0_vue-i18n@9.2.2
-      '@intlify/shared': 9.3.0-beta.16
+      '@intlify/shared': 9.3.0-beta.17
       '@rollup/pluginutils': 4.2.1
       '@vue/compiler-sfc': 3.2.45
       debug: 4.3.4
@@ -4489,6 +4491,13 @@ packages:
       safer-buffer: 2.1.2
     dev: true
 
+  /echarts/5.4.2:
+    resolution: {integrity: sha512-2W3vw3oI2tWJdyAz+b8DuWS0nfXtSDqlDmqgin/lfzbkB01cuMEN66KWBlmur3YMp5nEDEEt5s23pllnAzB4EA==}
+    dependencies:
+      tslib: 2.3.0
+      zrender: 5.4.3
+    dev: false
+
   /ee-first/1.1.1:
     resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
     dev: true
@@ -9678,6 +9687,10 @@ packages:
     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
     dev: true
 
+  /tslib/2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+    dev: false
+
   /tslib/2.5.0:
     resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
     dev: true
@@ -10603,3 +10616,9 @@ packages:
     resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
     engines: {node: '>=10'}
     dev: true
+
+  /zrender/5.4.3:
+    resolution: {integrity: sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==}
+    dependencies:
+      tslib: 2.3.0
+    dev: false

+ 0 - 14
src/api/database/dataApi.ts

@@ -1,14 +0,0 @@
-import apiRequest from '@/network/http/apiRequest'
-
-/**
- * 分页获取数据库数据
- * @param data
- * @returns
- */
-export function getDBDataByPage(data: any) {
-  return apiRequest({
-    url: '/database/data/page',
-    method: 'GET',
-    params: data,
-  })
-}

+ 37 - 0
src/api/database/dbApi.ts

@@ -0,0 +1,37 @@
+import apiRequest from '@/network/http/apiRequest'
+
+/**
+ * 分页获取数据库数据
+ * @param objectCode
+ * @returns
+ */
+export function getTableDataByPage(objectCode: string) {
+  return apiRequest({
+    url: `/db/table/${objectCode}/page`,
+    method: 'GET',
+  })
+}
+
+/**
+ * 列表获取数据库数据
+ * @param objectCode
+ * @returns
+ */
+export function getTableDataByList(objectCode: string) {
+  return apiRequest({
+    url: `/db/table/${objectCode}`,
+    method: 'GET',
+  })
+}
+
+/**
+ * 列表获取数据库数据
+ * @param objectCode
+ * @returns
+ */
+export function getTableFieldsByList(objectCode: string) {
+  return apiRequest({
+    url: `/db/table/${objectCode}/fields`,
+    method: 'GET',
+  })
+}

+ 2 - 2
src/network/http/apiRequest.ts

@@ -29,7 +29,7 @@ const apiRequest = axios.create({
 
 apiRequest.interceptors.request.use(
   (request) => {
-    loading = ElLoading.service({ text: 'LOADING……' })
+    // loading = ElLoading.service({ text: 'LOADING……' })
     const userStore = useUserStore()
     // 设置请求头
     if (request.headers) {
@@ -53,7 +53,7 @@ apiRequest.interceptors.request.use(
 
 apiRequest.interceptors.response.use(
   (response: AxiosResponse<any>) => {
-    loading.close()
+    // loading.close()
     /**
      * 全局拦截请求发送后返回的数据,如果数据有报错则在这做全局的错误提示
      * 假设返回数据格式为:{ status: 0, error: '', data: '' }

+ 30 - 0
src/router/modules/dashboard.ts

@@ -0,0 +1,30 @@
+import type { RouteRecordRaw } from 'vue-router'
+
+const Layout = () => import('@/layouts/index.vue')
+
+const routes: RouteRecordRaw[] = [
+  {
+    path: '/dashboard',
+    component: Layout,
+    redirect: '/dashboard/index',
+    name: 'dashboard',
+    meta: {
+      title: '工作台',
+      icon: 'bx:bxs-component',
+    },
+    children: [
+      {
+        path: 'index',
+        name: 'dashboard-index',
+        component: () => import('@/views/dashboard/index.vue'),
+        meta: {
+          title: '工作台',
+          sidebar: false,
+          breadcrumb: false,
+          activeMenu: '/dashboard',
+        },
+      },
+    ],
+  },
+]
+export default routes

+ 10 - 0
src/router/routes.ts

@@ -1,6 +1,7 @@
 import { setupLayouts } from 'virtual:generated-layouts'
 import generatedRoutes from 'virtual:generated-pages'
 import type { RouteRecordRaw } from 'vue-router'
+import DashboardEntries from './modules/dashboard'
 import SystemEntries from './modules/system'
 import DatabaseEntries from './modules/database'
 import ApiEntries from './modules/api'
@@ -98,6 +99,15 @@ const systemRoutes: RouteRecordRaw[] = [
 const asyncRoutes: Route.recordMainRaw[] = [
   {
     meta: {
+      title: '首页',
+      icon: 'ep:monitor',
+    },
+    children: [
+      ...DashboardEntries,
+    ],
+  },
+  {
+    meta: {
       title: '模型',
       icon: 'bx:bxs-component',
     },

+ 2 - 2
src/settings.default.ts

@@ -25,8 +25,8 @@ const globalSettingsDefault: RecursiveRequired<Settings.all> = {
     routeBaseOn: 'frontend',
   },
   home: {
-    enable: true,
-    title: '主页',
+    enable: false,
+    title: '工作台',
   },
   layout: {
     widthMode: 'adaption',

+ 4 - 4
src/settings.ts

@@ -44,9 +44,9 @@ const globalSettings: Settings.all = {
     enableHotkeys: true,
     baseOn: 'frontend',
     menuMode: 'side',
-    switchMainMenuAndPageJump: false,
+    switchMainMenuAndPageJump: true,
     subMenuUniqueOpened: true,
-    subMenuOnlyOneHide: false,
+    subMenuOnlyOneHide: true,
     subMenuCollapse: false,
   },
   topbar: {
@@ -78,8 +78,8 @@ const globalSettings: Settings.all = {
     iframeCacheMax: 9,
   },
   home: {
-    enable: true,
-    title: '页',
+    enable: false,
+    title: '页',
   },
   navSearch: {
     enable: true,

+ 1 - 1
src/views/api/debug/index.vue

@@ -322,7 +322,7 @@ function goBack() {
 
             <div class="action-bottom">
               <el-button v-if="state.viewData.oamType <= 4" type="primary" @click="onGenerateData">
-                生成参数
+                生成API数据
               </el-button>
               <el-button type="primary" @click="onExecute">
                 立即执行

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

@@ -183,7 +183,7 @@ defineExpose({
       <el-table-column label="数据对象" header-align="center" align="center">
         <template #default="scope">
           <model-object-selector v-if="scope.row.isEdit" v-model="scope.row.mappingObjectId" :model-id="scope.row.mappingModelId" size="small" />
-          <span v-else>{{ scope.row.mappingObjectTitle }}</span>
+          <span v-else>{{ scope.row.mappingObjectTitle }}: {{ scope.row.mappingObjectCode }}</span>
         </template>
       </el-table-column>
 

+ 6 - 5
src/views/biz/oam_params/index.vue

@@ -100,13 +100,13 @@ function onParamAdd() {
     bizId,
     objectId,
     oamId: state.conditions.oamId,
-    paramsId: 0,
+    paramId: 0,
     logicalOperator: 'AND',
-    paramsTarget: '', // 名称
+    paramName: '', // 名称
     compareOperator: '=', // 代码
-    paramsValue: '',
-    paramsType: 0,
-    paramsRemark: '',
+    paramValue: '',
+    paramType: 0,
+    paramRemark: '',
     isRuled: 0,
     ruleId: 0,
     sortNo: 0,
@@ -148,6 +148,7 @@ async function onParamSave(row: any) {
     state.dialogVisible = false
     ElMessage.success('修改成功')
   }
+  getData()
 }
 defineExpose({
   showData(oamId: number) {

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

@@ -125,6 +125,7 @@ async function onRuleSave(row: any) {
     state.dialogVisible = false
     ElMessage.success('修改成功')
   }
+  getData()
 }
 
 onMounted(() => {

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

@@ -59,19 +59,11 @@ onMounted(() => {
 })
 
 const activeTab = ref('first')
-
-const handleClick = (tab: TabsPaneContext, event: Event) => {
-  // console.log(tab, event)
-}
-
 const onObjectEdit = () => {
   objectRef.value.showEditModel(bizId, objectId)
 }
 
-const size = ref('')
 const bizObjectAttrList = ref()
-const bizObjectMappingList = ref()
-
 // 返回列表页
 function goBack() {
   if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
@@ -98,7 +90,7 @@ function goBack() {
 
     <page-main>
       <div class="container">
-        <el-tabs v-model="activeTab" class="demo-tabs" @tab-click="handleClick">
+        <el-tabs v-model="activeTab">
           <el-tab-pane label="基本信息" name="first">
             <el-descriptions
               :title="state.objectData.objectTitle"

+ 8 - 5
src/views/biz/object_attr/index.vue

@@ -21,6 +21,9 @@ const props = withDefaults(defineProps<{
   objectId: 0,
 })
 
+const bizId = props.bizId
+const objectId = props.objectId
+
 interface ObjectAttr {
   bizId: number
   objectId: number
@@ -46,8 +49,8 @@ const state = reactive({
     keywords: '',
   },
   conditions: {
-    bizId: props.bizId,
-    objectId: props.objectId,
+    bizId,
+    objectId,
   },
   attrs: [] as ObjectAttr[],
   actionType: 1,
@@ -72,7 +75,6 @@ const formRules = reactive<FormRules>({
 // 查询数据
 const getData = async () => {
   state.loading = true
-  const { bizId, objectId } = state.conditions
   const { data } = await getObjectAttrListById(bizId, objectId)
   state.loading = false
   state.attrs = data
@@ -86,8 +88,8 @@ const attrsTableKey = ref(0)
 
 function onAttrAdd() {
   state.attrs.push({
-    bizId: props.bizId,
-    objectId: props.objectId,
+    bizId,
+    objectId,
     attrId: 0,
     attrTitle: '', // 名称
     attrCode: '', // 代码
@@ -135,6 +137,7 @@ async function onAttrSave(row: any) {
     state.dialogVisible = false
     ElMessage.success('修改成功')
   }
+  getData()
 }
 
 onMounted(() => {

+ 331 - 111
src/views/dashboard/index.vue

@@ -1,53 +1,302 @@
 <route lang="yaml">
-name: home
+name: dashboard
 meta: ''
-title: 主页
+title: dashboard
 icon: ant-design:home-twotone
 </route>
 
-<script lang="ts" setup>
-function open(url: string) {
-  window.open(url, '_blank')
-}
+<script lang="ts" setup name="dashboard">
+import * as Echarts from 'echarts'
 
-const fantasticTemplateInfo = ref({
-  feature: [
-    '支持 TypeScript',
-    '默认集成 vue-router 和 pinia',
-    '全局 SCSS 资源引入',
-    '全局组件自动注册',
-    '支持 SVG 图标,CSS 精灵图自动合成',
-    '支持 mock 数据,可摆脱后端束缚独立开发',
-    '支持 gzip / brotli 优化项目体积,提高加载速度',
-    '结合 IDE 插件、ESlint 、stylelint 、Git 钩子,轻松实现团队代码规范',
-  ],
-})
+const chart1Ref = ref()
+const chart2Ref = ref()
+const chart3Ref = ref()
+const chart4Ref = ref()
+let chart1: any
+let chart2: any
+let chart3: any
+let chart4: any
 
-const fantasticAdminInfo = ref({
-  imageVisible: false,
-  index: 0,
-  data: [
-    'https://hooray.gitee.io/fantastic-admin/preview1.png',
-    'https://hooray.gitee.io/fantastic-admin/preview2.png',
-    'https://hooray.gitee.io/fantastic-admin/preview3.png',
-    'https://hooray.gitee.io/fantastic-admin/preview4.png',
-    'https://hooray.gitee.io/fantastic-admin/preview5.png',
-    'https://hooray.gitee.io/fantastic-admin/preview6.png',
-  ],
+onMounted(() => {
+  initChart1()
+  initChart2()
+  initChart3()
+  initChart4()
+  window.addEventListener('resize', () => {
+    chart1.resize()
+    chart2.resize()
+    chart3.resize()
+    chart4.resize()
+  })
 })
 
-const oneStepAdminInfo = ref({
-  imageVisible: false,
-  index: 0,
-  data: [
-    'https://hooray.gitee.io/one-step-admin/preview1.png',
-    'https://hooray.gitee.io/one-step-admin/preview2.png',
-    'https://hooray.gitee.io/one-step-admin/preview3.png',
-    'https://hooray.gitee.io/one-step-admin/preview4.png',
-    'https://hooray.gitee.io/one-step-admin/preview5.png',
-    'https://hooray.gitee.io/one-step-admin/preview6.png',
-  ],
-})
+function initChart1() {
+  chart1 = Echarts.init(chart1Ref.value)
+  // 配置数据
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: { // 坐标轴指示器,坐标轴触发有效
+        type: 'shadow', // 默认为直线,可选为:'line' | 'shadow'
+      },
+    },
+    legend: {
+      data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎', '百度', '谷歌', '必应', '其他'],
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true,
+    },
+    xAxis: [
+      {
+        type: 'category',
+        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+      },
+    ],
+    yAxis: [
+      {
+        type: 'value',
+      },
+    ],
+    series: [
+      {
+        name: '直接访问',
+        type: 'bar',
+        data: [320, 332, 301, 334, 390, 330, 320],
+      },
+      {
+        name: '邮件营销',
+        type: 'bar',
+        stack: '广告',
+        data: [120, 132, 101, 134, 90, 230, 210],
+      },
+      {
+        name: '联盟广告',
+        type: 'bar',
+        stack: '广告',
+        data: [220, 182, 191, 234, 290, 330, 310],
+      },
+      {
+        name: '视频广告',
+        type: 'bar',
+        stack: '广告',
+        data: [150, 232, 201, 154, 190, 330, 410],
+      },
+      {
+        name: '搜索引擎',
+        type: 'bar',
+        data: [862, 1018, 964, 1026, 1679, 1600, 1570],
+        markLine: {
+          lineStyle: {
+            type: 'dashed',
+          },
+          data: [
+            [{ type: 'min' }, { type: 'max' }],
+          ],
+        },
+      },
+      {
+        name: '百度',
+        type: 'bar',
+        barWidth: 5,
+        stack: '搜索引擎',
+        data: [620, 732, 701, 734, 1090, 1130, 1120],
+      },
+      {
+        name: '谷歌',
+        type: 'bar',
+        stack: '搜索引擎',
+        data: [120, 132, 101, 134, 290, 230, 220],
+      },
+      {
+        name: '必应',
+        type: 'bar',
+        stack: '搜索引擎',
+        data: [60, 72, 71, 74, 190, 130, 110],
+      },
+      {
+        name: '其他',
+        type: 'bar',
+        stack: '搜索引擎',
+        data: [62, 82, 91, 84, 109, 110, 120],
+      },
+    ],
+  }
+  // 传入数据
+  chart1.setOption(option)
+}
+function initChart2() {
+  chart2 = Echarts.init(chart2Ref.value)
+  // 配置数据
+  const option = {
+    title: {
+      text: '堆叠区域图',
+    },
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross',
+        label: {
+          backgroundColor: '#6a7985',
+        },
+      },
+    },
+    legend: {
+      data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
+    },
+    toolbox: {
+      feature: {
+        saveAsImage: {},
+      },
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true,
+    },
+    xAxis: [
+      {
+        type: 'category',
+        boundaryGap: false,
+        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+      },
+    ],
+    yAxis: [
+      {
+        type: 'value',
+      },
+    ],
+    series: [
+      {
+        name: '邮件营销',
+        type: 'line',
+        stack: '总量',
+        areaStyle: {},
+        data: [120, 132, 101, 134, 90, 230, 210],
+      },
+      {
+        name: '联盟广告',
+        type: 'line',
+        stack: '总量',
+        areaStyle: {},
+        data: [220, 182, 191, 234, 290, 330, 310],
+      },
+      {
+        name: '视频广告',
+        type: 'line',
+        stack: '总量',
+        areaStyle: {},
+        data: [150, 232, 201, 154, 190, 330, 410],
+      },
+      {
+        name: '直接访问',
+        type: 'line',
+        stack: '总量',
+        areaStyle: {},
+        data: [320, 332, 301, 334, 390, 330, 320],
+      },
+      {
+        name: '搜索引擎',
+        type: 'line',
+        stack: '总量',
+        areaStyle: {},
+        data: [820, 932, 901, 934, 1290, 1330, 1320],
+      },
+    ],
+  }
+  // 传入数据
+  chart2.setOption(option)
+}
+function initChart3() {
+  chart3 = Echarts.init(chart3Ref.value)
+  // 配置数据
+  const option = {
+    title: {
+      text: '某站点用户访问来源',
+      subtext: '纯属虚构',
+      left: 'center',
+    },
+    tooltip: {
+      trigger: 'item',
+      formatter: '{a} <br/>{b} : {c} ({d}%)',
+    },
+    legend: {
+      orient: 'vertical',
+      left: 'left',
+      data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'],
+    },
+    series: [
+      {
+        name: '访问来源',
+        type: 'pie',
+        radius: '55%',
+        center: ['50%', '60%'],
+        data: [
+          { value: 335, name: '直接访问' },
+          { value: 310, name: '邮件营销' },
+          { value: 234, name: '联盟广告' },
+          { value: 135, name: '视频广告' },
+          { value: 1548, name: '搜索引擎' },
+        ],
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowOffsetX: 0,
+            shadowColor: 'rgba(0, 0, 0, 0.5)',
+          },
+        },
+      },
+    ],
+  }
+  // 传入数据
+  chart3.setOption(option)
+}
+function initChart4() {
+  chart4 = Echarts.init(chart4Ref.value)
+  // 配置数据
+  const option = {
+    title: {
+      text: '基础雷达图',
+    },
+    legend: {
+      data: ['预算分配(Allocated Budget)', '实际开销(Actual Spending)'],
+    },
+    radar: {
+      // shape: 'circle',
+      indicator: [
+        { name: '销售(sales)', max: 6500 },
+        { name: '管理(Administration)', max: 16000 },
+        { name: '信息技术(Information Techology)', max: 30000 },
+        { name: '客服(Customer Support)', max: 38000 },
+        { name: '研发(Development)', max: 52000 },
+        { name: '市场(Marketing)', max: 25000 },
+      ],
+    },
+    series: [{
+      name: '预算 vs 开销(Budget vs spending)',
+      type: 'radar',
+      data: [
+        {
+          value: [4200, 3000, 20000, 35000, 50000, 18000],
+          name: '预算分配(Allocated Budget)',
+        },
+        {
+          value: [5000, 14000, 28000, 26000, 42000, 21000],
+          name: '实际开销(Actual Spending)',
+        },
+      ],
+    }],
+  }
+  // 传入数据
+  chart4.setOption(option)
+}
+
+function open(url: string) {
+  window.open(url, '_blank')
+}
 </script>
 
 <template>
@@ -56,9 +305,8 @@ const oneStepAdminInfo = ref({
       <template #content>
         <div>
           <div style="margin-bottom: 5px;">
-            这是一款<b class="text-emphasis">开箱即用</b>的中后台框架,同时它也经历过数十个真实项目的技术沉淀,确保框架在开发中可落地、可使用、可维护
+            <span>打破数据孤岛, 盘活数据资产价值</span>
           </div>
-          <div>注:在作者就职过的公司,本框架已在电商、直播、OA、ERP等多个不同领域的中后台系统中应用并稳定运行</div>
         </div>
       </template>
       <el-button-group>
@@ -96,81 +344,53 @@ const oneStepAdminInfo = ref({
       </el-button-group>
     </page-header>
 
-    <el-row :gutter="20" style="margin: 0 10px;">
-      <el-col :lg="8">
-        <page-main style="margin: 0;">
-          <div class="ecology vue">
-            <div class="main">
-              <h1>数据中心</h1>
-              <h2>集中式管理数据资产</h2>
-            </div>
-            <el-divider border-style="dashed" />
-            <ul>
-              <li v-for="item in fantasticTemplateInfo.feature" :key="item">
-                {{ item }}
-              </li>
-            </ul>
-          </div>
+    <page-main>
+      <el-row :gutter="5">
+        <el-col :md="4">
+          <ColorfulCard header="数据模型" :num="123" />
+        </el-col>
+        <el-col :md="4">
+          <ColorfulCard header="业务模型" :num="12323" />
+        </el-col>
+        <el-col :md="4">
+          <ColorfulCard header="API数目" :num="123" />
+        </el-col>
+        <el-col :md="4">
+          <ColorfulCard header="业务应用" :num="123" />
+        </el-col>
+        <el-col :md="4">
+          <ColorfulCard header="客户量" :num="123" />
+        </el-col>
+        <el-col :md="4">
+          <ColorfulCard header="访问量" :num="123" />
+        </el-col>
+      </el-row>
+    </page-main>
+
+    <el-row :gutter="20" style="margin: -10px 10px;">
+      <el-col :md="12">
+        <page-main title="柱状图" style="margin: 10px 0;">
+          <div ref="chart1Ref" style="width: 100%; height: 400px;" />
         </page-main>
       </el-col>
-      <el-col :lg="8">
-        <page-main style="margin: 0;">
-          <div class="ecology fa">
-            <div class="main">
-              <h1>业务中心</h1>
-              <h2>集中式管理业务中心数据</h2>
-            </div>
-            <el-divider border-style="dashed" />
-            <el-carousel trigger="click" indicator-position="none" :interval="5000" height="250px">
-              <el-carousel-item v-for="(item, index) in fantasticAdminInfo.data" :key="item">
-                <el-image :src="item" fit="cover" style="cursor: pointer; width: 100%; height: 250px; margin: auto;" @click="fantasticAdminInfo.imageVisible = true; fantasticAdminInfo.index = index" />
-              </el-carousel-item>
-            </el-carousel>
-            <el-image-viewer v-if="fantasticAdminInfo.imageVisible" :url-list="fantasticAdminInfo.data" :initial-index="fantasticAdminInfo.index" @close="fantasticAdminInfo.imageVisible = false" />
-          </div>
+      <el-col :md="12">
+        <page-main title="折线图" style="margin: 10px 0;">
+          <div ref="chart2Ref" style="width: 100%; height: 400px;" />
         </page-main>
       </el-col>
-
-      <el-col :lg="8">
-        <page-main style="margin: 0;">
-          <div class="ecology osa">
-            <div class="main">
-              <h1>应用中心</h1>
-              <h2>集中心管理应用系统</h2>
-            </div>
-            <el-divider border-style="dashed" />
-            <el-carousel trigger="click" indicator-position="none" :interval="5000" height="250px">
-              <el-carousel-item v-for="(item, index) in oneStepAdminInfo.data" :key="item">
-                <el-image :src="item" fit="cover" style="cursor: pointer; width: 100%; height: 250px; margin: auto;" @click="oneStepAdminInfo.imageVisible = true; oneStepAdminInfo.index = index" />
-              </el-carousel-item>
-            </el-carousel>
-            <el-image-viewer v-if="oneStepAdminInfo.imageVisible" :url-list="oneStepAdminInfo.data" :initial-index="oneStepAdminInfo.index" @close="oneStepAdminInfo.imageVisible = false" />
-          </div>
+    </el-row>
+    <el-row :gutter="20" style="margin: 0 10px;">
+      <el-col :md="12">
+        <page-main title="饼图" style="margin: 10px 0;">
+          <div ref="chart3Ref" style="width: 100%; height: 400px;" />
+        </page-main>
+      </el-col>
+      <el-col :md="12">
+        <page-main title="雷达图" style="margin: 10px 0;">
+          <div ref="chart4Ref" style="width: 100%; height: 400px;" />
         </page-main>
       </el-col>
     </el-row>
-    <page-main title="应用场景">
-      <div class="question">
-        <ol class="answer">
-          <li><span>没有前端开发人员的小型公司。</span>据了解,有些小型公司没有前端开发人员,而这些公司在开发中后台系统的时候,直接要求后端开发人员来进行开发工作。所以借助 Vue 的易学习易上手特性,再加上本框架的加持,可以让后端开发人员能在短时间内转型成为全栈开发。</li>
-          <li><span>前端开发人员不足的中小型公司。</span>根据招聘网站统计,几乎所有公司都缺前端,其中有很大一部分中小型公司标配只有1-2名前端开发人员,而这些公司在开发中后台系统的时候,如果能有一套现成的中后台框架系统,不仅能提高项目开发效率,同时还大大减轻前端开发人员工作压力。</li>
-          <li><span>项目型公司。</span>特点为项目多,周期短,甲方对页面布局和主题风格有绝对话语权,而通过专业版提供的布局和主题风格,可应对绝大部分甲方需求,并且可自定义扩展主题风格的样式,实现高度定制化。</li>
-          <li><span>产品型公司。</span>产品型公司最担心的就是产品开发中代码不可控的因素,本框架除了提供完善的开发文档和代码注释外,作者还提供一对一的技术支持,确保开发人员尽可能理解整套框架源码的方方面面,为产品保驾护航。</li>
-          <li><span>个人开发者。</span>手里有一套可高度定制化的中后台框架,什么项目都不用担心啦~</li>
-        </ol>
-      </div>
-    </page-main>
-    <page-main title="优势">
-      <div class="question">
-        <ol class="answer">
-          <li><span>作者拥有10年+的前后端开发经验。</span>部分框架的作者由于缺少后端开发经验,可能会在设计框架的时候,很少或者没有考虑后端的实现逻辑,导致框架在实际使用中,业务场景无法落地,开发人员得通过修改源码自行实现业务。</li>
-          <li><span>经历过数十个真实项目的打磨。</span>没用在真实业务场景中使用过的框架都是纸飞机,哪怕它提供的演示功能特别华丽。而本框架在作者就职的公司,已经稳定应用在电商、直播、OA、CRM、ERP等多个不同领域的中后台系统中。</li>
-          <li><span>丰富的组件库。</span>除了支持 ElementUI / Element Plus 自带的组件外,框架还扩充了部分业务组件,以及第三方插件。借助以往的项目经验,提供最佳实践方案,方便开发人员直接使用。</li>
-          <li><span>持续更新的业务应用静态页面。</span>通过项目积累,沉淀出数十个业务应用的静态页面,做到开发人员拿来即可使用,极大提升开发效率的同时,还省去了产品和设计人员的工作。</li>
-          <li><span>长期维护。</span>无论是免费的基础版,还是付费的专业版,均提供长期维护。区别在于基础版侧重于稳定性维护,主要在修复 bug ,不定期增加新特性;专业版侧重于新特性开发,在确保稳定的基础上,会长期深挖中后台系统框架,持续产出可落地的特性或开发规范。</li>
-        </ol>
-      </div>
-    </page-main>
   </div>
 </template>
 

+ 1 - 1
src/views/database/data/components/sql_data.vue

@@ -1,6 +1,6 @@
 <script lang="ts" setup name="sql_data">
 import { cloneDeep } from 'lodash-es'
-import { getTableByList, getTableFieldsByList } from '@/api/database/dataApi'
+import { getTableByList, getTableFieldsByList } from '@/api/database/dbApi'
 
 const state = reactive({
   layoutMode: 'table',

+ 3 - 2
src/views/database/data/components/table_data.vue

@@ -1,11 +1,12 @@
 <script lang="ts" setup name="table_data">
-import { getTableByList, getTableFieldsByList } from '@/api/database/dataApi'
+import { getTableDataByList, getTableFieldsByList } from '@/api/database/dbApi'
 
 interface TreeNode {
   label: string
   isLeaf: boolean
   children?: TreeNode[]
 }
+
 const state = reactive({
   layoutMode: 'table',
   sqlStatement: '',
@@ -25,7 +26,7 @@ function handleTreeNodeClick(data: any) {
 }
 
 const init = async () => {
-  const { data } = await getTableByList()
+  const { data } = await getTableDataByList()
   const children: TreeNode[] = []
   data.forEach((item: any) => {
     children.push({

+ 36 - 27
src/views/database/data/index.vue

@@ -5,11 +5,13 @@ meta:
 
 <script lang="ts" setup>
 import { getModelTreeData } from '@/api/model/modelApi'
+import { getTableDataByList, getTableFieldsByList } from '@/api/database/dbApi'
 
 interface TreeNode {
   label: string
   isLeaf: boolean
   value: number
+  objectCode: string
   children?: TreeNode[]
 }
 
@@ -19,7 +21,10 @@ const state = reactive({
   treeData: [] as TreeNode[],
   curModelId: 0,
   curObjectId: 0,
+  curObjectCode: '',
   dialogState: 1,
+  tableFields: [],
+  tableDataList: [],
   search: {
     keywords: '',
   },
@@ -38,12 +43,33 @@ const getTreeData = async () => {
     if (firstModel.children && firstModel.children.size > 0) {
       const firstObject = firstModel.children[0]
       nextTick(() => {
-        // leftTreeRef.value.setCurrentKey(firstObject.value)
+        leftTreeRef.value.setCurrentKey(firstObject.value)
       })
     }
   }
 }
 
+const getTableFields = async () => {
+  const tableName = state.curObjectCode
+  const res = await getTableFieldsByList(tableName)
+  state.tableFields = res.data
+}
+
+const getTableData = async () => {
+  const tableName = state.curObjectCode
+  const res = await getTableDataByList(tableName)
+  state.tableDataList = res.data
+}
+
+const getData = () => {
+  if (state.curObjectCode !== '') {
+    state.loading = true
+    getTableFields()
+    getTableData()
+    state.loading = false
+  }
+}
+
 onMounted(() => {
   getTreeData()
 })
@@ -52,16 +78,20 @@ onMounted(() => {
 const onTreeNodeClick = (node: TreeNode) => {
   if (node.isLeaf) {
     state.curObjectId = node.value
+    state.curObjectCode = node.objectCode
+    getTableFields()
+    getTableData()
   }
   else {
     state.curModelId = node.value
   }
 }
 
-const objectTableRef = ref()
+const tableDataRef = ref()
 
 // 检索对象
 const onSearch = () => {
+  getData()
 }
 </script>
 
@@ -108,33 +138,12 @@ const onSearch = () => {
             </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="tableDataRef" v-loading="state.loading" class="list-table" :data="state.tableDataList" 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">
-              <template #default="scope">
-                <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>
+            <template v-for="(field, index) in state.tableFields" :key="index">
+              <el-table-column :prop="field.attrCode" :label="field.attrCode" header-align="center" align="center" />
+            </template>
           </el-table>
         </div>
       </LayoutContainer>

+ 3 - 14
src/views/model/object/index.vue

@@ -4,8 +4,7 @@ meta:
 </route>
 
 <script lang="ts" setup name="modelObjectDetails">
-import type { TabsPaneContext } from 'element-plus'
-import ModelObjectAttrListComponent from '../attr/index.vue'
+import ModelObjectAttrListComponent from '../object_attr/index.vue'
 import ModelObjectComponent from './components/ModelObject.vue'
 import { getObjectById } from '@/api/model/modelObjectApi'
 import useSettingsStore from '@/store/modules/settings'
@@ -49,24 +48,14 @@ onMounted(() => {
 })
 
 const activeTab = ref('first')
-
-const handleClick = (tab: TabsPaneContext, event: Event) => {
-  // console.log(tab, event)
-}
-
 const modelObjectRef = ref()
 
-// 检索对象
-const onSearch = () => {
-  getObjectData()
-}
-
 // 编辑对象
 const onObjectEdit = (row: { modelId: number; objectId: number }) => {
   modelObjectRef.value.showEditModel(modelId, objectId)
 }
 
-const size = ref('')
+// Object Attrs
 const modelObjectAttrList = ref()
 
 // 返回列表页
@@ -95,7 +84,7 @@ function goBack() {
 
     <page-main>
       <div class="container">
-        <el-tabs v-model="activeTab" class="demo-tabs" @tab-click="handleClick">
+        <el-tabs v-model="activeTab" class="demo-tabs">
           <el-tab-pane label="基本信息" name="first">
             <el-descriptions
               :title="state.objectData.objectTitle"

+ 9 - 1
src/views/model/attr/index.vue → src/views/model/object_attr/index.vue

@@ -23,7 +23,12 @@ const props = withDefaults(defineProps<{
   objectId: 0,
 })
 
+const modelId = props.modelId
+const objectId = props.objectId
+
 interface ObjectAttr {
+  modelId: number
+  objectId: number
   attrId: number
   attrTitle: string
   attrCode: string
@@ -91,6 +96,8 @@ const attrsTableRef = ref()
 const attrsTableKey = ref(0)
 function onAttrAdd() {
   state.attrs.push({
+    modelId,
+    objectId,
     attrId: 0,
     attrTitle: '', // 名称
     attrCode: '', // 代码
@@ -113,10 +120,10 @@ function onAttrEdit(row: any) {
 }
 
 async function onAttrDelete(row: any) {
-  const { modelId, objectId } = state.search
   const attrId = row.attrId
   await deleteObjectAttrById(modelId, objectId, attrId)
   ElMessage.success('删除成功')
+  getData()
 }
 
 function onAttrCancel(row: any, index: number) {
@@ -139,6 +146,7 @@ async function onAttrSave(row: any) {
     state.dialogVisible = false
     ElMessage.success('修改成功')
   }
+  await getData()
 }
 
 onMounted(() => {