Ver código fonte

空间管理-新增

lvkun 2 anos atrás
pai
commit
c3c53eb743

+ 2 - 2
config/proxy.ts

@@ -12,8 +12,8 @@ module.exports = {
     },
     '/rts': {
       target: 'http://124.222.113.37:8080',
-      changeOrigin: true,
-      pathRewrite: { '^/rts': '' }
+      changeOrigin: true
+      // pathRewrite: { '^/cvs': '' }
     },
     '/iot': {
       target: 'http://124.222.113.37:8888',

+ 4 - 4
src/api/cvs/video.ts

@@ -3,7 +3,7 @@ import request from '@/service/request'
 
 export const getSpace = (params: COMMON.API.QueryParams) => {
   return request<CVS.space[]>({
-    url: '/api/space',
+    url: '/space/page',
     method: 'GET',
     params
   })
@@ -11,7 +11,7 @@ export const getSpace = (params: COMMON.API.QueryParams) => {
 
 export const addSpace = (data: Partial<CVS.space>) => {
   return request<string>({
-    url: '/api/space',
+    url: '/cvs/space',
     method: 'POST',
     data
   })
@@ -19,7 +19,7 @@ export const addSpace = (data: Partial<CVS.space>) => {
 
 export const updateSpace = (data: CVS.space) => {
   return request<string>({
-    url: '/api/space',
+    url: '/cvs/space',
     method: 'PUT',
     data
   })
@@ -27,7 +27,7 @@ export const updateSpace = (data: CVS.space) => {
 
 export const delSpace = (id: string) => {
   return request<string>({
-    url: `/api/space?id=${id}`,
+    url: `/cvs/space?id=${id}`,
     method: 'DELETE'
   })
 }

+ 11 - 39
src/components/StepModal/index.less

@@ -1,57 +1,29 @@
 
+v-deep .ant-modal {
+  background-color: #f7f7f9;
+}
 
 .step-modal {
-  width: 100vw;
-  height: 100vh;
-  background-color: #f7f7f9;
-  position: fixed;
-  top: 0;
-  left: 0;
+  min-width: 1000px;
+  height: 800px;
   z-index: 20;
-  .navbar {
-    width: 100vw;
-    height: 50px;
-    padding: 0 16px;
-    background-color: #fff;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    
-  }
   .steps {
     width: 70%;
     margin: 24px auto;
   }
-  .content {
-    padding: 0 16px;
+  .step-modal-content {
+    height: 600px;
+    overflow-y: scroll;
   }
   .footer {
-    width: 100vw;
-    height: 80px;
-    box-shadow: 0px 1px 1px #000;
+    width: 100%;
+    height: 60px;
     position: absolute;
     left: 0;
     bottom: 0;
     padding-left: 40px;
     background-color: #fff;
   }
-  
-}
 
+}
 
-.full-modal {
-  .ant-modal {
-    max-width: 100%;
-    top: 0;
-    padding-bottom: 0;
-    margin: 0;
-  }
-  .ant-modal-content {
-    display: flex;
-    flex-direction: column;
-    height: calc(100vh);
-  }
-  .ant-modal-body {
-    flex: 1;
-  }
-}

+ 9 - 13
src/components/StepModal/index.tsx

@@ -1,4 +1,4 @@
-import { Button, Col, Row, Space, Steps, Modal } from 'ant-design-vue'
+import { Button, Col, Row, Space, Steps, Modal, Card } from 'ant-design-vue'
 import { defineComponent, PropType, computed, ref, createApp } from 'vue'
 import './index.less'
 
@@ -34,6 +34,7 @@ export const StepModal = defineComponent({
     // 是否允许当前进行下一步
     const nextStep = () => {
       writeSteped.value.push(writeSteped.value.length)
+      ctx.emit('next')
     }
 
     const close = () => {
@@ -43,24 +44,19 @@ export const StepModal = defineComponent({
     return () => (
       <Modal
         class='step-modal'
-        visible={true}
-        width='100%'
+        visible={props.visible}
         title={false}
         closable={false}
-        wrapClassName="full-modal"
         footer={null}
       >
-        <Row class='navbar' >
-          <Col style="cursor: pointer;scale: 1.2" > &lt; 返回</Col>
-        </Row>
         <Row class='steps' >
-        <Steps
-          size="small"
-          current={1}
-          items={props.steps}
-        ></Steps>
+          <Steps
+            size="small"
+            current={props.step}
+            items={props.steps}
+          ></Steps>
         </Row>
-        <Row class='content' >
+        <Row class='step-modal-content' >
           {ctx.slots.default!()}
         </Row>
         <Row class='footer' align='middle' justify='space-between' >

+ 32 - 10
src/components/TableProV2/index.tsx

@@ -3,7 +3,7 @@ import {
   Button, DropdownButton, PaginationProps
 } from 'ant-design-vue'
 import { DownOutlined } from '@ant-design/icons-vue'
-import { PropType, computed, defineComponent, reactive, ref, defineEmits, FunctionalComponent } from 'vue'
+import { PropType, computed, defineComponent, reactive, ref, defineEmits, FunctionalComponent, onMounted } from 'vue'
 
 /**
  * @description Table Pro 超级table 将各种业务与操作融合起来
@@ -19,15 +19,16 @@ interface Props {
   request: {
     get: (record: any) => {data, sum},
     params: any
-  }
+  },
+  easy: boolean
 }
 
 const TablePro: FunctionalComponent<Props> = (props) => {
-  const { request } = props
+  const { request, easy, columns } = props
 
   console.log('defineComponent:', props)
 
-  const columns = [{ title: '', key: 'ky' }]
+  // const columns = [{ title: '', key: 'ky' }]
 
   const emits = defineEmits(['add'])
 
@@ -46,7 +47,7 @@ const TablePro: FunctionalComponent<Props> = (props) => {
     onChange: (page: number, pageSize: number) => onChangePage(page, pageSize)
   }, typeof props.pagination === 'boolean' ? {} : { ...props.pagination }))
 
-  const dataSource = ref()
+  const dataSource = ref([])
 
   const opraMeun = (
     <Menu>
@@ -69,6 +70,8 @@ const TablePro: FunctionalComponent<Props> = (props) => {
   }
 
   const dispatchRequest = async () => {
+    console.log('调用')
+
     loading.value = true
     const { data, sum } = await request.get({
       ...request.params,
@@ -80,6 +83,14 @@ const TablePro: FunctionalComponent<Props> = (props) => {
     dataSource.value = data
   }
 
+  // dispatchRequest()
+
+  onMounted(() => {
+    console.log('onMounted')
+
+    dispatchRequest()
+  })
+
   /**
    * 展示或者隐藏对应的column
    */
@@ -87,10 +98,12 @@ const TablePro: FunctionalComponent<Props> = (props) => {
     columnsPro.value[index].hidden = !columnsPro.value[index].hidden
   }
 
-  return (
+  const RenderOpraRow = () => {
+    return (
       <>
-
-        <Row gutter={[8, 8]} style={{ width: '100%' }} >
+        {easy
+          ? null
+          : <Row gutter={[8, 8]}>
           <Col span={12} >
             {/* <solt name="search" ></solt> */}
           </Col>
@@ -126,12 +139,21 @@ const TablePro: FunctionalComponent<Props> = (props) => {
                 </DropdownButton>
             </Space>
           </Col>
-        </Row>
-        {/*       dataSource={dataSource} */}
+        </Row>}
+      </>
+    )
+  }
+
+  return (
+      <>
+        <RenderOpraRow />
+        {/*       */}
         <Table
+          style="margin-top: 20px;"
           columns={columnsFilter.value}
           loading={loading.value}
           pagination={typeof props.pagination === 'boolean' ? false : pagination}
+          dataSource={dataSource.value}
         >
           <slot></slot>
           <slot name='action'></slot>

+ 13 - 0
src/controller/cvs/spaceController.ts

@@ -2,6 +2,19 @@ import { addSpace, delSpace, getSpace, updateSpace } from '@/api/cvs/video'
 import { message } from 'ant-design-vue'
 
 export class SpaceController {
+  static type = [
+    { key: 'RTMP', value: 'RTMP', label: 'RTMP' },
+    { key: 'GB28181', value: 'GB28181', label: 'GB28181' },
+    { key: 'RTSP', value: 'RTSP', label: 'RTSP' }
+  ]
+
+  static area = [
+    { key: '华北', value: '华北', label: '华北' },
+    { key: '华南', value: '华南', label: '华南' },
+    { key: '华东', value: '华东', label: '华东' },
+    { key: '华中', value: '华中', label: '华中' }
+  ]
+
   static async page (params: COMMON.API.QueryParams) {
     const { code, data } = await getSpace(params)
     if (code === 200) {

+ 2 - 0
src/controller/index.ts

@@ -12,3 +12,5 @@ export { OtaController } from './iot/ota'
 export { UserController } from './user/index'
 
 export { DataSourceController } from './schedule/dataSource'
+
+export { SpaceController } from './cvs/spaceController'

+ 1 - 1
src/hooks/effect.ts

@@ -55,7 +55,7 @@ export const usePort = (title: string) => {
   if (title === '物联网') {
     setBaseUrl('/iot')
   } else if (title === '视联网') {
-    setBaseUrl('/rts')
+    setBaseUrl('/cvs')
   } else if (title === '用户群组') {
     setBaseUrl('/user')
   } else {

+ 15 - 3
src/pages/Iot/dashboard/deviceAccess/index.vue

@@ -31,10 +31,16 @@
     <a-card
       style="margin-top: 20px"
     >
-      <TablePro
-        :request="CommonController.getTransport"
+      <a-table
+        :dataSource="dataSource"
         :columns="columns"
-      />
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'action'">
+            <a @click="useCopy(record.address)" >复制</a>
+          </template>
+        </template>
+      </a-table>
     </a-card>
 
     <!-- 资源 -->
@@ -56,6 +62,7 @@
 
 <script setup lang="ts" >
 import { CommonController, DeviceContriller, ModelController, RuleController } from '@/controller/index'
+import { useCopy } from '@/hooks/dom'
 import { useStaticImg } from '@/utils/static'
 import { nextTick, onMounted, reactive, Ref, ref, watch, watchEffect } from 'vue'
 
@@ -112,6 +119,8 @@ const sourceList = reactive([
   }
 ])
 
+const dataSource = ref([])
+
 const getStatisList = async () => {
   ModelController.count().then(r => {
     // sourceList[0].value = r.data.TOTAL
@@ -134,6 +143,9 @@ const getStatisList = async () => {
 
 onMounted(() => {
   getStatisList()
+  CommonController.getTransport().then(r => {
+    dataSource.value = r
+  })
 })
 
 </script>

+ 221 - 7
src/pages/cvs/video/space.vue

@@ -5,26 +5,174 @@
     <a-col><a-button type="primary"  @click="openModal">创建空间</a-button></a-col>
   </a-row>
 
-  <!-- <StepModal
+  <table-pro
+
+    :columns="columns"
+    :easy="true"
+  >
+
+  </table-pro>
+
+ <StepModal
     :step="step"
     :steps="steps"
     :visible="visible"
+    @close="visible = false"
+    @next="stepNext"
   >
-    7788
-  </StepModal> -->
+  <a-form  style="width: 100%;display: block;" :labelCol="{span: 2}" :wrapperCol="{span: 14}" >
+    <div v-show="step === 0" >
+      <a-form-item label="空间名称" v-bind="validateInfos.edgeName"  >
+        <InputTsx allowClear placeholder="请输入空间名称" v-model:value="spaceState.edgeName" />
+      </a-form-item>
+      <a-form-item  label="空间类型" v-bind="validateInfos.edgeName"  >
+        <a-radio-group v-model:value="spaceState.type" button-style="solid">
+          <a-radio-button
+            v-for="item in SpaceController.type"
+            :key="item.key"
+            :value="item.value"
+          >
+            {{item.label}}
+          </a-radio-button>
+        </a-radio-group>
+      </a-form-item>
+      <a-form-item label="空间描述" >
+        <a-textarea v-model:value="spaceState.description" placeholder="请输入空间描述" :rows="4" />
+      </a-form-item>
+    </div>
+    <div v-show="step === 1"  style="width: 100%;" >
+      <a-row class="section" >
+        <a-col class="title" :span="24" >推流鉴权</a-col>
+        <a-col class="content" >
+          <a-row class="content-item"  >
+            <a-col span="4" >推流配置:</a-col>
+            <a-col>
+              <a-radio-group v-model:value="spaceState.config.upstreamAuth.enabled" name="radioGroup">
+                <a-radio :value="false">关闭</a-radio>
+                <a-radio :value="true">开启</a-radio>
+              </a-radio-group>
+            </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.upstreamAuth.enabled">
+            <a-col span="4" >推流鉴权密钥:</a-col>
+            <a-col><InputTsx placeholder="请输入推流鉴权密钥" v-model:value="spaceState.config.upstreamAuth.key" /> </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.upstreamAuth.enabled">
+            <a-col span="4" >过期时间: </a-col>
+            <a-col><a-input-number id="inputNumber"  :min="1"    v-model:value="spaceState.config.upstreamAuth.expire"  /> 分钟 </a-col>
+          </a-row>
+        </a-col>
+      </a-row>
+      <a-row class="section" >
+        <a-col class="title" :span="24" >观看鉴权</a-col>
+        <a-col class="content" >
+          <a-row class="content-item" >
+            <a-col span="4" >观看配置:</a-col>
+            <a-col>
+              <a-radio-group v-model:value="spaceState.config.downstreamAuth.enabled" name="radioGroup">
+                <a-radio :value="false">关闭</a-radio>
+                <a-radio :value="true">开启</a-radio>
+              </a-radio-group>
+            </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.downstreamAuth.enabled"  >
+            <a-col span="4" >观看鉴权密钥:</a-col>
+            <a-col><InputTsx placeholder="请输入观看鉴权密钥" v-model:value="spaceState.config.downstreamAuth.key" /> </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.downstreamAuth.enabled"  >
+            <a-col span="4" >过期时间: </a-col>
+            <a-col><a-input-number id="inputNumber"  :min="1" v-model:value="spaceState.config.downstreamAuth.expire"   /> 分钟 </a-col>
+          </a-row>
+        </a-col>
+      </a-row>
+      <a-row class="section" >
+        <a-col class="title" :span="24" >录制回放配置</a-col>
+        <a-col class="content" >
+          <a-row class="content-item" >
+            <a-col span="4" >基础配置:</a-col>
+            <a-col>
+              <a-radio-group v-model:value="spaceState.config.recording.enabled" name="radioGroup">
+                <a-radio :value="false">关闭</a-radio>
+                <a-radio :value="true">开启</a-radio>
+              </a-radio-group>
+            </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.recording.enabled"  >
+            <a-col span="4">单个文件时常: </a-col>
+            <a-col><a-input-number id="inputNumber"  :min="1" v-model:value="spaceState.config.recording.duration"   /> 分钟 </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.recording.enabled"  >
+            <a-col span="4" >存储格式: </a-col>
+            <a-col>
+              <a-radio-group v-model:value="spaceState.config.recording.format" name="radioGroup">
+                <a-radio value="MP4">MP4</a-radio>
+                <a-radio value="FLV">FLV</a-radio>
+                <a-radio value="M3U8">M3U8</a-radio>
+              </a-radio-group>
+            </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.recording.enabled"  >
+            <a-col span="4" >录制方式: </a-col>
+            <a-col>周期录制 </a-col>
+          </a-row>
+        </a-col>
+      </a-row>
+      <a-row class="section" >
+        <a-col class="title" :span="24" >截图配置</a-col>
+        <a-col class="content" >
+          <a-row class="content-item" >
+            <a-col span="4" >截图配置:</a-col>
+            <a-col>
+              <a-radio-group v-model:value="spaceState.config.thumbnail.enabled" name="radioGroup">
+                <a-radio :value="false">关闭</a-radio>
+                <a-radio :value="true">开启</a-radio>
+              </a-radio-group>
+            </a-col>
+          </a-row>
+          <a-row class="content-item" v-show="spaceState.config.thumbnail.enabled"  >
+            <a-col span="4" >截图周期: </a-col>
+            <a-col><a-input-number id="inputNumber"  :min="1" v-model:value="spaceState.config.thumbnail.interval"   /> 秒 </a-col>
+          </a-row>
+        </a-col>
+      </a-row>
+      <a-row class="section" >
+        <a-col class="title" :span="24" >AI配置</a-col>
+        <a-col class="content" >
+          <a-row class="content-item" >
+            <a-col span="4" >AI配置:</a-col>
+            <a-col>
+              <a-radio-group v-model:value="spaceState.config.aiConfig.enabled" name="radioGroup">
+                <a-radio :value="false">关闭</a-radio>
+                <a-radio :value="true">开启</a-radio>
+              </a-radio-group>
+            </a-col>
+          </a-row>
+          <!-- <a-row class="content-item" v-show="spaceState.config.thumbnail.enabled"  >
+            <a-col span="4" >截图周期: </a-col>
+            <a-col><a-input-number id="inputNumber"  :min="1" v-model:value="spaceState.config.thumbnail.interval"   /> 秒 </a-col>
+          </a-row> -->
+        </a-col>
+      </a-row>
+    </div>
+  </a-form>
+  </StepModal>
 </a-card>
 </template>
 <script lang='ts' setup >
 
 import { StepModal } from '@/components/StepModal'
 import { InputTsx } from '@/components/MicroComponents/index'
-import { ref } from 'vue'
+import { ref, reactive } from 'vue'
+import { Form } from 'ant-design-vue'
+import { SpaceController } from '@/controller'
+
+const useForm = Form.useForm
 
-const steps = [{ title: '空间基本信息' }, { title: '空间配置' }, { title: '选择计费类型' }, { title: '确认订单' }]
+const steps = [{ title: '空间基本信息' }, { title: '空间配置' }]
 
-const step = ref(0)
+const step = ref(1)
 
-const visible = ref<boolean>(false)
+const visible = ref<boolean>(true)
 
 const columns = [
   {
@@ -69,10 +217,76 @@ const columns = [
   }
 ]
 
+const spaceState = reactive<CVS.space>({
+  description: '',
+  deviceCount: null,
+  deviceMode: '',
+  edgeId: null,
+  edgeName: '',
+  serialId: '',
+  spaceId: null,
+  status: '',
+  type: 'RTMP',
+  config: {
+    upstreamAuth: {
+      enabled: false,
+      key: '',
+      expire: 1
+    },
+    downstreamAuth: {
+      enabled: false,
+      key: '',
+      expire: 1
+    },
+    recording: {
+      enabled: true,
+      duration: 1,
+      format: 'FLV',
+      recordType: 'PERIOD',
+      bucket: 'cgtt8ufaq9pp493hqy8'
+    },
+    thumbnail: {
+      enabled: true,
+      interval: 1,
+      bucket: 'cgtt8ufaq9pp493hqy8'
+    },
+    aiConfig: {
+      enabled: true,
+      configuration: [],
+      bucket: 'cgtt8ufaq9pp493hqy8'
+    }
+  }
+})
+
+const { resetFields, validate, validateInfos } = useForm(spaceState, {
+  edgeName: [{ required: true, message: '请填写空间名称' }],
+  type: [{ required: true }]
+})
+
 const openModal = () => visible.value = true
 
 const closeModal = () => visible.value = false
 
+const stepNext = () => {
+  step.value++
+}
+
 </script>
 <style lang='less' scoped >
+.section {
+  width: 100%;
+  margin-bottom: 20px;
+  .title {
+    font-size: 20px;
+  }
+  .content {
+    margin-top: 20px;
+    width: 100%;
+    .content-item {
+      margin-bottom: 20px;
+      display: flex;
+      align-items: center;
+    }
+  }
+}
 </style>

+ 1 - 0
src/router/index.ts

@@ -325,6 +325,7 @@ const cvs = {
       path: '/cvs/video',
       name: '视频接入',
       icon: '',
+      redirect: '/cvs/video/space',
       children: [
         {
           path: '/cvs/video/space',

+ 38 - 8
src/type/cvs.d.ts

@@ -1,13 +1,43 @@
 declare namespace CVS {
   interface space {
     description: string
-    deviceCount: number
-    deviceMode: string
-    edgeId: number
-    edgeName: string
-    serialId: string
-    spaceId: number
-    status: 'RUNNING' | 'STOPPED' | 'OPERATING'
-    type: 'RTMP' | 'GB28181' | 'ONVIF' | 'BVCP' | 'RTSP' | 'JT808'
+    deviceCount: number | null
+    deviceMode: string | null
+    edgeId: number | null
+    edgeName: string | null
+    serialId: string | null
+    spaceId: number | null
+    status: 'RUNNING' | 'STOPPED' | 'OPERATING' | ''
+    type: 'RTMP' | 'GB28181' | 'ONVIF' | 'BVCP' | 'RTSP' | 'JT808' | '',
+    config: {
+      upstreamAuth: {
+        enabled: boolean
+        key: string
+        expire: number
+      },
+      downstreamAuth: {
+        enabled: boolean
+        key: string
+        expire: number
+      }
+      recording: {
+        'enabled': boolean,
+        'duration': number, // 单个文件市长,显示分钟 传递秒
+        'format': 'FLV',
+        'recordType': 'PERIOD' // 这个保留 只有周期录制
+        'bucket': 'cgtt8ufaq9pp493hqy8'
+      }
+      thumbnail: {
+        enabled: boolean
+        interval: number
+        bucket: 'cgtt8ufaq9pp493hqy8'
+      }
+      // ai 配置
+      'aiConfig': {
+        'enabled': boolean,
+        'configuration': [],
+        'bucket': 'cgtt8ufaq9pp493hqy8',
+      }
+    }
   }
 }