Commit a2b21f85eae544d52ec3203c13247f618423a1be
1 parent
c79a9d7e
feat: add streaming media config page
Showing
5 changed files
with
380 additions
and
1 deletions
| 1 | import { defHttp } from '/@/utils/http/axios'; | 1 | import { defHttp } from '/@/utils/http/axios'; | 
| 2 | -import { CameraModel, CameraQueryParam } from './model/cameraModel'; | 2 | +import { | 
| 3 | + CameraModel, | ||
| 4 | + CameraQueryParam, | ||
| 5 | + StreamingMediaDeleteParam, | ||
| 6 | + StreamingQueryParam, | ||
| 7 | + StreamingSubmitParam, | ||
| 8 | +} from './model/cameraModel'; | ||
| 3 | 9 | ||
| 4 | enum CameraManagerApi { | 10 | enum CameraManagerApi { | 
| 5 | CAMERA_POST_URL = '/video', | 11 | CAMERA_POST_URL = '/video', | 
| 6 | CAMERA_GET_URL = '/video', | 12 | CAMERA_GET_URL = '/video', | 
| 7 | CAMERA_DELETE_URL = '/video', | 13 | CAMERA_DELETE_URL = '/video', | 
| 8 | CAMERA_GET_DETAIL_URL = '/video', | 14 | CAMERA_GET_DETAIL_URL = '/video', | 
| 15 | + STERAMING_GET_URL = '/video/platform', | ||
| 16 | + STERAMING_POST_URL = '/video/platform', | ||
| 17 | + STERAMING_DELETE_URL = '/video/platform', | ||
| 9 | } | 18 | } | 
| 10 | 19 | ||
| 11 | export const cameraPage = (params: CameraQueryParam) => { | 20 | export const cameraPage = (params: CameraQueryParam) => { | 
| @@ -42,3 +51,39 @@ export const getCameraManageDetail = (id: string) => { | @@ -42,3 +51,39 @@ export const getCameraManageDetail = (id: string) => { | ||
| 42 | url: CameraManagerApi.CAMERA_GET_DETAIL_URL + `/${id}`, | 51 | url: CameraManagerApi.CAMERA_GET_DETAIL_URL + `/${id}`, | 
| 43 | }); | 52 | }); | 
| 44 | }; | 53 | }; | 
| 54 | + | ||
| 55 | +/** | ||
| 56 | + * @description 获取流媒体列表 | ||
| 57 | + * @param params | ||
| 58 | + * @returns | ||
| 59 | + */ | ||
| 60 | +export const getStreamingMediaList = (params: StreamingQueryParam) => { | ||
| 61 | + return defHttp.get({ | ||
| 62 | + url: CameraManagerApi.STERAMING_GET_URL, | ||
| 63 | + params, | ||
| 64 | + }); | ||
| 65 | +}; | ||
| 66 | + | ||
| 67 | +/** | ||
| 68 | + * @description 更新/新增流媒体记录 | ||
| 69 | + * @param params | ||
| 70 | + * @returns | ||
| 71 | + */ | ||
| 72 | +export const createOrUpdateStreamingMediaRecord = (params: StreamingSubmitParam) => { | ||
| 73 | + return defHttp.post({ | ||
| 74 | + url: CameraManagerApi.STERAMING_POST_URL, | ||
| 75 | + params, | ||
| 76 | + }); | ||
| 77 | +}; | ||
| 78 | + | ||
| 79 | +/** | ||
| 80 | + * @description 删除流媒体记录 | ||
| 81 | + * @param params | ||
| 82 | + * @returns | ||
| 83 | + */ | ||
| 84 | +export const deleteStreamingMediaRecord = (params: StreamingMediaDeleteParam) => { | ||
| 85 | + return defHttp.delete({ | ||
| 86 | + url: CameraManagerApi.STERAMING_POST_URL, | ||
| 87 | + params, | ||
| 88 | + }); | ||
| 89 | +}; | 
| @@ -35,3 +35,34 @@ export interface CameraModel { | @@ -35,3 +35,34 @@ export interface CameraModel { | ||
| 35 | updater?: string; | 35 | updater?: string; | 
| 36 | videoUrl: string; | 36 | videoUrl: string; | 
| 37 | } | 37 | } | 
| 38 | + | ||
| 39 | +export interface StreamingMediaModel { | ||
| 40 | + id: string; | ||
| 41 | + creator: string; | ||
| 42 | + createTime: string; | ||
| 43 | + enabled: boolean; | ||
| 44 | + tenantId: string; | ||
| 45 | + type: number; | ||
| 46 | + host: string; | ||
| 47 | + appKey: string; | ||
| 48 | + appSecret: string; | ||
| 49 | + ssl: number; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +export interface StreamingQueryParam { | ||
| 53 | + host?: string; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +export interface StreamingSubmitParam { | ||
| 57 | + type: number; | ||
| 58 | + ssl: number; | ||
| 59 | + host: string; | ||
| 60 | + appKey: string; | ||
| 61 | + appSecret: string; | ||
| 62 | + id?: string; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +export interface StreamingMediaDeleteParam { | ||
| 66 | + tenantId?: string; | ||
| 67 | + ids: string[]; | ||
| 68 | +} | 
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | ||
| 3 | + import { BasicForm, useForm } from '/@/components/Form'; | ||
| 4 | + import { formDetailSchema } from './config.data'; | ||
| 5 | + import type { DrawerParams } from './config.data'; | ||
| 6 | + import { ref, computed, unref } from 'vue'; | ||
| 7 | + import { createOrUpdateStreamingMediaRecord } from '/@/api/camera/cameraManager'; | ||
| 8 | + import { StreamingMediaModel } from '/@/api/camera/model/cameraModel'; | ||
| 9 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 10 | + | ||
| 11 | + const emit = defineEmits({ | ||
| 12 | + success: null, | ||
| 13 | + }); | ||
| 14 | + | ||
| 15 | + const createFlag = ref(false); | ||
| 16 | + | ||
| 17 | + const getDrawerTitle = computed(() => `${unref(createFlag) ? '新增' : '编辑'}流媒体配置`); | ||
| 18 | + | ||
| 19 | + let id: Nullable<string> = null; | ||
| 20 | + | ||
| 21 | + const [register, { getFieldsValue, setFieldsValue, resetFields, validate }] = useForm({ | ||
| 22 | + schemas: formDetailSchema, | ||
| 23 | + labelWidth: 90, | ||
| 24 | + showActionButtonGroup: false, | ||
| 25 | + }); | ||
| 26 | + | ||
| 27 | + const [registerDrawer, { setDrawerProps }] = useDrawerInner((data: DrawerParams) => { | ||
| 28 | + createFlag.value = data.createFlag; | ||
| 29 | + if (!unref(createFlag)) { | ||
| 30 | + id = data.record!.id; | ||
| 31 | + setFieldsValue(data.record); | ||
| 32 | + } else { | ||
| 33 | + resetFields(); | ||
| 34 | + } | ||
| 35 | + }); | ||
| 36 | + | ||
| 37 | + const handleSubmit = async () => { | ||
| 38 | + await validate(); | ||
| 39 | + let value = getFieldsValue() as StreamingMediaModel; | ||
| 40 | + let message = '修改成功'; | ||
| 41 | + if (unref(createFlag)) message = '添加成功'; | ||
| 42 | + else value = { ...value, id: id! }; | ||
| 43 | + try { | ||
| 44 | + setDrawerProps({ | ||
| 45 | + confirmLoading: true, | ||
| 46 | + }); | ||
| 47 | + await createOrUpdateStreamingMediaRecord(value); | ||
| 48 | + const { createMessage } = useMessage(); | ||
| 49 | + createMessage.success(message); | ||
| 50 | + emit('success'); | ||
| 51 | + } catch (e) { | ||
| 52 | + } finally { | ||
| 53 | + setDrawerProps({ | ||
| 54 | + confirmLoading: false, | ||
| 55 | + }); | ||
| 56 | + } | ||
| 57 | + }; | ||
| 58 | +</script> | ||
| 59 | + | ||
| 60 | +<template> | ||
| 61 | + <BasicDrawer | ||
| 62 | + @register="registerDrawer" | ||
| 63 | + v-bind="$attrs" | ||
| 64 | + showFooter | ||
| 65 | + :title="getDrawerTitle" | ||
| 66 | + width="30%" | ||
| 67 | + @ok="handleSubmit" | ||
| 68 | + > | ||
| 69 | + <BasicForm @register="register" /> | ||
| 70 | + </BasicDrawer> | ||
| 71 | +</template> | 
src/views/camera/streaming/config.data.ts
0 → 100644
| 1 | +import type { StreamingMediaModel } from '/@/api/camera/model/cameraModel'; | ||
| 2 | +import { BasicColumn, FormSchema } from '/@/components/Table'; | ||
| 3 | + | ||
| 4 | +export interface DrawerParams { | ||
| 5 | + createFlag: boolean; | ||
| 6 | + record?: StreamingMediaModel; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export const streamingMediaTypeMapping = { | ||
| 10 | + 0: '海康ISC平台', | ||
| 11 | +}; | ||
| 12 | + | ||
| 13 | +export const streamingMediaSSLMapping = { | ||
| 14 | + 0: 'http', | ||
| 15 | + 1: 'https', | ||
| 16 | +}; | ||
| 17 | + | ||
| 18 | +export const columnSchema: BasicColumn[] = [ | ||
| 19 | + { | ||
| 20 | + title: '平台类型', | ||
| 21 | + dataIndex: 'type', | ||
| 22 | + width: 80, | ||
| 23 | + slots: { customRender: 'type' }, | ||
| 24 | + }, | ||
| 25 | + { | ||
| 26 | + title: '部署环境', | ||
| 27 | + dataIndex: 'ssl', | ||
| 28 | + width: 80, | ||
| 29 | + slots: { customRender: 'ssl' }, | ||
| 30 | + }, | ||
| 31 | + { | ||
| 32 | + title: '平台地址', | ||
| 33 | + dataIndex: 'host', | ||
| 34 | + width: 80, | ||
| 35 | + }, | ||
| 36 | + { | ||
| 37 | + title: '用户Key', | ||
| 38 | + dataIndex: 'appKey', | ||
| 39 | + width: 80, | ||
| 40 | + }, | ||
| 41 | + { | ||
| 42 | + title: '用户密钥', | ||
| 43 | + dataIndex: 'appSecret', | ||
| 44 | + width: 80, | ||
| 45 | + }, | ||
| 46 | +]; | ||
| 47 | + | ||
| 48 | +export const formSchema: FormSchema[] = [ | ||
| 49 | + { | ||
| 50 | + field: 'host', | ||
| 51 | + label: '平台地址', | ||
| 52 | + component: 'Input', | ||
| 53 | + colProps: { span: 8 }, | ||
| 54 | + }, | ||
| 55 | +]; | ||
| 56 | + | ||
| 57 | +export const formDetailSchema: FormSchema[] = [ | ||
| 58 | + { | ||
| 59 | + label: '平台类型', | ||
| 60 | + field: 'type', | ||
| 61 | + component: 'Select', | ||
| 62 | + rules: [{ required: true, message: '平台类型为必填项', type: 'number' }], | ||
| 63 | + componentProps: { | ||
| 64 | + options: [{ label: '海康ISC平台', value: 0 }], | ||
| 65 | + }, | ||
| 66 | + }, | ||
| 67 | + { | ||
| 68 | + label: '部署环境', | ||
| 69 | + field: 'ssl', | ||
| 70 | + component: 'RadioGroup', | ||
| 71 | + rules: [{ required: true, message: '流媒体部署环境为必填项', type: 'number' }], | ||
| 72 | + componentProps: { | ||
| 73 | + options: [ | ||
| 74 | + { label: 'http', value: 0 }, | ||
| 75 | + { label: 'https', value: 1 }, | ||
| 76 | + ], | ||
| 77 | + }, | ||
| 78 | + }, | ||
| 79 | + { | ||
| 80 | + label: '平台地址', | ||
| 81 | + field: 'host', | ||
| 82 | + component: 'Input', | ||
| 83 | + helpMessage: ['平台IP + 端口'], | ||
| 84 | + rules: [{ required: true, message: '平台地址为必填项' }], | ||
| 85 | + }, | ||
| 86 | + { | ||
| 87 | + label: '用户Key', | ||
| 88 | + field: 'appKey', | ||
| 89 | + component: 'Input', | ||
| 90 | + rules: [{ required: true, message: '用户Key为必填项' }], | ||
| 91 | + }, | ||
| 92 | + { | ||
| 93 | + label: '用户密钥', | ||
| 94 | + field: 'appSecret', | ||
| 95 | + component: 'Input', | ||
| 96 | + rules: [{ required: true, message: '用户密钥为必填项' }], | ||
| 97 | + }, | ||
| 98 | +]; | 
src/views/camera/streaming/index.vue
0 → 100644
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { PageWrapper } from '/@/components/Page'; | ||
| 3 | + import { useDrawer } from '/@/components/Drawer'; | ||
| 4 | + import type { DrawerParams } from './config.data'; | ||
| 5 | + import { BasicTable, useTable, TableAction } from '/@/components/Table'; | ||
| 6 | + import { | ||
| 7 | + columnSchema, | ||
| 8 | + formSchema, | ||
| 9 | + streamingMediaTypeMapping, | ||
| 10 | + streamingMediaSSLMapping, | ||
| 11 | + } from './config.data'; | ||
| 12 | + import SteramingDrawer from './SteramingDrawer.vue'; | ||
| 13 | + import { getStreamingMediaList, deleteStreamingMediaRecord } from '/@/api/camera/cameraManager'; | ||
| 14 | + import { StreamingMediaModel } from '/@/api/camera/model/cameraModel'; | ||
| 15 | + import { ref } from 'vue'; | ||
| 16 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 17 | + | ||
| 18 | + const enabledBatchDelete = ref(true); | ||
| 19 | + const [register, { reload, getSelectRowKeys }] = useTable({ | ||
| 20 | + title: '流媒体列表', | ||
| 21 | + api: getStreamingMediaList, | ||
| 22 | + columns: columnSchema, | ||
| 23 | + showIndexColumn: false, | ||
| 24 | + clickToRowSelect: false, | ||
| 25 | + formConfig: { | ||
| 26 | + labelWidth: 120, | ||
| 27 | + schemas: formSchema, | ||
| 28 | + }, | ||
| 29 | + useSearchForm: true, | ||
| 30 | + showTableSetting: true, | ||
| 31 | + bordered: true, | ||
| 32 | + rowKey: 'id', | ||
| 33 | + actionColumn: { | ||
| 34 | + width: 100, | ||
| 35 | + title: '操作', | ||
| 36 | + dataIndex: 'action', | ||
| 37 | + slots: { customRender: 'action' }, | ||
| 38 | + fixed: 'right', | ||
| 39 | + }, | ||
| 40 | + | ||
| 41 | + rowSelection: { | ||
| 42 | + type: 'checkbox', | ||
| 43 | + onChange(rowKeys: string[]) { | ||
| 44 | + enabledBatchDelete.value = rowKeys.length <= 0; | ||
| 45 | + }, | ||
| 46 | + }, | ||
| 47 | + }); | ||
| 48 | + | ||
| 49 | + const [registerDrawer, { openDrawer }] = useDrawer(); | ||
| 50 | + | ||
| 51 | + const handleCreateStreamingMedia = () => { | ||
| 52 | + openDrawer<DrawerParams>(true, { | ||
| 53 | + createFlag: true, | ||
| 54 | + }); | ||
| 55 | + }; | ||
| 56 | + | ||
| 57 | + const handleUpdateStreamingMedia = (record: StreamingMediaModel) => { | ||
| 58 | + openDrawer<DrawerParams>(true, { | ||
| 59 | + createFlag: false, | ||
| 60 | + record, | ||
| 61 | + }); | ||
| 62 | + }; | ||
| 63 | + | ||
| 64 | + const handleDeleteRecord = async (record?: StreamingMediaModel) => { | ||
| 65 | + let ids = record ? [record.id] : getSelectRowKeys(); | ||
| 66 | + try { | ||
| 67 | + await deleteStreamingMediaRecord({ ids }); | ||
| 68 | + enabledBatchDelete.value = true; | ||
| 69 | + const { createMessage } = useMessage(); | ||
| 70 | + createMessage.success('删除成功'); | ||
| 71 | + } catch (e) { | ||
| 72 | + } finally { | ||
| 73 | + reload(); | ||
| 74 | + } | ||
| 75 | + }; | ||
| 76 | + | ||
| 77 | + const handleDrawerSubmitSuccess = () => { | ||
| 78 | + openDrawer(false); | ||
| 79 | + reload(); | ||
| 80 | + }; | ||
| 81 | +</script> | ||
| 82 | + | ||
| 83 | +<template> | ||
| 84 | + <PageWrapper dense contentFullHeight> | ||
| 85 | + <BasicTable @register="register"> | ||
| 86 | + <template #type="{ record }"> | ||
| 87 | + <span>{{ streamingMediaTypeMapping[record.type] }}</span> | ||
| 88 | + </template> | ||
| 89 | + <template #ssl="{ record }"> | ||
| 90 | + <span>{{ streamingMediaSSLMapping[record.ssl] }}</span> | ||
| 91 | + </template> | ||
| 92 | + <template #action="{ record }"> | ||
| 93 | + <TableAction | ||
| 94 | + :actions="[ | ||
| 95 | + { | ||
| 96 | + label: '编辑', | ||
| 97 | + auth: 'api:yt:streaming:update', | ||
| 98 | + icon: 'clarity:note-edit-line', | ||
| 99 | + onClick: handleUpdateStreamingMedia.bind(null, record), | ||
| 100 | + }, | ||
| 101 | + { | ||
| 102 | + label: '删除', | ||
| 103 | + auth: 'api:yt:streaming:delete', | ||
| 104 | + icon: 'ant-design:delete-outlined', | ||
| 105 | + color: 'error', | ||
| 106 | + popConfirm: { | ||
| 107 | + title: '是否确认删除', | ||
| 108 | + confirm: handleDeleteRecord.bind(null, record), | ||
| 109 | + }, | ||
| 110 | + }, | ||
| 111 | + ]" | ||
| 112 | + /> | ||
| 113 | + </template> | ||
| 114 | + <template #toolbar> | ||
| 115 | + <Authority value="api:yt:streaming:post"> | ||
| 116 | + <a-button type="primary" @click="handleCreateStreamingMedia">新增流媒体</a-button> | ||
| 117 | + </Authority> | ||
| 118 | + <Authority value="api:yt:streaming:delete"> | ||
| 119 | + <a-button | ||
| 120 | + color="error" | ||
| 121 | + type="primary" | ||
| 122 | + :disabled="enabledBatchDelete" | ||
| 123 | + @click="handleDeleteRecord()" | ||
| 124 | + > | ||
| 125 | + 批量删除 | ||
| 126 | + </a-button> | ||
| 127 | + </Authority> | ||
| 128 | + </template> | ||
| 129 | + </BasicTable> | ||
| 130 | + <SteramingDrawer @register="registerDrawer" @success="handleDrawerSubmitSuccess" /> | ||
| 131 | + </PageWrapper> | ||
| 132 | +</template> | ||
| 133 | + | ||
| 134 | +<style></style> |