fix: 修改组件按需引入插件

* fix: bug#17668

* fix: 视频屏蔽RTC

* fix: 修改组件按需引入插件
This commit is contained in:
XieYongHong 2023-08-29 19:03:11 +08:00 committed by GitHub
parent fa30b3feda
commit fa255488f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 583 additions and 67 deletions

View File

@ -306,7 +306,7 @@ function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _
} }
const filterName = ['message', 'Notification'] const filterName = ['message', 'Notification']
const primitiveNames = ['AIcon','Affix', 'Anchor', 'AnchorLink', 'message', 'Notification', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'SelectBoolean', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider', 'ProTable', 'Search', 'AdvancedSearch', 'Ellipsis', 'MonacoEditor', 'ProLayout', 'ScrollTable', 'TableCard', 'Scrollbar', 'CardSelect', 'ColorPicker', 'PopconfirmModal', 'DataTable', const primitiveNames = ['AIcon','Affix', 'Anchor', 'AnchorLink', 'message', 'Notification', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'SelectBoolean', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider', 'ProTable', 'Search', 'AdvancedSearch', 'Ellipsis', 'MonacoEditor', 'ProLayout', 'ScrollTable', 'TableCard', 'Scrollbar', 'CardSelect', 'PopconfirmModal', 'DataTable',
'DataTableArray', 'DataTableArray',
'DataTableString', 'DataTableString',
'DataTableInteger', 'DataTableInteger',

View File

@ -617,3 +617,17 @@ export const queryByParent = (deviceId: string) => server.get(`/device/gateway/$
export const queryCodeTips = (productId: string, deviceId: string) => server.get(`/device/transparent-codec/${productId}/${deviceId}.d.ts`) export const queryCodeTips = (productId: string, deviceId: string) => server.get(`/device/transparent-codec/${productId}/${deviceId}.d.ts`)
export const queryProductCodeTips = (productId: string) => server.get(`/device/transparent-codec/${productId}.d.ts`) export const queryProductCodeTips = (productId: string) => server.get(`/device/transparent-codec/${productId}.d.ts`)
/**
* TS
* @param deviceId ID
* @returns
*/
export const queryTypescript = (deviceId:string) => server.get(`/device/${deviceId}/virtual-property.d.ts`)
/**
* TS
* @param productId ID
* @returns
*/
export const queryProductTs = (productId:string) => server.get(`/product/${productId}/virtual-property.d.ts`)

View File

@ -36,3 +36,8 @@ export const save = (data: Object) => server.post(`/network/config`, data);
export const update = (data: Object) => server.patch(`/network/config`, data); export const update = (data: Object) => server.patch(`/network/config`, data);
export const detail = (id: string) => server.get(`/network/config/${id}`); export const detail = (id: string) => server.get(`/network/config/${id}`);
/**
* TCP粘拆包TS资源
*/
export const getTs = () => server.get('/system/resources/ScriptPayloadParser.d.ts')

View File

@ -25,4 +25,6 @@ export default {
queryProvider: (data?: any) => server.post<any>(`/gateway/device/detail/_query`, data), queryProvider: (data?: any) => server.post<any>(`/gateway/device/detail/_query`, data),
// 查询网关配置 // 查询网关配置
getConfiguration: (id: string, transport: string) => server.get<any>(`/protocol/${id}/${transport}/configuration`), getConfiguration: (id: string, transport: string) => server.get<any>(`/protocol/${id}/${transport}/configuration`),
//校验ID合法
validateId: (id:string) => server.get<any>('/media/device/id/_validate',{id:id})
} }

View File

@ -29,19 +29,28 @@
</div> </div>
</div> </div>
<div class="editor"> <div class="editor">
<j-monaco-editor v-if="loading" v-model:model-value="_value" theme="vs" ref="editor" language="javascript"/> <j-monaco-editor v-if="loading" v-model:model-value="_value" theme="vs" ref="editor" language="javascript" :registrationTypescript="typescriptTip"
:init="editorInit"/>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts" name="Editor"> <script setup lang="ts" name="Editor">
import {
queryTypescript,
queryProductTs
} from '@/api/device/instance';
import { useInstanceStore } from '@/store/instance';
import { useProductStore } from '@/store/product';
import { inject } from 'vue'
interface Props { interface Props {
mode?: 'advance' | 'simple'; mode?: 'advance' | 'simple';
id?: string; id?: string;
value?: string; value?: string;
} }
const props = defineProps<Props>() const props = defineProps<Props>()
const target = inject('target')
const instanceStore = useInstanceStore()
const productStore = useProductStore()
interface Emits { interface Emits {
(e: 'change', data: string): void; (e: 'change', data: string): void;
(e: 'update:value', data: string): void; (e: 'update:value', data: string): void;
@ -58,6 +67,56 @@ type SymbolType = {
key: string, key: string,
value: string value: string
} }
const typescriptTip = reactive({
typescript: ''
})
const queryCode = () => {
let id = ''
if(target==='device'){
id = instanceStore.current.id
queryTypescript(id).then(res => {
if (res.status===200) {
typescriptTip.typescript = res.result
}
})
}else if(target ==='product'){
id = productStore.current.id
queryProductTs(id).then(res => {
if (res.status===200) {
typescriptTip.typescript = res.result
}
})
}
}
queryCode()
const editorInit = (editor: any, monaco: any) => {
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: false,
});
// compiler options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
allowJs: true,
checkJs: true,
allowNonTsExtensions: true,
target: monaco.languages.typescript.ScriptTarget.ESNext,
strictNullChecks: false,
strictPropertyInitialization: true,
strictFunctionTypes: true,
strictBindCallApply: true,
useDefineForClassFields: true,//permit class static fields with private name to have initializer
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
typeRoots: ["types"],
lib: ["esnext"]
});
}
const symbolList = [ const symbolList = [
{ {
key: 'add', key: 'add',

View File

@ -35,7 +35,7 @@ type PlayerProps = {
updateTime?: number; updateTime?: number;
key?: string | number; key?: string | number;
loading?: boolean; loading?: boolean;
protocol?: 'mp4' | 'flv' | 'hls'; protocol?: 'mp4' | 'flv' | 'hls' | 'rtc';
onDestroy?: (e?: any) => void; onDestroy?: (e?: any) => void;
onMessage?: (msg: any) => void; onMessage?: (msg: any) => void;
onError?: (err: any) => void; onError?: (err: any) => void;

View File

@ -50,6 +50,7 @@ export const SystemConst = {
export const USER_CENTER_MENU_CODE = 'account-center' export const USER_CENTER_MENU_CODE = 'account-center'
export const USER_CENTER_MENU_BUTTON_CODE = 'user-center-passwd-update' export const USER_CENTER_MENU_BUTTON_CODE = 'user-center-passwd-update'
export const messageSubscribe = 'message-subscribe'
/**协议列表 */ /**协议列表 */
export const protocolList = [ export const protocolList = [

View File

@ -150,43 +150,50 @@ const showNotification = (message: string, description: string, key?: string, sh
* @returns {Promise<never>} * @returns {Promise<never>}
*/ */
const errorHandler = (error: any) => { const errorHandler = (error: any) => {
if (error.response) { if (error.response) {
const data = error.response.data const data = error.response.data
const status = error.response.status const status = error.response.status
if (data?.code === 'license required') { if(data instanceof Blob){
Modal.error({ data.text().then((res)=>{
key: 'License', showNotification(error.message, (JSON.parse(res).message + '').substr(0,90))
title: 'License已到期或者错误',
content: h(
'a',
{
onClick: () =>
{
Modal.destroyAll?.();
window.location.href = '/#/init-license';
}
},
'请更新License'
)
}) })
} else if (status === 403) { }else{
showNotification('Forbidden', (data.message + '').substr(0, 90), '403') if (data?.code === 'license required') {
} else if (status === 500) { Modal.error({
showNotification('Server Side Error', (data.message + '').substr(0, 90), '500') key: 'License',
} else if (status === 400) { title: 'License已到期或者错误',
showNotification('Request Error', (data.message + '').substr(0, 90), '400') content: h(
} else if (status === 401) { 'a',
showNotification('Unauthorized', '用户未登录', '401') {
setTimeout(() => { onClick: () =>
cleanToken() {
router.replace({ Modal.destroyAll?.();
path: LoginPath window.location.href = '/#/init-license';
}) }
}, 0) },
} else if (status === 404) { '请更新License'
const data = error?.response?.data )
const message = error?.response?.data?.message || `${data?.error} ${data?.path}` })
showNotification(error?.code, message, '404') } else if (status === 403) {
showNotification('Forbidden', (data.message + '').substr(0, 90), '403')
} else if (status === 500) {
showNotification('Server Side Error', (data.message + '').substr(0, 90), '500')
} else if (status === 400) {
showNotification('Request Error', (data.message + '').substr(0, 90), '400')
} else if (status === 401) {
showNotification('Unauthorized', '用户未登录', '401')
setTimeout(() => {
cleanToken()
router.replace({
path: LoginPath
})
}, 0)
} else if (status === 404) {
const data = error?.response?.data
const message = error?.response?.data?.message || `${data?.error} ${data?.path}`
showNotification(error?.code, message, '404')
}
} }
} else if (error.response === undefined) { } else if (error.response === undefined) {
if (error.message.includes('timeout')) { if (error.message.includes('timeout')) {

View File

@ -13,10 +13,11 @@ const timeout = 5000
const tempQueue: any[] = [] // websocket未连接上时缓存消息列 const tempQueue: any[] = [] // websocket未连接上时缓存消息列
export const initWebSocket = () => { export const initWebSocket = () => {
const token = getToken()
if (!token) return
if (ws) { if (ws) {
return ws return ws
} }
const token = getToken()
const url = `${document.location.protocol.replace('http', 'ws')}//${document.location.host}${BASE_API_PATH}/messaging/${token}?:X_Access_Token=${token}`; const url = `${document.location.protocol.replace('http', 'ws')}//${document.location.host}${BASE_API_PATH}/messaging/${token}?:X_Access_Token=${token}`;
if (count < total) { if (count < total) {
count += 1 count += 1
@ -102,6 +103,13 @@ export const getWebSocket = (id: string, topic: string, parameter: Record<string
} }
}) })
export const closeWs = () => {
if (ws) {
ws.close()
timer && clearInterval(timer)
}
}
/** /**
* *
*/ */

View File

@ -123,7 +123,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useInstanceStore } from '@/store/instance'; import { useInstanceStore } from '@/store/instance';
import { import {
getEdgeMap,
saveEdgeMap, saveEdgeMap,
removeEdgeMap, removeEdgeMap,
edgeChannel, edgeChannel,

View File

@ -89,6 +89,7 @@
:key="i.key" :key="i.key"
> >
<PermissionButton <PermissionButton
v-if="i.key !== 'update' || detail.accessProvider === 'official-edge-gateway'"
:disabled="i.disabled" :disabled="i.disabled"
:popConfirm="i.popConfirm" :popConfirm="i.popConfirm"
:tooltip="{ :tooltip="{
@ -307,6 +308,9 @@ const closeBindDevice = (val: boolean) => {
const closeChildSave = () => { const closeChildSave = () => {
childVisible.value = false; childVisible.value = false;
}; };
onMounted(()=>{
console.log(detail.value.accessProvider)
})
</script> </script>
<style lang="less"> <style lang="less">

View File

@ -162,6 +162,7 @@ const save = () => {
const importCancel = () => { const importCancel = () => {
importVisible.value = false importVisible.value = false
emit('save') emit('save')
cancel()
} }
</script> </script>

View File

@ -177,7 +177,7 @@ const checkChange = (e: any) => { // 全选
const keys = deviceList.value.filter(item => { const keys = deviceList.value.filter(item => {
// //
const type = !checkKeys.value.includes(item.id) && !disabledKeys.value.includes(item.id) const type = !checkKeys.value.includes(item.id) && !disabledKeys.value.includes(item.id)
if (type && checkCache.value.has(item.id)) { if (type && !checkCache.value.has(item.id)) {
checkCache.value.set(item.id, item) checkCache.value.set(item.id, item)
} }
return type return type

View File

@ -99,6 +99,7 @@ import { useInstanceStore } from '@/store/instance';
import { resetRule } from '@/api/device/instance'; import { resetRule } from '@/api/device/instance';
import { updata } from '@/api/rule-engine/configuration'; import { updata } from '@/api/rule-engine/configuration';
import { onlyMessage } from '@/utils/comm'; import { onlyMessage } from '@/utils/comm';
import { provide } from 'vue';
const instanceStore = useInstanceStore(); const instanceStore = useInstanceStore();
const PropertySource: { label: string; value: string }[] = isNoCommunity const PropertySource: { label: string; value: string }[] = isNoCommunity
? [ ? [
@ -156,7 +157,7 @@ const props = defineProps({
default: [] default: []
} }
}); });
provide('target',props.target)
const emit = defineEmits<Emit>(); const emit = defineEmits<Emit>();
const formItemContext = Form.useInjectFormItemContext(); const formItemContext = Form.useInjectFormItemContext();

View File

@ -49,6 +49,7 @@
:hasPermission="`link/AccessConfig:${ :hasPermission="`link/AccessConfig:${
id === ':id' ? 'add' : 'update' id === ':id' ? 'add' : 'update'
}`" }`"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -88,6 +89,7 @@ const route = useRoute();
const view = route.query.view as string; const view = route.query.view as string;
const id = route.params.id as string; const id = route.params.id as string;
const loading = ref(false)
const props = defineProps({ const props = defineProps({
provider: { provider: {
type: Object, type: Object,
@ -106,6 +108,7 @@ const formState = ref<FormState>({
description: '', description: '',
}); });
const onFinish = async (values: any) => { const onFinish = async (values: any) => {
loading.value = true
const providerId = props.provider.id; const providerId = props.provider.id;
const params = { const params = {
...values, ...values,
@ -124,6 +127,7 @@ const onFinish = async (values: any) => {
setTimeout(() => window.close(), 300); setTimeout(() => window.close(), 300);
} }
} }
loading.value = false
}; };
onMounted(() => { onMounted(() => {

View File

@ -328,6 +328,7 @@
:hasPermission="`link/AccessConfig:${ :hasPermission="`link/AccessConfig:${
id === ':id' ? 'add' : 'update' id === ':id' ? 'add' : 'update'
}`" }`"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -376,6 +377,7 @@ const route = useRoute();
const view = route.query.view as string; const view = route.query.view as string;
const id = route.params.id as string; const id = route.params.id as string;
const loading = ref(false)
const props = defineProps({ const props = defineProps({
provider: { provider: {
type: Object, type: Object,
@ -434,6 +436,7 @@ const procotolSearch = (value: string) => {
const saveData = async () => { const saveData = async () => {
const data: any = await formRef2.value?.validate(); const data: any = await formRef2.value?.validate();
loading.value = true
const params = { const params = {
...data, ...data,
configuration: { configuration: {
@ -460,6 +463,7 @@ const saveData = async () => {
setTimeout(() => window.close(), 300); setTimeout(() => window.close(), 300);
} }
} }
loading.value = false
}; };
const queryProcotolList = async (id: string, params = {}) => { const queryProcotolList = async (id: string, params = {}) => {

View File

@ -406,6 +406,7 @@
:hasPermission="`link/AccessConfig:${ :hasPermission="`link/AccessConfig:${
id === ':id' ? 'add' : 'update' id === ':id' ? 'add' : 'update'
}`" }`"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -486,6 +487,7 @@ const formData = ref<Form>({
description: '', description: '',
}); });
const loading = ref(false)
const current = ref(0); const current = ref(0);
const stepCurrent = ref(0); const stepCurrent = ref(0);
const steps = ref(['接入配置', '消息协议', '完成']); const steps = ref(['接入配置', '消息协议', '完成']);
@ -515,6 +517,7 @@ const procotolSearch = (value: string) => {
const saveData = async () => { const saveData = async () => {
const data: any = await formRef2.value?.validate(); const data: any = await formRef2.value?.validate();
loading.value = true
const params = { const params = {
...data, ...data,
configuration: { configuration: {
@ -542,6 +545,7 @@ const saveData = async () => {
setTimeout(() => window.close(), 300); setTimeout(() => window.close(), 300);
} }
} }
loading.value =false
}; };
const queryProcotolList = async (id: string, params = {}) => { const queryProcotolList = async (id: string, params = {}) => {

View File

@ -49,6 +49,7 @@
}`" }`"
html-type="submit" html-type="submit"
type="primary" type="primary"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -88,6 +89,7 @@ const route = useRoute();
const view = route.query.view as string; const view = route.query.view as string;
const id = route.params.id as string; const id = route.params.id as string;
const loading = ref(false)
const props = defineProps({ const props = defineProps({
provider: { provider: {
type: Object, type: Object,
@ -106,6 +108,7 @@ const formState = ref<FormState>({
description: '', description: '',
}); });
const onFinish = async (values: any) => { const onFinish = async (values: any) => {
loading.value = true
const providerId = props.provider.id; const providerId = props.provider.id;
const params = { const params = {
...values, ...values,
@ -124,6 +127,7 @@ const onFinish = async (values: any) => {
setTimeout(() => window.close(), 300); setTimeout(() => window.close(), 300);
} }
} }
loading.value = false
}; };
onMounted(() => { onMounted(() => {

View File

@ -156,6 +156,7 @@
:hasPermission="`link/AccessConfig:${ :hasPermission="`link/AccessConfig:${
id === ':id' ? 'add' : 'update' id === ':id' ? 'add' : 'update'
}`" }`"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -187,6 +188,7 @@
:hasPermission="`link/AccessConfig:${ :hasPermission="`link/AccessConfig:${
id === ':id' ? 'add' : 'update' id === ':id' ? 'add' : 'update'
}`" }`"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -223,6 +225,7 @@ const route = useRoute();
const view = route.query.view as string; const view = route.query.view as string;
const id = route.params.id as string; const id = route.params.id as string;
const loading = ref(false)
const props = defineProps({ const props = defineProps({
provider: { provider: {
type: Object, type: Object,
@ -252,6 +255,7 @@ const networkList: any = ref([]);
const allNetworkList: any = ref([]); const allNetworkList: any = ref([]);
const onFinish = async (values: any) => { const onFinish = async (values: any) => {
loading.value = true
const providerId = props.provider.id; const providerId = props.provider.id;
const params = { const params = {
...values, ...values,
@ -270,6 +274,7 @@ const onFinish = async (values: any) => {
setTimeout(() => window.close(), 300); setTimeout(() => window.close(), 300);
} }
} }
loading.value = false
}; };
const checkedChange = (id: string) => { const checkedChange = (id: string) => {

View File

@ -50,6 +50,7 @@
:hasPermission="`link/AccessConfig:${ :hasPermission="`link/AccessConfig:${
id === ':id' ? 'add' : 'update' id === ':id' ? 'add' : 'update'
}`" }`"
:loading="loading"
> >
保存 保存
</PermissionButton> </PermissionButton>
@ -104,6 +105,7 @@ const props = defineProps({
}, },
}); });
const loading = ref(false);
const channel = ref(props.provider.channel); const channel = ref(props.provider.channel);
const formState = ref<FormState>({ const formState = ref<FormState>({
@ -111,6 +113,7 @@ const formState = ref<FormState>({
description: '', description: '',
}); });
const onFinish = async (values: any) => { const onFinish = async (values: any) => {
loading.value = true
const params = { const params = {
...values, ...values,
provider: 'fixed-media', provider: 'fixed-media',
@ -132,6 +135,7 @@ const onFinish = async (values: any) => {
history.back(); history.back();
} }
} }
loading.value = false
}; };
onMounted(() => { onMounted(() => {

View File

@ -903,6 +903,8 @@
.script .script
" "
language="javascript" language="javascript"
:init="editorInit"
:registrationTypescript="typescriptTip"
/> />
</div> </div>
</j-form-item> </j-form-item>
@ -1127,6 +1129,7 @@ import {
start, start,
resourceClusters, resourceClusters,
resourceClustersById, resourceClustersById,
getTs
} from '@/api/link/type'; } from '@/api/link/type';
import { import {
ParserConfiguration, ParserConfiguration,
@ -1172,6 +1175,34 @@ const portOptionsIndex: any = ref([]);
const certIdOptions = ref([]); const certIdOptions = ref([]);
const configClustersList = ref<any[]>([]); const configClustersList = ref<any[]>([]);
const typescriptTip = reactive({
typescript: ''
})
const editorInit = (editor: any, monaco: any) => {
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: false,
});
// compiler options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
allowJs: true,
checkJs: true,
allowNonTsExtensions: true,
target: monaco.languages.typescript.ScriptTarget.ESNext,
strictNullChecks: false,
strictPropertyInitialization: true,
strictFunctionTypes: true,
strictBindCallApply: true,
useDefineForClassFields: true,//permit class static fields with private name to have initializer
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
typeRoots: ["types"],
lib: ["esnext"]
});
}
const dynamicValidateForm = reactive<{ cluster: FormData2Type[] }>({ const dynamicValidateForm = reactive<{ cluster: FormData2Type[] }>({
cluster: [{ ...cloneDeep(FormStates2), id: '1' }], cluster: [{ ...cloneDeep(FormStates2), id: '1' }],
}); });
@ -1250,6 +1281,13 @@ const changeType = (value: string) => {
const { configuration } = dynamicValidateForm.cluster[0]; const { configuration } = dynamicValidateForm.cluster[0];
value && (configuration.host = '0.0.0.0'); value && (configuration.host = '0.0.0.0');
} }
if(value ==='TCP_SERVER'){
getTs().then((res:any)=>{
if (res.status===200) {
typescriptTip.typescript = res.result
}
})
}
}; };
const updateClustersListIndex = () => { const updateClustersListIndex = () => {

View File

@ -4,7 +4,7 @@
:columns="columns" :columns="columns"
:dataSource="dataSource" :dataSource="dataSource"
:pagination="false" :pagination="false"
:scroll="{ y: 200 }" :scroll="share ? { y: 560} :{ y: 200 }"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'actions'"> <template v-if="column.dataIndex === 'actions'">
@ -61,6 +61,10 @@ const props = defineProps({
type: Object as PropType<Partial<Record<string, any>>>, type: Object as PropType<Partial<Record<string, any>>>,
default: () => ({}), default: () => ({}),
}, },
share:{
type:Boolean,
default:false
}
}); });
const emits = defineEmits(['refresh']) const emits = defineEmits(['refresh'])

View File

@ -41,7 +41,7 @@ const url = ref('');
const urlRef = ref<HTMLInputElement>() const urlRef = ref<HTMLInputElement>()
watchEffect(() => { watchEffect(() => {
url.value = `${window.location.origin}#/media/device/Share?deviceId=${props.data.deviceId}&channelId=${props.data.channelId}&type=${route.query.type}&${TOKEN_KEY}=${token}` url.value = `${window.location.origin}#/media/device/Share?deviceId=${props.data.deviceId}&channelId=${props.data.channelId}&type=${route.query.type}&id=${props.data.id}&${TOKEN_KEY}=${token}`
}) })
const onCopy = () => { const onCopy = () => {

View File

@ -3,7 +3,8 @@
<j-modal <j-modal
v-model:visible="_vis" v-model:visible="_vis"
title="播放" title="播放"
:width="_type ? 1200 : 900" :width="type === 'share'? '100%' : _type ? 1200 : 900"
:class="{share: type === 'share'}"
:maskClosable="false" :maskClosable="false"
@ok="_vis = false" @ok="_vis = false"
:destroyOnClose="true" :destroyOnClose="true"
@ -20,6 +21,7 @@
<j-radio-button value="mp4">MP4</j-radio-button> <j-radio-button value="mp4">MP4</j-radio-button>
<j-radio-button value="flv">FLV</j-radio-button> <j-radio-button value="flv">FLV</j-radio-button>
<j-radio-button value="m3u8">HLS</j-radio-button> <j-radio-button value="m3u8">HLS</j-radio-button>
<!-- <j-radio-button value='rtc'>RTC</j-radio-button> -->
</j-radio-group> </j-radio-group>
<div class="media-live-share" v-if="type !== 'share'"> <div class="media-live-share" v-if="type !== 'share'">
<j-button type="link" @click="onShare" <j-button type="link" @click="onShare"
@ -132,9 +134,9 @@
</div> </div>
</div> </div>
<template #footer> <template #footer>
<j-space> <j-space v-if="type !== 'share'">
<j-button :disabled="type === 'share'" @click="_vis = false">取消</j-button> <j-button @click="_vis = false">取消</j-button>
<j-button :disabled="type === 'share'" @click="_vis = false" type="primary">确定</j-button> <j-button @click="_vis = false" type="primary">确定</j-button>
</j-space> </j-space>
</template> </template>
</j-modal> </j-modal>
@ -179,7 +181,7 @@ const player = ref();
// //
const url = ref(''); const url = ref('');
// //
const mediaType = ref<'mp4' | 'flv' | 'hls'>('mp4'); const mediaType = ref<'mp4' | 'flv' | 'hls' | 'rtc'>('mp4');
const showTool = ref(false); const showTool = ref(false);
const showToolLock = ref(false); const showToolLock = ref(false);

View File

@ -0,0 +1,335 @@
<template>
<div style="width: 95%; margin: 0 auto;">
<div class="media-live-tool">
<j-radio-group
v-model:value="mediaType"
button-style="solid"
@change="mediaStart"
>
<j-radio-button value="mp4">MP4</j-radio-button>
<j-radio-button value="flv">FLV</j-radio-button>
<j-radio-button value="m3u8">HLS</j-radio-button>
</j-radio-group>
<div class="media-live-share" v-if="type !== 'share'">
<j-button type="link" @click="onShare"
><AIcon type="ShareAltOutlined" />分享视频</j-button
>
</div>
</div>
<div class="media-live">
<div class="media-live-video">
<div
:class="mediaToolClass"
@mouseleave="mouseleave"
@mouseenter="showTool = true"
>
<div class="tool-item" v-if="type !== 'share'">
<template v-if="isRecord === 0">
<j-dropdown
trigger="click"
@visibleChange="visibleChange"
@click="showToolLock = true"
>
<div>开始录像</div>
<template #overlay>
<j-menu @click="recordStart">
<j-menu-item
key="true"
v-if="_type"
>
<span style="padding-right: 12px"
>本地存储</span
>
<j-tooltip title="存储在设备本地">
<a-icon
type="QuestionCircleOutlined"
/>
</j-tooltip>
</j-menu-item>
<j-menu-item key="false">
<span style="padding-right: 12px"
>云端存储</span
>
<j-tooltip title="存储在服务器中">
<a-icon
type="QuestionCircleOutlined"
/>
</j-tooltip>
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
</template>
<div v-else-if="isRecord === 1">请求录像中</div>
<div
v-else-if="isRecord === 2"
@click.stop="recordStop"
>
停止录像
</div>
</div>
<div class="tool-item" @click.stop="handleRefresh">
刷新
</div>
<div class="tool-item">
<j-popconfirm
title="重置将断开直播, 可能会影响其他播放者"
@confirm="handleReset"
>
重置
</j-popconfirm>
</div>
</div>
<LivePlayer
ref="player"
:live="true"
:url="url"
:protocol="mediaType"
autoplay
/>
</div>
<div class="media-live-actions" v-if="_type">
<div class="actions-tool">
<MediaTool
@onMouseDown="handleMouseDown"
@onMouseUp="handleMouseUp"
>
<template #center>
<div class="center">
<div>转速控制</div>
<j-dropdown>
<span
>{{ _speed }}<AIcon type="DownOutlined"
/></span>
<template #overlay>
<j-menu @click="onMenuChange">
<j-menu-item
:key="item.value"
v-for="item in speedList"
>
{{ item.label }}
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
</div>
</template>
</MediaTool>
</div>
<Preset :data="data" @refresh="onRefresh" :share="true"/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue';
import LivePlayer from '@/components/Player/index.vue';
import MediaTool from '@/components/Player/mediaTool.vue';
import channelApi from '@/api/media/channel';
import Share from './Share.vue';
import Preset from './Preset.vue';
type Emits = {
(e: 'update:visible', data: boolean): void;
(e: 'refresh'): void;
};
const emit = defineEmits<Emits>();
const props = defineProps({
visible: { type: Boolean, default: false },
data: {
type: Object as PropType<Partial<Record<string, any>>>,
default: () => ({}),
},
type: {
type: String as PropType<'share' | 'normal'>,
default: 'normal',
},
});
const route = useRoute();
const _vis = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val),
});
//
const player = ref();
//
const url = ref('');
//
const mediaType = ref<'mp4' | 'flv' | 'hls' | 'rtc'>('mp4');
const showTool = ref(false);
const showToolLock = ref(false);
const visible = ref(false);
const _type = computed(() => {
return route.query.type !== 'fixed-media'
})
const speedList = [
{ label: '高', value: 180 },
{ label: '中', value: 90 },
{ label: '低', value: 45 },
];
const speed = ref(90);
const _speed = computed(() => {
return speedList.find((item) => item.value === speed.value)?.label;
});
const onMenuChange = (val: any) => {
speed.value = val.key;
};
const mouseleave = () => {
if (!showToolLock.value) {
showTool.value = false;
}
};
const visibleChange = (v: boolean) => {
showTool.value = v;
};
const getPopupContainer = (trigger: HTMLElement) => {
return trigger?.parentNode || document.body;
};
const mediaToolClass = computed(() => {
return {
'media-tool': true,
'media-tool-show': showTool.value,
};
});
/**
* 媒体开始播放
*/
const mediaStart = () => {
url.value = channelApi.ptzStart(
props.data.deviceId,
props.data.channelId,
mediaType.value,
);
};
//
const isRecord = ref(0); // 0 1 2
/**
* 查询录像状态
*/
const getIsRecord = async () => {
const { result } = await channelApi.ptzIsRecord(
props.data.deviceId,
props.data.channelId,
);
isRecord.value = result ? 2 : 0;
};
/**
* 开始录像
*/
const recordStart = async ({ key }: { key: string }) => {
showToolLock.value = false;
showTool.value = false;
isRecord.value = 1;
const local = key === 'true';
const res = await channelApi
.recordStart(props.data.deviceId, props.data.channelId, { local })
.catch(() => ({ success: false }));
if (res.success) {
isRecord.value = 2;
} else {
isRecord.value = 0;
}
};
/**
* 停止录像
*/
const recordStop = async () => {
const res = await channelApi.recordStop(
props.data.deviceId,
props.data.channelId,
);
if (res.success) {
isRecord.value = 0;
}
};
/**
* 刷新
*/
const handleRefresh = () => {
// player.value.play();
url.value = '';
setTimeout(() => {
mediaStart();
}, 500);
};
/**
* 重置
*/
const handleReset = async () => {
channelApi.mediaStop(props.data.deviceId, props.data.channelId);
};
/**
* 点击控制按钮
* @param type 控制类型
*/
const handleMouseDown = (type: string) => {
channelApi.ptzTool(props.data.deviceId, props.data.channelId, type, speed.value);
};
const handleMouseUp = () => {
channelApi.ptzStop(props.data.deviceId, props.data.channelId);
};
/**
* 分享视频
*/
const onShare = () => {
visible.value = true;
};
const onRefresh = () => {
emit('refresh')
}
watch(
() => _vis.value,
(val: boolean) => {
if (val) {
mediaStart();
getIsRecord();
} else {
// url,
url.value = '';
}
},
{
immediate: true
}
);
</script>
<style lang="less" scoped>
@import './index.less';
:deep(.live-player-stretch-btn) {
display: none;
}
:deep(.vjs-icon-spinner) {
display: none;
}
.center {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

View File

@ -1,16 +1,17 @@
<template> <template>
<Live :visible="true" type="share" :data="playData" /> <ShareLive :visible="true" type="share" :data="playData"/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { LocalStore } from '@/utils/comm'; import { LocalStore } from '@/utils/comm';
import { TOKEN_KEY } from '@/utils/variable'; import { TOKEN_KEY } from '@/utils/variable';
import Live from '../Live/index.vue'; import ShareLive from '../Live/shareLive.vue';
const playData = ref({ const playData = ref({
deviceId: '', deviceId: '',
channelId: '', channelId: '',
type: '' type: '',
id:''
}); });
// url // url
@ -21,7 +22,8 @@ watchEffect(() => {
playData.value = { playData.value = {
deviceId: obj?.deviceId || '', deviceId: obj?.deviceId || '',
channelId: obj?.channelId || '', channelId: obj?.channelId || '',
type: obj?.type type: obj?.type,
id:obj.id || ''
}; };
if(obj?.[TOKEN_KEY]){ if(obj?.[TOKEN_KEY]){
LocalStore.set(TOKEN_KEY, obj?.[TOKEN_KEY]); LocalStore.set(TOKEN_KEY, obj?.[TOKEN_KEY]);

View File

@ -310,6 +310,7 @@ import DeviceApi from '@/api/media/device';
import { PROVIDER_OPTIONS } from '@/views/media/Device/const'; import { PROVIDER_OPTIONS } from '@/views/media/Device/const';
import type { ProductType } from '@/views/media/Device/typings'; import type { ProductType } from '@/views/media/Device/typings';
import SaveProduct from './SaveProduct.vue'; import SaveProduct from './SaveProduct.vue';
import { notification } from 'jetlinks-ui-components';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -425,13 +426,18 @@ const handleSubmit = () => {
formRef.value formRef.value
?.validate() ?.validate()
.then(async () => { .then(async () => {
btnLoading.value = true; const resq:any = await DeviceApi.validateId(id)
const res = !route.query.id if(resq.status === 200 && resq?.result?.passed){
? await DeviceApi.save(params) btnLoading.value = true;
: await DeviceApi.update(params); const res = !route.query.id
if (res?.success) { ? await DeviceApi.save(params)
onlyMessage('保存成功'); : await DeviceApi.update(params);
history.back(); if (res?.success) {
onlyMessage('保存成功');
history.back();
}
}else{
notification.error({ key: 'error', message: '设备ID已重复'})
} }
}) })
.catch((err: any) => { .catch((err: any) => {

View File

@ -42,7 +42,7 @@
:columns="searchColumns" :columns="searchColumns"
target="category-bind-modal" target="category-bind-modal"
@search="search" @search="search"
style="width: 75%;" style="width: 75%; margin-bottom: 0;"
/> />
</template> </template>
<template #card="slotProps"> <template #card="slotProps">

View File

@ -81,6 +81,7 @@ import { cloneDeep } from 'lodash';
import { onlyMessage } from '@/utils/comm'; import { onlyMessage } from '@/utils/comm';
import { import {
USER_CENTER_MENU_CODE, USER_CENTER_MENU_CODE,
messageSubscribe
} from '@/utils/consts'; } from '@/utils/consts';
import { protocolList } from '@/utils/consts'; import { protocolList } from '@/utils/consts';
import { getProviders } from '@/api/data-collect/channel'; import { getProviders } from '@/api/data-collect/channel';
@ -201,7 +202,7 @@ const onDragend = (info: AntTreeNodeDropEvent) => {
onMounted(() => { onMounted(() => {
getSystemPermission_api().then((resp: any) => { getSystemPermission_api().then((resp: any) => {
const filterBaseMenu = BaseMenu.filter(item => ![ const filterBaseMenu = BaseMenu.filter(item => ![
USER_CENTER_MENU_CODE, USER_CENTER_MENU_CODE,messageSubscribe
].includes(item.code)) ].includes(item.code))
baseMenu.value = filterMenu( baseMenu.value = filterMenu(
resp.result.map((item: any) => JSON.parse(item).id), resp.result.map((item: any) => JSON.parse(item).id),
@ -212,7 +213,7 @@ onMounted(() => {
systemMenu.value = resp.result?.filter( systemMenu.value = resp.result?.filter(
(item: { code: string }) => (item: { code: string }) =>
![ ![
USER_CENTER_MENU_CODE USER_CENTER_MENU_CODE,messageSubscribe
].includes(item.code), ].includes(item.code),
); );
// //
@ -221,12 +222,12 @@ onMounted(() => {
selectedKeys.value = systemMenuData.checkedKeys; selectedKeys.value = systemMenuData.checkedKeys;
const AllMenu = filterMenus(mergeArr( const AllMenu = filterMenus(mergeArr(
cloneDeep(filterBaseMenu), cloneDeep(baseMenu.value),
cloneDeep(systemMenu.value), cloneDeep(systemMenu.value),
)) ))
console.log(AllMenu); console.log(AllMenu);
// //
treeData.value = handleSortsArr(AllMenu); treeData.value = handleSortsArr(systemMenu.value);
} }
}); });
}); });

View File

@ -83,7 +83,7 @@ import PermissionButton from '@/components/PermissionButton/index.vue';
import { getMenuTree_api, delMenuInfo_api } from '@/api/system/menu'; import { getMenuTree_api, delMenuInfo_api } from '@/api/system/menu';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useUserInfo } from '@/store/userInfo'; import { useUserInfo } from '@/store/userInfo';
import { USER_CENTER_MENU_CODE } from '@/utils/consts' import { USER_CENTER_MENU_CODE,messageSubscribe } from '@/utils/consts'
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { onlyMessage } from '@/utils/comm'; import { onlyMessage } from '@/utils/comm';
@ -212,7 +212,7 @@ const table = reactive({
return { return {
code: resp.message, code: resp.message,
result: { result: {
data: resp.result?.filter((item: { code: string }) => ![USER_CENTER_MENU_CODE].includes(item.code)), data: resp.result?.filter((item: { code: string }) => ![USER_CENTER_MENU_CODE,messageSubscribe].includes(item.code)),
pageIndex: resp.pageIndex, pageIndex: resp.pageIndex,
pageSize: resp.pageSize, pageSize: resp.pageSize,
total: resp.total, total: resp.total,

View File

@ -178,6 +178,7 @@ import { LocalStore } from '@/utils/comm';
import { BASE_API_PATH, TOKEN_KEY, Version_Code } from '@/utils/variable'; import { BASE_API_PATH, TOKEN_KEY, Version_Code } from '@/utils/variable';
import { SystemConst } from '@/utils/consts'; import { SystemConst } from '@/utils/consts';
import {encrypt} from '@/utils/encrypt' import {encrypt} from '@/utils/encrypt'
import { closeWs } from '@/utils/websocket'
const store = useUserInfo(); const store = useUserInfo();
const systemStore = useSystem(); const systemStore = useSystem();
@ -382,6 +383,7 @@ getOpen();
getCode(); getCode();
screenRotation(screenWidth.value, screenHeight.value); screenRotation(screenWidth.value, screenHeight.value);
closeWs()
onMounted(()=>{ onMounted(()=>{
getRsa() getRsa()
}) })