Browse Source

fix🐛: 告警监控界面实现,修复其他

gitboyzcf 1 month ago
parent
commit
e2b2e7fcea

+ 1 - 1
src/router/guard.ts

@@ -43,7 +43,7 @@ export function setupRouterGuard(router: Router) {
           name: 'layout',
           component: Layout,
           // if you need to have a redirect when accessing / routing
-          redirect: '/dashboard',
+          redirect: '/monitoring-platform',
           children: routeList.value,
         })
 

+ 40 - 13
src/router/record.ts

@@ -1,18 +1,6 @@
 import type { MenuMixedOptions } from './helper'
 
 export const routeRecordRaw: MenuMixedOptions[] = [
-  {
-    path: 'dashboard',
-    name: 'dashboard',
-    icon: 'iconify-[mage--dashboard-3]',
-    label: '鸟情总览',
-    meta: {
-      componentName: 'Dashboard',
-      pinned: true,
-      showTab: true,
-    },
-    component: 'dashboard/index',
-  },
   {
     path: 'monitoring-platform',
     name: 'MonitoringPlatform',
@@ -23,8 +11,47 @@ export const routeRecordRaw: MenuMixedOptions[] = [
       title: '监控平台',
       showTab: true,
     },
-    component: 'monitoring-platform/index',
+    redirect: 'monitoring-platform/data-monitoring',
+    children: [
+      {
+        path: 'video-monitoring',
+        name: 'VideoMonitoring',
+        icon: 'iconify-[ph--table]',
+        label: '全景监控',
+        meta: {
+          componentName: 'VideoMonitoring',
+          title: '全景监控',
+          showTab: true,
+        },
+        component: 'monitoring-platform/video-monitoring',
+      },
+      {
+        path: 'data-monitoring',
+        name: 'DataMonitoring',
+        icon: 'iconify-[ph--article]',
+        label: '告警监控',
+        meta: {
+          componentName: 'DataMonitoring',
+          title: '告警监控',
+          showTab: true,
+          pinned: true,
+        },
+        component: 'monitoring-platform/data-monitoring',
+      },
+    ],
   },
+  {
+    path: 'dashboard',
+    name: 'dashboard',
+    icon: 'iconify-[mage--dashboard-3]',
+    label: '鸟情总览',
+    meta: {
+      componentName: 'Dashboard',
+      showTab: true,
+    },
+    component: 'dashboard/index',
+  },
+
   {
     path: 'alarm-management',
     name: 'AlarmManagement',

+ 0 - 2
src/stores/alarm.ts

@@ -180,8 +180,6 @@ export const useAlarmStore = defineStore('alarm', {
           }
         }
         if (flag) {
-          console.log(flag);
-
           pushAlarm()
           flag = false
         }

+ 0 - 2
src/utils/index.ts

@@ -1,8 +1,6 @@
 // 绘制边界框的函数
 export function drawBoundingBox(item: any) {
   return new Promise((resolve, reject) => {
-    console.log(item)
-
     const img = new Image()
     img.crossOrigin = 'Anonymous' // 处理跨域图片
 

+ 10 - 10
src/views/dashboard/index.vue

@@ -174,7 +174,7 @@ const initRevenueChartOption = async (chart: ECharts) => {
         left: 0,
         top: 0,
         textStyle: {
-          fontSize: 18,
+          fontSize: 16,
           color: isDark.value ? twc.neutral[400] : twc.neutral[600],
           fontWeight: 'normal',
         },
@@ -386,7 +386,7 @@ const initHighestRevenueChartOption = async (chart: ECharts) => {
     title: {
       text: '24小时活动分布',
       textStyle: {
-        fontSize: 15,
+        fontSize: 16,
         color: isDark.value ? twc.neutral[400] : twc.neutral[600],
         fontWeight: 'normal',
       },
@@ -419,8 +419,8 @@ const initHighestRevenueChartOption = async (chart: ECharts) => {
       },
     },
     grid: {
-      left: '3%',
-      right: '4%',
+      left: '1%',
+      right: '2%',
       bottom: '3%',
       containLabel: true,
     },
@@ -431,9 +431,9 @@ const initHighestRevenueChartOption = async (chart: ECharts) => {
         axisTick: {
           alignWithLabel: true,
         },
-        axisLabel: {
-          rotate: 45,
-        },
+        // axisLabel: {
+        //   rotate: 45,
+        // },
         splitLine: {
           show: true,
           lineStyle: {
@@ -731,7 +731,7 @@ const alarmShow = (item: any) => {
     </div>
 
     <div class="grid grid-cols-1 gap-4 overflow-hidden max-sm:gap-2 lg:grid-cols-12">
-      <div class="col-span-1 lg:col-span-5">
+      <!-- <div class="col-span-1 lg:col-span-5">
         <div
           class="rounded bg-naive-card px-5 pt-5 pb-3 shadow-xs transition-[background-color]"
           style="height: 340px"
@@ -741,8 +741,8 @@ const alarmShow = (item: any) => {
             class="h-full"
           />
         </div>
-      </div>
-      <div class="col-span-1 lg:col-span-7">
+      </div> -->
+      <div class="col-span-1 lg:col-span-12">
         <div
           class="rounded bg-naive-card p-5 shadow-xs transition-[background-color]"
           style="height: 340px; position: relative"

+ 0 - 547
src/views/data-show/data-form/index.vue

@@ -1,547 +0,0 @@
-<script setup lang="ts">
-import {
-  NCard,
-  NForm,
-  NFormItem,
-  NInput,
-  NSelect,
-  NButton,
-  NUploadDragger,
-  NUpload,
-  NSwitch,
-  NCheckbox,
-  NCheckboxGroup,
-  NSplit,
-  NDatePicker,
-  NAutoComplete,
-  NAlert,
-  NScrollbar,
-  NInputNumber,
-  NRadio,
-  useMessage,
-} from 'naive-ui'
-import { computed, ref, useTemplateRef, watch } from 'vue'
-
-import { ScrollContainer } from '@/components'
-import { useInjection, useResettableReactive } from '@/composables'
-import { mediaQueryInjectionKey } from '@/injection'
-
-import type { FormRules } from 'naive-ui'
-
-interface BaseForm {
-  name: string
-  age: number | null
-  sex: string
-  hobby: Array<string | number>
-  phones: Array<{ phone: string }>
-  email: string
-  dateBirth: number | null
-  job: string | null
-  address: string
-}
-
-defineOptions({
-  name: 'DataForm',
-})
-
-let codeToHtml: any
-
-const { isMaxMd, isMaxLg, isMaxXl } = useInjection(mediaQueryInjectionKey)
-
-const message = useMessage()
-
-const formRef = useTemplateRef<InstanceType<typeof NForm>>('formRef')
-
-const [form, setForm, reset] = useResettableReactive<BaseForm>({
-  name: '',
-  age: null,
-  sex: '',
-  hobby: [],
-  phones: [
-    {
-      phone: '',
-    },
-  ],
-  email: '',
-  dateBirth: null,
-  job: null,
-  address: '',
-})
-
-const formCodeHighlight = ref()
-
-const rulesCodeHighlight = ref()
-
-const formDisabled = ref(false)
-
-const isRequestLoading = ref(false)
-
-const rules: FormRules = {
-  name: { required: true, message: '请输入用户名' },
-}
-
-const hobbyOptions = [
-  {
-    value: '唱',
-    label: '唱',
-  },
-  {
-    value: '跳',
-    label: '跳',
-  },
-  {
-    value: 'Rap',
-    label: 'Rap',
-  },
-  {
-    value: '篮球',
-    label: '篮球',
-  },
-]
-
-const jobSelectOptions = [
-  {
-    label: '前端工程师',
-    value: '前端工程师',
-  },
-  {
-    label: '后端工程师',
-    value: '后端工程师',
-  },
-  {
-    label: '全栈工程师',
-    value: '全栈工程师',
-  },
-  {
-    label: '产品经理',
-    value: '产品经理',
-  },
-  {
-    label: 'UI设计师',
-    value: 'UI设计师',
-  },
-  {
-    label: '测试工程师',
-    value: '测试工程师',
-  },
-  {
-    label: '运维工程师',
-    value: '运维工程师',
-  },
-  {
-    label: '数据分析师',
-    value: '数据分析师',
-  },
-  {
-    label: '项目经理',
-    value: '项目经理',
-  },
-  {
-    label: '其他',
-    value: '其他',
-  },
-]
-
-const emailOptions = computed(() => {
-  return ['@gmail.com', '@outlook.com', '@hotmail.com', '@163.com', '@qq.com'].map((suffix) => {
-    const prefix = form.email.split('@')[0]
-    return {
-      label: prefix + suffix,
-      value: prefix + suffix,
-    }
-  })
-})
-
-function inputOnlyAllowNumber(value: string) {
-  return !value || /^\d+$/.test(value)
-}
-
-function generateRandomForm() {
-  isRequestLoading.value = true
-  fetch('https://lithe-admin-serverless.havenovelgod.com/api/faker')
-    .then((res) => res.json())
-    .then((res) => {
-      setForm(res.data)
-    })
-    .finally(() => {
-      isRequestLoading.value = false
-    })
-}
-
-function handleSubmitClick() {
-  formRef.value?.validate().then((valid) => {
-    if (valid) {
-      message.success('通过验证')
-    }
-  })
-}
-
-function addPhoneField() {
-  form.phones.push({
-    phone: '',
-  })
-}
-
-function removePhoneField(index: number) {
-  form.phones.splice(index, 1)
-}
-
-watch(
-  form,
-  async (newForm) => {
-    if (!codeToHtml) {
-      // @ts-ignore
-      const shiki = await import('https://cdn.jsdelivr.net/npm/shiki@3.7.0/+esm')
-      codeToHtml = shiki.codeToHtml
-    }
-
-    codeToHtml(JSON.stringify(newForm, null, 2), {
-      lang: 'json',
-      themes: {
-        dark: 'dark-plus',
-        light: 'min-light',
-      },
-    })
-      .then((result: string) => (formCodeHighlight.value = result))
-      .catch(() => (formCodeHighlight.value = JSON.stringify(newForm, null, 2)))
-
-    codeToHtml(JSON.stringify(rules, null, 2), {
-      lang: 'json',
-      themes: {
-        dark: 'dark-plus',
-        light: 'min-light',
-      },
-    })
-      .then((result: string) => (rulesCodeHighlight.value = result))
-      .catch(() => (rulesCodeHighlight.value = JSON.stringify(rules, null, 2)))
-  },
-  {
-    immediate: true,
-  },
-)
-</script>
-<template>
-  <ScrollContainer wrapper-class="flex flex-col gap-y-2">
-    <NAlert
-      type="info"
-      closable
-    >
-      一个数据表单的例子,做了一些验证的限制,右边是规则和表单的数据,你可以拖动它们之间的分割线
-    </NAlert>
-    <NCard :size="isMaxMd ? 'small' : undefined">
-      <NSplit
-        :pane1-class="isMaxLg ? 'pb-4' : 'pr-8'"
-        :pane2-class="isMaxLg ? 'pt-4' : 'pl-8'"
-        :default-size="0.6"
-        :direction="isMaxXl ? 'vertical' : 'horizontal'"
-      >
-        <template #1>
-          <NScrollbar>
-            <NForm
-              ref="formRef"
-              :model="form"
-              :rules="rules"
-              label-placement="left"
-              :label-width="78"
-              :disabled="formDisabled"
-            >
-              <div class="flex gap-x-8 max-lg:flex-col">
-                <div class="flex-1">
-                  <NFormItem
-                    label="姓名"
-                    path="name"
-                  >
-                    <NInput
-                      v-model:value="form.name"
-                      clearable
-                      class="w-full"
-                      placeholder="请输入姓名"
-                    />
-                  </NFormItem>
-                  <NFormItem
-                    label="年龄"
-                    path="age"
-                  >
-                    <NInputNumber
-                      v-model:value="form.age"
-                      clearable
-                      class="w-full"
-                      placeholder="请输入年龄"
-                    />
-                  </NFormItem>
-                  <NFormItem
-                    label="性别"
-                    path="sex"
-                  >
-                    <NRadio
-                      :checked="form.sex === '男'"
-                      value="男"
-                      name="basic-demo"
-                      @change="form.sex = '男'"
-                    >
-                      男
-                    </NRadio>
-                    <NRadio
-                      :checked="form.sex === '女'"
-                      value="女"
-                      name="basic-demo"
-                      @change="form.sex = '女'"
-                    >
-                      女
-                    </NRadio>
-                  </NFormItem>
-                  <NFormItem
-                    label="出生日期"
-                    path="dateBirth"
-                  >
-                    <NDatePicker
-                      v-model:value="form.dateBirth"
-                      type="datetime"
-                      clearable
-                      class="w-full"
-                    />
-                  </NFormItem>
-                  <NFormItem
-                    v-for="(item, index) in form.phones"
-                    :key="index"
-                    :label="`手机号${index + 1}`"
-                    :path="`phones[${index}].phone`"
-                    :rule="{
-                      required: true,
-                      trigger: ['input'],
-                      validator: (_, value: string) => {
-                        if (!value) {
-                          return new Error('请输入手机号')
-                        }
-                        if (!/^[1]+[3,8]+\d{9}$/.test(value)) {
-                          return new Error('请输入1[3-8]开头的手机号')
-                        }
-                        return true
-                      },
-                    }"
-                  >
-                    <NInput
-                      v-model:value="item.phone"
-                      clearable
-                      :allow-input="inputOnlyAllowNumber"
-                      maxlength="11"
-                      class="mr-1 w-full"
-                      placeholder="请输入手机号"
-                    />
-                    <NButton
-                      type="warning"
-                      secondary
-                      @click="removePhoneField(index)"
-                    >
-                      删除
-                    </NButton>
-                  </NFormItem>
-                  <NFormItem
-                    label="邮箱"
-                    path="email"
-                  >
-                    <NAutoComplete
-                      v-model:value="form.email"
-                      :input-props="{
-                        autocomplete: 'disabled',
-                      }"
-                      :options="emailOptions"
-                      clearable
-                      class="w-full"
-                      placeholder="请输入邮箱"
-                    />
-                  </NFormItem>
-
-                  <NFormItem
-                    label="职业"
-                    path="job"
-                  >
-                    <NSelect
-                      v-model:value="form.job"
-                      :options="jobSelectOptions"
-                    />
-                  </NFormItem>
-                  <NFormItem
-                    label="爱好"
-                    path="hobby"
-                  >
-                    <NCheckboxGroup v-model:value="form.hobby">
-                      <div>
-                        <NCheckbox
-                          v-for="{ value, label } in hobbyOptions"
-                          :key="value"
-                          :value="value"
-                          :label="label"
-                        />
-                      </div>
-                    </NCheckboxGroup>
-                  </NFormItem>
-                  <NFormItem
-                    label="住址"
-                    path="address"
-                  >
-                    <NInput
-                      type="textarea"
-                      v-model:value="form.address"
-                      clearable
-                      class="w-full"
-                      placeholder="请输入地址"
-                    />
-                  </NFormItem>
-                </div>
-                <div class="flex-1">
-                  <NFormItem
-                    label="一个头像"
-                    path="avatar"
-                    label-placement="top"
-                  >
-                    <NUpload
-                      :default-file-list="[]"
-                      list-type="image-card"
-                      :max="1"
-                    >
-                      点击上传
-                    </NUpload>
-                  </NFormItem>
-                  <NFormItem
-                    label="多个图像"
-                    path="image"
-                    label-placement="top"
-                  >
-                    <NUpload
-                      action="#"
-                      :default-file-list="[
-                        {
-                          id: 'c',
-                          name: '我是自带url的图片.png',
-                          status: 'finished',
-                          url: 'https://avatars.githubusercontent.com/u/147876815?s=400&u=586cf97ad6f4c788ffae93441d7fcedc6de89d2c&v=4',
-                        },
-                      ]"
-                      list-type="image-card"
-                      multiple
-                    >
-                    </NUpload>
-                  </NFormItem>
-                  <NFormItem
-                    label="文件上传"
-                    path="file"
-                    label-placement="top"
-                  >
-                    <NUpload
-                      action="#"
-                      :default-file-list="[
-                        {
-                          id: 'c',
-                          name: '我是自带url的图片.png',
-                          status: 'finished',
-                          url: 'https://avatars.githubusercontent.com/u/147876815?s=400&u=586cf97ad6f4c788ffae93441d7fcedc6de89d2c&v=4',
-                        },
-                      ]"
-                      multiple
-                    >
-                      <NButton type="primary">选择文件</NButton>
-                    </NUpload>
-                  </NFormItem>
-                  <NFormItem
-                    label="拖拽上传"
-                    path="dragUpload"
-                    label-placement="top"
-                  >
-                    <NUpload
-                      multiple
-                      directory-dnd
-                      action="#"
-                      :max="3"
-                    >
-                      <NUploadDragger>
-                        <div style="margin-bottom: 12px">
-                          <div>
-                            <span class="iconify text-3xl ph--upload"></span>
-                          </div>
-                          <p>点击或者拖动文件到该区域来上传</p>
-                          <p class="text-xs text-neutral-400 dark:text-neutral-500">
-                            请不要上传隐私的文件数据
-                          </p>
-                        </div>
-                      </NUploadDragger>
-                    </NUpload>
-                  </NFormItem>
-                </div>
-              </div>
-            </NForm>
-            <div class="flex items-center max-lg:flex-col">
-              <NFormItem
-                label="禁用表单"
-                label-placement="left"
-                :show-feedback="false"
-              >
-                <NSwitch v-model:value="formDisabled" />
-              </NFormItem>
-
-              <div class="flex gap-4 max-md:flex-wrap max-md:gap-2 md:ml-8">
-                <NButton
-                  type="success"
-                  :disabled="formDisabled"
-                  @click="handleSubmitClick"
-                >
-                  提交表单
-                </NButton>
-                <NButton
-                  type="warning"
-                  :disabled="formDisabled"
-                  @click="reset"
-                >
-                  重置表单
-                </NButton>
-                <NButton
-                  type="info"
-                  :disabled="formDisabled || isRequestLoading"
-                  @click="generateRandomForm"
-                  :loading="isRequestLoading"
-                >
-                  随机填充表单
-                </NButton>
-                <NButton
-                  type="primary"
-                  :disabled="formDisabled"
-                  @click="addPhoneField"
-                >
-                  添加手机号
-                </NButton>
-              </div>
-            </div>
-          </NScrollbar>
-        </template>
-        <template #2>
-          <NSplit
-            direction="vertical"
-            pane1-class="pb-4"
-            pane2-class="pt-4"
-          >
-            <template #1>
-              <NScrollbar>
-                <div v-html="formCodeHighlight"></div>
-              </NScrollbar>
-            </template>
-            <template #2>
-              <NScrollbar>
-                <div v-html="rulesCodeHighlight"></div>
-              </NScrollbar>
-            </template>
-            <template #resize-trigger>
-              <div
-                class="h-px w-full cursor-col-resize bg-neutral-200 transition-[background-color] dark:bg-neutral-700"
-              ></div>
-            </template>
-          </NSplit>
-        </template>
-        <template #resize-trigger>
-          <div
-            class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] max-lg:h-px max-lg:w-full dark:bg-neutral-700"
-          ></div>
-        </template>
-      </NSplit>
-    </NCard>
-  </ScrollContainer>
-</template>

+ 0 - 174
src/views/data-show/data-table/ActionModal.vue

@@ -1,174 +0,0 @@
-<script setup lang="ts">
-import { NForm, NFormItem, NInput, NSelect, NButton, NInputNumber, NDatePicker } from 'naive-ui'
-import { onMounted, useTemplateRef } from 'vue'
-
-import { useComponentThemeOverrides, useResettableReactive } from '@/composables'
-
-import type { UserInfo } from './index.vue'
-import type { FormRules } from 'naive-ui'
-
-const { data } = defineProps<{
-  data: Partial<UserInfo>
-}>()
-
-const { selectInPopover, datePickerInPopover } = useComponentThemeOverrides()
-
-const formRef = useTemplateRef<InstanceType<typeof NForm>>('formRef')
-
-const emits = defineEmits<{
-  (e: 'submit' | 'update', value: Partial<UserInfo>): void
-  (e: 'cancel'): void
-}>()
-
-const [form, setUserForm, reset, updateInitialState] = useResettableReactive<Partial<UserInfo>>({
-  fullName: '',
-  age: null,
-  company: '',
-  email: '',
-  address: '',
-  sex: null,
-  phone: '',
-  registerDate: null,
-})
-
-const rules: FormRules = {
-  fullName: { required: true, message: '请输入姓名', trigger: ['input'] },
-  age: [{ required: true, message: '请输入年龄', type: 'number', trigger: ['change'] }],
-  company: { required: true, message: '请输入公司', trigger: ['input'] },
-  email: { required: true, message: '请输入邮箱', trigger: ['input'] },
-  address: { required: true, message: '请输入地址', trigger: ['input'] },
-  sex: { required: true, message: '请选择性别', trigger: ['input'] },
-  phone: { required: true, message: '请输入手机号', trigger: ['input'] },
-  registerDate: { required: true, message: '请选择注册日期', trigger: ['input'] },
-  jobTitle: { required: true, message: '请输入职位', trigger: ['input'] },
-}
-
-const sexSelectOptions = [
-  { label: '男', value: '男' },
-  { label: '女', value: '女' },
-]
-
-const handleSubmitClick = () => {
-  formRef.value?.validate().then((valid) => {
-    if (valid) {
-      if (form.number) {
-        emits('update', form)
-      } else {
-        emits('submit', form)
-      }
-    }
-  })
-}
-
-onMounted(() => {
-  if (data.number) {
-    setUserForm({ ...data })
-    updateInitialState({ ...data })
-  }
-})
-</script>
-<template>
-  <div class="p-2">
-    <NForm
-      :model="form"
-      :rules="rules"
-      label-placement="left"
-      style="width: 100%"
-      ref="formRef"
-      label-width="88px"
-    >
-      <NFormItem
-        label="姓名"
-        path="fullName"
-      >
-        <NInput v-model:value="form.fullName" />
-      </NFormItem>
-      <div class="flex gap-x-2">
-        <NFormItem
-          label="性别"
-          path="sex"
-          class="flex-1"
-        >
-          <NSelect
-            v-model:value="form.sex"
-            :options="sexSelectOptions"
-            :theme-overrides="selectInPopover"
-          />
-        </NFormItem>
-        <NFormItem
-          label="年龄"
-          path="age"
-          class="flex-1"
-          label-width="60px"
-        >
-          <NInputNumber v-model:value="form.age" />
-        </NFormItem>
-      </div>
-      <NFormItem
-        label="邮箱"
-        path="email"
-      >
-        <NInput v-model:value="form.email" />
-      </NFormItem>
-      <NFormItem
-        label="手机号"
-        path="phone"
-      >
-        <NInput v-model:value="form.phone" />
-      </NFormItem>
-      <NFormItem
-        label="注册日期"
-        path="registerDate"
-      >
-        <NDatePicker
-          v-model:formatted-value="form.registerDate"
-          value-format="yyyy-MM-dd"
-          type="datetime"
-          clearable
-          style="width: 100%"
-          :theme-overrides="datePickerInPopover"
-        />
-      </NFormItem>
-      <NFormItem
-        label="公司"
-        path="company"
-      >
-        <NInput
-          type="textarea"
-          v-model:value="form.company"
-        />
-      </NFormItem>
-
-      <NFormItem
-        label="地址"
-        path="address"
-      >
-        <NInput
-          type="textarea"
-          v-model:value="form.address"
-        />
-      </NFormItem>
-    </NForm>
-    <div class="flex justify-end gap-x-4">
-      <NButton
-        secondary
-        @click="emits('cancel')"
-      >
-        取消
-      </NButton>
-      <NButton
-        secondary
-        type="warning"
-        @click="reset"
-      >
-        重置
-      </NButton>
-      <NButton
-        type="success"
-        @click="handleSubmitClick"
-      >
-        确定
-      </NButton>
-    </div>
-  </div>
-</template>

+ 0 - 620
src/views/data-show/data-table/index.vue

@@ -1,620 +0,0 @@
-<script setup lang="tsx">
-import {
-  NButton,
-  NDataTable,
-  NCard,
-  NForm,
-  NFormItem,
-  NInput,
-  NSelect,
-  NPopconfirm,
-  useMessage,
-  useModal,
-  NPagination,
-  NButtonGroup,
-  NDropdown,
-  NTag,
-  NNumberAnimation,
-  NAlert,
-} from 'naive-ui'
-import { defineComponent, reactive, ref, useTemplateRef, nextTick } from 'vue'
-
-import { ScrollContainer } from '@/components'
-import { useInjection, useComponentModifier, useResettableReactive } from '@/composables'
-import { mediaQueryInjectionKey } from '@/injection'
-
-import ActionModal from './ActionModal.vue'
-
-import type { DataTableColumns, PaginationProps, FormRules, DropdownProps } from 'naive-ui'
-import type { PropType } from 'vue'
-
-export interface UserInfo {
-  address: string
-  age: number | null
-  company: string
-  email: string
-  fullName: string
-  number: number
-  id: number | string
-  phone: string
-  registerDate: null | null
-  sex: string | null
-  children: UserInfo[]
-}
-
-defineOptions({
-  name: 'DataTable',
-})
-
-const { isMaxMd, isMaxLg } = useInjection(mediaQueryInjectionKey)
-
-const formRef = useTemplateRef<InstanceType<typeof NForm>>('formRef')
-
-const dataTableRef = useTemplateRef<InstanceType<typeof NDataTable>>('dataTableRef')
-
-const message = useMessage()
-
-const modal = useModal()
-
-const { getPopconfirmModifier } = useComponentModifier()
-
-const [form, , resetForm] = useResettableReactive<Partial<UserInfo>>({
-  fullName: '',
-  sex: null,
-  phone: '',
-  company: '',
-})
-
-const rules: FormRules = {
-  sex: {
-    required: true,
-    message: '请选择性别',
-  },
-}
-
-const sexOptions = [
-  { label: '男', value: '男' },
-  { label: '女', value: '女' },
-]
-
-const isRequestLoading = ref(false)
-const enableStriped = ref(false)
-const enableScrollX = ref(true)
-const enableSingleLine = ref(true)
-const enableContextmenu = ref(true)
-const showDropdown = ref(false)
-const contextmenuId = ref<number | string | null>(null)
-
-const dataList = ref<UserInfo[]>([])
-
-const checkedRowKeys = ref<Array<number | string>>([])
-
-const CellActions = (row: UserInfo) => (
-  <div class='flex gap-2'>
-    <NButton
-      secondary
-      type='primary'
-      size='small'
-      onClick={() => createOrEditData(row)}
-    >
-      编辑
-    </NButton>
-    <NPopconfirm
-      {...getPopconfirmModifier()}
-      positiveText='确定'
-      negativeText='取消'
-      onPositiveClick={() => {
-        message.success('点击了删除')
-      }}
-    >
-      {{
-        default: () => '确认删除吗?',
-        trigger: () => (
-          <NButton
-            secondary
-            type='error'
-            size='small'
-          >
-            删除
-          </NButton>
-        ),
-      }}
-    </NPopconfirm>
-  </div>
-)
-
-const ShowOrEdit = defineComponent({
-  name: 'ShowOrEdit',
-  props: {
-    value: {
-      type: String,
-      required: true,
-    },
-    onUpdateValue: {
-      type: Function as PropType<(value: string) => void>,
-    },
-  },
-  setup(props) {
-    const isEdit = ref(false)
-    const inputRef = ref<InstanceType<typeof NInput> | null>(null)
-    const inputValue = ref(props.value)
-
-    function onClick() {
-      isEdit.value = true
-      nextTick(() => {
-        inputRef.value?.focus()
-      })
-    }
-
-    function onBlur() {
-      if (!inputValue.value.trim()) {
-        message.error('为空就再也编辑不了了')
-        inputValue.value = props.value
-      }
-      isEdit.value = false
-      props.onUpdateValue?.(inputValue.value)
-    }
-
-    return () => (
-      <div onClick={onClick}>
-        {isEdit.value ? (
-          <NInput
-            ref={inputRef}
-            value={inputValue.value}
-            clearable
-            onUpdateValue={(value) => {
-              inputValue.value = value
-            }}
-            onBlur={onBlur}
-          />
-        ) : (
-          <span>{props.value}</span>
-        )}
-      </div>
-    )
-  },
-})
-
-const columns: DataTableColumns<UserInfo> = [
-  {
-    type: 'selection',
-    options: [
-      'all',
-      'none',
-      {
-        label: '选中前 3 行可选数据',
-        key: 'f2',
-        onSelect: (pageData) => {
-          checkedRowKeys.value = pageData
-            .filter((row) => row.number < 500)
-            .map((row) => row.id)
-            .slice(0, 3)
-        },
-      },
-    ],
-    disabled: (row) => {
-      return ['4', '5', '8', '9'].includes(String(row.number)[0])
-    },
-  },
-  {
-    key: 'number',
-    title: '编号',
-    width: 100,
-  },
-  {
-    key: 'fullName',
-    width: 160,
-    title: () => {
-      return (
-        <div class='flex items-center gap-x-2'>
-          <span>姓名</span>
-          <span class='iconify ph--pencil-simple-line' />
-        </div>
-      )
-    },
-    render: (row, index) => (
-      <ShowOrEdit
-        value={row.fullName}
-        onUpdateValue={(value) => {
-          dataList.value[index].fullName = value
-        }}
-      />
-    ),
-  },
-  {
-    key: 'sex',
-    title: '性别',
-    width: 100,
-    render: (row) => {
-      const isMale = row.sex === '男'
-      return (
-        <div>
-          <span
-            class={
-              isMale
-                ? 'iconify text-sky-500 ph--gender-male'
-                : 'iconify text-pink-500 ph--gender-female'
-            }
-          />
-        </div>
-      )
-    },
-  },
-  {
-    key: 'age',
-    title: '年龄',
-    width: 100,
-    render: (row) => {
-      const age = row.age ?? 0
-
-      return (
-        <NTag
-          bordered={false}
-          size='small'
-          type={age > 50 ? 'error' : age > 40 ? 'warning' : age > 30 ? 'info' : 'success'}
-        >
-          {row.age}
-        </NTag>
-      )
-    },
-  },
-  {
-    key: 'email',
-    title: '邮箱',
-  },
-  {
-    key: 'phone',
-    title: '电话',
-  },
-  {
-    key: 'address',
-    title: '地址',
-  },
-  {
-    key: 'company',
-    title: '公司',
-  },
-  {
-    key: 'registerDate',
-    title: '注册日期',
-  },
-  {
-    width: 140,
-    key: 'actions',
-    align: 'center',
-    title: '操作',
-    fixed: 'right',
-    render: (row) => <CellActions {...row} />,
-  },
-]
-
-function rowProps(row: UserInfo) {
-  return {
-    onContextmenu: (e: MouseEvent) => {
-      e.preventDefault()
-      showDropdown.value = false
-      nextTick().then(() => {
-        contextmenuId.value = row.number
-        showDropdown.value = true
-        dropdownOptions.x = e.clientX
-        dropdownOptions.y = e.clientY
-      })
-    },
-  }
-}
-
-const pagination = reactive<PaginationProps>({
-  page: 1,
-  pageSize: 10,
-  showSizePicker: true,
-  pageSizes: [10, 20, 50, 100],
-  itemCount: 0,
-  showQuickJumper: true,
-  showQuickJumpDropdown: true,
-  onUpdatePage: (page: number) => {
-    pagination.page = page
-    getDataList()
-  },
-  onUpdatePageSize: (pageSize: number) => {
-    pagination.pageSize = pageSize
-    pagination.page = 1
-    getDataList()
-  },
-})
-
-const prevUserListTotal = ref(0)
-
-const paginationPrefix: PaginationProps['prefix'] = (info) => {
-  const { itemCount } = info
-  return (
-    itemCount && (
-      <div>
-        <span>总&nbsp;</span>
-        <NNumberAnimation
-          from={prevUserListTotal.value}
-          to={itemCount}
-          onFinish={() => {
-            prevUserListTotal.value = itemCount
-          }}
-        />
-        <span>&nbsp;条</span>
-      </div>
-    )
-  )
-}
-
-const dropdownOptions = reactive<DropdownProps>({
-  x: 0,
-  y: 0,
-  options: [
-    {
-      label: '编辑',
-      key: 'edit',
-    },
-    {
-      label: () => <span class='text-red-500'>删除</span>,
-      key: 'delete',
-    },
-  ],
-  onClickoutside: () => {
-    showDropdown.value = false
-  },
-  onSelect: () => {
-    message.info(`id: ${contextmenuId.value}`)
-    showDropdown.value = false
-  },
-})
-
-async function request(pageSize: number): Promise<{ data: UserInfo[]; total: number }> {
-  return fetch(`https://lithe-admin-serverless.havenovelgod.com/api/faker?limit=${pageSize}`, {
-    method: 'GET',
-  }).then((res) => res.json())
-}
-
-function inputOnlyAllowNumber(value: string) {
-  return !value || /^\d+$/.test(value)
-}
-
-function createOrEditData(data?: UserInfo) {
-  const title = data ? '编辑数据' : '新增数据'
-
-  const handleSubmitClick = () => {
-    message.success('点击了提交')
-    m.destroy()
-  }
-
-  function handleUpdateClick() {
-    message.info('点击了更新')
-    m.destroy()
-  }
-
-  function handleCancelClick() {
-    m.destroy()
-  }
-
-  const m = modal.create({
-    autoFocus: false,
-    title,
-    preset: 'card',
-    draggable: true,
-    style: {
-      width: '500px',
-      ...(isMaxMd.value ? { marginInline: '16px' } : {}),
-    },
-    content: () => (
-      <ActionModal
-        data={data || {}}
-        onSubmit={handleSubmitClick}
-        onUpdate={handleUpdateClick}
-        onCancel={handleCancelClick}
-      />
-    ),
-  })
-}
-
-const handleQueryClick = () => {
-  formRef.value?.validate((errors) => {
-    if (!errors) {
-      getDataList()
-    }
-  })
-}
-
-function handleDownloadCsvClick() {
-  if (!dataTableRef.value) return
-  dataTableRef.value.downloadCsv()
-}
-
-async function getDataList() {
-  isRequestLoading.value = true
-  const pageSize = pagination.pageSize || 10
-  const res = await request(pageSize).finally(() => {
-    isRequestLoading.value = false
-  })
-
-  dataList.value = res.data
-  pagination.itemCount = 300
-}
-
-getDataList()
-</script>
-<template>
-  <ScrollContainer
-    wrapper-class="flex flex-col gap-y-2"
-    :scrollable="isMaxLg"
-  >
-    <NAlert
-      type="info"
-      closable
-    >
-      一个数据表格的例子,不算复杂,也许对你有帮助
-    </NAlert>
-    <NCard
-      :size="isMaxMd ? 'small' : undefined"
-      class="flex-1"
-      content-class="flex flex-col"
-    >
-      <div class="mb-2 flex justify-end gap-x-4 max-xl:mb-4 max-xl:flex-wrap">
-        <NForm
-          ref="formRef"
-          :model="form"
-          :rules="rules"
-          :inline="!isMaxLg"
-          label-placement="left"
-          class="max-lg:w-full max-lg:flex-col"
-          :label-width="isMaxLg ? 70 : undefined"
-        >
-          <NFormItem
-            label="姓名"
-            path="fullName"
-          >
-            <NInput
-              v-model:value="form.fullName"
-              clearable
-            />
-          </NFormItem>
-          <NFormItem
-            label="性别"
-            path="sex"
-          >
-            <NSelect
-              v-model:value="form.sex"
-              :options="sexOptions"
-              style="min-width: 88px"
-              clearable
-            />
-          </NFormItem>
-          <NFormItem
-            label="联系方式"
-            path="phone"
-          >
-            <NInput
-              v-model:value="form.phone"
-              clearable
-              :allow-input="inputOnlyAllowNumber"
-            />
-          </NFormItem>
-          <NFormItem
-            label="公司"
-            path="company"
-          >
-            <NInput
-              v-model:value="form.company"
-              clearable
-            />
-          </NFormItem>
-        </NForm>
-        <div class="flex gap-2">
-          <NButton
-            type="success"
-            @click="createOrEditData()"
-          >
-            <template #icon>
-              <span class="iconify ph--plus-circle" />
-            </template>
-            新增数据
-          </NButton>
-          <NButton
-            type="info"
-            @click="handleQueryClick"
-            :loading="isRequestLoading"
-            :disabled="isRequestLoading"
-          >
-            <template #icon>
-              <span class="iconify ph--magnifying-glass" />
-            </template>
-            查询
-          </NButton>
-          <NButton
-            type="warning"
-            @click="resetForm"
-          >
-            <template #icon>
-              <span class="iconify ph--arrow-clockwise" />
-            </template>
-            重置
-          </NButton>
-        </div>
-      </div>
-      <div class="flex flex-1 flex-col">
-        <NDataTable
-          class="flex-1"
-          ref="dataTableRef"
-          v-model:checked-row-keys="checkedRowKeys"
-          :remote="true"
-          :flex-height="!isMaxLg"
-          :scroll-x="enableScrollX ? 1800 : 0"
-          :columns="columns"
-          :data="dataList"
-          :row-key="(row) => row.id"
-          :loading="isRequestLoading"
-          :striped="enableStriped"
-          :row-props="rowProps"
-          :single-line="enableSingleLine"
-        />
-        <div class="mt-3 flex items-end justify-between max-xl:flex-col max-xl:gap-y-2">
-          <div class="flex items-center justify-between gap-x-3">
-            <span>已选择&nbsp;{{ checkedRowKeys.length }}&nbsp; 条</span>
-            <NButtonGroup
-              size="small"
-              :ghost="true"
-            >
-              <NButton
-                @click="enableStriped = !enableStriped"
-                :type="enableStriped ? 'primary' : 'default'"
-                secondary
-              >
-                条纹风格
-              </NButton>
-              <NButton
-                @click="enableSingleLine = !enableSingleLine"
-                :type="!enableSingleLine ? 'primary' : 'default'"
-                secondary
-              >
-                单线风格
-              </NButton>
-              <NButton
-                @click="enableScrollX = !enableScrollX"
-                :type="enableScrollX ? 'primary' : 'default'"
-                secondary
-              >
-                横向滚动
-              </NButton>
-
-              <NButton
-                v-show="!isMaxMd"
-                @click="enableContextmenu = !enableContextmenu"
-                :type="enableContextmenu ? 'primary' : 'default'"
-                secondary
-              >
-                右键菜单
-              </NButton>
-              <NButton
-                v-show="!isMaxMd"
-                @click="handleDownloadCsvClick"
-                secondary
-                type="info"
-              >
-                下载为Csv
-              </NButton>
-            </NButtonGroup>
-          </div>
-          <NPagination
-            v-bind="pagination"
-            :prefix="paginationPrefix"
-            :page-slot="isMaxMd ? 5 : undefined"
-            :show-quick-jump-dropdown="!isMaxMd"
-            :show-quick-jumper="!isMaxMd"
-            :show-size-picker="!isMaxMd"
-          />
-        </div>
-      </div>
-    </NCard>
-    <NDropdown
-      placement="bottom-start"
-      trigger="manual"
-      v-bind="dropdownOptions"
-      :show="enableContextmenu && showDropdown"
-    />
-  </ScrollContainer>
-</template>

+ 0 - 305
src/views/drag-drop/index.vue

@@ -1,305 +0,0 @@
-<script setup lang="ts">
-import { NAlert, NCard, NSplit, NScrollbar, NButton } from 'naive-ui'
-import { ref, watch } from 'vue'
-import { VueDraggable } from 'vue-draggable-plus'
-
-import { ScrollContainer, EmptyPlaceholder } from '@/components'
-import { useInjection } from '@/composables'
-import { mediaQueryInjectionKey } from '@/injection'
-
-import type { UseDraggableReturn } from 'vue-draggable-plus'
-
-defineOptions({
-  name: 'DragDrop',
-})
-
-let codeToHtml: any
-
-const { isMaxMd } = useInjection(mediaQueryInjectionKey)
-
-const APP_NAME = import.meta.env.VITE_APP_NAME
-
-const baseListCodeHighlight = ref()
-
-const gridListCodeHighlight = ref()
-
-const cloneList2CodeHighlight = ref()
-
-const baseList = ref(
-  Object.keys(Array.from({ length: 4 }).fill(0)).map((item, index) => ({
-    name: item,
-    id: index,
-  })),
-)
-
-const gridList = ref(
-  Object.keys(Array.from({ length: 50 }).fill(0)).map((item, index) => ({
-    name: Number(item) + 4,
-    id: index + 4,
-  })),
-)
-
-const taskList = ref(
-  Object.keys(Array.from({ length: 5 }).fill(0)).map((item) => ({
-    name: `任务-${item}`,
-    id: `任务-${item}`,
-  })),
-)
-
-const cloneTaskList = ref<{ name: string; id: string; key: string }[]>([])
-
-const baseDragRef = ref<UseDraggableReturn>()
-const gridDragRef = ref<UseDraggableReturn>()
-const taskDragRef = ref<UseDraggableReturn>()
-const cloneTaskListDragRef = ref<UseDraggableReturn>()
-
-function cloneTask(element: Record<'name' | 'id', string>) {
-  return {
-    name: `${element.name}`,
-    id: `${element.id}`,
-    key: element.name + new Date().getTime(),
-  }
-}
-
-function removeTask(element: Record<'name' | 'id' | 'key', string>) {
-  const find = cloneTaskList.value.find((item) => item.key === element.key)
-  if (find) {
-    cloneTaskList.value = cloneTaskList.value.filter((item) => item.key !== element.key)
-  }
-}
-
-watch(
-  [baseList, gridList, cloneTaskList],
-  async (newVal) => {
-    const [baseList, gridList, cloneList2] = newVal
-
-    if (!codeToHtml) {
-      // @ts-ignore
-      const shiki = await import('https://cdn.jsdelivr.net/npm/shiki@3.7.0/+esm')
-      codeToHtml = shiki.codeToHtml
-    }
-
-    codeToHtml(JSON.stringify(baseList, null, 2), {
-      lang: 'json',
-      themes: {
-        dark: 'dark-plus',
-        light: 'min-light',
-      },
-    })
-      .then((result: string) => (baseListCodeHighlight.value = result))
-      .catch(() => (baseListCodeHighlight.value = JSON.stringify(baseList, null, 2)))
-
-    codeToHtml(JSON.stringify(gridList, null, 2), {
-      lang: 'json',
-      themes: {
-        dark: 'dark-plus',
-        light: 'min-light',
-      },
-    })
-      .then((result: string) => (gridListCodeHighlight.value = result))
-      .catch(() => (gridListCodeHighlight.value = JSON.stringify(gridList, null, 2)))
-
-    codeToHtml(JSON.stringify(cloneList2, null, 2), {
-      lang: 'json',
-      themes: {
-        dark: 'dark-plus',
-        light: 'min-light',
-      },
-    })
-      .then((result: string) => (cloneList2CodeHighlight.value = result))
-      .catch(() => (cloneList2CodeHighlight.value = JSON.stringify(cloneList2, null, 2)))
-  },
-  {
-    immediate: true,
-  },
-)
-</script>
-<template>
-  <ScrollContainer wrapper-class="flex flex-col gap-y-2">
-    <NAlert
-      type="info"
-      closable
-    >
-      {{ APP_NAME }} 的 Tabs 栏的拖拽模块使用了
-      vue-draggable-plus,在一些拖拽的场景下,它也许可以帮助到你
-    </NAlert>
-    <NCard
-      title="基础使用"
-      :size="isMaxMd ? 'small' : undefined"
-    >
-      <NSplit
-        :direction="isMaxMd ? 'vertical' : 'horizontal'"
-        :pane1-class="isMaxMd ? 'pb-4' : 'pr-8'"
-        :pane2-class="isMaxMd ? 'pt-4' : 'pl-8'"
-        :style="{
-          height: isMaxMd ? '580px' : '280px',
-        }"
-      >
-        <template #1>
-          <NScrollbar>
-            <VueDraggable
-              ref="baseDragRef"
-              v-model="baseList"
-              :animation="150"
-              :scrollSensitivity="100"
-              ghostClass="ghost"
-              group="drag1"
-              class="flex flex-col gap-2 rounded bg-neutral-500/5 p-4 select-none"
-            >
-              <div
-                v-for="{ id, name } in baseList"
-                :key="id"
-                class="flex h-14 cursor-move items-center justify-center rounded bg-neutral-500/8 p-3"
-              >
-                {{ name }}
-              </div>
-            </VueDraggable>
-          </NScrollbar>
-        </template>
-        <template #2>
-          <NScrollbar>
-            <div v-html="baseListCodeHighlight"></div>
-          </NScrollbar>
-        </template>
-        <template #resize-trigger>
-          <div
-            class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] max-lg:h-px max-lg:w-full dark:bg-neutral-700"
-          ></div>
-        </template>
-      </NSplit>
-    </NCard>
-    <NCard
-      title="网格布局"
-      :size="isMaxMd ? 'small' : undefined"
-    >
-      <div class="mb-4">
-        你可以把<span class="text-primary">网格布局</span>的元素拖进<span class="text-primary"
-          >基础使用</span
-        >中,它们可以相互拖放
-      </div>
-      <NSplit
-        :direction="isMaxMd ? 'vertical' : 'horizontal'"
-        :pane1-class="isMaxMd ? 'pb-4' : 'pr-8'"
-        :pane2-class="isMaxMd ? 'pt-4' : 'pl-8'"
-        :style="{
-          height: isMaxMd ? '680px' : '280px',
-        }"
-        :default-size="0.7"
-      >
-        <template #1>
-          <NScrollbar>
-            <VueDraggable
-              ref="gridDragRef"
-              v-model="gridList"
-              :animation="150"
-              ghostClass="ghost"
-              group="drag1"
-              class="m-auto grid grid-cols-8 gap-2 rounded bg-neutral-500/5 p-4 select-none max-lg:grid-cols-4"
-            >
-              <div
-                v-for="{ id, name } in gridList"
-                :key="id"
-                class="flex h-14 cursor-move items-center justify-center rounded bg-neutral-500/8 p-3"
-              >
-                {{ name }}
-              </div>
-            </VueDraggable>
-          </NScrollbar>
-        </template>
-        <template #2>
-          <NScrollbar>
-            <div v-html="gridListCodeHighlight"></div>
-          </NScrollbar>
-        </template>
-        <template #resize-trigger>
-          <div
-            class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] max-lg:h-px max-lg:w-full dark:bg-neutral-700"
-          ></div>
-        </template>
-      </NSplit>
-    </NCard>
-    <NCard
-      title="克隆使用"
-      :size="isMaxMd ? 'small' : undefined"
-    >
-      <NSplit
-        :direction="isMaxMd ? 'vertical' : 'horizontal'"
-        :pane1-class="isMaxMd ? 'pb-4' : 'pr-8'"
-        :pane2-class="isMaxMd ? 'pt-4' : 'pl-8'"
-        :style="{
-          height: isMaxMd ? '780px' : '280px',
-        }"
-        :default-size="0.7"
-      >
-        <template #1>
-          <div class="flex h-full gap-4 max-lg:flex-col">
-            <NScrollbar>
-              <VueDraggable
-                ref="taskDragRef"
-                v-model="taskList"
-                :animation="150"
-                :scrollSensitivity="100"
-                ghostClass="ghost"
-                :group="{ name: 'clone', pull: 'clone', put: false }"
-                class="flex flex-col gap-2 rounded bg-neutral-500/5 p-4 select-none"
-                :clone="cloneTask"
-              >
-                <div
-                  v-for="{ id, name } in taskList"
-                  :key="id"
-                  class="flex h-14 cursor-move items-center justify-center rounded bg-neutral-500/8 p-3"
-                >
-                  {{ name }}
-                </div>
-              </VueDraggable>
-            </NScrollbar>
-            <NScrollbar>
-              <EmptyPlaceholder
-                :show="cloneTaskList.length <= 0"
-                :description="`把${isMaxMd ? '上' : '左'}边的任务拖拽到这里`"
-              />
-              <VueDraggable
-                ref="cloneTaskListDragRef"
-                v-model="cloneTaskList"
-                :animation="150"
-                :scrollSensitivity="100"
-                ghostClass="ghost"
-                group="clone"
-                class="flex h-full flex-col gap-2 rounded bg-neutral-500/5 p-4 select-none"
-                style="min-height: 300px"
-              >
-                <div
-                  v-for="item in cloneTaskList"
-                  :key="item.key"
-                  class="flex h-14 cursor-move items-center justify-between rounded bg-neutral-500/8 px-4 py-3"
-                >
-                  <span>{{ item.name }}</span>
-                  <NButton
-                    quaternary
-                    circle
-                    size="small"
-                    @click="removeTask(item)"
-                  >
-                    <template #icon>
-                      <span class="iconify ph--x"></span>
-                    </template>
-                  </NButton>
-                </div>
-              </VueDraggable>
-            </NScrollbar>
-          </div>
-        </template>
-        <template #2>
-          <NScrollbar>
-            <div v-html="cloneList2CodeHighlight"></div>
-          </NScrollbar>
-        </template>
-        <template #resize-trigger>
-          <div
-            class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] dark:bg-neutral-700"
-          ></div>
-        </template>
-      </NSplit>
-    </NCard>
-  </ScrollContainer>
-</template>

+ 0 - 51
src/views/dynamic-route/index.vue

@@ -1,51 +0,0 @@
-<script setup lang="ts">
-import { NCard, NAlert, NButton } from 'naive-ui'
-import { RouterLink, useRouter } from 'vue-router'
-
-import { ScrollContainer } from '@/components'
-import { useInjection } from '@/composables'
-import { mediaQueryInjectionKey } from '@/injection'
-
-const { isMaxMd } = useInjection(mediaQueryInjectionKey)
-
-defineOptions({
-  name: 'DynamicRoute',
-})
-
-const router = useRouter()
-</script>
-<template>
-  <ScrollContainer wrapper-class="flex flex-col gap-y-2">
-    <NAlert
-      type="info"
-      closable
-    >
-      在路由配置的 meta 中添加 enableMultiTab 属性,访问不同的动态路径时都会创建新的 tab
-    </NAlert>
-    <NCard :size="isMaxMd ? 'small' : undefined">
-      <div class="grid grid-cols-5 gap-4 max-lg:grid-cols-3 max-md:grid-cols-2 max-sm:grid-cols-1">
-        <RouterLink
-          v-for="value in 50"
-          :key="value"
-          :to="value <= 25 ? `/dynamic-route/${value}` : `/dynamic-route/${value}/${value}`"
-        >
-          <NButton
-            block
-            secondary
-            :type="
-              [`/dynamic-route/${value}`, `/dynamic-route/${value}/${value}`].includes(
-                router.currentRoute.value.fullPath,
-              )
-                ? 'primary'
-                : 'default'
-            "
-          >
-            {{
-              value <= 25 ? `/dynamic-route/${value}` : `/dynamic-route/${value}/${value}`
-            }}</NButton
-          >
-        </RouterLink>
-      </div>
-    </NCard>
-  </ScrollContainer>
-</template>

+ 0 - 36
src/views/feedback/discreteApi.ts

@@ -1,36 +0,0 @@
-import { useComponentModifier, useDiscreteApi } from '@/composables'
-
-import type { MessageType, ModalOptions, NotificationType } from 'naive-ui'
-
-const { message, modal, notification } = useDiscreteApi()
-
-const { getModalModifier } = useComponentModifier()
-
-export const useMessageDiscrete = (type: MessageType = 'info') => {
-  message.create(`${type} - 与Setup里调用单独分开显示`, {
-    type,
-    duration: 5000,
-    closable: true,
-  })
-}
-
-export const useModalDiscrete = (type: ModalOptions['type'] = 'info') => {
-  modal.create({
-    ...getModalModifier(),
-    title: `${type}`,
-    content: `Setup 外的 ${type} Dialog`,
-    preset: 'dialog',
-    type,
-    positiveText: '确认',
-    negativeText: '算了',
-  })
-}
-
-export const useNotificationDiscrete = (type: NotificationType = 'info') => {
-  notification.create({
-    type,
-    content: '又要说点啥呢',
-    meta: '我是 Setup 外的 Notification, 出现位置当然可以不一样',
-    duration: 5000,
-  })
-}

+ 0 - 464
src/views/feedback/index.vue

@@ -1,464 +0,0 @@
-<script setup lang="ts">
-import { NAlert, NCard, useMessage, NButton, useModal, NModal, useNotification } from 'naive-ui'
-import { reactive } from 'vue'
-
-import { ScrollContainer } from '@/components'
-
-import type { ModalProps } from 'naive-ui'
-
-defineOptions({
-  name: 'Feedback',
-})
-
-const APP_NAME = import.meta.env.VITE_APP_NAME
-
-const message = useMessage()
-
-const modal = useModal()
-
-import { useComponentModifier } from '@/composables'
-
-import { useMessageDiscrete, useModalDiscrete, useNotificationDiscrete } from './discreteApi'
-
-const { getModalModifier } = useComponentModifier()
-
-const notification = useNotification()
-
-const showModal = reactive({
-  modal1: false,
-  modal2: false,
-  modal3: false,
-  modal4: false,
-})
-
-const changeMessageState = () => {
-  let counter = 5
-
-  let timer: ReturnType<typeof setInterval> | null = null
-
-  const messageInstance = message.create('5秒后 根据状态更换图标', {
-    type: 'info',
-    duration: 0,
-    closable: true,
-  })
-
-  timer = setInterval(() => {
-    counter -= 1
-    if (counter <= 0) {
-      if (timer) {
-        clearInterval(timer)
-      }
-      messageInstance.type = 'error'
-      messageInstance.content = '切换图标了'
-      return
-    }
-    messageInstance.content = `${counter}秒后 根据状态更换图标`
-  }, 1000)
-}
-
-const createDialogApi = (type: ModalProps['type'] = 'success') => {
-  const title = {
-    success: '成功的Dialog',
-    warning: '警告的Dialog',
-    error: '失败的Dialog',
-    info: '信息的Dialog',
-    default: '默认的Dialog',
-  } as const
-
-  modal.create({
-    ...getModalModifier(),
-    title: `命令式${title[type]}`,
-    content: `命令式${title[type]}`,
-    preset: 'dialog',
-    type,
-    positiveText: '确认',
-    negativeText: '算了',
-  })
-}
-</script>
-<template>
-  <ScrollContainer wrapper-class="flex flex-col gap-y-2">
-    <NAlert
-      type="info"
-      closable
-    >
-      {{ APP_NAME }} 修饰了一些反馈组件的默认设计,这似乎还不错
-    </NAlert>
-    <NAlert
-      type="success"
-      closable
-    >
-      {{ APP_NAME }} 修饰了一些反馈组件的默认设计,这似乎还不错
-    </NAlert>
-    <NAlert
-      type="error"
-      closable
-    >
-      {{ APP_NAME }} 修饰了一些反馈组件的默认设计,这似乎还不错
-    </NAlert>
-    <NAlert
-      type="warning"
-      closable
-    >
-      {{ APP_NAME }} 修饰了一些反馈组件的默认设计,这似乎还不错
-    </NAlert>
-    <NCard title="Message 消息">
-      <div class="flex flex-wrap gap-4">
-        <NButton
-          type="success"
-          @click="message.success('成功Message', { closable: true })"
-        >
-          成功Message
-        </NButton>
-        <NButton
-          type="warning"
-          secondary
-          @click="message.warning('警告Message', { closable: true })"
-        >
-          警告Message
-        </NButton>
-        <NButton
-          type="error"
-          tertiary
-          @click="message.error('失败Message', { closable: true })"
-        >
-          失败Message
-        </NButton>
-        <NButton
-          type="info"
-          dashed
-          @click="message.info('信息Message', { closable: true })"
-        >
-          信息Message
-        </NButton>
-        <NButton
-          type="primary"
-          ghost
-          @click="message.loading('加载Message', { closable: true })"
-        >
-          加载Message
-        </NButton>
-        <NButton @click="changeMessageState"> 根据状态切换图标 </NButton>
-        <NCard
-          size="small"
-          title="Setup 外使用"
-          :bordered="false"
-        >
-          <div class="flex flex-wrap gap-4">
-            <NButton
-              type="success"
-              @click="useMessageDiscrete('success')"
-              size="small"
-            >
-              成功Message
-            </NButton>
-            <NButton
-              type="warning"
-              secondary
-              @click="useMessageDiscrete('warning')"
-              size="small"
-            >
-              警告Message
-            </NButton>
-            <NButton
-              type="error"
-              tertiary
-              @click="useMessageDiscrete('error')"
-              size="small"
-            >
-              Message失败
-            </NButton>
-            <NButton
-              type="info"
-              dashed
-              @click="useMessageDiscrete('info')"
-              size="small"
-            >
-              信息Message
-            </NButton>
-            <NButton
-              type="primary"
-              ghost
-              @click="useMessageDiscrete('loading')"
-              size="small"
-            >
-              加载Message
-            </NButton>
-          </div>
-        </NCard>
-      </div>
-    </NCard>
-    <NCard title="Modal 模态框">
-      <div class="flex flex-wrap gap-4">
-        <NCard
-          size="small"
-          title="Dialog 预设"
-          :bordered="false"
-        >
-          <div class="flex flex-wrap gap-4">
-            <NButton
-              type="success"
-              secondary
-              @click="showModal.modal1 = true"
-            >
-              成功Dialog
-            </NButton>
-            <NButton
-              type="warning"
-              tertiary
-              @click="showModal.modal2 = true"
-            >
-              警告Dialog
-            </NButton>
-            <NButton
-              type="error"
-              dashed
-              @click="showModal.modal3 = true"
-            >
-              失败Dialog
-            </NButton>
-            <NButton
-              type="info"
-              ghost
-              @click="showModal.modal4 = true"
-            >
-              信息Dialog
-            </NButton>
-          </div>
-        </NCard>
-        <NCard
-          size="small"
-          title="命令式 Dialog 预设"
-          :bordered="false"
-        >
-          <div class="flex flex-wrap gap-4">
-            <NButton
-              type="success"
-              size="small"
-              secondary
-              @click="createDialogApi('success')"
-            >
-              成功Dialog
-            </NButton>
-            <NButton
-              type="warning"
-              size="small"
-              tertiary
-              @click="createDialogApi('warning')"
-            >
-              警告Dialog
-            </NButton>
-            <NButton
-              type="error"
-              size="small"
-              dashed
-              @click="createDialogApi('error')"
-            >
-              失败Dialog
-            </NButton>
-            <NButton
-              type="info"
-              size="small"
-              ghost
-              @click="createDialogApi('info')"
-            >
-              信息Dialog
-            </NButton>
-          </div>
-        </NCard>
-        <NCard
-          size="small"
-          title="Setup 外使用 Dialog 预设"
-          :bordered="false"
-        >
-          <div class="flex flex-wrap gap-4">
-            <NButton
-              type="success"
-              size="small"
-              secondary
-              @click="useModalDiscrete('success')"
-            >
-              成功Dialog
-            </NButton>
-            <NButton
-              type="warning"
-              size="small"
-              tertiary
-              @click="useModalDiscrete('warning')"
-            >
-              警告Dialog
-            </NButton>
-            <NButton
-              type="error"
-              size="small"
-              dashed
-              @click="useModalDiscrete('error')"
-            >
-              失败Dialog
-            </NButton>
-            <NButton
-              type="info"
-              size="small"
-              ghost
-              @click="useModalDiscrete('info')"
-            >
-              信息Dialog
-            </NButton>
-          </div>
-        </NCard>
-      </div>
-    </NCard>
-    <NCard title="Notification 通知">
-      <div class="flex flex-wrap gap-4">
-        <NButton
-          type="success"
-          @click="
-            notification.success({
-              content: '说点啥呢',
-              meta: '我不到啊',
-              duration: 5000,
-            })
-          "
-        >
-          成功Notification
-        </NButton>
-        <NButton
-          type="warning"
-          secondary
-          @click="
-            notification.warning({
-              content: '说点啥呢',
-              meta: '我不到啊',
-              duration: 5000,
-            })
-          "
-        >
-          警告Notification
-        </NButton>
-        <NButton
-          type="error"
-          tertiary
-          @click="
-            notification.error({
-              content: '说点啥呢',
-              meta: '我不到啊',
-              duration: 5000,
-            })
-          "
-        >
-          错误Notification
-        </NButton>
-        <NButton
-          type="info"
-          ghost
-          @click="
-            notification.info({
-              content: '说点啥呢',
-              meta: '我不到啊',
-              duration: 5000,
-            })
-          "
-        >
-          信息Notification
-        </NButton>
-      </div>
-      <NCard
-        size="small"
-        :bordered="false"
-        title="Setup 外使用 Notification 通知"
-      >
-        <div class="flex flex-wrap gap-4">
-          <NButton
-            type="success"
-            size="small"
-            secondary
-            @click="useNotificationDiscrete('success')"
-          >
-            成功Notification
-          </NButton>
-          <NButton
-            type="warning"
-            secondary
-            size="small"
-            @click="useNotificationDiscrete('warning')"
-          >
-            警告Notification
-          </NButton>
-          <NButton
-            type="error"
-            tertiary
-            size="small"
-            @click="useNotificationDiscrete('error')"
-          >
-            错误Notification
-          </NButton>
-          <NButton
-            type="info"
-            ghost
-            size="small"
-            @click="useNotificationDiscrete('info')"
-          >
-            信息Notification
-          </NButton>
-        </div>
-      </NCard>
-    </NCard>
-    <NModal
-      v-bind="
-        getModalModifier({
-          type: 'success',
-        })
-      "
-      v-model:show="showModal.modal1"
-      preset="dialog"
-      title="确认"
-      content="你确认?"
-      positive-text="确认"
-      negative-text="算了"
-    >
-    </NModal>
-    <NModal
-      v-bind="
-        getModalModifier({
-          type: 'info',
-        })
-      "
-      v-model:show="showModal.modal2"
-      preset="dialog"
-      title="确认"
-      content="你确认?"
-      positive-text="确认"
-      negative-text="算了"
-    >
-    </NModal>
-    <NModal
-      v-bind="
-        getModalModifier({
-          type: 'error',
-        })
-      "
-      v-model:show="showModal.modal3"
-      type="error"
-      preset="dialog"
-      title="确认"
-      content="你确认?"
-      positive-text="确认"
-      negative-text="算了"
-    >
-    </NModal>
-    <NModal
-      v-bind="
-        getModalModifier({
-          type: 'info',
-        })
-      "
-      v-model:show="showModal.modal4"
-      preset="dialog"
-      title="确认"
-      content="你确认?"
-      positive-text="确认"
-      negative-text="算了"
-    >
-    </NModal>
-  </ScrollContainer>
-</template>

+ 233 - 0
src/views/monitoring-platform/components/ballCameraTracking.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="flex h-full flex-col">
+    <div class="mb-4 aspect-video bg-[#525252]">
+      <canvas class="bollVideo"></canvas>
+    </div>
+    <NCard
+      size="small"
+      class="flex-1"
+    >
+      <template #header>
+        <div class="flex items-center gap-2 text-lg">
+          <i class="iconify-[ri--alarm-warning-fill] iconify"></i>
+          <span>追踪模式</span>
+        </div>
+      </template>
+      <div class="flex h-full flex-col items-center gap-2">
+        <NCheckboxGroup v-model:value="typeV">
+          <NSpace item-style="display: flex;">
+            <NCheckbox
+              v-for="item in typeOptions"
+              :key="item.label"
+              :value="item.value"
+              :label="item.label"
+            />
+          </NSpace>
+        </NCheckboxGroup>
+        <div class="flex items-center gap-3">
+          <label
+            class="text-nowrap"
+            for="ai"
+            >AI模式</label
+          >
+          <NSelect
+            name="ai"
+            v-model:value="aiMode"
+            :options="aiModeOptions"
+            style="min-width: 150px"
+          />
+          <NButton
+            type="primary"
+            size="medium"
+            >应用</NButton
+          >
+        </div>
+        <div class="flex items-center gap-3">
+          <label
+            class="text-nowrap"
+            for="show"
+            >显示模式</label
+          >
+          <NSelect
+            name="show"
+            v-model:value="showMode"
+            :options="showModeOptions"
+            style="min-width: 210px"
+          />
+        </div>
+        <div class="flex items-center gap-3">
+          <label>P</label>
+          <NInput
+            v-model:value="ptData.p"
+            style="min-width: 50px"
+          />
+          <label>T</label>
+          <NInput
+            v-model:value="ptData.t"
+            style="min-width: 50px"
+          />
+          <NButton
+            type="primary"
+            size="medium"
+            >跳转</NButton
+          >
+          <NButton
+            type="primary"
+            size="medium"
+            >回零</NButton
+          >
+        </div>
+        <div class="flex-1 flex gap-2 items-center justify-center">
+          <div class="grid h-full max-h-[140px] max-w-[140px] grid-cols-3 grid-rows-3 gap-1">
+            <NButton
+              v-for="item in ptzButton"
+              :key="item.type"
+              class="!h-full !w-full"
+              type="primary"
+            >
+              <template #icon>
+                <i
+                  :class="['scale-160', item.icon]"
+                  :style="{ transform: item.rotate ? `rotate(${item.rotate}deg)` : '' }"
+                ></i>
+              </template>
+            </NButton>
+          </div>
+          <div class="grid h-full max-h-[140px] max-w-[140px] grid-cols-2 grid-rows-2 gap-1">
+            <NButton
+              v-for="item in outerBotton"
+              :key="item.type"
+              class="!h-full !w-full"
+              type="primary"
+            >
+              <template #icon>
+                <i :class="['scale-160', item.icon]"></i>
+              </template>
+            </NButton>
+          </div>
+        </div>
+      </div>
+    </NCard>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  NCard,
+  NCheckboxGroup,
+  NSpace,
+  NCheckbox,
+  NButton,
+  NSelect,
+  NInput,
+  NDivider,
+} from 'naive-ui'
+import { ref } from 'vue'
+defineOptions({
+  name: 'BallCameraTracking',
+})
+
+const typeV = ref(null)
+const typeOptions = [
+  {
+    label: '无人机',
+    value: 0,
+  },
+  {
+    label: '人形',
+    value: 1,
+  },
+  {
+    label: '车辆',
+    value: 2,
+  },
+  {
+    label: '船只',
+    value: 3,
+  },
+]
+
+const aiMode = ref(0)
+const aiModeOptions = [
+  {
+    label: '关闭AI',
+    value: 0,
+  },
+  {
+    label: '打开AI',
+    value: 1,
+  },
+]
+
+const showMode = ref()
+const showModeOptions: any[] = []
+
+const ptData = ref({
+  p: '',
+  t: '',
+})
+
+const ptzButton = [
+  {
+    icon: 'iconify iconify-[mingcute--up-fill]',
+    type: 'lt',
+    rotate: -45,
+  },
+  {
+    icon: 'iconify iconify-[mingcute--up-fill]',
+    type: 't',
+  },
+  {
+    icon: 'iconify iconify-[mingcute--up-fill]',
+    type: 'rt',
+    rotate: 45,
+  },
+  {
+    icon: 'iconify iconify-[mingcute--left-fill]',
+    type: 'l',
+  },
+  {
+    icon: 'iconify iconify-[ic--outline-loop]',
+    type: 'hr',
+  },
+  {
+    icon: 'iconify iconify-[mingcute--right-fill]',
+    type: 'r',
+  },
+  {
+    icon: 'iconify iconify-[mingcute--down-fill]',
+    rotate: 45,
+    type: 'rb',
+  },
+
+  {
+    icon: 'iconify iconify-[mingcute--down-fill]',
+    type: 'b',
+  },
+  {
+    icon: 'iconify iconify-[mingcute--down-fill]',
+    type: 'lb',
+    rotate: -45,
+  },
+]
+
+const outerBotton = [
+  {
+    icon: 'iconify iconify-[mingcute--zoom-in-fill]',
+    type: 'zoomIn',
+  },
+  {
+    icon: 'iconify iconify-[mingcute--zoom-out-fill]',
+    type: 'zoomOut',
+  },
+
+  {
+    icon: 'iconify iconify-[material-symbols--border-inner-rounded]',
+    type: 'jia',
+  },
+  {
+    icon: 'iconify iconify-[material-symbols--border-horizontal-rounded]',
+    type: 'jian',
+  },
+]
+</script>

+ 254 - 0
src/views/monitoring-platform/data-monitoring.vue

@@ -0,0 +1,254 @@
+<template>
+  <ScrollContainer
+    wrapper-class=""
+    :scrollable="false"
+  >
+    <div class="data-m-container relative flex h-full flex-col gap-4 overflow-hidden">
+      <div class="video-container-box flex">
+        <div
+          class="relative flex-1"
+          v-for="(v, i) in videoInfo"
+          :key="v"
+        >
+          <canvas :class="['dm-video' + i, 'h-30 w-full']"></canvas>
+        </div>
+      </div>
+      <div class="flex flex-1 gap-4 max-sm:flex-col">
+        <div class="flex flex-2 flex-col gap-4">
+          <div
+            class="grid flex-2 grid-cols-2 grid-rows-3 gap-1 overflow-hidden sm:grid-cols-3 lg:grid-cols-5"
+          >
+            <div
+              v-for="v in dataListComputed"
+              :key="v.EventId"
+              class="h-full w-full bg-naive-card"
+            >
+              <div class="flex h-full flex-col">
+                <img
+                  class="h-[100px] w-full object-cover"
+                  :src="v.imageFileName"
+                  alt=""
+                />
+                <div class="flex h-full items-center justify-between pl-2">
+                  <div class="flex gap-1 text-[10px]">
+                    <span :style="{ color: parseFloat(v.Score) > 0.5 ? 'red' : 'green' }">{{
+                      v.Score
+                    }}</span>
+                    <span>{{ v.EventTime.split(' ')[1] }}</span>
+                    <span>{{ v.Type == '0' ? '鸟' : '未知' }}</span>
+                  </div>
+                  <NButton
+                    quaternary
+                    circle
+                  >
+                    <template #icon>
+                      <i class="iconify-[fe--warning] iconify"></i>
+                    </template>
+                  </NButton>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="flex-1">
+            <NDataTable
+              :columns="columns"
+              :data="dataList"
+              :single-line="false"
+              :loading="isRequestLoading"
+              flex-height
+              size="small"
+              virtual-scroll
+              :class="[!isMaxLg ? 'h-full' : 'h-200px']"
+            />
+          </div>
+        </div>
+        <div class="flex-1">
+          <BallCameraTracking />
+        </div>
+      </div>
+      <!-- <div
+        v-if="loading"
+        class="absolute top-0 right-0 bottom-0 left-0 flex items-center justify-center bg-[rgba(0,0,0,0.1)]"
+      >
+        <NSpin size="medium" />
+      </div> -->
+    </div>
+  </ScrollContainer>
+</template>
+
+<script setup lang="ts">
+import dayjs from 'dayjs'
+import { NDataTable, NButton } from 'naive-ui'
+import useWorker from 'omnimatrix-video-player'
+import { onMounted, onUnmounted, ref, h, watch, computed } from 'vue'
+
+import { useRequest } from '@/api'
+import { ScrollContainer } from '@/components'
+import { useInjection } from '@/composables'
+import { mediaQueryInjectionKey } from '@/injection'
+import { useAlarmStore } from '@/stores'
+
+import BallCameraTracking from './components/ballCameraTracking.vue'
+
+import type { DataTableColumns } from 'naive-ui'
+// import { enableImageManipulation } from '@/utils'
+interface RowData {
+  EventId: string
+  EventTime: string
+  Score: string
+  Pan: string
+  Tilt: string
+  Type: string
+  Status: number
+  imageFileName?: string
+}
+
+defineOptions({ name: 'DataMonitoring' })
+
+const { isMaxLg } = useInjection(mediaQueryInjectionKey)
+const { API_ALERT_ESEARCH_GET } = useRequest()
+const alarmStore = useAlarmStore()
+const { getFileUrl } = useAlarmStore()
+
+const workerObj: any[] = []
+const videoInfo = ref([
+  `wss://${alarmStore.ipF}/VideoShow/Preview/Full/Main?DeviceID=1`,
+  `wss://${alarmStore.ipF}/VideoShow/Preview/Full/Main?DeviceID=2`,
+])
+const loading = ref(false)
+const isRequestLoading = ref(false)
+const tableRowsStyle = { fontSize: '12px' }
+const columns: DataTableColumns<RowData> = [
+  {
+    key: 'number',
+    title: '序号',
+    width: 90,
+    align: 'center',
+    render(row, index) {
+      return h('span', { style: tableRowsStyle }, index + 1)
+    },
+  },
+  {
+    title: '事件ID',
+    key: 'EventId',
+    width: 150,
+    align: 'center',
+    render(row) {
+      return h('span', { style: tableRowsStyle }, row.EventId)
+    },
+  },
+  {
+    title: '日期时间',
+    key: 'EventTime',
+    align: 'center',
+    width: 200,
+    render(row) {
+      return h('span', { style: tableRowsStyle }, row.EventTime)
+    },
+  },
+  {
+    title: '置信度',
+    key: 'Score',
+    align: 'center',
+    render(row) {
+      return h(
+        'span',
+        { style: tableRowsStyle },
+        parseFloat(row.Score) > 0.5
+          ? h('span', { style: { color: 'red' } }, row.Score)
+          : h('span', { style: { color: 'green' } }, row.Score),
+      )
+    },
+  },
+  {
+    title: '位置',
+    key: 'Pan',
+    align: 'center',
+    render(row) {
+      return h('span', { style: tableRowsStyle }, `${row.Pan}, ${row.Tilt}`)
+    },
+  },
+  {
+    title: '类型',
+    key: 'Type',
+    align: 'center',
+    width: 90,
+    render(row) {
+      return h('span', { style: tableRowsStyle }, '鸟')
+    },
+  },
+  {
+    title: '状态',
+    key: 'Status',
+    width: 90,
+    align: 'center',
+    render(row) {
+      return h(
+        'span',
+        { style: tableRowsStyle },
+        row.Status
+          ? h('span', { style: { color: 'green' } }, '已处理')
+          : h('span', { style: { color: 'red' } }, '未处理'),
+      )
+    },
+  },
+]
+
+const dataList = ref<RowData[]>([])
+const dataListComputed = computed(() => dataList.value.slice(0, 15))
+
+const showVideo = () => {
+  videoInfo.value.forEach((item: string, i: number) => {
+    workerObj.push(
+      useWorker(item, '.dm-video' + i, () => {
+        // enableImageManipulation(
+        //   document.querySelector('.video-container'),
+        //   document.querySelector('.video-container-box'),
+        //   {
+        //     enableZoom: true, // 启用缩放
+        //     enableDrag: true, // 启用拖动
+        //     minScale: 1, // 最小缩放比例
+        //     dragSpeed: 1.9, // 拖动速度
+        //   },
+        // )
+        loading.value = false
+      }),
+    )
+  })
+}
+
+watch(
+  () => alarmStore.alarms,
+  (v) => {
+    console.log(v)
+  },
+)
+async function getDataList() {
+  isRequestLoading.value = true
+  const res: any = await API_ALERT_ESEARCH_GET({ page: 1, page_size: 20 }).finally(() => {
+    isRequestLoading.value = false
+  })
+
+  dataList.value = res.items.map((item: any) => ({
+    EventId: item.event_id,
+    EventTime: dayjs(item.event_timestamp).format('YYYY-MM-DD HH:mm:ss'),
+    Score: item.confidence_score,
+    Pan: item.pan_angle,
+    Tilt: item.tilt_angle,
+    Type: item.alarm_type,
+    Status: item.status,
+    imageFileName: getFileUrl(item.image_file_name),
+  }))
+}
+
+onMounted(() => {
+  showVideo()
+  getDataList()
+})
+
+onUnmounted(() => {
+  workerObj.forEach((item: any) => {
+    item.close()
+  })
+})
+</script>

+ 1 - 2
src/views/monitoring-platform/index.vue → src/views/monitoring-platform/video-monitoring.vue

@@ -78,7 +78,7 @@ interface IraRes {
   index: number
 }
 
-defineOptions({ name: 'MonitoringPlatform' })
+defineOptions({ name: 'VideoMonitoring' })
 
 const { API_RA_GET } = useRequest()
 const alarmStore = useAlarmStore()
@@ -145,7 +145,6 @@ const dblclickFn = async (event: MouseEvent, DeviceID: number) => {
     t: res.Top * ratioY,
     index: DeviceID - 1,
   }
-  console.log(rsRes.value)
 }
 
 let partWorker: any = null

+ 0 - 14
src/views/multi-level-menu/index.vue

@@ -1,14 +0,0 @@
-<script setup lang="ts">
-import { useRoute } from 'vue-router'
-
-import { ScrollContainer } from '@/components'
-
-const route = useRoute()
-</script>
-<template>
-  <ScrollContainer>
-    <div class="flex h-96 items-center justify-center text-3xl">
-      {{ route.meta.title }}
-    </div>
-  </ScrollContainer>
-</template>