CzRger пре 2 месеци
родитељ
комит
dce5fe3744

+ 1 - 0
src/components/czr-ui/CzrContent.vue

@@ -385,6 +385,7 @@ onMounted(() => {
   .field-column {
     display: flex;
     justify-content: space-between;
+    gap: 16px;
     .fc-block {
       .fcb-title {
         font-size: 14px;

+ 10 - 0
src/router/modules/big-model.ts

@@ -34,6 +34,16 @@ const BigModelRouter = [
     },
   },
   {
+    path: '/app/:id/monitor',
+    name: 'a36ccd46-f399-4bf6-8db9-157ca8d3e8c8',
+    component: () => import('@/views/manage/app/monitor/index.vue'),
+    meta: {
+      title: '应用监测',
+      single: true,
+      root: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe',
+    },
+  },
+  {
     path: '/knowledge',
     name: '4342bfff-1ea8-4f4c-b562-3cdc1fde116f',
     component: () => import('@/views/manage/knowledge/index.vue'),

+ 12 - 0
src/style/tailwind.css

@@ -1 +1,13 @@
 @import 'tailwindcss';
+
+
+/*text-xs	font-size: 0.75rem; !* 12px *!*/
+/*          line-height: 1rem;*/
+/*text-sm	font-size: 0.875rem; !* 14px *!*/
+/*          line-height: 1.25rem;*/
+/*text-base	font-size: 1rem; !* 16px *!*/
+/*          line-height: 1.5rem;*/
+/*text-lg	font-size: 1.125rem; !* 18px *!*/
+/*          line-height: 1.75rem;*/
+/*text-xl	font-size: 1.25rem; !* 20px *!*/
+/*          line-height: 1.75rem;*/

+ 11 - 1
src/views/manage/app/index.vue

@@ -51,7 +51,17 @@
         :col="4"
       >
         <template #model="{ row, index }">
-          <div class="model __hover">
+          <div
+            class="model __hover"
+            @click="
+              $router.push({
+                name: 'a36ccd46-f399-4bf6-8db9-157ca8d3e8c8',
+                params: {
+                  id: row.id,
+                },
+              })
+            "
+          >
             <div class="flex">
               <img
                 src="@/assets/images/app-default-logo.png"

+ 264 - 0
src/views/manage/app/monitor/index.vue

@@ -0,0 +1,264 @@
+<template>
+  <div class="flex h-full w-full flex-col">
+    <div
+      class="__hover flex items-center text-sm text-[var(--czr-main-color)]"
+      @click="toBack"
+    >
+      <SvgIcon
+        name="czr_arrow"
+        rotate="180"
+        size="13"
+        :active="true"
+        class="mr-1"
+      />应用中心
+    </div>
+    <div class="bm-main-box mt-4">
+      <div class="mb-4 flex items-center">
+        <img
+          src="@/assets/images/app-default-logo.png"
+          class="mr-[var(--czr-gap)] h-[3.25rem] w-[3.25rem]"
+        />
+        <div>
+          <div class="flex items-center">
+            <div class="text-[20px] font-bold">应用名称</div>
+            <div
+              class="ml-1 rounded-2xl bg-gradient-to-r from-[#C3E3FC] to-[#86C4FF] px-3 py-1 text-xs text-[#0876FF]"
+            >
+              简洁
+            </div>
+            <div
+              class="ml-1 rounded-xs bg-[#5AD8A6] px-2.5 py-1 text-xs text-white"
+            >
+              已发布
+            </div>
+          </div>
+          <div class="mt-2 text-sm text-[#6F7889]">(用户名称)</div>
+        </div>
+        <CzrButton
+          :type="state.tab == 1 ? 'primary' : 'normal'"
+          title="分析"
+          class="ml-auto"
+          @click="state.tab = 1"
+        />
+        <CzrButton
+          :type="state.tab == 2 ? 'primary' : 'normal'"
+          title="日志"
+          class="ml-4"
+          @click="state.tab = 2"
+        />
+      </div>
+      <template v-if="state.tab == 1"></template>
+      <template v-else-if="state.tab == 2">
+        <div class="log flex-1">
+          <CzrContent
+            v-model:tableHead="state.query.head"
+            @handleReset="onReset"
+            @handleSearch="onSearch"
+          >
+            <template #table>
+              <CzrTable
+                v-loading="state.query.loading"
+                :data="state.query.result.data"
+                :head="state.query.head"
+                :total="state.query.result.total"
+                :page="state.query.page.pageNum"
+                :pageSize="state.query.page.pageSize"
+                @handlePage="onPage"
+                v-model:selected="state.query.selected"
+              >
+              </CzrTable>
+            </template>
+          </CzrContent>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  computed,
+  getCurrentInstance,
+  inject,
+  onMounted,
+  reactive,
+  ref,
+  watch,
+} from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
+import lineChart from './line-chart.vue'
+import pieChart from './pie-chart.vue'
+import { pluginDetail } from '@/api/modules/model'
+import { useDictionaryStore } from '@/stores'
+import { YMDHms } from '@/utils/czr-util'
+import { debounce } from 'lodash'
+import { documentGetDocumentsByPage } from '@/api/modules/knowledge/document'
+
+const DictionaryStore = useDictionaryStore()
+const route = useRoute()
+const router = useRouter()
+const emit = defineEmits([])
+const props = defineProps({})
+const { proxy }: any = getCurrentInstance()
+const state: any = reactive({
+  ID: route.params.id,
+  tab: 2,
+  detail: {},
+  texts: {
+    keyword: '',
+  },
+  query: {
+    init: false,
+    loading: false,
+    head: [
+      { value: 'p1', label: '消息ID', show: false },
+      { value: 'p1', label: '会话ID', show: false },
+      { value: 'p1', label: '用户ID', show: false },
+      { value: 'p1', label: '意图识别', show: true },
+      { value: 'p1', label: '输入', show: true },
+      { value: 'p1', label: '输出', show: true },
+      { value: 'p1', label: '输入Token', show: true },
+      { value: 'p1', label: '输出Token', show: true },
+      { value: 'p1', label: '请求时间', show: true },
+      { value: 'p1', label: '整体耗时', show: true },
+      { value: 'p1', label: '反馈', show: true },
+      { value: 'p1', label: '回答效果', show: true },
+      { value: 'p1', label: '渠道', show: true },
+      {
+        value: 'caozuo',
+        label: '操作',
+        show: true,
+        width: 100,
+        fixed: 'right',
+        popover: false,
+      },
+    ],
+    page: {
+      pageNum: 1,
+      pageSize: 20,
+    },
+    form: {},
+    formReal: {},
+    result: {
+      total: 0,
+      data: [],
+    },
+    selected: [],
+  },
+})
+const initDetail = () => {
+  if (state.ID) {
+    state.detail = {}
+    // pluginDetail(state.ID)
+    //   .then(({ data }: any) => {
+    //     state.detail = data
+    //   })
+    //   .catch(() => {})
+    //   .finally(() => {})
+    initStatistic()
+  } else {
+    router.push({ name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe' })
+  }
+}
+
+const toBack = () => {
+  router.push({
+    name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe',
+  })
+}
+const initStatistic = () => {
+  // state.statistic.table.query.loading = true
+  // setTimeout(() => {
+  //   const arr: any = []
+  //   for (let i = 1; i <= 100; i++) {
+  //     arr.push({
+  //       id: i + '',
+  //       p1: '文件' + i + '.md',
+  //     })
+  //   }
+  //   state.statistic.table.query.result.data = arr
+  //   // setTimeout(() => {
+  //   //   state.query.selected = [arr[1], arr[5]]
+  //   // })
+  //   state.statistic.table.query.loading = false
+  // }, 1000)
+}
+
+const setTexts = debounce((v) => {
+  // state.query.form[k] = v
+}, 1000)
+watch(
+  () => state.texts,
+  (n) => {
+    setTexts(n)
+  },
+)
+watch(
+  () => state.query.form,
+  (n) => {
+    if (state.query.init) {
+      onSearch()
+    }
+  },
+  { deep: true },
+)
+const onPage = (pageNum, pageSize) => {
+  setTimeout(() => {
+    state.query.init = true
+  }, 100)
+  state.query.page = {
+    pageNum: pageNum,
+    pageSize: pageSize,
+  }
+  const params = {
+    page: state.query.page.pageNum,
+    size: state.query.page.pageSize,
+  }
+  //  添加表单参数
+  for (const [k, v] of Object.entries(state.query.formReal)) {
+    if (proxy.$czrUtil.isValue(v)) {
+      params[k] = v
+    }
+  }
+  state.query.loading = true
+  documentGetDocumentsByPage(params)
+    .then(({ data }: any) => {
+      state.query.result.total = data.totalElements
+      state.query.result.data = data.content
+    })
+    .catch(() => {})
+    .finally(() => {
+      state.query.loading = false
+    })
+}
+const onSearch = () => {
+  state.query.selected = []
+  state.query.formReal = JSON.parse(JSON.stringify(state.query.form))
+  onPage(1, state.query.page.pageSize)
+}
+const onReset = () => {
+  state.query.page = {
+    pageNum: 1,
+    pageSize: 20,
+  }
+  state.query.form = {}
+  onSearch()
+}
+onMounted(() => {
+  initDictionary()
+  initDetail()
+})
+const initDictionary = () => {
+  // DictionaryStore.initModelProvides()
+  // DictionaryStore.initModelTypes()
+}
+</script>
+
+<style lang="scss" scoped>
+:deep(.log) {
+  .cc-bottom {
+    padding: 0;
+  }
+}
+</style>

+ 140 - 0
src/views/manage/app/monitor/line-chart.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class="chart-main" ref="ref_main">
+    <div class="chart-ref" ref="ref_chart" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  getCurrentInstance,
+  reactive,
+  ref,
+  onMounted,
+  watch,
+  nextTick,
+} from 'vue'
+import * as echarts from 'echarts'
+
+const props = defineProps({
+  data: <any>{},
+})
+
+const { proxy } = getCurrentInstance()
+const state = reactive({
+  resizeObserver: <any>null,
+  chart: <any>null,
+})
+const ref_chart = ref()
+const ref_main = ref()
+const initChart = () => {
+  echarts.dispose(ref_chart.value)
+  const chart = echarts.init(ref_chart.value)
+  const option = {
+    grid: {
+      bottom: 20,
+      right: 20,
+      left: 40,
+      top: 20,
+    },
+    tooltip: {
+      trigger: 'axis',
+      formatter: (p) => {
+        let str = ''
+        str += p[0].name + '<br/>'
+        str += p[0].marker + '\r' + p[0].value + (props.unit || '')
+        return str
+      },
+    },
+    xAxis: {
+      type: 'category',
+      data: props.data.map((v) => v.name),
+      axisLine: { show: false },
+      axisTick: { show: false },
+      splitLine: { show: false },
+      axisLabel: {
+        fontSize: 10,
+        interval: 0,
+        color: '#606266',
+      },
+    },
+    yAxis: {
+      type: 'value',
+      axisLine: { show: false },
+      axisTick: { show: false },
+      splitNumber: 3,
+      splitLine: {
+        lineStyle: {
+          type: 'dashed',
+          color: 'rgba(239, 241, 248, 1)',
+        },
+      },
+      axisLabel: {
+        fontSize: 10,
+        color: '#606266',
+        formatter: `{value}`,
+      },
+    },
+    series: [
+      {
+        type: 'line',
+        data: props.data,
+        smooth: true,
+        showSymbol: false,
+        itemStyle: {
+          color: '#45B1FF',
+        },
+        // areaStyle: {
+        //   color: {
+        //     type: 'linear',
+        //     x: 0,
+        //     y: 1,
+        //     x2: 0,
+        //     y2: 0,
+        //     colorStops: [{
+        //       offset: 0, color: 'red' // 0% 处的颜色
+        //     }, {
+        //       offset: 1, color: 'blue' // 100% 处的颜色
+        //     }],
+        //     global: false // 缺省为 false
+        //   },
+        // },
+      },
+    ],
+  }
+  chart.setOption(option)
+  state.resizeObserver = new ResizeObserver((entries) => {
+    for (const entry of entries) {
+      chart && chart.resize()
+    }
+  })
+  state.resizeObserver.observe(ref_main.value)
+  return chart
+}
+watch(
+  () => props.data,
+  () => {
+    state.chart = initChart()
+  },
+)
+onMounted(() => {
+  nextTick(() => {
+    state.chart = initChart()
+  })
+  return () => {
+    state.resizeObserver?.unobserve(ref_main?.value)
+    state.resizeObserver?.disconnect()
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.chart-main {
+  flex: 1;
+  width: 100%;
+  height: 100%;
+  .chart-ref {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 91 - 0
src/views/manage/app/monitor/pie-chart.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="chart-main" ref="ref_main">
+    <div class="chart-ref" ref="ref_chart" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  getCurrentInstance,
+  reactive,
+  ref,
+  onMounted,
+  watch,
+  nextTick,
+} from 'vue'
+import * as echarts from 'echarts'
+
+const props = defineProps({
+  data: <any>{},
+})
+
+const { proxy } = getCurrentInstance()
+const state = reactive({
+  resizeObserver: <any>null,
+  chart: <any>null,
+})
+const ref_chart = ref()
+const ref_main = ref()
+const initChart = () => {
+  echarts.dispose(ref_chart.value)
+  const chart = echarts.init(ref_chart.value)
+  const option = {
+    tooltip: {
+      trigger: 'item',
+    },
+    series: [
+      {
+        type: 'pie',
+        data: props.data,
+        radius: ['15%', '70%'],
+        padAngle: 1,
+        label: {
+          formatter: '{b}\n{d}%',
+          color: '#606266',
+          fontSize: 16,
+          fontFamily: 'PingFang SC',
+          fontWeight: 'bold',
+        },
+        labelLine: {
+          length2: 100,
+        },
+      },
+    ],
+  }
+  chart.setOption(option)
+  state.resizeObserver = new ResizeObserver((entries) => {
+    for (const entry of entries) {
+      chart && chart.resize()
+    }
+  })
+  state.resizeObserver.observe(ref_main.value)
+  return chart
+}
+watch(
+  () => props.data,
+  () => {
+    state.chart = initChart()
+  },
+)
+onMounted(() => {
+  nextTick(() => {
+    state.chart = initChart()
+  })
+  return () => {
+    state.resizeObserver?.unobserve(ref_main?.value)
+    state.resizeObserver?.disconnect()
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.chart-main {
+  flex: 1;
+  width: 100%;
+  height: 100%;
+  .chart-ref {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 1 - 2
src/views/manage/knowledge/documents/document/index.vue

@@ -5,10 +5,9 @@
     </div>
     <template v-if="!state.stage.show">
       <CzrContent
-        v-model:tableHead="state.query.tableHead"
+        v-model:tableHead="state.query.head"
         @handleReset="onReset"
         @handleSearch="onSearch"
-        v-model:full="state.query.isFull"
       >
         <template #tableTitle>
           <div class="flex gap-[var(--czr-gap)]">

+ 1 - 2
src/views/manage/knowledge/documents/qa/index.vue

@@ -4,10 +4,9 @@
       <div class="bm-main-box-title">问答</div>
     </div>
     <CzrContent
-      v-model:tableHead="state.query.tableHead"
+      v-model:tableHead="state.query.head"
       @handleReset="onReset"
       @handleSearch="onSearch"
-      v-model:full="state.query.isFull"
     >
       <template #tableTitle>
         <div class="flex gap-[var(--czr-gap)]">