Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
41e1ca9f9c
|
@ -0,0 +1,49 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
import { LocalStore } from '@/utils/comm';
|
||||||
|
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||||
|
import { recordsItemType } from '@/views/media/Device/Playback/typings';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 开始直播
|
||||||
|
ptzStart: (deviceId: string, channelId: string, type: string) =>
|
||||||
|
`${BASE_API_PATH}/media/device/${deviceId}/${channelId}/live.${type}?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`,
|
||||||
|
|
||||||
|
// 查询设备通道详情
|
||||||
|
queryDetail: (deviceId: string, data: any) => server.post(`/media/device/${deviceId}/channel/_query`, data),
|
||||||
|
|
||||||
|
// 查询本地回放记录
|
||||||
|
queryRecordLocal: (deviceId: string, channelId: string, data?: any) =>
|
||||||
|
server.post<any>(`/media/device/${deviceId}/${channelId}/records/in-local`, data),
|
||||||
|
|
||||||
|
// 下载到云端
|
||||||
|
downloadRecord: (deviceId: string, channelId: string, data: any) =>
|
||||||
|
server.post(`/media/device/${deviceId}/${channelId}/_record`, data),
|
||||||
|
|
||||||
|
// 播放本地回放
|
||||||
|
playbackLocal: (
|
||||||
|
deviceId: string,
|
||||||
|
channelId: string,
|
||||||
|
suffix: string,
|
||||||
|
startTime: string,
|
||||||
|
endTime: string,
|
||||||
|
speed: number = 1
|
||||||
|
) =>
|
||||||
|
`${BASE_API_PATH}/media/device/${deviceId}/${channelId}/playback.${suffix}?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&startTime=${startTime}&endTime=${endTime}&speed=${speed}`,
|
||||||
|
|
||||||
|
// 本地录像播放控制
|
||||||
|
playbackControl: (deviceId: string, channelId: string) =>
|
||||||
|
server.post(`/media/device/${deviceId}/${channelId}/stream-control`),
|
||||||
|
|
||||||
|
// 查询云端回放记录
|
||||||
|
recordsInServer: (deviceId: string, channelId: string, data: any) =>
|
||||||
|
server.post<recordsItemType[]>(`/media/device/${deviceId}/${channelId}/records/in-server`, data),
|
||||||
|
|
||||||
|
// 查询云端回放文件信息
|
||||||
|
recordsInServerFiles: (deviceId: string, channelId: string, data: any) =>
|
||||||
|
server.post<recordsItemType[]>(`/media/device/${deviceId}/${channelId}/records/in-server/files`, data),
|
||||||
|
|
||||||
|
// 播放云端回放
|
||||||
|
playbackStart: (recordId: string) => `${BASE_API_PATH}/record/${recordId}.mp4?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`,
|
||||||
|
|
||||||
|
downLoadFile: (recordId: string) => `${BASE_API_PATH}/record/${recordId}.mp4?download=true&:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`
|
||||||
|
}
|
|
@ -73,7 +73,9 @@ const iconKeys = [
|
||||||
'BellOutlined',
|
'BellOutlined',
|
||||||
'UserOutlined',
|
'UserOutlined',
|
||||||
'LogoutOutlined',
|
'LogoutOutlined',
|
||||||
'ReadIconOutlined'
|
'ReadIconOutlined',
|
||||||
|
'CloudDownloadOutlined',
|
||||||
|
'PauseCircleOutlined',
|
||||||
]
|
]
|
||||||
|
|
||||||
const Icon = (props: {type: string}) => {
|
const Icon = (props: {type: string}) => {
|
||||||
|
|
|
@ -28,5 +28,4 @@ const props = defineProps({
|
||||||
*/
|
*/
|
||||||
statusNames: { type: Object },
|
statusNames: { type: Object },
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="upload-image-warp">
|
<div class="upload-image-warp">
|
||||||
<div class="upload-image-border">
|
<div class="upload-image-border">
|
||||||
<a-upload
|
<j-upload
|
||||||
name="file"
|
name="file"
|
||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
class="avatar-uploader"
|
class="avatar-uploader"
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</a-upload>
|
</j-upload>
|
||||||
<div class="upload-loading-mask" v-if="props.disabled"></div>
|
<div class="upload-loading-mask" v-if="props.disabled"></div>
|
||||||
<div class="upload-loading-mask" v-if="imageUrl && loading">
|
<div class="upload-loading-mask" v-if="imageUrl && loading">
|
||||||
<AIcon type="LoadingOutlined" style="font-size: 20px" />
|
<AIcon type="LoadingOutlined" style="font-size: 20px" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
<div class="player-screen-tool" v-if="showScreen">
|
<div class="player-screen-tool" v-if="showScreen">
|
||||||
<a-radio-group
|
<a-radio-group
|
||||||
v-model:value="screen"
|
:value="screen"
|
||||||
button-style="solid"
|
button-style="solid"
|
||||||
@change="handleScreenChange"
|
@change="handleScreenChange"
|
||||||
>
|
>
|
||||||
|
@ -208,9 +208,7 @@ const formData = ref({
|
||||||
|
|
||||||
// 全屏元素
|
// 全屏元素
|
||||||
const fullscreenRef = ref(null);
|
const fullscreenRef = ref(null);
|
||||||
const { isFullscreen, enter, exit, toggle } = useFullscreen(
|
const { isFullscreen, enter, exit, toggle } = useFullscreen(fullscreenRef);
|
||||||
fullscreenRef.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新视频
|
* 刷新视频
|
||||||
|
|
|
@ -30,7 +30,7 @@ const options = reactive({
|
||||||
muted: false, //静音
|
muted: false, //静音
|
||||||
webFullScreen: false,
|
webFullScreen: false,
|
||||||
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
|
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
|
||||||
autoPlay: true, //自动播放
|
autoPlay: false, //自动播放
|
||||||
loop: false, //循环播放
|
loop: false, //循环播放
|
||||||
mirror: false, //镜像画面
|
mirror: false, //镜像画面
|
||||||
ligthOff: false, //关灯模式
|
ligthOff: false, //关灯模式
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'checked-icon',
|
'checked-icon',
|
||||||
disabled && myValue === item.value
|
(disabled && myValue === item.value) || item.disabled
|
||||||
? 'checked-icon-disabled'
|
? 'checked-icon-disabled'
|
||||||
: '',
|
: '',
|
||||||
]"
|
]"
|
||||||
|
@ -45,6 +45,7 @@ interface IOption {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
logo: string;
|
logo: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<!-- 参数类型输入组件 -->
|
<!-- 参数类型输入组件 -->
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<a-select
|
<j-select
|
||||||
v-if="typeMap.get(itemType) === 'select'"
|
v-if="typeMap.get(itemType) === 'select'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
:options="options"
|
:options="options"
|
||||||
allowClear
|
allowClear
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
<a-date-picker
|
<j-date-picker
|
||||||
v-else-if="typeMap.get(itemType) === 'date'"
|
v-else-if="typeMap.get(itemType) === 'date'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
allowClear
|
allowClear
|
||||||
|
@ -17,13 +17,13 @@
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
<a-input-number
|
<j-input-number
|
||||||
v-else-if="typeMap.get(itemType) === 'inputNumber'"
|
v-else-if="typeMap.get(itemType) === 'inputNumber'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
allowClear
|
allowClear
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
<a-input
|
<j-input
|
||||||
allowClear
|
allowClear
|
||||||
v-else-if="typeMap.get(itemType) === 'object'"
|
v-else-if="typeMap.get(itemType) === 'object'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
|
@ -31,19 +31,19 @@
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<form-outlined @click="modalVis = true" />
|
<form-outlined @click="modalVis = true" />
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</j-input>
|
||||||
<GeoComponent
|
<GeoComponent
|
||||||
v-else-if="typeMap.get(itemType) === 'geoPoint'"
|
v-else-if="typeMap.get(itemType) === 'geoPoint'"
|
||||||
v-model:point="myValue"
|
v-model:point="myValue"
|
||||||
/>
|
/>
|
||||||
<a-input
|
<j-input
|
||||||
v-else-if="typeMap.get(itemType) === 'file'"
|
v-else-if="typeMap.get(itemType) === 'file'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
placeholder="请输入图片链接"
|
placeholder="请输入图片链接"
|
||||||
allowClear
|
allowClear
|
||||||
>
|
>
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<a-upload
|
<j-upload
|
||||||
name="file"
|
name="file"
|
||||||
:action="FILE_UPLOAD"
|
:action="FILE_UPLOAD"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
|
@ -51,10 +51,10 @@
|
||||||
@change="handleFileChange"
|
@change="handleFileChange"
|
||||||
>
|
>
|
||||||
<cloud-upload-outlined />
|
<cloud-upload-outlined />
|
||||||
</a-upload>
|
</j-upload>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</j-input>
|
||||||
<a-input
|
<j-input
|
||||||
v-else
|
v-else
|
||||||
allowClear
|
allowClear
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 代码编辑器弹窗 -->
|
<!-- 代码编辑器弹窗 -->
|
||||||
<a-modal
|
<j-modal
|
||||||
title="编辑"
|
title="编辑"
|
||||||
ok-text="确认"
|
ok-text="确认"
|
||||||
cancel-text="取消"
|
cancel-text="取消"
|
||||||
|
@ -75,13 +75,12 @@
|
||||||
<div style="width: 100%; height: 400px">
|
<div style="width: 100%; height: 400px">
|
||||||
<MonacoEditor v-model:modelValue="objectValue" />
|
<MonacoEditor v-model:modelValue="objectValue" />
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
import { FormOutlined, CloudUploadOutlined } from '@ant-design/icons-vue';
|
|
||||||
import { UploadChangeParam, UploadFile } from 'ant-design-vue';
|
import { UploadChangeParam, UploadFile } from 'ant-design-vue';
|
||||||
import { DefaultOptionType } from 'ant-design-vue/lib/select';
|
import { DefaultOptionType } from 'ant-design-vue/lib/select';
|
||||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||||
|
|
|
@ -8,7 +8,7 @@ import CardBox from './CardBox/index.vue';
|
||||||
// import Search from './Search'
|
// import Search from './Search'
|
||||||
import NormalUpload from './NormalUpload/index.vue'
|
import NormalUpload from './NormalUpload/index.vue'
|
||||||
import FileFormat from './FileFormat/index.vue'
|
import FileFormat from './FileFormat/index.vue'
|
||||||
// import JUpload from './JUpload/index.vue'
|
import JProUpload from './JUpload/index.vue'
|
||||||
import { BasicLayoutPage, BlankLayoutPage } from './Layout'
|
import { BasicLayoutPage, BlankLayoutPage } from './Layout'
|
||||||
import { PageContainer } from 'jetlinks-ui-components/es/components'
|
import { PageContainer } from 'jetlinks-ui-components/es/components'
|
||||||
import Ellipsis from './Ellipsis/index.vue'
|
import Ellipsis from './Ellipsis/index.vue'
|
||||||
|
@ -27,7 +27,7 @@ export default {
|
||||||
// .component('Search', Search)
|
// .component('Search', Search)
|
||||||
.component('NormalUpload', NormalUpload)
|
.component('NormalUpload', NormalUpload)
|
||||||
.component('FileFormat', FileFormat)
|
.component('FileFormat', FileFormat)
|
||||||
// .component('JUpload', JUpload)
|
.component('JProUpload', JProUpload)
|
||||||
.component('BasicLayoutPage', BasicLayoutPage)
|
.component('BasicLayoutPage', BasicLayoutPage)
|
||||||
.component('BlankLayoutPage', BlankLayoutPage)
|
.component('BlankLayoutPage', BlankLayoutPage)
|
||||||
.component('PageContainer', PageContainer)
|
.component('PageContainer', PageContainer)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-table
|
<j-table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data-source="dataSource"
|
:data-source="dataSource"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</j-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<a-form
|
<j-form
|
||||||
:layout="'vertical'"
|
:layout="'vertical'"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="modelRef"
|
:model="modelRef"
|
||||||
>
|
>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="24" v-if="actionType === 'command'">
|
<j-col :span="24" v-if="actionType === 'command'">
|
||||||
<a-form-item name="messageType" label="指令类型" :rules="{
|
<j-form-item name="messageType" label="指令类型" :rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择指令类型',
|
message: '请选择指令类型',
|
||||||
}">
|
}">
|
||||||
<a-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
<j-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
||||||
<a-select-option value="READ_PROPERTY">读取属性</a-select-option>
|
<j-select-option value="READ_PROPERTY">读取属性</j-select-option>
|
||||||
<a-select-option value="WRITE_PROPERTY">修改属性</a-select-option>
|
<j-select-option value="WRITE_PROPERTY">修改属性</j-select-option>
|
||||||
<a-select-option value="INVOKE_FUNCTION">调用功能</a-select-option>
|
<j-select-option value="INVOKE_FUNCTION">调用功能</j-select-option>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="(modelRef.messageType === 'READ_PROPERTY' || actionType === 'latestData') ? 24 : 12" v-if="(actionType === 'command' && ['READ_PROPERTY','WRITE_PROPERTY'].includes(modelRef.messageType)) || actionType === 'latestData'">
|
<j-col :span="(modelRef.messageType === 'READ_PROPERTY' || actionType === 'latestData') ? 24 : 12" v-if="(actionType === 'command' && ['READ_PROPERTY','WRITE_PROPERTY'].includes(modelRef.messageType)) || actionType === 'latestData'">
|
||||||
<a-form-item :name="['message', 'properties']" label="属性" :rules="{
|
<j-form-item :name="['message', 'properties']" label="属性" :rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择属性',
|
message: '请选择属性',
|
||||||
}">
|
}">
|
||||||
<a-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
<j-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
||||||
<a-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
<j-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12" v-if="modelRef.messageType === 'WRITE_PROPERTY' && actionType === 'command'">
|
<j-col :span="12" v-if="modelRef.messageType === 'WRITE_PROPERTY' && actionType === 'command'">
|
||||||
<a-form-item :name="['message', 'value']" label="值" :rules="{
|
<j-form-item :name="['message', 'value']" label="值" :rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入值',
|
message: '请输入值',
|
||||||
}">
|
}">
|
||||||
<a-input />
|
<j-input />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||||
<a-form-item :name="['message', 'functionId']" label="功能" :rules="{
|
<j-form-item :name="['message', 'functionId']" label="功能" :rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择功能',
|
message: '请选择功能',
|
||||||
}">
|
}">
|
||||||
<a-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
<j-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
||||||
<a-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</a-select-option>
|
<j-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION' && modelRef.message.functionId">
|
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION' && modelRef.message.functionId">
|
||||||
<a-form-item :name="['message', 'inputs']" label="参数列表" :rules="{
|
<j-form-item :name="['message', 'inputs']" label="参数列表" :rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入参数列表',
|
message: '请输入参数列表',
|
||||||
}">
|
}">
|
||||||
<EditTable v-model="modelRef.message.inputs"/>
|
<EditTable v-model="modelRef.message.inputs"/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-form>
|
</j-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -62,8 +62,6 @@ import EditTable from './EditTable.vue'
|
||||||
|
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
|
|
||||||
const funcList = ref<Record<string, any>[]>([])
|
|
||||||
|
|
||||||
const filterOption = (input: string, option: any) => {
|
const filterOption = (input: string, option: any) => {
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<div>
|
<div>
|
||||||
<h2>1、在百度小度技能平台创建技能,并授权。完成物联网平台与DuerOS的关联。</h2>
|
<h2>1、在百度小度技能平台创建技能,并授权。完成物联网平台与DuerOS的关联。</h2>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc.jpg')" />
|
<j-image width="100%" :src="getImage('/cloud/dueros-doc.jpg')" />
|
||||||
</div>
|
</div>
|
||||||
<h1>授权地址</h1>
|
<h1>授权地址</h1>
|
||||||
<div>物联网平台的登录地址。注意需要为https。</div>
|
<div>物联网平台的登录地址。注意需要为https。</div>
|
||||||
|
@ -26,19 +26,19 @@
|
||||||
<h1>Client_Id</h1>
|
<h1>Client_Id</h1>
|
||||||
<div>请填写系统管理-应用管理中的clientId。</div>
|
<div>请填写系统管理-应用管理中的clientId。</div>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc1.png')" />
|
<j-image width="100%" :src="getImage('/cloud/dueros-doc1.png')" />
|
||||||
</div>
|
</div>
|
||||||
<h1>回调地址</h1>
|
<h1>回调地址</h1>
|
||||||
<div>请复制DuerOS平台中的值,填写到系统管理-应用管理中-redirectUrl中。</div>
|
<div>请复制DuerOS平台中的值,填写到系统管理-应用管理中-redirectUrl中。</div>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc2.png')" />
|
<j-image width="100%" :src="getImage('/cloud/dueros-doc2.png')" />
|
||||||
</div>
|
</div>
|
||||||
<h1>Token地址</h1>
|
<h1>Token地址</h1>
|
||||||
<div>请复制并填写:HTTPS://{location.host}/api/v1/token</div>
|
<div>请复制并填写:HTTPS://{location.host}/api/v1/token</div>
|
||||||
<h1>ClientSecret</h1>
|
<h1>ClientSecret</h1>
|
||||||
<div>请复制系统管理-应用管理中的secureKey,填写到DuerOS平台。</div>
|
<div>请复制系统管理-应用管理中的secureKey,填写到DuerOS平台。</div>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<a-image width="100%" :src="getImage('/cloud/dueros-doc3.png')" />
|
<j-image width="100%" :src="getImage('/cloud/dueros-doc3.png')" />
|
||||||
</div>
|
</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
<h1>WebService</h1>
|
<h1>WebService</h1>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<a-card>
|
<j-card>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="16">
|
<j-col :span="16">
|
||||||
<TitleComponent data="基本信息" />
|
<TitleComponent data="基本信息" />
|
||||||
<a-form
|
<j-form
|
||||||
:layout="'vertical'"
|
:layout="'vertical'"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="modelRef"
|
:model="modelRef"
|
||||||
>
|
>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="24">
|
<j-col :span="24">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="名称"
|
label="名称"
|
||||||
name="name"
|
name="name"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -25,14 +25,14 @@
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<a-input
|
<j-input
|
||||||
placeholder="请输入名称"
|
placeholder="请输入名称"
|
||||||
v-model:value="modelRef.name"
|
v-model:value="modelRef.name"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="产品"
|
label="产品"
|
||||||
name="id"
|
name="id"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
:disabled="
|
:disabled="
|
||||||
type !== 'edit' &&
|
type !== 'edit' &&
|
||||||
modelRef.id &&
|
modelRef.id &&
|
||||||
|
@ -54,18 +54,18 @@
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
@change="productChange"
|
@change="productChange"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="item in productList"
|
v-for="item in productList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
>{{ item.name }}</a-select-option
|
>{{ item.name }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="applianceType"
|
name="applianceType"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -75,50 +75,50 @@
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
设备类型
|
设备类型
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
title="DuerOS平台拟定的规范"
|
title="DuerOS平台拟定的规范"
|
||||||
>
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="margin-left: 2px"
|
style="margin-left: 2px"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择设备类型"
|
placeholder="请选择设备类型"
|
||||||
v-model:value="modelRef.applianceType"
|
v-model:value="modelRef.applianceType"
|
||||||
show-search
|
show-search
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
@change="typeChange"
|
@change="typeChange"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="item in typeList"
|
v-for="item in typeList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
>{{ item.name }}</a-select-option
|
>{{ item.name }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="productName"
|
name="productName"
|
||||||
v-show="false"
|
v-show="false"
|
||||||
label="产品名称"
|
label="产品名称"
|
||||||
>
|
>
|
||||||
<a-input
|
<j-input
|
||||||
v-model:value="modelRef.productName"
|
v-model:value="modelRef.productName"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24">
|
<j-col :span="24">
|
||||||
<p>动作映射</p>
|
<p>动作映射</p>
|
||||||
<a-collapse
|
<j-collapse
|
||||||
v-if="modelRef.actionMappings.length"
|
v-if="modelRef.actionMappings.length"
|
||||||
:activeKey="actionActiveKey"
|
:activeKey="actionActiveKey"
|
||||||
@change="onActionCollChange"
|
@change="onActionCollChange"
|
||||||
>
|
>
|
||||||
<a-collapse-panel
|
<j-collapse-panel
|
||||||
v-for="(
|
v-for="(
|
||||||
item, index
|
item, index
|
||||||
) in modelRef.actionMappings"
|
) in modelRef.actionMappings"
|
||||||
|
@ -139,9 +139,9 @@
|
||||||
type="DeleteOutlined"
|
type="DeleteOutlined"
|
||||||
@click="delItem(index)"
|
@click="delItem(index)"
|
||||||
/></template>
|
/></template>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="[
|
:name="[
|
||||||
'actionMappings',
|
'actionMappings',
|
||||||
index,
|
index,
|
||||||
|
@ -155,16 +155,16 @@
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
动作
|
动作
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
title="DuerOS平台拟定的设备类型具有的相关动作"
|
title="DuerOS平台拟定的设备类型具有的相关动作"
|
||||||
>
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择动作"
|
placeholder="请选择动作"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
item.action
|
item.action
|
||||||
|
@ -174,7 +174,7 @@
|
||||||
filterOption
|
filterOption
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="i in getTypesActions(
|
v-for="i in getTypesActions(
|
||||||
item.action,
|
item.action,
|
||||||
)"
|
)"
|
||||||
|
@ -183,13 +183,13 @@
|
||||||
:label="i.name"
|
:label="i.name"
|
||||||
>{{
|
>{{
|
||||||
i.name
|
i.name
|
||||||
}}</a-select-option
|
}}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="[
|
:name="[
|
||||||
'actionMappings',
|
'actionMappings',
|
||||||
index,
|
index,
|
||||||
|
@ -203,16 +203,16 @@
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
操作
|
操作
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
title="映射物联网平台中所选产品具备的动作"
|
title="映射物联网平台中所选产品具备的动作"
|
||||||
>
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择操作"
|
placeholder="请选择操作"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
item.actionType
|
item.actionType
|
||||||
|
@ -222,22 +222,22 @@
|
||||||
filterOption
|
filterOption
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
value="command"
|
value="command"
|
||||||
>下发指令</a-select-option
|
>下发指令</j-select-option
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
value="latestData"
|
value="latestData"
|
||||||
>获取历史数据</a-select-option
|
>获取历史数据</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col
|
<j-col
|
||||||
:span="24"
|
:span="24"
|
||||||
v-if="item.actionType"
|
v-if="item.actionType"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="[
|
:name="[
|
||||||
'actionMappings',
|
'actionMappings',
|
||||||
index,
|
index,
|
||||||
|
@ -256,14 +256,14 @@
|
||||||
item.actionType
|
item.actionType
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-collapse-panel>
|
</j-collapse-panel>
|
||||||
</a-collapse>
|
</j-collapse>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24">
|
<j-col :span="24">
|
||||||
<a-button
|
<j-button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
style="width: 100%; margin-top: 10px"
|
style="width: 100%; margin-top: 10px"
|
||||||
@click="addItem"
|
@click="addItem"
|
||||||
|
@ -272,16 +272,16 @@
|
||||||
type="PlusOutlined"
|
type="PlusOutlined"
|
||||||
style="margin-left: 2px"
|
style="margin-left: 2px"
|
||||||
/>新增动作
|
/>新增动作
|
||||||
</a-button>
|
</j-button>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24">
|
<j-col :span="24">
|
||||||
<p style="margin-top: 20px">属性映射</p>
|
<p style="margin-top: 20px">属性映射</p>
|
||||||
<a-collapse
|
<j-collapse
|
||||||
v-if="modelRef.propertyMappings.length"
|
v-if="modelRef.propertyMappings.length"
|
||||||
:activeKey="propertyActiveKey"
|
:activeKey="propertyActiveKey"
|
||||||
@change="onPropertyCollChange"
|
@change="onPropertyCollChange"
|
||||||
>
|
>
|
||||||
<a-collapse-panel
|
<j-collapse-panel
|
||||||
v-for="(
|
v-for="(
|
||||||
item, index
|
item, index
|
||||||
) in modelRef.propertyMappings"
|
) in modelRef.propertyMappings"
|
||||||
|
@ -302,9 +302,9 @@
|
||||||
type="DeleteOutlined"
|
type="DeleteOutlined"
|
||||||
@click="delPropertyItem(index)"
|
@click="delPropertyItem(index)"
|
||||||
/></template>
|
/></template>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="DuerOS属性"
|
label="DuerOS属性"
|
||||||
:name="[
|
:name="[
|
||||||
'propertyMappings',
|
'propertyMappings',
|
||||||
|
@ -317,7 +317,7 @@
|
||||||
'请选择DuerOS属性',
|
'请选择DuerOS属性',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择DuerOS属性"
|
placeholder="请选择DuerOS属性"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
item.source
|
item.source
|
||||||
|
@ -327,7 +327,7 @@
|
||||||
filterOption
|
filterOption
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="i in getDuerOSProperties(
|
v-for="i in getDuerOSProperties(
|
||||||
item.source,
|
item.source,
|
||||||
)"
|
)"
|
||||||
|
@ -335,13 +335,13 @@
|
||||||
:value="i.id"
|
:value="i.id"
|
||||||
>{{
|
>{{
|
||||||
i.name
|
i.name
|
||||||
}}</a-select-option
|
}}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="平台属性"
|
label="平台属性"
|
||||||
:name="[
|
:name="[
|
||||||
'propertyMappings',
|
'propertyMappings',
|
||||||
|
@ -354,7 +354,7 @@
|
||||||
'请选择平台属性',
|
'请选择平台属性',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择平台属性"
|
placeholder="请选择平台属性"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
item.target
|
item.target
|
||||||
|
@ -365,7 +365,7 @@
|
||||||
filterOption
|
filterOption
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="i in getProductProperties(
|
v-for="i in getProductProperties(
|
||||||
item.target,
|
item.target,
|
||||||
)"
|
)"
|
||||||
|
@ -373,17 +373,17 @@
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
>{{
|
>{{
|
||||||
i.name
|
i.name
|
||||||
}}</a-select-option
|
}}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-collapse-panel>
|
</j-collapse-panel>
|
||||||
</a-collapse>
|
</j-collapse>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24">
|
<j-col :span="24">
|
||||||
<a-button
|
<j-button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
style="width: 100%; margin-top: 10px"
|
style="width: 100%; margin-top: 10px"
|
||||||
@click="addPropertyItem"
|
@click="addPropertyItem"
|
||||||
|
@ -392,10 +392,10 @@
|
||||||
type="PlusOutlined"
|
type="PlusOutlined"
|
||||||
style="margin-left: 2px"
|
style="margin-left: 2px"
|
||||||
/>新增属性
|
/>新增属性
|
||||||
</a-button>
|
</j-button>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="24" style="margin-top: 20px">
|
<j-col :span="24" style="margin-top: 20px">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="说明"
|
label="说明"
|
||||||
name="description"
|
name="description"
|
||||||
:rules="{
|
:rules="{
|
||||||
|
@ -403,16 +403,16 @@
|
||||||
message: '最多输入200个字符',
|
message: '最多输入200个字符',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-textarea
|
<j-textarea
|
||||||
v-model:value="modelRef.description"
|
v-model:value="modelRef.description"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
showCount
|
showCount
|
||||||
:maxlength="200"
|
:maxlength="200"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-form>
|
</j-form>
|
||||||
<div v-if="type === 'edit'">
|
<div v-if="type === 'edit'">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -423,12 +423,12 @@
|
||||||
保存
|
保存
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="8">
|
<j-col :span="8">
|
||||||
<Doc />
|
<Doc />
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-card>
|
</j-card>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -442,9 +442,10 @@ import {
|
||||||
detail,
|
detail,
|
||||||
} from '@/api/northbound/dueros';
|
} from '@/api/northbound/dueros';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
|
||||||
const router = useRouter();
|
const menuStory = useMenuStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
|
@ -640,7 +641,8 @@ const saveBtn = async () => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
formRef.value.resetFields();
|
formRef.value.resetFields();
|
||||||
router.push('/iot/northbound/DuerOS/');
|
menuStory.jumpPage('Northbound/DuerOS');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="northbound-dueros"
|
target="northbound-dueros"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
/>
|
/>
|
||||||
<JTable
|
<JProTable
|
||||||
ref="instanceRef"
|
ref="instanceRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="query"
|
:request="query"
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
:params="params"
|
:params="params"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-space>
|
<j-space>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleAdd"
|
@click="handleAdd"
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<template #icon><AIcon type="PlusOutlined" /></template>
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
新增
|
新增
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<CardBox
|
<CardBox
|
||||||
|
@ -45,18 +45,18 @@
|
||||||
>
|
>
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</h3>
|
</h3>
|
||||||
<a-row>
|
<j-row>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<div class="card-item-content-text">产品</div>
|
<div class="card-item-content-text">产品</div>
|
||||||
<div>{{ slotProps?.productName }}</div>
|
<div>{{ slotProps?.productName }}</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<div class="card-item-content-text">
|
<div class="card-item-content-text">
|
||||||
设备类型
|
设备类型
|
||||||
</div>
|
</div>
|
||||||
<div>{{ slotProps?.applianceType?.text }}</div>
|
<div>{{ slotProps?.applianceType?.text }}</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
<template #state="slotProps">
|
<template #state="slotProps">
|
||||||
<a-badge
|
<j-badge
|
||||||
:text="slotProps.state?.text"
|
:text="slotProps.state?.text"
|
||||||
:status="statusMap.get(slotProps.state?.value)"
|
:status="statusMap.get(slotProps.state?.value)"
|
||||||
/>
|
/>
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
{{ slotProps.applianceType.text }}
|
{{ slotProps.applianceType.text }}
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space>
|
<j-space>
|
||||||
<template
|
<template
|
||||||
v-for="i in getActions(slotProps, 'table')"
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
:key="i.key"
|
:key="i.key"
|
||||||
|
@ -109,9 +109,9 @@
|
||||||
<template #icon><AIcon :type="i.icon" /></template>
|
<template #icon><AIcon :type="i.icon" /></template>
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JProTable>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -124,15 +124,13 @@ import {
|
||||||
queryProductList,
|
queryProductList,
|
||||||
queryTypes,
|
queryTypes,
|
||||||
} from '@/api/northbound/dueros';
|
} from '@/api/northbound/dueros';
|
||||||
import type { ActionsType } from '@/components/Table/index.vue';
|
import type { ActionsType } from '@/views/device/Instance/typings';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
import { useMenuStore } from 'store/menu';
|
import { useMenuStore } from 'store/menu';
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const instanceRef = ref<Record<string, any>>({});
|
const instanceRef = ref<Record<string, any>>({});
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
const current = ref<Record<string, any>>({});
|
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
|
|
||||||
const statusMap = new Map();
|
const statusMap = new Map();
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="dialog-box">
|
<div class="dialog-box">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<div class="dialog-title">
|
<div class="dialog-title">
|
||||||
<a-badge
|
<j-badge
|
||||||
:color="
|
:color="
|
||||||
statusColor.get(
|
statusColor.get(
|
||||||
item.error ? 'error' : 'success',
|
item.error ? 'error' : 'success',
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
class="dialog-editor"
|
class="dialog-editor"
|
||||||
v-if="visible.includes(item.key)"
|
v-if="visible.includes(item.key)"
|
||||||
>
|
>
|
||||||
<a-textarea autoSize :bordered="false" :value="item?.detail" />
|
<j-textarea autoSize :bordered="false" :value="item?.detail" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,8 +50,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const operationMap = new Map();
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const operationMap = new Map();
|
||||||
operationMap.set('connection', '连接');
|
operationMap.set('connection', '连接');
|
||||||
operationMap.set('auth', '权限验证');
|
operationMap.set('auth', '权限验证');
|
||||||
operationMap.set('decode', '解码');
|
operationMap.set('decode', '解码');
|
||||||
|
@ -80,19 +81,9 @@ const getDetail = (item: any) => {
|
||||||
visible.value.splice(index, 1);
|
visible.value.splice(index, 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
console.log(props.data)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
// @import 'ant-design-vue/es/style/themes/default.less';
|
|
||||||
|
|
||||||
// :root {
|
|
||||||
// --dialog-primary-color: @primary-color;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.dialog-item {
|
.dialog-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-table
|
<j-table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data-source="dataSource"
|
:data-source="dataSource"
|
||||||
|
@ -36,12 +36,11 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</j-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PropType } from "vue-demi";
|
import { PropType } from "vue";
|
||||||
|
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
(e: 'update:modelValue', data: Record<string, any>[]): void;
|
(e: 'update:modelValue', data: Record<string, any>[]): void;
|
||||||
|
|
|
@ -1,110 +1,110 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="function">
|
<div class="function">
|
||||||
<a-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="6">
|
<j-col :span="6">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="type"
|
name="type"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择',
|
message: '请选择',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
v-model:value="modelRef.type"
|
v-model:value="modelRef.type"
|
||||||
show-search
|
show-search
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
>
|
>
|
||||||
<a-select-option value="READ_PROPERTY"
|
<j-select-option value="READ_PROPERTY"
|
||||||
>读取属性</a-select-option
|
>读取属性</j-select-option
|
||||||
>
|
>
|
||||||
<a-select-option value="WRITE_PROPERTY"
|
<j-select-option value="WRITE_PROPERTY"
|
||||||
>修改属性</a-select-option
|
>修改属性</j-select-option
|
||||||
>
|
>
|
||||||
<a-select-option value="INVOKE_FUNCTION"
|
<j-select-option value="INVOKE_FUNCTION"
|
||||||
>调用功能</a-select-option
|
>调用功能</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col
|
<j-col
|
||||||
:span="6"
|
:span="6"
|
||||||
v-if="
|
v-if="
|
||||||
['READ_PROPERTY', 'WRITE_PROPERTY'].includes(
|
['READ_PROPERTY', 'WRITE_PROPERTY'].includes(
|
||||||
modelRef.type,
|
modelRef?.type || '',
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="properties"
|
name="properties"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择属性',
|
message: '请选择属性',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择属性"
|
placeholder="请选择属性"
|
||||||
v-model:value="modelRef.properties"
|
v-model:value="modelRef.properties"
|
||||||
show-search
|
show-search
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="i in metadata?.properties || []"
|
v-for="i in metadata?.properties || []"
|
||||||
:key="i.id"
|
:key="i.id"
|
||||||
:value="i.id"
|
:value="i.id"
|
||||||
:label="i.name"
|
:label="i.name"
|
||||||
>{{ i.name }}</a-select-option
|
>{{ i.name }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="6" v-if="modelRef.type === 'WRITE_PROPERTY'">
|
<j-col :span="6" v-if="modelRef.type === 'WRITE_PROPERTY'">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="propertyValue"
|
name="propertyValue"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入值',
|
message: '请输入值',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="modelRef.propertyValue" />
|
<j-input v-model:value="modelRef.propertyValue" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="6" v-if="modelRef.type === 'INVOKE_FUNCTION'">
|
<j-col :span="6" v-if="modelRef.type === 'INVOKE_FUNCTION'">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="function"
|
name="function"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择功能',
|
message: '请选择功能',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
placeholder="请选择功能"
|
placeholder="请选择功能"
|
||||||
v-model:value="modelRef.function"
|
v-model:value="modelRef.function"
|
||||||
show-search
|
show-search
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
@change="funcChange"
|
@change="funcChange"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="i in metadata?.functions || []"
|
v-for="i in metadata?.functions || []"
|
||||||
:key="i.id"
|
:key="i.id"
|
||||||
:value="i.id"
|
:value="i.id"
|
||||||
:label="i.name"
|
:label="i.name"
|
||||||
>{{ i.name }}</a-select-option
|
>{{ i.name }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="4">
|
<j-col :span="4">
|
||||||
<a-button type="primary" @click="saveBtn">发送</a-button>
|
<j-button type="primary" @click="saveBtn">发送</j-button>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col
|
<j-col
|
||||||
:span="24"
|
:span="24"
|
||||||
v-if="
|
v-if="
|
||||||
modelRef.type === 'INVOKE_FUNCTION' && modelRef.function && modelRef.inputs.length
|
modelRef.type === 'INVOKE_FUNCTION' && modelRef.function && modelRef.inputs.length
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="inputs"
|
name="inputs"
|
||||||
label="参数列表"
|
label="参数列表"
|
||||||
:rules="{
|
:rules="{
|
||||||
|
@ -113,10 +113,10 @@
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<EditTable v-model="modelRef.inputs" />
|
<EditTable v-model="modelRef.inputs" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,106 @@
|
||||||
<template>
|
<template>
|
||||||
log
|
<div class="log-item" :key="data.id">
|
||||||
</template>
|
<div class="log-card">
|
||||||
|
<div class="log-icon" @click="visible = !visible">
|
||||||
|
<AIcon :type="visible ? 'DownOutlined' : 'RightOutlined'" />
|
||||||
|
</div>
|
||||||
|
<div class="log-box">
|
||||||
|
<div class="log-header">
|
||||||
|
<div class="log-title">
|
||||||
|
<j-tag color="error">ERROR</j-tag>
|
||||||
|
{{ operationMap.get(data.operation) }}
|
||||||
|
</div>
|
||||||
|
<div class="log-time">
|
||||||
|
{{ dayjs(data.endTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="log-editor" v-if="visible">
|
||||||
|
<j-textarea
|
||||||
|
autoSize
|
||||||
|
:bordered="false"
|
||||||
|
:value="data?.detail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const operationMap = new Map();
|
||||||
|
operationMap.set('connection', '连接');
|
||||||
|
operationMap.set('auth', '权限验证');
|
||||||
|
operationMap.set('decode', '解码');
|
||||||
|
operationMap.set('encode', '编码');
|
||||||
|
operationMap.set('request', '请求');
|
||||||
|
operationMap.set('response', '响应');
|
||||||
|
operationMap.set('downstream', '下行消息');
|
||||||
|
operationMap.set('upstream', '上行消息');
|
||||||
|
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.log-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
|
||||||
|
.log-card {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.log-icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.log-header {
|
||||||
|
.log-title {
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-time {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-editor {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
color: black !important;
|
||||||
|
background-color: #fafafa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar {
|
||||||
|
width: 5px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,20 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<a-row :gutter="24">
|
<j-row :gutter="24">
|
||||||
<a-col :span="16">
|
<j-col :span="16">
|
||||||
<a-row :gutter="24" style="margin-bottom: 20px">
|
<j-row :gutter="24" style="margin-bottom: 20px">
|
||||||
<a-col :span="12" v-for="item in messageArr" :key="item">
|
<j-col :span="12" v-for="item in messageArr" :key="item">
|
||||||
<div
|
<div
|
||||||
:style="messageStyleMap.get(item.status)"
|
:style="messageStyleMap.get(item.status)"
|
||||||
class="message-status"
|
class="message-status"
|
||||||
>
|
>
|
||||||
<a-badge
|
<j-badge
|
||||||
:status="messageStatusMap.get(item.status)"
|
:status="messageStatusMap.get(item.status)"
|
||||||
style="margin-right: 5px"
|
style="margin-right: 5px"
|
||||||
/>
|
/>
|
||||||
<span>{{ item.text }}</span>
|
<span>{{ item.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
<div>
|
<div>
|
||||||
<TitleComponent data="调试" />
|
<TitleComponent data="调试" />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div><Function /></div>
|
<div><Function /></div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="8">
|
<j-col :span="8">
|
||||||
<div class="right-log">
|
<div class="right-log">
|
||||||
<TitleComponent data="日志" />
|
<TitleComponent data="日志" />
|
||||||
<div :style="{ marginTop: '10px' }">
|
<div :style="{ marginTop: '10px' }">
|
||||||
|
@ -38,11 +38,11 @@
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<a-empty v-else />
|
<j-empty v-else />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Badge, Descriptions, Modal, Tooltip } from "ant-design-vue"
|
import { Badge, Descriptions, Modal, Tooltip, AIcon, DescriptionsItem } from "jetlinks-ui-components"
|
||||||
import TitleComponent from '@/components/TitleComponent/index.vue'
|
import TitleComponent from '@/components/TitleComponent/index.vue'
|
||||||
import styles from './index.module.less'
|
import styles from './index.module.less'
|
||||||
import AIcon from "@/components/AIcon";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
const DiagnosticAdvice = defineComponent({
|
const DiagnosticAdvice = defineComponent({
|
||||||
|
@ -42,11 +41,11 @@ const DiagnosticAdvice = defineComponent({
|
||||||
<div style={{ marginTop: 15 }}>
|
<div style={{ marginTop: 15 }}>
|
||||||
<TitleComponent data="连接信息" />
|
<TitleComponent data="连接信息" />
|
||||||
<Descriptions column={2}>
|
<Descriptions column={2}>
|
||||||
<Descriptions.Item span={1} label="设备ID">
|
<DescriptionsItem span={1} label="设备ID">
|
||||||
{data?.info?.id || ''}
|
{data?.info?.id || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
{data?.info?.address?.length > 0 && (
|
{data?.info?.address?.length > 0 && (
|
||||||
<Descriptions.Item span={1} label="连接地址">
|
<DescriptionsItem span={1} label="连接地址">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
title={
|
title={
|
||||||
|
@ -69,11 +68,11 @@ const DiagnosticAdvice = defineComponent({
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(_.flatten(_.map(data?.info?.config, 'properties')) || []).map((item: any, index: number) => (
|
{(_.flatten(_.map(data?.info?.config, 'properties')) || []).map((item: any, index: number) => (
|
||||||
<Descriptions.Item
|
<DescriptionsItem
|
||||||
key={index}
|
key={index}
|
||||||
span={1}
|
span={1}
|
||||||
label={
|
label={
|
||||||
|
@ -90,7 +89,7 @@ const DiagnosticAdvice = defineComponent({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{data?.info?.configValue[item?.property] || ''}
|
{data?.info?.configValue[item?.property] || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
))}
|
))}
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import AIcon from "@/components/AIcon";
|
|
||||||
import { useInstanceStore } from "@/store/instance";
|
import { useInstanceStore } from "@/store/instance";
|
||||||
import { useMenuStore } from "@/store/menu";
|
import { useMenuStore } from "@/store/menu";
|
||||||
import { Button, Descriptions, Modal } from "ant-design-vue"
|
import { AIcon, Button, Modal, Descriptions, DescriptionsItem, Space } from "jetlinks-ui-components"
|
||||||
import styles from './index.module.less'
|
import styles from './index.module.less'
|
||||||
|
|
||||||
const ManualInspection = defineComponent({
|
const ManualInspection = defineComponent({
|
||||||
|
@ -39,12 +38,12 @@ const ManualInspection = defineComponent({
|
||||||
<div style={{ marginTop: '10px' }}>
|
<div style={{ marginTop: '10px' }}>
|
||||||
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||||
{(data?.data?.properties || []).map((item: any) => (
|
{(data?.data?.properties || []).map((item: any) => (
|
||||||
<Descriptions.Item
|
<DescriptionsItem
|
||||||
key={item.property}
|
key={item.property}
|
||||||
label={`${item.name}${item?.description ? `(${item.description})` : ''}`}
|
label={`${item.name}${item?.description ? `(${item.description})` : ''}`}
|
||||||
>
|
>
|
||||||
{data?.configuration[item.property] || ''}
|
{data?.configuration[item.property] || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
))}
|
))}
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,30 +79,30 @@ const ManualInspection = defineComponent({
|
||||||
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||||
{data.configuration?.provider === 'OneNet' ? (
|
{data.configuration?.provider === 'OneNet' ? (
|
||||||
<>
|
<>
|
||||||
<Descriptions.Item label={'接口地址'}>
|
<DescriptionsItem label={'接口地址'}>
|
||||||
{data?.configuration?.configuration?.apiAddress || ''}
|
{data?.configuration?.configuration?.apiAddress || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'apiKey'}>
|
<DescriptionsItem label={'apiKey'}>
|
||||||
{data?.configuration?.configuration?.apiKey || ''}
|
{data?.configuration?.configuration?.apiKey || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'通知Token'}>
|
<DescriptionsItem label={'通知Token'}>
|
||||||
{data?.configuration?.configuration?.validateToken || ''}
|
{data?.configuration?.configuration?.validateToken || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'aesKey'}>
|
<DescriptionsItem label={'aesKey'}>
|
||||||
{data?.configuration?.configuration?.aesKey || ''}
|
{data?.configuration?.configuration?.aesKey || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Descriptions.Item label={'接口地址'}>
|
<DescriptionsItem label={'接口地址'}>
|
||||||
{data?.configuration?.configuration?.apiAddress || ''}
|
{data?.configuration?.configuration?.apiAddress || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'appKey'}>
|
<DescriptionsItem label={'appKey'}>
|
||||||
{data?.configuration?.configuration?.appKey || ''}
|
{data?.configuration?.configuration?.appKey || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'appSecret'}>
|
<DescriptionsItem label={'appSecret'}>
|
||||||
{data?.configuration?.configuration?.appSecret || ''}
|
{data?.configuration?.configuration?.appSecret || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
@ -140,45 +139,45 @@ const ManualInspection = defineComponent({
|
||||||
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
<Descriptions title={data?.data?.name} layout="vertical" bordered>
|
||||||
{data?.configuration?.configuration?.shareCluster ? (
|
{data?.configuration?.configuration?.shareCluster ? (
|
||||||
<>
|
<>
|
||||||
<Descriptions.Item label={'SIP 域'}>
|
<DescriptionsItem label={'SIP 域'}>
|
||||||
{data?.configuration?.configuration?.domain || ''}
|
{data?.configuration?.configuration?.domain || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'SIP ID'}>
|
<DescriptionsItem label={'SIP ID'}>
|
||||||
{data?.configuration?.configuration?.sipId || ''}
|
{data?.configuration?.configuration?.sipId || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'集群'}>
|
<DescriptionsItem label={'集群'}>
|
||||||
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'SIP 地址'}>
|
<DescriptionsItem label={'SIP 地址'}>
|
||||||
{`${data?.configuration?.configuration?.hostPort?.host}:${data?.configuration?.configuration?.hostPort?.port}`}
|
{`${data?.configuration?.configuration?.hostPort?.host}:${data?.configuration?.configuration?.hostPort?.port}`}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'公网 Host'}>
|
<DescriptionsItem label={'公网 Host'}>
|
||||||
{`${data?.configuration?.configuration?.hostPort?.publicHost}:${data?.configuration?.configuration?.hostPort?.publicPort}`}
|
{`${data?.configuration?.configuration?.hostPort?.publicHost}:${data?.configuration?.configuration?.hostPort?.publicPort}`}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Descriptions.Item label={'SIP 域'}>
|
<DescriptionsItem label={'SIP 域'}>
|
||||||
{data?.configuration?.configuration?.domain || ''}
|
{data?.configuration?.configuration?.domain || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'SIP ID'}>
|
<DescriptionsItem label={'SIP ID'}>
|
||||||
{data?.configuration?.configuration?.sipId || ''}
|
{data?.configuration?.configuration?.sipId || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'集群'}>
|
<DescriptionsItem label={'集群'}>
|
||||||
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
{data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
{data?.configuration?.configuration?.cluster.map((i: any, it: number) => (
|
{data?.configuration?.configuration?.cluster.map((i: any, it: number) => (
|
||||||
<div key={it}>
|
<div key={it}>
|
||||||
<div>节点{it + 1}</div>
|
<div>节点{it + 1}</div>
|
||||||
<Descriptions.Item label={'节点名称'}>
|
<DescriptionsItem label={'节点名称'}>
|
||||||
{i?.clusterNodeId || ''}
|
{i?.clusterNodeId || ''}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'SIP 地址'}>
|
<DescriptionsItem label={'SIP 地址'}>
|
||||||
{`${i.host}:${i?.port}`}
|
{`${i.host}:${i?.port}`}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
<Descriptions.Item label={'公网 Host'}>
|
<DescriptionsItem label={'公网 Host'}>
|
||||||
{`${i?.publicHost}:${i?.publicPort}`}
|
{`${i?.publicHost}:${i?.publicPort}`}
|
||||||
</Descriptions.Item>
|
</DescriptionsItem>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -207,20 +206,29 @@ const ManualInspection = defineComponent({
|
||||||
title="人工检查"
|
title="人工检查"
|
||||||
visible
|
visible
|
||||||
width={1000}
|
width={1000}
|
||||||
cancelText="去修改"
|
|
||||||
okText="确认无误"
|
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
emit('save', data)
|
emit('save', data)
|
||||||
}}
|
}}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
if (data.type === 'device') {
|
emit('close')
|
||||||
instanceStore.tabActiveKey = 'Info'
|
}}
|
||||||
} else if (data.type === 'product') {
|
v-slots={{
|
||||||
menuStory.jumpPage('device/Product/Detail', { id: data.productId, tab: 'access' });
|
footer: <Space>
|
||||||
} else {
|
<Button onClick={() => {
|
||||||
menuStory.jumpPage('link/AccessConfig/Detail', { id: data.configuration?.id });
|
if (data.type === 'device') {
|
||||||
}
|
instanceStore.tabActiveKey = 'Info'
|
||||||
}}>
|
} else if (data.type === 'product') {
|
||||||
|
menuStory.jumpPage('device/Product/Detail', { id: data.productId, tab: 'access' });
|
||||||
|
} else {
|
||||||
|
menuStory.jumpPage('link/AccessConfig/Detail', { id: data.configuration?.id });
|
||||||
|
}
|
||||||
|
}}>去修改</Button>
|
||||||
|
<Button onClick={() => {
|
||||||
|
emit('save', data)
|
||||||
|
}}>确认无误</Button>
|
||||||
|
</Space>
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div style={{ display: 'flex' }}>{dataRender()}</div>
|
<div style={{ display: 'flex' }}>{dataRender()}</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Badge, Button, message, Popconfirm, Space } from "ant-design-vue"
|
import { Badge, Button, message, Popconfirm, Space } from "jetlinks-ui-components"
|
||||||
import TitleComponent from '@/components/TitleComponent/index.vue'
|
import TitleComponent from '@/components/TitleComponent/index.vue'
|
||||||
import styles from './index.module.less'
|
import styles from './index.module.less'
|
||||||
import type { ListProps } from './util'
|
import type { ListProps } from './util'
|
||||||
|
@ -13,6 +13,7 @@ import ManualInspection from './ManualInspection'
|
||||||
import { deployDevice } from "@/api/initHome"
|
import { deployDevice } from "@/api/initHome"
|
||||||
import PermissionButton from '@/components/PermissionButton/index.vue'
|
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||||
import { useMenuStore } from "@/store/menu"
|
import { useMenuStore } from "@/store/menu"
|
||||||
|
import BindParentDevice from '../../components/BindParentDevice/index.vue'
|
||||||
|
|
||||||
type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
|
type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
|
||||||
|
|
||||||
|
@ -1691,6 +1692,102 @@ const Status = defineComponent({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{
|
||||||
|
bindParentVisible && (
|
||||||
|
<BindParentDevice
|
||||||
|
data={device.value}
|
||||||
|
onCancel={() => {
|
||||||
|
bindParentVisible.value = false
|
||||||
|
}}
|
||||||
|
onOk={async (parentId: string) => {
|
||||||
|
let item: ListProps | undefined = undefined;
|
||||||
|
const response = await detail(parentId);
|
||||||
|
if (response.status === 200) {
|
||||||
|
if (response?.result?.state?.value === 'notActive') {
|
||||||
|
item = {
|
||||||
|
key: 'parent-device',
|
||||||
|
name: '网关父设备',
|
||||||
|
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||||
|
status: 'error',
|
||||||
|
text: '异常',
|
||||||
|
info: (
|
||||||
|
<div>
|
||||||
|
<div class={styles.infoItem}>
|
||||||
|
<Badge
|
||||||
|
status="default"
|
||||||
|
text={
|
||||||
|
<span>
|
||||||
|
网关父设备已禁用,请先
|
||||||
|
<PermissionButton
|
||||||
|
hasPermission="device/Product:action"
|
||||||
|
popConfirm={{
|
||||||
|
title: '确认启用',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const resp = await _deploy(response?.result?.id || '');
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
list.value = modifyArrayList(
|
||||||
|
list.value,
|
||||||
|
{
|
||||||
|
key: 'parent-device',
|
||||||
|
name: '网关父设备',
|
||||||
|
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||||
|
status: 'success',
|
||||||
|
text: '正常',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
启用
|
||||||
|
</PermissionButton>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (response?.state?.value === 'online') {
|
||||||
|
item = {
|
||||||
|
key: 'parent-device',
|
||||||
|
name: '网关父设备',
|
||||||
|
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||||
|
status: 'success',
|
||||||
|
text: '正常',
|
||||||
|
info: null,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
item = {
|
||||||
|
key: 'parent-device',
|
||||||
|
name: '网关父设备',
|
||||||
|
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||||
|
status: 'error',
|
||||||
|
text: '异常',
|
||||||
|
info: (
|
||||||
|
<div>
|
||||||
|
<div class={styles.infoItem}>
|
||||||
|
<Badge
|
||||||
|
status="default"
|
||||||
|
text={<span>网关父设备已离线,请先排查网关设备故障</span>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (item) {
|
||||||
|
list.value = modifyArrayList(unref(list), item);
|
||||||
|
}
|
||||||
|
instanceStore.current.parentId = parentId;
|
||||||
|
bindParentVisible.value = false
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-card>
|
<j-card>
|
||||||
<div class="diagnose">
|
<div class="diagnose">
|
||||||
<div class="diagnose-header" :style="{background: headerColorMap.get(topState)}">
|
<div class="diagnose-header" :style="{background: headerColorMap.get(topState)}">
|
||||||
<div class="diagnose-top">
|
<div class="diagnose-top">
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="diagnose-progress">
|
<div class="diagnose-progress">
|
||||||
<a-progress
|
<j-progress
|
||||||
:percent="percent"
|
:percent="percent"
|
||||||
:showInfo="false"
|
:showInfo="false"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
<Status v-show="activeKey !== 'message'" :providerType="providerType" @countChange="countChange" @percentChange="percentChange" @stateChange="stateChange" />
|
<Status v-show="activeKey !== 'message'" :providerType="providerType" @countChange="countChange" @percentChange="percentChange" @stateChange="stateChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</j-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<j-form layout="vertical" ref="formRef" :model="modelRef">
|
<j-form layout="vertical" ref="formRef" :model="modelRef">
|
||||||
<template v-for="(item, index) in props.config" :key="index">
|
<template v-for="(item, index) in (props.config || [])" :key="index">
|
||||||
<j-form-item
|
<j-form-item
|
||||||
:name="item.property"
|
:name="item.property"
|
||||||
v-for="i in item.properties"
|
v-for="i in item.properties"
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { modify } from '@/api/device/instance';
|
import { modify } from '@/api/device/instance';
|
||||||
import { useInstanceStore } from '@/store/instance';
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'save']);
|
const emit = defineEmits(['close', 'save']);
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ import {
|
||||||
_deploy,
|
_deploy,
|
||||||
configurationReset,
|
configurationReset,
|
||||||
} from '@/api/device/instance';
|
} from '@/api/device/instance';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
import Save from './Save.vue';
|
import Save from './Save.vue';
|
||||||
|
|
||||||
const instanceStore = useInstanceStore();
|
const instanceStore = useInstanceStore();
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { queryUserListNoPaging, saveRelations } from '@/api/device/instance';
|
import { queryUserListNoPaging, saveRelations } from '@/api/device/instance';
|
||||||
import { useInstanceStore } from '@/store/instance';
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'save']);
|
const emit = defineEmits(['close', 'save']);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<j-table
|
<j-table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:datj-source="dataSource"
|
:data-source="dataSource"
|
||||||
bordered
|
bordered
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
>
|
>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useInstanceStore } from '@/store/instance';
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { saveTags, delTags } from '@/api/device/instance'
|
import { saveTags, delTags } from '@/api/device/instance'
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<j-card>
|
<j-card>
|
||||||
<Search
|
<j-advanced-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="device-instance-log"
|
target="device-instance-log"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
type="simple"
|
||||||
|
class="search"
|
||||||
/>
|
/>
|
||||||
<JProTable
|
<JProTable
|
||||||
ref="instanceRefLog"
|
ref="instanceRefLog"
|
||||||
|
@ -12,6 +14,7 @@
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:defaultParams="{ sorts: [{ name: 'timestamp', order: 'desc' }] }"
|
:defaultParams="{ sorts: [{ name: 'timestamp', order: 'desc' }] }"
|
||||||
:params="params"
|
:params="params"
|
||||||
|
:bodyStyle="{ padding: 0 }"
|
||||||
>
|
>
|
||||||
<template #type="slotProps">
|
<template #type="slotProps">
|
||||||
{{ slotProps?.type?.text }}
|
{{ slotProps?.type?.text }}
|
||||||
|
@ -150,4 +153,7 @@ const handleSearch = (_params: any) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.search {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,28 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<Search :columns="columns" target="device-instance-running-events" />
|
<j-advanced-search class="search" type="simple" :columns="columns" target="device-instance-running-events" @search="handleSearch" />
|
||||||
<JTable
|
<JProTable
|
||||||
ref="eventsRef"
|
ref="eventsRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="_getEventList"
|
:request="_getEventList"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:bodyStyle="{ padding: '0 24px' }"
|
:params="params"
|
||||||
|
:bodyStyle="{ padding: '0 0 0 24px' }"
|
||||||
>
|
>
|
||||||
<template #timestamp="slotProps">
|
<template #timestamp="slotProps">
|
||||||
{{ moment(slotProps.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
{{ dayjs(slotProps.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-button type="link" @click="detail(slotProps)">
|
<j-button type="link" @click="detail(slotProps)">
|
||||||
<AIcon type="SearchOutlined" />
|
<AIcon type="SearchOutlined" />
|
||||||
</a-button>
|
</j-button>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JProTable>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
import { getEventList } from '@/api/device/instance';
|
import { getEventList } from '@/api/device/instance';
|
||||||
import { useInstanceStore } from '@/store/instance';
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { Modal } from 'ant-design-vue';
|
import { Modal } from 'jetlinks-ui-components';
|
||||||
|
import JsonViewer from 'vue-json-viewer';
|
||||||
|
|
||||||
const events = defineProps({
|
const events = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -51,11 +53,11 @@ const columns = ref<Record<string, any>>([
|
||||||
]);
|
]);
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
|
|
||||||
const _getEventList = () =>
|
const _getEventList = (_params: any) =>
|
||||||
getEventList(
|
getEventList(
|
||||||
instanceStore.current.id || '',
|
instanceStore.current.id || '',
|
||||||
events.data.id || '',
|
events.data.id || '',
|
||||||
params.value,
|
_params
|
||||||
);
|
);
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
|
@ -78,12 +80,25 @@ watchEffect(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const detail = () => {
|
const handleSearch = (_params: any) => {
|
||||||
|
params.value = _params;
|
||||||
|
};
|
||||||
|
|
||||||
|
const detail = (_info: any) => {
|
||||||
Modal.info({
|
Modal.info({
|
||||||
title: () => '详情',
|
title: () => '详情',
|
||||||
width: 850,
|
width: 850,
|
||||||
content: () => h('div', {}, [h('p', '暂未开发')]),
|
content: () => h('JsonViewer', {
|
||||||
|
'expand-depth': 5,
|
||||||
|
value: _info
|
||||||
|
}),
|
||||||
okText: '关闭',
|
okText: '关闭',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.search {
|
||||||
|
padding: 0 0 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,36 +1,36 @@
|
||||||
<template>
|
<template>
|
||||||
<a-spin :spinning="loading">
|
<j-spin :spinning="loading">
|
||||||
<div>
|
<div>
|
||||||
<a-space>
|
<j-space>
|
||||||
<div>
|
<div>
|
||||||
统计周期:
|
统计周期:
|
||||||
<a-select v-model:value="cycle" style="width: 120px">
|
<j-select v-model:value="cycle" style="width: 120px">
|
||||||
<a-select-option value="*" v-if="_type"
|
<j-select-option value="*" v-if="_type"
|
||||||
>实际值</a-select-option
|
>实际值</j-select-option
|
||||||
>
|
>
|
||||||
<a-select-option value="1m">按分钟统计</a-select-option>
|
<j-select-option value="1m">按分钟统计</j-select-option>
|
||||||
<a-select-option value="1h">按小时统计</a-select-option>
|
<j-select-option value="1h">按小时统计</j-select-option>
|
||||||
<a-select-option value="1d">按天统计</a-select-option>
|
<j-select-option value="1d">按天统计</j-select-option>
|
||||||
<a-select-option value="1w">按周统计</a-select-option>
|
<j-select-option value="1w">按周统计</j-select-option>
|
||||||
<a-select-option value="1M">按月统计</a-select-option>
|
<j-select-option value="1M">按月统计</j-select-option>
|
||||||
</a-select>
|
</j-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="cycle !== '*' && _type">
|
<div v-if="cycle !== '*' && _type">
|
||||||
统计规则:
|
统计规则:
|
||||||
<a-select v-model:value="agg" style="width: 120px">
|
<j-select v-model:value="agg" style="width: 120px">
|
||||||
<a-select-option value="AVG">平均值</a-select-option>
|
<j-select-option value="AVG">平均值</j-select-option>
|
||||||
<a-select-option value="MAX">最大值</a-select-option>
|
<j-select-option value="MAX">最大值</j-select-option>
|
||||||
<a-select-option value="MIN">最小值</a-select-option>
|
<j-select-option value="MIN">最小值</j-select-option>
|
||||||
<a-select-option value="COUNT">总数</a-select-option>
|
<j-select-option value="COUNT">总数</j-select-option>
|
||||||
</a-select>
|
</j-select>
|
||||||
</div>
|
</div>
|
||||||
</a-space>
|
</j-space>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 100%; height: 500px">
|
<div style="width: 100%; height: 500px">
|
||||||
<Chart :options="options" v-if="chartsList.length" />
|
<Chart :options="options" v-if="chartsList.length" />
|
||||||
<JEmpty v-else />
|
<JEmpty v-else />
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</j-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<a-spin :spinning="loading">
|
<j-spin :spinning="loading">
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
<div style="position: absolute; right: 0; top: 5px; z-index: 999">
|
<div style="position: absolute; right: 0; top: 5px; z-index: 999">
|
||||||
<a-space>
|
<j-space>
|
||||||
<a-button type="primary" @click="onStart">开始动画</a-button>
|
<j-button type="primary" @click="onStart">开始动画</j-button>
|
||||||
<a-button type="primary" @click="onStop">停止动画</a-button>
|
<j-button type="primary" @click="onStop">停止动画</j-button>
|
||||||
</a-space>
|
</j-space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AMapComponent style="height: 500px">
|
<AMapComponent style="height: 500px">
|
||||||
<PathSimplifier :pathData="geoList" ref="amapPath"></PathSimplifier>
|
<PathSimplifier :pathData="geoList" ref="amapPath"></PathSimplifier>
|
||||||
</AMapComponent>
|
</AMapComponent>
|
||||||
</a-spin>
|
</j-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-table
|
<j-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
size="small"
|
size="small"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
pageSize: dataSource?.pageSize || 10,
|
pageSize: dataSource?.pageSize || 10,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
total: dataSource?.total || 0,
|
total: dataSource?.total || 0,
|
||||||
pageSizeOptions: [5, 10, 20, 50],
|
pageSizeOptions: ['8', '12', '24', '60', '100']
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'action'">
|
<template v-else-if="column.key === 'action'">
|
||||||
<a-space>
|
<j-space>
|
||||||
<a-button
|
<j-button
|
||||||
v-if="
|
v-if="
|
||||||
showLoad ||
|
showLoad ||
|
||||||
(!getType(record?.value) &&
|
(!getType(record?.value) &&
|
||||||
|
@ -36,16 +36,16 @@
|
||||||
type="link"
|
type="link"
|
||||||
@click="_download(record)"
|
@click="_download(record)"
|
||||||
><AIcon type="DownloadOutlined"
|
><AIcon type="DownloadOutlined"
|
||||||
/></a-button>
|
/></j-button>
|
||||||
<a-button type="link" @click="showDetail(record)"
|
<j-button type="link" @click="showDetail(record)"
|
||||||
><AIcon type="SearchOutlined"
|
><AIcon type="SearchOutlined"
|
||||||
/></a-button>
|
/></j-button>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</j-table>
|
||||||
</div>
|
</div>
|
||||||
<a-modal
|
<j-modal
|
||||||
title="详情"
|
title="详情"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
@ok="visible = false"
|
@ok="visible = false"
|
||||||
|
@ -60,13 +60,13 @@
|
||||||
:expand-depth="5"
|
:expand-depth="5"
|
||||||
:value="current.formatValue"
|
:value="current.formatValue"
|
||||||
/>
|
/>
|
||||||
<a-textarea
|
<j-textarea
|
||||||
v-else-if="data?.valueType?.type === 'file'"
|
v-else-if="data?.valueType?.type === 'file'"
|
||||||
:value="current.formatValue"
|
:value="current.formatValue"
|
||||||
:row="3"
|
:row="3"
|
||||||
/>
|
/>
|
||||||
<a-input v-else disabled :value="current.formatValue" />
|
<j-input v-else disabled :value="current.formatValue" />
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -90,7 +90,12 @@ const _props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const instanceStore = useInstanceStore();
|
const instanceStore = useInstanceStore();
|
||||||
const dataSource = ref({});
|
const dataSource = ref({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
data: [],
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
const current = ref<any>({});
|
const current = ref<any>({});
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<a-space>
|
<j-space>
|
||||||
<a-radio-group
|
<j-radio-group
|
||||||
:value="radioValue"
|
:value="radioValue"
|
||||||
button-style="solid"
|
button-style="solid"
|
||||||
@change="onRadioChange"
|
@change="onRadioChange"
|
||||||
>
|
>
|
||||||
<a-radio-button value="today">今日</a-radio-button>
|
<j-radio-button value="today">今日</j-radio-button>
|
||||||
<a-radio-button value="week">近一周</a-radio-button>
|
<j-radio-button value="week">近一周</j-radio-button>
|
||||||
<a-radio-button value="month">近一月</a-radio-button>
|
<j-radio-button value="month">近一月</j-radio-button>
|
||||||
</a-radio-group>
|
</j-radio-group>
|
||||||
<a-range-picker
|
<j-range-picker
|
||||||
show-time
|
show-time
|
||||||
v-model:value="dateValue"
|
v-model:value="dateValue"
|
||||||
:placeholder="['开始时间', '结束时间']"
|
:placeholder="['开始时间', '结束时间']"
|
||||||
@change="onRangeChange"
|
@change="onRangeChange"
|
||||||
:allowClear="false"
|
:allowClear="false"
|
||||||
/>
|
/>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal title="详情" visible width="50vw" @ok="onCancel" @cancel="onCancel">
|
<j-modal title="详情" visible width="50vw" @ok="onCancel" @cancel="onCancel">
|
||||||
<div style="margin-bottom: 10px"><TimeComponent v-model="dateValue" /></div>
|
<div style="margin-bottom: 10px"><TimeComponent v-model="dateValue" /></div>
|
||||||
<div>
|
<div>
|
||||||
<a-tabs :destroyInactiveTabPane="true" v-model:activeKey="activeKey" style="max-height: 600px; overflow-y: auto">
|
<j-tabs :destroyInactiveTabPane="true" v-model:activeKey="activeKey" style="max-height: 600px; overflow-y: auto">
|
||||||
<a-tab-pane key="table" tab="列表">
|
<j-tab-pane key="table" tab="列表">
|
||||||
<Table :data="props.data" :time="_getTimes" />
|
<Table :data="props.data" :time="_getTimes" />
|
||||||
</a-tab-pane>
|
</j-tab-pane>
|
||||||
<a-tab-pane key="charts" tab="图表">
|
<j-tab-pane key="charts" tab="图表">
|
||||||
<Charts :data="props.data" :time="_getTimes" />
|
<Charts :data="props.data" :time="_getTimes" />
|
||||||
</a-tab-pane>
|
</j-tab-pane>
|
||||||
<a-tab-pane key="geo" tab="轨迹" v-if="data?.valueType?.type === 'geoPoint'">
|
<j-tab-pane key="geo" tab="轨迹" v-if="data?.valueType?.type === 'geoPoint'">
|
||||||
<PropertyAMap :data="props.data" :time="_getTimes" />
|
<PropertyAMap :data="props.data" :time="_getTimes" />
|
||||||
</a-tab-pane>
|
</j-tab-pane>
|
||||||
</a-tabs>
|
</j-tabs>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:visible="true"
|
:visible="true"
|
||||||
title="编辑指标"
|
title="编辑指标"
|
||||||
|
@ -7,17 +7,32 @@
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
:confirmLoading="loading"
|
:confirmLoading="loading"
|
||||||
>
|
>
|
||||||
<a-alert message="场景联动页面可引用指标配置触发条件" type="warning" showIcon />
|
<j-alert
|
||||||
<a-form layout="vertical" ref="formRef" :model="modelRef" style="margin-top: 20px">
|
message="场景联动页面可引用指标配置触发条件"
|
||||||
|
type="warning"
|
||||||
|
showIcon
|
||||||
|
/>
|
||||||
|
<j-form
|
||||||
|
layout="vertical"
|
||||||
|
ref="formRef"
|
||||||
|
:model="modelRef"
|
||||||
|
style="margin-top: 20px"
|
||||||
|
>
|
||||||
<template v-for="(item, index) in modelRef.metrics" :key="index">
|
<template v-for="(item, index) in modelRef.metrics" :key="index">
|
||||||
<a-row type="flex" justify="space-between" align="bottom">
|
<j-row type="flex" justify="space-between" :align="'bottom'">
|
||||||
<a-col :span="11">
|
<j-col :span="item.range ? 11 : 24">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: `请${['date', 'boolean'].includes(data?.valueType?.type)? '选择': '输入'}指标值`,
|
message: `请${
|
||||||
}"
|
['date', 'boolean'].includes(
|
||||||
:name="['metrics', index, 'value', 0]"
|
data?.valueType?.type,
|
||||||
|
)
|
||||||
|
? '选择'
|
||||||
|
: '输入'
|
||||||
|
}指标值`,
|
||||||
|
}"
|
||||||
|
:name="['metrics', index, 'value', 0]"
|
||||||
:label="item?.name || '指标值'"
|
:label="item?.name || '指标值'"
|
||||||
>
|
>
|
||||||
<ValueItem
|
<ValueItem
|
||||||
|
@ -26,100 +41,131 @@
|
||||||
:options="
|
:options="
|
||||||
data.valueType?.type === 'boolean'
|
data.valueType?.type === 'boolean'
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
label: data.valueType?.trueText,
|
label: data.valueType
|
||||||
value: String(data.valueType?.trueValue),
|
?.trueText,
|
||||||
},
|
value: String(
|
||||||
{
|
data.valueType?.trueValue,
|
||||||
label: data.valueType?.falseText,
|
),
|
||||||
value: String(data.valueType?.falseValue),
|
},
|
||||||
},
|
{
|
||||||
]
|
label: data.valueType
|
||||||
|
?.falseText,
|
||||||
|
value: String(
|
||||||
|
data.valueType
|
||||||
|
?.falseValue,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<template v-if="item.range">
|
<template v-if="item.range">
|
||||||
<a-col><div class="center-icon">~</div></a-col>
|
<j-col><div class="center-icon">~</div></j-col>
|
||||||
<a-col :span="11">
|
<j-col :span="11">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="['metrics', index, 'value', 1]"
|
:name="['metrics', index, 'value', 1]"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: `请${['date', 'boolean'].includes(data?.valueType?.type)? '选择': '输入'}指标值`,
|
message: `请${
|
||||||
}"
|
['date', 'boolean'].includes(
|
||||||
|
data?.valueType?.type,
|
||||||
|
)
|
||||||
|
? '选择'
|
||||||
|
: '输入'
|
||||||
|
}指标值`,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<ValueItem
|
<ValueItem
|
||||||
v-model:modelValue="item.value[1]"
|
v-model:modelValue="item.value[1]"
|
||||||
:itemType="data.valueType?.type"
|
:itemType="data.valueType?.type"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</template>
|
</template>
|
||||||
</a-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
</a-form>
|
</j-form>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { queryMetric, saveMetric } from '@/api/device/instance'
|
import { queryMetric, saveMetric } from '@/api/device/instance';
|
||||||
const emit = defineEmits(['close']);
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { useInstanceStore } from "@/store/instance"
|
import { message } from 'jetlinks-ui-components';
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const loading = ref<boolean>(false)
|
const emit = defineEmits(['close']);
|
||||||
const instanceStore = useInstanceStore()
|
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
const instanceStore = useInstanceStore();
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
|
|
||||||
const modelRef = reactive({
|
const modelRef = reactive({
|
||||||
metrics: []
|
metrics: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('close')
|
emit('close');
|
||||||
}
|
};
|
||||||
|
|
||||||
watch(() => props.data.id, (newVal) => {
|
watch(
|
||||||
if(newVal && instanceStore.current.id){
|
() => props.data.id,
|
||||||
queryMetric(instanceStore.current.id, props.data.id).then(resp => {
|
(newVal) => {
|
||||||
if (resp.status === 200) {
|
if (newVal && instanceStore.current.id) {
|
||||||
if (Array.isArray(resp?.result) && resp?.result.length) {
|
queryMetric(instanceStore.current.id, props.data.id).then(
|
||||||
const list = resp?.result.map((item: any) => {
|
(resp) => {
|
||||||
const val = Array.isArray(item?.value) ? [item?.value] : item?.value?.split(',')
|
if (resp.status === 200) {
|
||||||
return {
|
if (
|
||||||
...item,
|
Array.isArray(resp?.result) &&
|
||||||
value: val
|
resp?.result.length
|
||||||
};
|
) {
|
||||||
});
|
const list = resp?.result.map((item: any) => {
|
||||||
modelRef.metrics = list as any
|
const val = Array.isArray(item?.value)
|
||||||
} else {
|
? [item?.value]
|
||||||
const type = props.data.valueType?.type;
|
: item?.value?.split(',');
|
||||||
if (type === 'boolean') {
|
return {
|
||||||
const list = props.data.expands?.metrics.map((item: any) => {
|
...item,
|
||||||
const value = (item?.value || {}).map((i: any) => String(i)) || {};
|
value: val,
|
||||||
return {
|
};
|
||||||
...item,
|
});
|
||||||
value,
|
modelRef.metrics = list as any;
|
||||||
};
|
} else {
|
||||||
});
|
const type = props.data.valueType?.type;
|
||||||
modelRef.metrics = list || []
|
if (type === 'boolean') {
|
||||||
} else {
|
const list = props.data.expands?.metrics.map(
|
||||||
modelRef.metrics = props.data.expands?.metrics || []
|
(item: any) => {
|
||||||
}
|
const value =
|
||||||
}
|
(item?.value || {}).map((i: any) =>
|
||||||
|
String(i),
|
||||||
|
) || {};
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
modelRef.metrics = list || [];
|
||||||
|
} else {
|
||||||
|
modelRef.metrics =
|
||||||
|
props.data.expands?.metrics || [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
}
|
{ immediate: true, deep: true },
|
||||||
}, {immediate: true, deep: true})
|
);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
formRef.value
|
formRef.value
|
||||||
|
@ -132,19 +178,23 @@ const handleSave = () => {
|
||||||
value: item.value.join(','),
|
value: item.value.join(','),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const resp = await saveMetric(instanceStore.current.id || '', props.data.id || '', list).finally(() => {
|
const resp = await saveMetric(
|
||||||
loading.value = false
|
instanceStore.current.id || '',
|
||||||
})
|
props.data.id || '',
|
||||||
|
list,
|
||||||
|
).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
emit('close')
|
emit('close');
|
||||||
formRef.value.resetFields();
|
formRef.value.resetFields();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
console.log('error', err);
|
console.log('error', err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<a-card :hoverable="true" class="card-box">
|
<j-card :hoverable="true" class="card-box">
|
||||||
<!-- <a-spin :spinning="loading"> -->
|
<!-- <j-spin :spinning="loading"> -->
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">{{ _props.data.name }}</div>
|
<div class="title">{{ _props.data.name }}</div>
|
||||||
<div class="extra">
|
<div class="extra">
|
||||||
<a-space :size="16">
|
<j-space :size="16">
|
||||||
<template v-for="i in actions" :key="i.key">
|
<template v-for="i in actions" :key="i.key">
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
v-bind="i.tooltip"
|
v-bind="i.tooltip"
|
||||||
v-if="i.key !== 'edit'"
|
v-if="i.key !== 'edit'"
|
||||||
>
|
>
|
||||||
<a-button
|
<j-button
|
||||||
style="padding: 0; margin: 0"
|
style="padding: 0; margin: 0"
|
||||||
type="link"
|
type="link"
|
||||||
:disabled="i.disabled"
|
:disabled="i.disabled"
|
||||||
|
@ -21,8 +21,8 @@
|
||||||
:type="i.icon"
|
:type="i.icon"
|
||||||
style="color: #323130; font-size: 12px"
|
style="color: #323130; font-size: 12px"
|
||||||
/>
|
/>
|
||||||
</a-button>
|
</j-button>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
:disabled="i.disabled"
|
:disabled="i.disabled"
|
||||||
v-else
|
v-else
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
/></template>
|
/></template>
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</a-space>
|
</j-space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
|
@ -53,8 +53,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- </a-spin> -->
|
<!-- </j-spin> -->
|
||||||
</a-card>
|
</j-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:visible="true"
|
:visible="true"
|
||||||
title="编辑"
|
title="编辑"
|
||||||
|
@ -7,9 +7,9 @@
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
:confirmLoading="loading"
|
:confirmLoading="loading"
|
||||||
>
|
>
|
||||||
<a-alert message="当数据来源为设备时,填写的值将下发到设备" type="warning" showIcon />
|
<j-alert message="当数据来源为设备时,填写的值将下发到设备" type="warning" showIcon />
|
||||||
<a-form :rules="rules" layout="vertical" ref="formRef" :model="modelRef" style="margin-top: 20px">
|
<j-form :rules="rules" layout="vertical" ref="formRef" :model="modelRef" style="margin-top: 20px">
|
||||||
<a-form-item name="propertyValue" :label="data?.name || '自定义属性'">
|
<j-form-item name="propertyValue" :label="data?.name || '自定义属性'">
|
||||||
<ValueItem
|
<ValueItem
|
||||||
v-model:modelValue="modelRef.propertyValue"
|
v-model:modelValue="modelRef.propertyValue"
|
||||||
:itemType="data?.valueType?.type || data?.dataType"
|
:itemType="data?.valueType?.type || data?.dataType"
|
||||||
|
@ -24,16 +24,15 @@
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form>
|
</j-form>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { setProperty } from '@/api/device/instance'
|
import { setProperty } from '@/api/device/instance'
|
||||||
const emit = defineEmits(['close']);
|
|
||||||
import { useInstanceStore } from "@/store/instance"
|
import { useInstanceStore } from "@/store/instance"
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -42,6 +41,8 @@ const props = defineProps({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
const instanceStore = useInstanceStore()
|
const instanceStore = useInstanceStore()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
width="600px"
|
width="600px"
|
||||||
:visible="true"
|
:visible="true"
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
>
|
>
|
||||||
<template v-if="['.jpg', '.png'].includes(type)">
|
<template v-if="['.jpg', '.png'].includes(type)">
|
||||||
<a-image :src="value?.formatValue" />
|
<j-image :src="value?.formatValue" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)">
|
<template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)">
|
||||||
</template>
|
</template>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
:value="value?.formatValue"
|
:value="value?.formatValue"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,59 +1,110 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<div v-if="value?.formatValue !== 0 && !value?.formatValue" :class="valueClass">--</div>
|
<div
|
||||||
|
v-if="value?.formatValue !== 0 && !value?.formatValue"
|
||||||
|
:class="valueClass"
|
||||||
|
>
|
||||||
|
--
|
||||||
|
</div>
|
||||||
<div v-else-if="_data.data?.valueType?.type === 'file'">
|
<div v-else-if="_data.data?.valueType?.type === 'file'">
|
||||||
<template v-if="data?.valueType?.fileType === 'base64'">
|
<template v-if="data?.valueType?.fileType === 'base64'">
|
||||||
<div :class="valueClass" v-if="!!getType(value?.formatValue)">
|
<div :class="valueClass" v-if="!!getType(value?.formatValue)">
|
||||||
<img :src="imgMap.get(_type)" @error="onError" />
|
<img :src="imgMap.get(_type)" @error="onError" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else :class="valueClass">
|
<div v-else :class="valueClass">
|
||||||
<img :src="imgMap.get('other')" />
|
<img :src="imgMap.get('other')" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
<div v-else-if="data?.valueType?.fileType === 'Binary(二进制)'" :class="valueClass">
|
|
||||||
<img :src="imgMap.get('other')" />
|
|
||||||
</div>
|
|
||||||
<template v-else>
|
|
||||||
<template v-if="imgList.some((item) => value?.formatValue.includes(item))">
|
|
||||||
<div :class="valueClass" @click="getDetail('img')">
|
|
||||||
<img :src="value?.formatValue" @error="imgError" />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="videoList.some((item) => value?.formatValue.includes(item))">
|
<div
|
||||||
<div :class="valueClass" @click="getDetail('video')">
|
v-else-if="data?.valueType?.fileType === 'Binary(二进制)'"
|
||||||
<img :src="imgMap.get('video')" />
|
:class="valueClass"
|
||||||
</div>
|
>
|
||||||
</template>
|
|
||||||
<template v-else-if="fileList.some((item) => value?.formatValue.includes(item))">
|
|
||||||
<div :class="valueClass">
|
|
||||||
<img :src="imgMap.get(fileList.find((item) => value?.formatValue.includes(item)).slice(1))" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div :class="valueClass">
|
|
||||||
<img :src="imgMap.get('other')" />
|
<img :src="imgMap.get('other')" />
|
||||||
</div>
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
imgList.some((item) =>
|
||||||
|
value?.formatValue.includes(item),
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div :class="valueClass" @click="getDetail('img')">
|
||||||
|
<img :src="value?.formatValue" @error="imgError" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
videoList.some((item) =>
|
||||||
|
value?.formatValue.includes(item),
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div :class="valueClass" @click="getDetail('video')">
|
||||||
|
<img :src="imgMap.get('video')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
fileList.some((item) =>
|
||||||
|
value?.formatValue.includes(item),
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div :class="valueClass">
|
||||||
|
<img
|
||||||
|
:src="
|
||||||
|
imgMap.get(
|
||||||
|
fileList
|
||||||
|
.find((item) =>
|
||||||
|
value?.formatValue.includes(item),
|
||||||
|
)
|
||||||
|
.slice(1),
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div :class="valueClass">
|
||||||
|
<img :src="imgMap.get('other')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="_data.data?.valueType?.type === 'object'" @click="getDetail('obj')" :class="valueClass">
|
<div
|
||||||
<img :src="imgMap.get('obj')" />
|
v-else-if="_data.data?.valueType?.type === 'object'"
|
||||||
|
@click="getDetail('obj')"
|
||||||
|
:class="valueClass"
|
||||||
|
>
|
||||||
|
<img :src="imgMap.get('obj')" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="_data.data?.valueType?.type === 'geoPoint' || _data.data?.valueType?.type === 'array'" :class="valueClass">
|
<div
|
||||||
{{JSON.stringify(value?.formatValue)}}
|
v-else-if="
|
||||||
|
_data.data?.valueType?.type === 'geoPoint' ||
|
||||||
|
_data.data?.valueType?.type === 'array'
|
||||||
|
"
|
||||||
|
:class="valueClass"
|
||||||
|
>
|
||||||
|
{{ JSON.stringify(value?.formatValue) }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else :class="valueClass">
|
<div v-else :class="valueClass">
|
||||||
{{String(value?.formatValue)}}
|
{{ String(value?.formatValue) }}
|
||||||
</div>
|
</div>
|
||||||
<ValueDetail v-if="visible" :type="_types" :value="value" @close="visible = false" />
|
<ValueDetail
|
||||||
|
v-if="visible"
|
||||||
|
:type="_types"
|
||||||
|
:value="value"
|
||||||
|
@close="visible = false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getImage } from "@/utils/comm";
|
import { getImage } from '@/utils/comm';
|
||||||
import { message } from "ant-design-vue";
|
import { message } from 'jetlinks-ui-components';
|
||||||
import ValueDetail from './ValueDetail.vue'
|
import ValueDetail from './ValueDetail.vue';
|
||||||
import {getType, imgMap, imgList, videoList, fileList} from './index'
|
import { getType, imgMap, imgList, videoList, fileList } from './index';
|
||||||
|
|
||||||
const _data = defineProps({
|
const _data = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -62,88 +113,99 @@ const _data = defineProps({
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {},
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'card'
|
default: 'card',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const valueClass = computed(() => {
|
const valueClass = computed(() => {
|
||||||
return _data.type === 'card' ? 'cardValue' : 'otherValue'
|
return _data.type === 'card' ? 'cardValue' : 'otherValue';
|
||||||
})
|
});
|
||||||
|
|
||||||
const isHttps = document.location.protocol === 'https:';
|
const isHttps = document.location.protocol === 'https:';
|
||||||
|
|
||||||
const _types = ref<string>('')
|
const _types = ref<string>('');
|
||||||
const visible = ref<boolean>(false)
|
const visible = ref<boolean>(false);
|
||||||
const temp = ref<boolean>(false)
|
const temp = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
const onError = (e: any) => {
|
const onError = (e: any) => {
|
||||||
e.target.src = imgMap.get('other')
|
e.target.src = imgMap.get('other');
|
||||||
}
|
};
|
||||||
|
|
||||||
const imgError = (e: any) => {
|
const imgError = (e: any) => {
|
||||||
e.target.src = imgMap.get('error')
|
e.target.src = imgMap.get('error');
|
||||||
temp.value = true
|
temp.value = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getDetail = (_type: string) => {
|
const getDetail = (_type: string) => {
|
||||||
const value = _data.value
|
const value = _data.value;
|
||||||
let flag: string = ''
|
let flag: string = '';
|
||||||
if(_type === 'img'){
|
if (_type === 'img') {
|
||||||
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
||||||
message.error('域名为https时,不支持访问http地址');
|
message.error('域名为https时,不支持访问http地址');
|
||||||
} else if (temp.value) {
|
} else if (temp.value) {
|
||||||
message.error('该图片无法访问');
|
message.error('该图片无法访问');
|
||||||
} else {
|
} else {
|
||||||
flag = ['.jpg', '.png'].find((item) => value?.formatValue.includes(item)) || '--';
|
flag =
|
||||||
|
['.jpg', '.png'].find((item) =>
|
||||||
|
value?.formatValue.includes(item),
|
||||||
|
) || '--';
|
||||||
|
_types.value = flag;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
} else if (_type === 'video') {
|
||||||
|
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
||||||
|
message.error('域名为https时,不支持访问http地址');
|
||||||
|
} else if (
|
||||||
|
['.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))
|
||||||
|
) {
|
||||||
|
message.error('当前仅支持播放.mp4,.flv,.m3u8格式的视频');
|
||||||
|
} else {
|
||||||
|
flag =
|
||||||
|
['.m3u8', '.flv', '.mp4'].find((item) =>
|
||||||
|
value?.formatValue.includes(item),
|
||||||
|
) || '--';
|
||||||
|
_types.value = flag;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
} else if (_type === 'obj') {
|
||||||
|
flag = 'obj';
|
||||||
|
_types.value = flag;
|
||||||
|
visible.value = true;
|
||||||
}
|
}
|
||||||
} else if(_type === 'video'){
|
};
|
||||||
if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
|
|
||||||
message.error('域名为https时,不支持访问http地址');
|
|
||||||
} else if (['.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))) {
|
|
||||||
message.error('当前仅支持播放.mp4,.flv,.m3u8格式的视频');
|
|
||||||
} else {
|
|
||||||
flag = ['.m3u8', '.flv', '.mp4'].find((item) => value?.formatValue.includes(item)) || '--';
|
|
||||||
}
|
|
||||||
}else if(_type === 'obj'){
|
|
||||||
flag = 'obj'
|
|
||||||
}
|
|
||||||
_types.value = flag
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.value {
|
.value {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.cardValue {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60px;
|
|
||||||
overflow: hidden;
|
|
||||||
color: #323130;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 24px;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
img {
|
.cardValue {
|
||||||
width: 60px;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
}
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #323130;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
.otherValue {
|
img {
|
||||||
img {
|
width: 60px;
|
||||||
width: 40px;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.otherValue {
|
||||||
|
img {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -2,7 +2,8 @@
|
||||||
<j-spin :spinning="loading">
|
<j-spin :spinning="loading">
|
||||||
<JProTable
|
<JProTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:dataSource="dataSource"
|
:request="query"
|
||||||
|
:params="_params"
|
||||||
:bodyStyle="{ padding: '0 0 0 20px' }"
|
:bodyStyle="{ padding: '0 0 0 20px' }"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
|
@ -58,26 +59,6 @@
|
||||||
</template>
|
</template>
|
||||||
</j-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
<template #paginationRender>
|
|
||||||
<j-pagination
|
|
||||||
size="small"
|
|
||||||
:total="total"
|
|
||||||
:showQuickJumper="false"
|
|
||||||
:showSizeChanger="true"
|
|
||||||
:current="pageIndex + 1"
|
|
||||||
:pageSize="pageSize"
|
|
||||||
:pageSizeOptions="['8', '12', '24', '60', '100']"
|
|
||||||
:show-total="
|
|
||||||
(num) =>
|
|
||||||
`第 ${pageIndex * pageSize + 1} - ${
|
|
||||||
(pageIndex + 1) * pageSize > num
|
|
||||||
? num
|
|
||||||
: (pageIndex + 1) * pageSize
|
|
||||||
} 条/总共 ${num} 条`
|
|
||||||
"
|
|
||||||
@change="pageChange"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</JProTable>
|
</JProTable>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
<Save v-if="editVisible" @close="editVisible = false" :data="currentInfo" />
|
<Save v-if="editVisible" @close="editVisible = false" :data="currentInfo" />
|
||||||
|
@ -107,6 +88,7 @@ import { message } from 'ant-design-vue';
|
||||||
import { getWebSocket } from '@/utils/websocket';
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { queryDashboard } from '@/api/comm';
|
import { queryDashboard } from '@/api/comm';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '名称',
|
title: '名称',
|
||||||
|
@ -142,9 +124,6 @@ const _data = defineProps({
|
||||||
const value = ref<string>('');
|
const value = ref<string>('');
|
||||||
const dataSource = ref<PropertyData[]>([]);
|
const dataSource = ref<PropertyData[]>([]);
|
||||||
const _dataSource = ref<PropertyData[]>([]);
|
const _dataSource = ref<PropertyData[]>([]);
|
||||||
const pageIndex = ref<number>(0);
|
|
||||||
const pageSize = ref<number>(8);
|
|
||||||
const total = ref<number>(0);
|
|
||||||
const editVisible = ref<boolean>(false); // 编辑
|
const editVisible = ref<boolean>(false); // 编辑
|
||||||
const detailVisible = ref<boolean>(false); // 详情
|
const detailVisible = ref<boolean>(false); // 详情
|
||||||
const currentInfo = ref<Record<string, any>>({});
|
const currentInfo = ref<Record<string, any>>({});
|
||||||
|
@ -152,6 +131,9 @@ const instanceStore = useInstanceStore();
|
||||||
const indicatorVisible = ref<boolean>(false); // 指标
|
const indicatorVisible = ref<boolean>(false); // 指标
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const propertyValue = ref<Record<string, any>>({});
|
const propertyValue = ref<Record<string, any>>({});
|
||||||
|
const _params = reactive({
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
|
||||||
const subRef = ref();
|
const subRef = ref();
|
||||||
|
|
||||||
|
@ -309,39 +291,35 @@ const getDashboard = async () => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const query = (page: number, size: number, value: string) => {
|
const query = (params: Record<string, any>) =>
|
||||||
pageIndex.value = page || 0;
|
new Promise((resolve) => {
|
||||||
pageSize.value = size || 8;
|
const _from = params.pageIndex * params.pageSize;
|
||||||
const _from = pageIndex.value * pageSize.value;
|
const _to = (params.pageIndex + 1) * params.pageSize;
|
||||||
const _to = (pageIndex.value + 1) * pageSize.value;
|
let arr = _.cloneDeep(_dataSource.value);
|
||||||
const arr = _.cloneDeep(_dataSource.value);
|
if (params?.name) {
|
||||||
if (unref(value)) {
|
const li = _dataSource.value.filter((i: any) => {
|
||||||
const li = arr.filter((i: any) => {
|
return i?.name.indexOf(params.name) !== -1;
|
||||||
return i?.name.indexOf(unref(value)) !== -1;
|
});
|
||||||
|
arr = _.cloneDeep(li);
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
result: {
|
||||||
|
data: arr.slice(_from, _to),
|
||||||
|
pageIndex: params.pageIndex || 0,
|
||||||
|
pageSize: params.pageSize || 12,
|
||||||
|
total: arr.length,
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
});
|
});
|
||||||
dataSource.value = li.slice(_from, _to);
|
getDashboard();
|
||||||
total.value = li.length;
|
});
|
||||||
} else {
|
|
||||||
dataSource.value = arr.slice(_from, _to);
|
|
||||||
total.value = arr.length;
|
|
||||||
}
|
|
||||||
getDashboard();
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageChange = (page: number, size: number) => {
|
|
||||||
if (size === pageSize.value) {
|
|
||||||
query(page - 1, size, value.value);
|
|
||||||
} else {
|
|
||||||
query(0, size, value.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => _data.data,
|
() => _data.data,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal.length) {
|
if (newVal.length) {
|
||||||
_dataSource.value = newVal as PropertyData[];
|
_dataSource.value = newVal as PropertyData[];
|
||||||
query(0, 8, value.value);
|
_params.name = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -351,7 +329,7 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSearch = () => {
|
const onSearch = () => {
|
||||||
query(0, 8, value.value);
|
_params.name = value.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
<!-- 绑定设备 -->
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
:maskClosable="false"
|
||||||
|
width="1000px"
|
||||||
|
:visible="true"
|
||||||
|
title="绑定父设备"
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
:confirmLoading="btnLoading"
|
||||||
|
>
|
||||||
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
target="child-device-bind"
|
||||||
|
@search="handleSearch"
|
||||||
|
type="simple"
|
||||||
|
/>
|
||||||
|
<JProTable
|
||||||
|
ref="bindDeviceRef"
|
||||||
|
:columns="columns"
|
||||||
|
:request="query"
|
||||||
|
model="TABLE"
|
||||||
|
:bodyStyle="{ padding: '0 0 0 24px' }"
|
||||||
|
:defaultParams="defaultParams"
|
||||||
|
:rowSelection="{
|
||||||
|
selectedRowKeys: _selectedRowKeys,
|
||||||
|
onChange: onSelectChange,
|
||||||
|
}"
|
||||||
|
:params="params"
|
||||||
|
>
|
||||||
|
<template #registryTime="slotProps">
|
||||||
|
{{
|
||||||
|
slotProps.registryTime
|
||||||
|
? dayjs(slotProps.registryTime).format(
|
||||||
|
'YYYY-MM-DD HH:mm:ss',
|
||||||
|
)
|
||||||
|
: ''
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<j-badge
|
||||||
|
:text="slotProps.state.text"
|
||||||
|
:status="statusMap.get(slotProps.state.value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</JProTable>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { query, bindDevice } from '@/api/device/instance';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
|
||||||
|
const emit = defineEmits(['cancel', 'ok']);
|
||||||
|
|
||||||
|
const bindDeviceRef = ref<Record<string, any>>({});
|
||||||
|
const params = ref<Record<string, any>>({});
|
||||||
|
const _selectedRowKeys = ref<string[]>([]);
|
||||||
|
const btnLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const statusMap = new Map();
|
||||||
|
statusMap.set('online', 'success');
|
||||||
|
statusMap.set('offline', 'error');
|
||||||
|
statusMap.set('notActive', 'warning');
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'productId$product-info',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
column: 'deviceType',
|
||||||
|
termType: 'eq',
|
||||||
|
value: 'gateway',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属产品',
|
||||||
|
dataIndex: 'productName',
|
||||||
|
key: 'productName',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注册时间',
|
||||||
|
dataIndex: 'registryTime',
|
||||||
|
key: 'registryTime',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '禁用', value: 'notActive' },
|
||||||
|
{ label: '离线', value: 'offline' },
|
||||||
|
{ label: '在线', value: 'online' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleSearch = (e: any) => {
|
||||||
|
params.value = e;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelectChange = (keys: string[]) => {
|
||||||
|
_selectedRowKeys.value = [...keys];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
if (_selectedRowKeys.value.length === 0) {
|
||||||
|
message.warning('请选择需要绑定的设备');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
btnLoading.value = true;
|
||||||
|
bindDevice(_selectedRowKeys.value[0], props.data.id, )
|
||||||
|
.then((resp) => {
|
||||||
|
if(resp.status === 200){
|
||||||
|
emit('ok', _selectedRowKeys.value[0]);
|
||||||
|
message.success('操作成功');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
btnLoading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('cancel', false);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
width="900px"
|
width="900px"
|
||||||
title="批量映射"
|
title="批量映射"
|
||||||
visible
|
visible
|
||||||
|
@ -10,49 +10,49 @@
|
||||||
<div class="map-tree-top">
|
<div class="map-tree-top">
|
||||||
采集器的点位名称与属性名称一致时将自动映射绑定;有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定
|
采集器的点位名称与属性名称一致时将自动映射绑定;有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定
|
||||||
</div>
|
</div>
|
||||||
<a-spin :spinning="loading">
|
<j-spin :spinning="loading">
|
||||||
<div class="map-tree-content">
|
<div class="map-tree-content">
|
||||||
<a-card class="map-tree-content-card" title="源数据">
|
<j-card class="map-tree-content-card" title="源数据">
|
||||||
<a-tree
|
<j-tree
|
||||||
checkable
|
checkable
|
||||||
:height="300"
|
:height="300"
|
||||||
:tree-data="dataSource"
|
:tree-data="dataSource"
|
||||||
:checkedKeys="checkedKeys"
|
:checkedKeys="checkedKeys"
|
||||||
@check="onCheck"
|
@check="onCheck"
|
||||||
/>
|
/>
|
||||||
</a-card>
|
</j-card>
|
||||||
<div style="width: 100px">
|
<div style="width: 100px">
|
||||||
<a-button
|
<j-button
|
||||||
:disabled="rightList.length >= leftList.length"
|
:disabled="rightList.length >= leftList.length"
|
||||||
@click="onRight"
|
@click="onRight"
|
||||||
>加入右侧</a-button
|
>加入右侧</j-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<a-card class="map-tree-content-card" title="采集器">
|
<j-card class="map-tree-content-card" title="采集器">
|
||||||
<a-list
|
<j-list
|
||||||
size="small"
|
size="small"
|
||||||
:data-source="rightList"
|
:data-source="rightList"
|
||||||
class="map-tree-content-card-list"
|
class="map-tree-content-card-list"
|
||||||
>
|
>
|
||||||
<template #renderItem="{ item }">
|
<template #renderItem="{ item }">
|
||||||
<a-list-item>
|
<j-list-item>
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a-popconfirm
|
<j-popconfirm
|
||||||
title="确定删除?"
|
title="确定删除?"
|
||||||
@confirm="_delete(item.key)"
|
@confirm="_delete(item.key)"
|
||||||
>
|
>
|
||||||
<AIcon type="DeleteOutlined" />
|
<AIcon type="DeleteOutlined" />
|
||||||
</a-popconfirm>
|
</j-popconfirm>
|
||||||
</template>
|
</template>
|
||||||
</a-list-item>
|
</j-list-item>
|
||||||
</template>
|
</template>
|
||||||
</a-list>
|
</j-list>
|
||||||
</a-card>
|
</j-card>
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</j-spin>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
<template>
|
<template>
|
||||||
<a-spin :spinning="loading" v-if="metadata.properties.length">
|
<j-spin :spinning="loading" v-if="metadata.properties.length">
|
||||||
<a-card>
|
<j-card>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space>
|
<j-space>
|
||||||
<a-button @click="visible = true">批量映射</a-button>
|
<j-button @click="visible = true">批量映射</j-button>
|
||||||
<a-button type="primary" @click="onSave">保存</a-button>
|
<j-button type="primary" @click="onSave">保存</j-button>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
<a-form ref="formRef" :model="modelRef">
|
<j-form ref="formRef" :model="modelRef">
|
||||||
<a-table :dataSource="modelRef.dataSource" :columns="columns">
|
<j-table :dataSource="modelRef.dataSource" :columns="columns">
|
||||||
<template #headerCell="{ column }">
|
<template #headerCell="{ column }">
|
||||||
<template v-if="column.key === 'collectorId'">
|
<template v-if="column.key === 'collectorId'">
|
||||||
采集器
|
采集器
|
||||||
<a-tooltip title="数据采集中配置的真实物理设备">
|
<j-tooltip title="数据采集中配置的真实物理设备">
|
||||||
<AIcon type="QuestionCircleOutlined" />
|
<AIcon type="QuestionCircleOutlined" />
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, record, index }">
|
<template #bodyCell="{ column, record, index }">
|
||||||
<template v-if="column.dataIndex === 'channelId'">
|
<template v-if="column.dataIndex === 'channelId'">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="['dataSource', index, 'channelId']"
|
:name="['dataSource', index, 'channelId']"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-model:value="record[column.dataIndex]"
|
v-model:value="record[column.dataIndex]"
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
allowClear
|
allowClear
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="item in channelList"
|
v-for="item in channelList"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
>{{ item.label }}</a-select-option
|
>{{ item.label }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'collectorId'">
|
<template v-if="column.dataIndex === 'collectorId'">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="['dataSource', index, 'collectorId']"
|
:name="['dataSource', index, 'collectorId']"
|
||||||
:rules="[
|
:rules="[
|
||||||
{
|
{
|
||||||
|
@ -54,10 +54,10 @@
|
||||||
:id="record.channelId"
|
:id="record.channelId"
|
||||||
type="COLLECTOR"
|
type="COLLECTOR"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'pointId'">
|
<template v-if="column.dataIndex === 'pointId'">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
:name="['dataSource', index, 'pointId']"
|
:name="['dataSource', index, 'pointId']"
|
||||||
:rules="[
|
:rules="[
|
||||||
{
|
{
|
||||||
|
@ -71,33 +71,33 @@
|
||||||
:id="record.collectorId"
|
:id="record.collectorId"
|
||||||
type="POINT"
|
type="POINT"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'id'">
|
<template v-if="column.dataIndex === 'id'">
|
||||||
<a-badge
|
<j-badge
|
||||||
v-if="record[column.dataIndex]"
|
v-if="record[column.dataIndex]"
|
||||||
status="success"
|
status="success"
|
||||||
text="已绑定"
|
text="已绑定"
|
||||||
/>
|
/>
|
||||||
<a-badge v-else status="error" text="未绑定" />
|
<j-badge v-else status="error" text="未绑定" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<a-tooltip title="解绑">
|
<j-tooltip title="解绑">
|
||||||
<a-popconfirm
|
<j-popconfirm
|
||||||
title="确认解绑"
|
title="确认解绑"
|
||||||
:disabled="!record.id"
|
:disabled="!record.id"
|
||||||
@confirm="unbind(record.id)"
|
@confirm="unbind(record.id)"
|
||||||
>
|
>
|
||||||
<a-button type="link" :disabled="!record.id"
|
<j-button type="link" :disabled="!record.id"
|
||||||
><AIcon type="icon-jiebang"
|
><AIcon type="icon-jiebang"
|
||||||
/></a-button>
|
/></j-button>
|
||||||
</a-popconfirm>
|
</j-popconfirm>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</j-table>
|
||||||
</a-form>
|
</j-form>
|
||||||
</a-card>
|
</j-card>
|
||||||
<PatchMapping
|
<PatchMapping
|
||||||
:deviceId="instanceStore.current.id"
|
:deviceId="instanceStore.current.id"
|
||||||
v-if="visible"
|
v-if="visible"
|
||||||
|
@ -106,10 +106,10 @@
|
||||||
:type="provider"
|
:type="provider"
|
||||||
:metaData="modelRef.dataSource"
|
:metaData="modelRef.dataSource"
|
||||||
/>
|
/>
|
||||||
</a-spin>
|
</j-spin>
|
||||||
<a-card v-else>
|
<j-card v-else>
|
||||||
<JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" />
|
<JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" />
|
||||||
</a-card>
|
</j-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<a-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%">
|
<j-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%">
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
>{{ item.name }}</a-select-option
|
>{{ item.name }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
<div style="display: flex; align-items: center">
|
<div style="display: flex; align-items: center">
|
||||||
<AIcon type="ArrowLeftOutlined" @click="onBack" />
|
<AIcon type="ArrowLeftOutlined" @click="onBack" />
|
||||||
<div style="margin-left: 20px">{{ instanceStore.current.name }}</div>
|
<div style="margin-left: 20px">{{ instanceStore.current.name }}</div>
|
||||||
<a-divider type="vertical" />
|
<j-divider type="vertical" />
|
||||||
<a-space>
|
<j-space>
|
||||||
<a-badge
|
<j-badge
|
||||||
:text="instanceStore.current.state?.text"
|
:text="instanceStore.current.state?.text"
|
||||||
:status="
|
:status="
|
||||||
statusMap.get(
|
statusMap.get(
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
>
|
>
|
||||||
断开连接
|
断开连接
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
v-if="
|
v-if="
|
||||||
instanceStore.current?.accessProvider ===
|
instanceStore.current?.accessProvider ===
|
||||||
'child-device' &&
|
'child-device' &&
|
||||||
|
@ -68,15 +68,15 @@
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
style="font-size: 14px"
|
style="font-size: 14px"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</a-space>
|
</j-space>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding-top: 10px">
|
<div style="padding-top: 10px">
|
||||||
<a-descriptions size="small" :column="4">
|
<j-descriptions size="small" :column="4">
|
||||||
<a-descriptions-item label="ID">{{
|
<j-descriptions-item label="ID">{{
|
||||||
instanceStore.current.id
|
instanceStore.current.id
|
||||||
}}</a-descriptions-item>
|
}}</j-descriptions-item>
|
||||||
<a-descriptions-item label="所属产品">
|
<j-descriptions-item label="所属产品">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="link"
|
type="link"
|
||||||
style="margin-top: -5px; padding: 0"
|
style="margin-top: -5px; padding: 0"
|
||||||
|
@ -85,8 +85,8 @@
|
||||||
>
|
>
|
||||||
{{ instanceStore.current.productName }}
|
{{ instanceStore.current.productName }}
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</a-descriptions-item>
|
</j-descriptions-item>
|
||||||
</a-descriptions>
|
</j-descriptions>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -119,7 +119,7 @@ import EdgeMap from './EdgeMap/index.vue';
|
||||||
import Parsing from './Parsing/index.vue'
|
import Parsing from './Parsing/index.vue'
|
||||||
import Log from './Log/index.vue'
|
import Log from './Log/index.vue'
|
||||||
import { _deploy, _disconnect } from '@/api/device/instance';
|
import { _deploy, _disconnect } from '@/api/device/instance';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { getWebSocket } from '@/utils/websocket';
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="导出" @ok="handleOk" @cancel="handleCancel">
|
<j-modal
|
||||||
|
:maskClosable="false"
|
||||||
|
width="800px"
|
||||||
|
:visible="true"
|
||||||
|
title="导出"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
<div style="background-color: rgb(236, 237, 238)">
|
<div style="background-color: rgb(236, 237, 238)">
|
||||||
<p style="padding: 10px">
|
<p style="padding: 10px">
|
||||||
<AIcon type="ExclamationCircleOutlined" />
|
<AIcon type="ExclamationCircleOutlined" />
|
||||||
|
@ -7,63 +14,86 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 20px">
|
<div style="margin-top: 20px">
|
||||||
<a-form :layout="'vertical'">
|
<j-form :layout="'vertical'">
|
||||||
<a-form-item label="产品">
|
<j-form-item label="产品">
|
||||||
<a-select showSearch v-model:value="modelRef.product" placeholder="请选择产品">
|
<j-select
|
||||||
<a-select-option :value="item.id" v-for="item in productList" :key="item.id" :title="item.name"></a-select-option>
|
show-search
|
||||||
</a-select>
|
:filter-option="filterOption"
|
||||||
</a-form-item>
|
v-model:value="modelRef.product"
|
||||||
<a-form-item label="文件格式">
|
placeholder="请选择产品"
|
||||||
<a-radio-group button-style="solid" v-model:value="modelRef.fileType" placeholder="请选择文件格式">
|
allowClear
|
||||||
<a-radio-button value="xlsx">xlsx</a-radio-button>
|
>
|
||||||
<a-radio-button value="csv">csv</a-radio-button>
|
<j-select-option
|
||||||
</a-radio-group>
|
:value="item.id"
|
||||||
</a-form-item>
|
v-for="item in productList"
|
||||||
</a-form>
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>{{ item.name }}</j-select-option
|
||||||
|
>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="文件格式">
|
||||||
|
<j-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="modelRef.fileType"
|
||||||
|
placeholder="请选择文件格式"
|
||||||
|
>
|
||||||
|
<j-radio-button value="xlsx">xlsx</j-radio-button>
|
||||||
|
<j-radio-button value="csv">csv</j-radio-button>
|
||||||
|
</j-radio-group>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { queryNoPagingPost } from '@/api/device/product'
|
import { queryNoPagingPost } from '@/api/device/product';
|
||||||
import { downloadFile } from '@/utils/utils'
|
import { downloadFile } from '@/utils/utils';
|
||||||
import encodeQuery from '@/utils/encodeQuery'
|
import encodeQuery from '@/utils/encodeQuery';
|
||||||
import { BASE_API_PATH } from '@/utils/variable'
|
import { BASE_API_PATH } from '@/utils/variable';
|
||||||
import { deviceExport } from '@/api/device/instance'
|
import { deviceExport } from '@/api/device/instance';
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: undefined
|
default: undefined,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const modelRef = reactive({
|
const modelRef = reactive({
|
||||||
product: undefined,
|
product: undefined,
|
||||||
fileType: 'xlsx'
|
fileType: 'xlsx',
|
||||||
});
|
});
|
||||||
|
|
||||||
const productList = ref<Record<string, any>[]>([])
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const productList = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
() => {
|
() => {
|
||||||
queryNoPagingPost({paging: false}).then(resp => {
|
queryNoPagingPost({ paging: false }).then((resp) => {
|
||||||
if(resp.status === 200){
|
if (resp.status === 200) {
|
||||||
productList.value = resp.result as Record<string, any>[]
|
productList.value = resp.result as Record<string, any>[];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
{immediate: true, deep: true}
|
{ immediate: true, deep: true },
|
||||||
)
|
);
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
const params = encodeQuery(props.data);
|
const params = encodeQuery(props.data);
|
||||||
downloadFile(deviceExport(modelRef.product || "", modelRef.fileType),params);
|
downloadFile(
|
||||||
emit('close')
|
deviceExport(modelRef.product || '', modelRef.fileType),
|
||||||
}
|
params,
|
||||||
|
);
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('close')
|
emit('close');
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
|
@ -1,36 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="导入" @ok="handleCancel" @cancel="handleCancel">
|
<j-modal :maskClosable="false" width="800px" :visible="true" title="导入" @ok="handleSave" @cancel="handleCancel">
|
||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<a-form :layout="'vertical'">
|
<j-form :layout="'vertical'">
|
||||||
<a-row>
|
<j-row>
|
||||||
<a-col span="24">
|
<j-col span="24">
|
||||||
<a-form-item label="产品" required>
|
<j-form-item label="产品" required>
|
||||||
<a-select showSearch v-model:value="modelRef.product" placeholder="请选择产品">
|
<j-select showSearch v-model:value="modelRef.product" placeholder="请选择产品">
|
||||||
<a-select-option :value="item.id" v-for="item in productList" :key="item.id" :title="item.name"></a-select-option>
|
<j-select-option :value="item.id" v-for="item in productList" :key="item.id" :label="item.name">{{ item.name }}</j-select-option>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col span="24">
|
<j-col span="24">
|
||||||
<a-form-item label="文件格式" v-if="modelRef.product">
|
<j-form-item label="文件格式" v-if="modelRef.product">
|
||||||
<FileFormat v-model="modelRef.file" />
|
<FileFormat v-model="modelRef.file" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col span="12">
|
<j-col span="12">
|
||||||
<a-form-item label="文件上传" v-if="modelRef.product">
|
<j-form-item label="文件上传" v-if="modelRef.product">
|
||||||
<NormalUpload :product="modelRef.product" v-model="modelRef.upload" :file="modelRef.file" />
|
<NormalUpload :product="modelRef.product" v-model="modelRef.upload" :file="modelRef.file" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</a-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { queryNoPagingPost } from '@/api/device/product'
|
import { queryNoPagingPost } from '@/api/device/product'
|
||||||
import { Form } from 'ant-design-vue';
|
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close', 'save'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -38,7 +37,6 @@ const props = defineProps({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const productList = ref<Record<string, any>[]>([])
|
const productList = ref<Record<string, any>[]>([])
|
||||||
const useForm = Form.useForm;
|
|
||||||
|
|
||||||
const modelRef = reactive({
|
const modelRef = reactive({
|
||||||
product: undefined,
|
product: undefined,
|
||||||
|
@ -64,4 +62,8 @@ watch(
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
emit('save')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal :maskClosable="false" width="800px" :visible="true" title="当前进度" @ok="handleCancel" @cancel="handleCancel">
|
<j-modal :maskClosable="false" width="800px" :visible="true" title="当前进度" @ok="handleCancel" @cancel="handleCancel">
|
||||||
<div>
|
<div>
|
||||||
<a-badge v-if="flag" status="processing" text="进行中" />
|
<j-badge v-if="flag" status="processing" text="进行中" />
|
||||||
<a-badge v-else status="success" text="已完成" />
|
<j-badge v-else status="success" text="已完成" />
|
||||||
</div>
|
</div>
|
||||||
<p>总数量:{{count}}</p>
|
<p>总数量:{{count}}</p>
|
||||||
<a style="color: red">{{errMessage}}</a>
|
<a style="color: red">{{errMessage}}</a>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<j-row type="flex">
|
<j-row type="flex">
|
||||||
<j-col flex="180px">
|
<j-col flex="180px">
|
||||||
<j-form-item name="photoUrl">
|
<j-form-item name="photoUrl">
|
||||||
<JUpload v-model="modelRef.photoUrl" />
|
<JProUpload v-model="modelRef.photoUrl" />
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col flex="auto">
|
<j-col flex="auto">
|
||||||
|
@ -60,15 +60,14 @@
|
||||||
<j-select
|
<j-select
|
||||||
showSearch
|
showSearch
|
||||||
v-model:value="modelRef.productId"
|
v-model:value="modelRef.productId"
|
||||||
placeholder="请选择所属产品"
|
:disabled="!!data?.id"
|
||||||
:filter-option="filterOption"
|
placeholder="请选择状态为“正常”的产品"
|
||||||
>
|
>
|
||||||
<j-select-option
|
<j-select-option
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
v-for="item in productList"
|
v-for="item in productList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:disabled="!!data?.id"
|
|
||||||
>{{item.name}}</j-select-option>
|
>{{item.name}}</j-select-option>
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
@ -89,7 +88,7 @@
|
||||||
import { queryNoPagingPost } from '@/api/device/product';
|
import { queryNoPagingPost } from '@/api/device/product';
|
||||||
import { isExists, update } from '@/api/device/instance';
|
import { isExists, update } from '@/api/device/instance';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'save']);
|
const emit = defineEmits(['close', 'save']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -105,16 +104,12 @@ const formRef = ref();
|
||||||
|
|
||||||
const modelRef = reactive({
|
const modelRef = reactive({
|
||||||
productId: undefined,
|
productId: undefined,
|
||||||
id: '',
|
id: undefined,
|
||||||
name: '',
|
name: '',
|
||||||
describe: '',
|
describe: '',
|
||||||
photoUrl: getImage('/device/instance/device-card.png'),
|
photoUrl: getImage('/device/instance/device-card.png'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const filterOption = (input: string, option: any) => {
|
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const vailId = async (_: Record<string, any>, value: string) => {
|
const vailId = async (_: Record<string, any>, value: string) => {
|
||||||
if (!props?.data?.id && value) {
|
if (!props?.data?.id && value) {
|
||||||
const resp = await isExists(value);
|
const resp = await isExists(value);
|
||||||
|
@ -202,10 +197,15 @@ const handleCancel = () => {
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
formRef.value
|
formRef.value
|
||||||
.validate()
|
.validate()
|
||||||
.then(async () => {
|
.then(async (_data: any) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const resp = await update(toRaw(modelRef));
|
const obj = {...toRaw(modelRef), ..._data}
|
||||||
loading.value = false;
|
if(!obj.id){
|
||||||
|
delete obj.id
|
||||||
|
}
|
||||||
|
const resp = await update(obj).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
emit('save');
|
emit('save');
|
||||||
|
|
|
@ -156,7 +156,10 @@
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<Ellipsis style="width: calc(100% - 100px)">
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
<span style="font-size: 16px; font-weight: 600" @click.stop="handleView(slotProps.id)">
|
<span
|
||||||
|
style="font-size: 16px; font-weight: 600"
|
||||||
|
@click.stop="handleView(slotProps.id)"
|
||||||
|
>
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</span>
|
</span>
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
|
@ -205,6 +208,11 @@
|
||||||
:status="statusMap.get(slotProps.state?.value)"
|
:status="statusMap.get(slotProps.state?.value)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<template #createTime="slotProps">
|
||||||
|
<span>{{
|
||||||
|
dayjs(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<j-space>
|
<j-space>
|
||||||
<template
|
<template
|
||||||
|
@ -229,11 +237,16 @@
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
</page-container>
|
</page-container>
|
||||||
<Import v-if="importVisible" @close="importVisible = false" />
|
<Import
|
||||||
|
v-if="importVisible"
|
||||||
|
@close="importVisible = false"
|
||||||
|
@save="onRefresh"
|
||||||
|
/>
|
||||||
<Export
|
<Export
|
||||||
v-if="exportVisible"
|
v-if="exportVisible"
|
||||||
@close="exportVisible = false"
|
@close="exportVisible = false"
|
||||||
:data="params"
|
:data="params"
|
||||||
|
@save="onRefresh"
|
||||||
/>
|
/>
|
||||||
<Process
|
<Process
|
||||||
v-if="operationVisible"
|
v-if="operationVisible"
|
||||||
|
@ -259,9 +272,8 @@ import {
|
||||||
batchDeployDevice,
|
batchDeployDevice,
|
||||||
batchDeleteDevice,
|
batchDeleteDevice,
|
||||||
} from '@/api/device/instance';
|
} from '@/api/device/instance';
|
||||||
// import type { ActionsType } from '@/components/Table/index.vue';
|
|
||||||
import { getImage, LocalStore } from '@/utils/comm';
|
import { getImage, LocalStore } from '@/utils/comm';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'jetlinks-ui-components';
|
||||||
import Import from './Import/index.vue';
|
import Import from './Import/index.vue';
|
||||||
import Export from './Export/index.vue';
|
import Export from './Export/index.vue';
|
||||||
import Process from './Process/index.vue';
|
import Process from './Process/index.vue';
|
||||||
|
@ -275,8 +287,9 @@ import {
|
||||||
} from '@/api/device/product';
|
} from '@/api/device/product';
|
||||||
import { queryTree } from '@/api/device/category';
|
import { queryTree } from '@/api/device/category';
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
import type { ActionsType } from './typings';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const instanceRef = ref<Record<string, any>>({});
|
const instanceRef = ref<Record<string, any>>({});
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
const _selectedRowKeys = ref<string[]>([]);
|
const _selectedRowKeys = ref<string[]>([]);
|
||||||
|
@ -696,4 +709,8 @@ const saveBtn = () => {
|
||||||
const handleSearch = (_params: any) => {
|
const handleSearch = (_params: any) => {
|
||||||
params.value = _params;
|
params.value = _params;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
instanceRef.value?.reload();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -83,4 +83,17 @@ type InstanceModel = {
|
||||||
params: Set<string>; // 处理无限循环Card
|
params: Set<string>; // 处理无限循环Card
|
||||||
active?: string; // 当前编辑的Card
|
active?: string; // 当前编辑的Card
|
||||||
selectedRows: Map<string, any>;
|
selectedRows: Map<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionsType {
|
||||||
|
key: string;
|
||||||
|
text?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
permission?: boolean;
|
||||||
|
onClick?: (data: any) => void;
|
||||||
|
style?: CSSProperties;
|
||||||
|
tooltip?: TooltipProps;
|
||||||
|
popConfirm?: PopconfirmProps;
|
||||||
|
icon?: string;
|
||||||
|
children?: ActionsType[];
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
@cancel="_vis = false"
|
@cancel="_vis = false"
|
||||||
:confirmLoading="loading"
|
:confirmLoading="loading"
|
||||||
>
|
>
|
||||||
<Search
|
<j-advanced-search
|
||||||
type="simple"
|
type="simple"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="media"
|
target="media"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!-- 国标级联-通道列表 -->
|
<!-- 国标级联-通道列表 -->
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
type="simple"
|
type="simple"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="media"
|
target="media"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="media-cascade"
|
target="media-cascade"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!-- 视频设备-通道列表 -->
|
<!-- 视频设备-通道列表 -->
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
type="simple"
|
type="simple"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="product"
|
target="product"
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<!-- 视频图标组件 -->
|
||||||
|
<template>
|
||||||
|
<a @click="handleClick">
|
||||||
|
<AIcon :type="iconType" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { recordsItemType } from './typings';
|
||||||
|
import playBackApi from '@/api/media/playback';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
type: string;
|
||||||
|
item: recordsItemType;
|
||||||
|
onCloudView: (startTime: number, endTime: number) => void;
|
||||||
|
onDownLoad: () => void;
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
// type 为local时有效,0:未下载; 1:下载中:2:已下载
|
||||||
|
const status = ref(props.item?.isServer ? 2 : 0);
|
||||||
|
// const status = computed({
|
||||||
|
// get: () => (props.item?.isServer ? 2 : 0),
|
||||||
|
// set: (val: number) => {},
|
||||||
|
// });
|
||||||
|
|
||||||
|
const getLocalIcon = (s: number) => {
|
||||||
|
if (s === 0) {
|
||||||
|
return 'CloudDownloadOutlined';
|
||||||
|
} else if (s === 2) {
|
||||||
|
return 'EyeOutlined';
|
||||||
|
} else {
|
||||||
|
return 'LoadingOutlined';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconType = computed(() =>
|
||||||
|
props.type === 'local' ? getLocalIcon(status.value) : 'DownloadOutlined',
|
||||||
|
);
|
||||||
|
|
||||||
|
const downLoadCloud = (item: recordsItemType) => {
|
||||||
|
status.value = 1;
|
||||||
|
playBackApi
|
||||||
|
.downloadRecord(item.deviceId, item.channelId, {
|
||||||
|
local: false,
|
||||||
|
downloadSpeed: 4,
|
||||||
|
startTime: item.startTime,
|
||||||
|
endTime: item.endTime,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
message.success(
|
||||||
|
'操作成功。上传云端需要一定时间,请稍后查看云端数据',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
status.value = res.status === 200 ? 2 : 0;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
status.value = 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (props.type === 'local') {
|
||||||
|
if (status.value === 2) {
|
||||||
|
// 已下载,进行跳转
|
||||||
|
props.onCloudView(props.item.startTime, props.item.endTime);
|
||||||
|
} else if (status.value === 0) {
|
||||||
|
// 未下载 进行下载
|
||||||
|
downLoadCloud(props.item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props.onDownLoad();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,119 @@
|
||||||
|
@borderColor: #d9d9d9;
|
||||||
|
|
||||||
|
.playback-warp {
|
||||||
|
display: flex;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.playback-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
|
.playback-media {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playback-right {
|
||||||
|
width: 300px;
|
||||||
|
margin-left: 24px;
|
||||||
|
|
||||||
|
.playback-calendar {
|
||||||
|
margin-top: 16px;
|
||||||
|
border: 1px solid @borderColor;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
:deep(.ant-picker-calendar-header) {
|
||||||
|
justify-content: space-between;
|
||||||
|
& > div:nth-child(3) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playback-list {
|
||||||
|
display: flex;
|
||||||
|
height: 300px;
|
||||||
|
margin-top: 16px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid @borderColor;
|
||||||
|
|
||||||
|
&.no-list {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playback-list-items {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.ant-list-item {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-line-warp {
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
.time-line-clock {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-line-content {
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
|
||||||
|
.time-line-progress {
|
||||||
|
position: relative;
|
||||||
|
height: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #52c41a;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.time-line-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
left: 0;
|
||||||
|
width: 3px;
|
||||||
|
height: 19px;
|
||||||
|
background-color: @primary-color;
|
||||||
|
border-radius: 2px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-line {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8px;
|
||||||
|
left: -30px;
|
||||||
|
width: 60px;
|
||||||
|
padding: 2px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 0 12px rgba(#000, 0.15);
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,366 @@
|
||||||
|
<!-- 回放 -->
|
||||||
<template>
|
<template>
|
||||||
<page-container> 回放 </page-container>
|
<page-container>
|
||||||
|
<div class="playback-warp">
|
||||||
|
<div class="playback-left">
|
||||||
|
<LivePlayer
|
||||||
|
ref="player"
|
||||||
|
:src="url"
|
||||||
|
width="758px"
|
||||||
|
height="462px"
|
||||||
|
/>
|
||||||
|
<TimeLine
|
||||||
|
ref="playTimeNode"
|
||||||
|
:type="type"
|
||||||
|
:data="historyList"
|
||||||
|
:date-time="time"
|
||||||
|
:on-change="handleTimeLineChange"
|
||||||
|
:play-status="playStatus"
|
||||||
|
:play-time="playNowTime + playTime * 1000"
|
||||||
|
:local-to-server="cloudTime"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="playback-right">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>云端:存储在服务器中</div>
|
||||||
|
<div>本地:存储在设备本地</div>
|
||||||
|
</template>
|
||||||
|
<div>类型: <AIcon type="QuestionCircleOutlined" /></div>
|
||||||
|
</a-tooltip>
|
||||||
|
<RadioCard
|
||||||
|
layout="horizontal"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: '云端',
|
||||||
|
value: 'cloud',
|
||||||
|
logo: getImage('/media/cloud.png'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本地',
|
||||||
|
value: 'local',
|
||||||
|
logo: getImage('/local.png'),
|
||||||
|
disabled: deviceType === 'fixed-media',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:checkStyle="true"
|
||||||
|
v-model="type"
|
||||||
|
/>
|
||||||
|
<div class="playback-calendar">
|
||||||
|
<a-calendar
|
||||||
|
v-model:value="time"
|
||||||
|
:fullscreen="false"
|
||||||
|
:disabledDate="
|
||||||
|
(currentDate) => currentDate > dayjs(new Date())
|
||||||
|
"
|
||||||
|
@change="handlePanelChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="playback-list"
|
||||||
|
:class="{ 'no-list': !historyList.length }"
|
||||||
|
>
|
||||||
|
<a-empty
|
||||||
|
v-if="!historyList.length"
|
||||||
|
description="暂无数据"
|
||||||
|
/>
|
||||||
|
<a-list
|
||||||
|
v-else
|
||||||
|
class="playback-list-items"
|
||||||
|
itemLayout="horizontal"
|
||||||
|
:dataSource="historyList"
|
||||||
|
>
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item>
|
||||||
|
<template #actions>
|
||||||
|
<a-tooltip
|
||||||
|
key="play-btn"
|
||||||
|
:title="
|
||||||
|
(item.startTime ||
|
||||||
|
item.mediaStartTime) ===
|
||||||
|
playNowTime &&
|
||||||
|
playStatus === 1
|
||||||
|
? '暂停'
|
||||||
|
: '播放'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
@click="
|
||||||
|
handlePlay(
|
||||||
|
item.startTime ||
|
||||||
|
item.mediaStartTime,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
:type="
|
||||||
|
(item.startTime ||
|
||||||
|
item.mediaStartTime) ===
|
||||||
|
playNowTime &&
|
||||||
|
playStatus === 1
|
||||||
|
? 'PauseCircleOutlined'
|
||||||
|
: 'PlayCircleOutlined'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip
|
||||||
|
key="download"
|
||||||
|
:title="
|
||||||
|
type !== 'local'
|
||||||
|
? '下载录像文件'
|
||||||
|
: item.isServer
|
||||||
|
? '查看'
|
||||||
|
: '下载到云端'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<IconNode
|
||||||
|
:type="type"
|
||||||
|
:item="item"
|
||||||
|
:on-cloud-view="cloudView"
|
||||||
|
:on-down-load="
|
||||||
|
() => downloadClick(item)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
dayjs(
|
||||||
|
item.startTime ||
|
||||||
|
item.mediaStartTime,
|
||||||
|
).format('HH:mm:ss')
|
||||||
|
}}
|
||||||
|
~
|
||||||
|
{{
|
||||||
|
dayjs(
|
||||||
|
item.endTime ||
|
||||||
|
item.mediaEndTime,
|
||||||
|
).format('HH:mm:ss')
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
<div></div>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import playBackApi from '@/api/media/playback';
|
||||||
|
import TimeLine from './timeLine.vue';
|
||||||
|
import IconNode from './iconNode.vue';
|
||||||
|
import type { recordsItemType } from './typings';
|
||||||
|
import LivePlayer from '@/components/Player/index.vue';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import type { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
const route = useRoute();
|
||||||
|
|
||||||
|
const url = ref('');
|
||||||
|
const type = ref('local');
|
||||||
|
const historyList = ref<recordsItemType[]>([]);
|
||||||
|
const time = ref<Dayjs | undefined>(undefined);
|
||||||
|
const loading = ref(false);
|
||||||
|
const cloudTime = ref<any>();
|
||||||
|
// const location = ref();
|
||||||
|
const player = ref<any>();
|
||||||
|
const playStatus = ref(0); // 播放状态, 0 停止, 1 播放, 2 暂停, 3 播放完成
|
||||||
|
const playTime = ref(0);
|
||||||
|
|
||||||
|
const playNowTime = ref(0); // 当前播放视频标识
|
||||||
|
const playTimeNode = ref<any>(null);
|
||||||
|
const isEnded = ref(false); // 是否结束播放
|
||||||
|
const deviceId = computed(() => route.query.id as string);
|
||||||
|
const channelId = computed(() => route.query.channelId as string);
|
||||||
|
|
||||||
|
const deviceType = ref('');
|
||||||
|
|
||||||
|
const queryLocalRecords = async (date: Dayjs) => {
|
||||||
|
console.log('date: ', date);
|
||||||
|
|
||||||
|
playStatus.value = 0;
|
||||||
|
url.value = '';
|
||||||
|
|
||||||
|
if (deviceId.value && channelId.value && date) {
|
||||||
|
loading.value = true;
|
||||||
|
const params = {
|
||||||
|
startTime: date.format('YYYY-MM-DD 00:00:00'),
|
||||||
|
endTime: date.format('YYYY-MM-DD 23:59:59'),
|
||||||
|
};
|
||||||
|
const localResp = await playBackApi.queryRecordLocal(
|
||||||
|
deviceId.value,
|
||||||
|
channelId.value,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
if (localResp.status === 200 && localResp.result.length) {
|
||||||
|
const serviceResp = await playBackApi.recordsInServer(
|
||||||
|
deviceId.value,
|
||||||
|
channelId.value,
|
||||||
|
{
|
||||||
|
...params,
|
||||||
|
includeFiles: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
loading.value = false;
|
||||||
|
let newList: recordsItemType[] = serviceResp.result;
|
||||||
|
// console.log(newList)
|
||||||
|
|
||||||
|
if (serviceResp.status === 200 && serviceResp.result) {
|
||||||
|
// 判断是否已下载云端视频
|
||||||
|
newList = localResp.result.map((item: recordsItemType) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
isServer: serviceResp.result.length
|
||||||
|
? serviceResp.result.some(
|
||||||
|
(serverFile: any) =>
|
||||||
|
item.startTime <=
|
||||||
|
serverFile.streamStartTime &&
|
||||||
|
serverFile.streamEndTime <= item.endTime,
|
||||||
|
)
|
||||||
|
: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
historyList.value = newList;
|
||||||
|
} else {
|
||||||
|
historyList.value = newList;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loading.value = false;
|
||||||
|
historyList.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询云端视频
|
||||||
|
* @param date
|
||||||
|
*/
|
||||||
|
const queryServiceRecords = async (date: Dayjs) => {
|
||||||
|
playStatus.value = 0;
|
||||||
|
url.value = '';
|
||||||
|
if (deviceId.value && channelId.value && date) {
|
||||||
|
loading.value = true;
|
||||||
|
const params = {
|
||||||
|
startTime: date.format('YYYY-MM-DD 00:00:00'),
|
||||||
|
endTime: date.format('YYYY-MM-DD 23:59:59'),
|
||||||
|
includeFiles: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const resp = await playBackApi.recordsInServerFiles(
|
||||||
|
deviceId.value,
|
||||||
|
channelId.value,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
loading.value = false;
|
||||||
|
if (resp.status === 200) {
|
||||||
|
historyList.value = resp.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cloudView = (startTime: number, endTime: number) => {
|
||||||
|
type.value = 'cloud';
|
||||||
|
cloudTime.value = { startTime, endTime };
|
||||||
|
queryServiceRecords(time.value!);
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadClick = async (item: recordsItemType) => {
|
||||||
|
const downloadUrl = playBackApi.downLoadFile(item.id);
|
||||||
|
const downNode = document.createElement('a');
|
||||||
|
downNode.href = downloadUrl;
|
||||||
|
downNode.download = `${channelId}-${dayjs(item.startTime).format(
|
||||||
|
'YYYY-MM-DD-HH-mm-ss',
|
||||||
|
)}.mp4`;
|
||||||
|
downNode.style.display = 'none';
|
||||||
|
document.body.appendChild(downNode);
|
||||||
|
downNode.click();
|
||||||
|
document.body.removeChild(downNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const _type = route.query.type as string;
|
||||||
|
if (_type) {
|
||||||
|
deviceType.value = _type;
|
||||||
|
const _timeStr = dayjs(new Date());
|
||||||
|
time.value = _timeStr;
|
||||||
|
if (_type === 'fixed-media') {
|
||||||
|
type.value = 'cloud';
|
||||||
|
queryServiceRecords(_timeStr);
|
||||||
|
} else {
|
||||||
|
queryLocalRecords(_timeStr);
|
||||||
|
type.value = 'local';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTimeLineChange = (times: any) => {
|
||||||
|
if (times) {
|
||||||
|
playNowTime.value = Number(times.startTime.valueOf());
|
||||||
|
playTime.value = 0;
|
||||||
|
url.value =
|
||||||
|
type.value === 'local'
|
||||||
|
? playBackApi.playbackLocal(
|
||||||
|
times.deviceId,
|
||||||
|
times.channelId,
|
||||||
|
'mp4',
|
||||||
|
dayjs(times.startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
dayjs(times.endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
)
|
||||||
|
: playBackApi.playbackStart(times.deviceId);
|
||||||
|
} else {
|
||||||
|
url.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => type.value,
|
||||||
|
(val: string) => {
|
||||||
|
if (val === 'cloud') {
|
||||||
|
queryServiceRecords(time.value!);
|
||||||
|
} else {
|
||||||
|
queryLocalRecords(time.value!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePanelChange = (date: any) => {
|
||||||
|
console.log('type: ', typeof date);
|
||||||
|
console.log('date: ', date);
|
||||||
|
time.value = date;
|
||||||
|
if (type.value === 'cloud') {
|
||||||
|
queryServiceRecords(date);
|
||||||
|
} else {
|
||||||
|
queryLocalRecords(date);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 播放/暂停
|
||||||
|
const handlePlay = (_startTime: any) => {
|
||||||
|
if (playStatus.value === 0 || _startTime !== playNowTime.value) {
|
||||||
|
if (playTimeNode.value) {
|
||||||
|
playTimeNode.value.playByStartTime(_startTime);
|
||||||
|
}
|
||||||
|
} else if (playStatus.value == 1 && _startTime === playNowTime.value) {
|
||||||
|
if (player.value.getVueInstance) {
|
||||||
|
// player.value.getVueInstance().pause();
|
||||||
|
}
|
||||||
|
} else if (playStatus.value == 2 && _startTime === playNowTime.value) {
|
||||||
|
if (player.value.getVueInstance) {
|
||||||
|
// player.value.getVueInstance().play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './index.less';
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
<!-- 播放器时间刻度 -->
|
||||||
|
<template>
|
||||||
|
<div class="time-line-warp">
|
||||||
|
<div class="time-line-clock">
|
||||||
|
<div
|
||||||
|
v-for="item in Array.from(Array(25), (v, k) => k)"
|
||||||
|
:key="item"
|
||||||
|
style="width: 12px"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time-line-content" ref="LineContent">
|
||||||
|
<div class="time-line-progress">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="`time_${index}`"
|
||||||
|
@click="handleProgress($event, item)"
|
||||||
|
:style="
|
||||||
|
getLineItemStyle(
|
||||||
|
item.startTime || item.mediaStartTime,
|
||||||
|
item.endTime || item.mediaEndTime,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div id="btn" class="time-line-btn"></div>
|
||||||
|
<div id="time" class="time-line">
|
||||||
|
{{ dayjs(playTime || 0).format('HH:mm:ss') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import type { recordsItemType } from './typings';
|
||||||
|
import type { Dayjs } from 'dayjs';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
export type TimeChangeType = {
|
||||||
|
endTime: Dayjs;
|
||||||
|
startTime: Dayjs;
|
||||||
|
deviceId: string;
|
||||||
|
channelId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onChange: (times: TimeChangeType | undefined) => void;
|
||||||
|
data: recordsItemType[];
|
||||||
|
dateTime?: Dayjs;
|
||||||
|
type: string;
|
||||||
|
playStatus: number;
|
||||||
|
playTime: number;
|
||||||
|
server?: any;
|
||||||
|
localToServer?: {
|
||||||
|
endTime: number;
|
||||||
|
startTime: number;
|
||||||
|
};
|
||||||
|
getPlayList?: (data: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
// 获取选中当天开始时间戳
|
||||||
|
const startT = ref<number>(
|
||||||
|
new Date(
|
||||||
|
dayjs(props.dateTime).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
).getTime(),
|
||||||
|
);
|
||||||
|
// 获取选中当天结束时间戳
|
||||||
|
const endT = ref<number>(
|
||||||
|
new Date(
|
||||||
|
dayjs(props.dateTime).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
).getTime(),
|
||||||
|
);
|
||||||
|
const list = ref<any[]>([]);
|
||||||
|
const playTime = ref<number>(0);
|
||||||
|
const LineContent = ref<HTMLDivElement>();
|
||||||
|
// const LineContentSize = LineContent.value;
|
||||||
|
const LineContentSize = ref({ width: 100 });
|
||||||
|
|
||||||
|
const setTimeAndPosition = (ob: number) => {
|
||||||
|
const oBtn = document.getElementById('btn');
|
||||||
|
const oTime = document.getElementById('time');
|
||||||
|
|
||||||
|
if (oBtn && oTime && LineContentSize.value.width) {
|
||||||
|
oBtn.style.visibility = 'visible';
|
||||||
|
oBtn.style.left = `${ob * LineContentSize.value.width}px`;
|
||||||
|
oTime.style.visibility = 'visible';
|
||||||
|
oTime.style.left = `${ob * LineContentSize.value.width - 15}px`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.dateTime,
|
||||||
|
(val: any) => {
|
||||||
|
startT.value = new Date(
|
||||||
|
dayjs(val).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
).getTime();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChange = (
|
||||||
|
startTime: number,
|
||||||
|
endTime: number,
|
||||||
|
deviceId: string,
|
||||||
|
channelId: string,
|
||||||
|
) => {
|
||||||
|
playTime.value = startTime;
|
||||||
|
props.onChange({
|
||||||
|
startTime: dayjs(startTime),
|
||||||
|
endTime: dayjs(endTime),
|
||||||
|
deviceId,
|
||||||
|
channelId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const playByStartTime = (time: any) => {
|
||||||
|
const playNow = props.data.find((item) => {
|
||||||
|
const startTime = item.startTime || item.mediaStartTime;
|
||||||
|
return startTime === time;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (playNow) {
|
||||||
|
const startTime = playNow.startTime || playNow.mediaStartTime;
|
||||||
|
const endTime = playNow.endTime || playNow.mediaEndTime;
|
||||||
|
const deviceId = props.type === 'local' ? playNow.deviceId : playNow.id;
|
||||||
|
onChange(startTime, endTime, deviceId, playNow.channelId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
playByStartTime(0);
|
||||||
|
|
||||||
|
const onNextPlay = () => {
|
||||||
|
if (playTime.value) {
|
||||||
|
// 查找下一个视频
|
||||||
|
const nowIndex = props.data.findIndex((item) => {
|
||||||
|
const startTime = item.startTime || item.mediaStartTime;
|
||||||
|
return startTime === playTime.value;
|
||||||
|
});
|
||||||
|
// 是否为最后一个
|
||||||
|
if (nowIndex !== props.data.length - 1) {
|
||||||
|
const nextPlay = props.data[nowIndex + 1];
|
||||||
|
const startTime = nextPlay.startTime || nextPlay.mediaStartTime;
|
||||||
|
const endTime = nextPlay.endTime || nextPlay.mediaEndTime;
|
||||||
|
const deviceId =
|
||||||
|
props.type === 'local' ? nextPlay.deviceId : nextPlay.id;
|
||||||
|
onChange(startTime, endTime, deviceId, nextPlay.channelId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onNextPlay();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(val: any) => {
|
||||||
|
const { data, localToServer, type } = props;
|
||||||
|
if (data && Array.isArray(data) && data.length > 0) {
|
||||||
|
list.value = [...data];
|
||||||
|
if (type === 'local') {
|
||||||
|
// 播放第一个
|
||||||
|
onChange(
|
||||||
|
data[0].startTime,
|
||||||
|
data[0].endTime,
|
||||||
|
data[0].deviceId,
|
||||||
|
data[0].channelId,
|
||||||
|
);
|
||||||
|
} else if (type === 'cloud') {
|
||||||
|
// 是否从本地跳转到云端播放
|
||||||
|
if (localToServer && Object.keys(localToServer).length > 0) {
|
||||||
|
// 获取跳转播放段
|
||||||
|
const playItem = data.find((item) => {
|
||||||
|
return (
|
||||||
|
item.mediaEndTime <= localToServer.endTime &&
|
||||||
|
item.mediaStartTime >= localToServer.startTime
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (playItem) {
|
||||||
|
//播放片段
|
||||||
|
onChange(
|
||||||
|
playItem.mediaStartTime,
|
||||||
|
playItem.mediaEndTime,
|
||||||
|
playItem.id,
|
||||||
|
playItem.channelId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
props.onChange(undefined);
|
||||||
|
message.error('没有可播放的视频资源');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onChange(
|
||||||
|
data[0].mediaStartTime,
|
||||||
|
data[0].mediaEndTime,
|
||||||
|
data[0].id,
|
||||||
|
data[0].channelId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (localToServer && localToServer.startTime) {
|
||||||
|
// 本地跳转云端但是无资源
|
||||||
|
props.onChange(undefined);
|
||||||
|
message.error('没有可播放的视频资源');
|
||||||
|
list.value = [];
|
||||||
|
} else {
|
||||||
|
// 啥都没有
|
||||||
|
list.value = [];
|
||||||
|
props.onChange(undefined);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const getLineItemStyle = (
|
||||||
|
startTime: number,
|
||||||
|
endTime: number,
|
||||||
|
): { left: string; width: string } => {
|
||||||
|
const start = startTime - startT.value > 0 ? startTime - startT.value : 0;
|
||||||
|
const _width = LineContentSize.value.width!;
|
||||||
|
const itemWidth = ((endTime - startTime) / (24 * 3600000)) * _width;
|
||||||
|
return {
|
||||||
|
left: `${(start / (24 * 3600000)) * _width}px`,
|
||||||
|
width: `${itemWidth < 1 ? 1 : itemWidth}px`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const playTimeChange = () => {
|
||||||
|
if (
|
||||||
|
props.playTime &&
|
||||||
|
props.playTime >= startT.value &&
|
||||||
|
props.playTime <= endT.value &&
|
||||||
|
props.data &&
|
||||||
|
props.data.length
|
||||||
|
) {
|
||||||
|
setTimeAndPosition((props.playTime - startT.value) / 3600000 / 24);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
watch(
|
||||||
|
() => props.playTime,
|
||||||
|
() => {
|
||||||
|
playTimeChange();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => startT.value,
|
||||||
|
() => {
|
||||||
|
playTimeChange();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleProgress = (event: any, item: any) => {
|
||||||
|
const pos = LineContent.value?.getBoundingClientRect();
|
||||||
|
if (pos && item.endTime) {
|
||||||
|
const dt = event.clientX - pos.x;
|
||||||
|
const start = (dt / pos.width) * 24 * 3600000 + startT.value;
|
||||||
|
const _start = start < item.startTime ? item.startTime : start;
|
||||||
|
onChange(_start, item.endTime, item.deviceId, item.channelId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ playByStartTime });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './index.less';
|
||||||
|
</style>
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="notice-config"
|
target="notice-config"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal v-model:visible="_vis" title="通知记录" :footer="null" width="70%">
|
<a-modal v-model:visible="_vis" title="通知记录" :footer="null" width="70%">
|
||||||
<Search
|
<j-search
|
||||||
type="simple"
|
type="simple"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="product"
|
target="product"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="notice-config"
|
target="notice-config"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal v-model:visible="_vis" title="通知记录" :footer="null" width="70%">
|
<a-modal v-model:visible="_vis" title="通知记录" :footer="null" width="70%">
|
||||||
<Search
|
<j-search
|
||||||
type="simple"
|
type="simple"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="product"
|
target="product"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<j-advanced-search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="notice-config"
|
target="notice-config"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search :columns="columns" target="scene" @search="handleSearch" />
|
<j-advanced-search :columns="columns" target="scene" @search="handleSearch" />
|
||||||
<JProTable
|
<JProTable
|
||||||
ref="sceneRef"
|
ref="sceneRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -23,7 +23,6 @@
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<SceneCard
|
<SceneCard
|
||||||
:value="slotProps"
|
:value="slotProps"
|
||||||
@click="handleClick"
|
|
||||||
:actions="getActions(slotProps, 'card')"
|
:actions="getActions(slotProps, 'card')"
|
||||||
:status="slotProps.state?.value"
|
:status="slotProps.state?.value"
|
||||||
:statusText="slotProps.state?.text"
|
:statusText="slotProps.state?.text"
|
||||||
|
@ -50,7 +49,7 @@
|
||||||
<Ellipsis style="width: calc(100% - 100px)">
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
<span
|
<span
|
||||||
style="font-size: 16px; font-weight: 600"
|
style="font-size: 16px; font-weight: 600"
|
||||||
@click.stop="handleView(slotProps.id)"
|
@click.stop="handleView(slotProps.id, slotProps.triggerType)"
|
||||||
>
|
>
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -126,7 +125,7 @@
|
||||||
import SaveModal from './Save/save.vue';
|
import SaveModal from './Save/save.vue';
|
||||||
import type { SceneItem } from './typings';
|
import type { SceneItem } from './typings';
|
||||||
import { useMenuStore } from 'store/menu';
|
import { useMenuStore } from 'store/menu';
|
||||||
import { query, _delete, _action } from '@/api/rule-engine/scene';
|
import { query, _delete, _action, _execute } from '@/api/rule-engine/scene';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import type { ActionsType } from '@/components/Table';
|
import type { ActionsType } from '@/components/Table';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
@ -180,10 +179,12 @@ const columns = [
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: Array.from(typeMap).map((item) => ({
|
options: Array.from(typeMap).map((item) => {
|
||||||
label: item[1],
|
return {
|
||||||
value: item[0],
|
label: item[1]?.text,
|
||||||
})),
|
value: item[0],
|
||||||
|
}
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -305,9 +306,18 @@ const getActions = (
|
||||||
: '未启用,不能手动触发',
|
: '未启用,不能手动触发',
|
||||||
},
|
},
|
||||||
icon: 'LikeOutlined',
|
icon: 'LikeOutlined',
|
||||||
onClick: () => {
|
popConfirm: {
|
||||||
// handleView(data.id, data.triggerType);
|
title: '',
|
||||||
},
|
onConfirm: async () => {
|
||||||
|
const resp = await _execute(data.id)
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
sceneRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
actions.splice(1, 0, _item);
|
actions.splice(1, 0, _item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="product-container">
|
<div class="product-container">
|
||||||
<Search :columns="query.columns" @search="query.search" />
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
@search="(params:any) => (queryParams = params)"
|
||||||
|
/>
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:request="table.requestFun"
|
:request="table.requestFun"
|
||||||
:gridColumn="2"
|
:gridColumn="2"
|
||||||
model="CARD"
|
:params="queryParams"
|
||||||
:params="query.params.value"
|
|
||||||
:rowSelection="{
|
:rowSelection="{
|
||||||
selectedRowKeys: table._selectedRowKeys.value,
|
selectedRowKeys: table._selectedRowKeys.value,
|
||||||
}"
|
}"
|
||||||
@cancelSelect="table.cancelSelect"
|
@cancelSelect="table.cancelSelect"
|
||||||
|
:columns="columns"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<j-space>
|
<j-space>
|
||||||
|
@ -130,13 +133,46 @@
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #permission="slotProps">
|
||||||
|
{{
|
||||||
|
table.permissionList.value.length &&
|
||||||
|
table.getPermissLabel(slotProps.permission)
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="slotProps.state.value"
|
||||||
|
:text="slotProps.state.text"
|
||||||
|
:statusNames="{
|
||||||
|
online: 'success',
|
||||||
|
offline: 'error',
|
||||||
|
notActive: 'warning',
|
||||||
|
}"
|
||||||
|
></BadgeStatus>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space :size="16">
|
||||||
|
<PermissionButton
|
||||||
|
v-for="i in table.getActions(slotProps, 'table')"
|
||||||
|
:uhasPermission="i.permission"
|
||||||
|
type="link"
|
||||||
|
:tooltip="i?.tooltip"
|
||||||
|
:pop-confirm="i.popConfirm"
|
||||||
|
@click="i.onClick"
|
||||||
|
:disabled="i?.disabled"
|
||||||
|
>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</PermissionButton>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
|
|
||||||
<div class="dialogs">
|
<div class="dialogs">
|
||||||
<AddDeviceOrProductDialog
|
<AddDeviceOrProductDialog
|
||||||
v-if="dialogs.addShow"
|
v-if="dialogs.addShow"
|
||||||
v-model:visible="dialogs.addShow"
|
v-model:visible="dialogs.addShow"
|
||||||
:query-columns="query.columns"
|
:query-columns="columns"
|
||||||
:parent-id="props.parentId"
|
:parent-id="props.parentId"
|
||||||
:all-permission="table.permissionList.value"
|
:all-permission="table.permissionList.value"
|
||||||
asset-type="device"
|
asset-type="device"
|
||||||
|
@ -171,7 +207,7 @@ import {
|
||||||
} from '@/api/system/department';
|
} from '@/api/system/department';
|
||||||
import { intersection } from 'lodash-es';
|
import { intersection } from 'lodash-es';
|
||||||
|
|
||||||
import type { dictType } from '../typing';
|
import type { dictType, optionsType } from '../typing';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
const permission = 'system/Department';
|
const permission = 'system/Department';
|
||||||
|
@ -181,93 +217,97 @@ const props = defineProps<{
|
||||||
parentId: string;
|
parentId: string;
|
||||||
bindBool: boolean;
|
bindBool: boolean;
|
||||||
}>();
|
}>();
|
||||||
const query = {
|
const columns = [
|
||||||
columns: [
|
{
|
||||||
{
|
title: 'ID',
|
||||||
title: 'ID',
|
dataIndex: 'id',
|
||||||
dataIndex: 'id',
|
key: 'id',
|
||||||
key: 'id',
|
ellipsis: true,
|
||||||
ellipsis: true,
|
fixed: 'left',
|
||||||
fixed: 'left',
|
search: {
|
||||||
search: {
|
type: 'string',
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
ellipsis: true,
|
|
||||||
fixed: 'left',
|
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '所属产品',
|
|
||||||
dataIndex: 'productId$product-info',
|
|
||||||
key: 'productId$product-info',
|
|
||||||
ellipsis: true,
|
|
||||||
fixed: 'left',
|
|
||||||
search: {
|
|
||||||
type: 'select',
|
|
||||||
options: () =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
const params = {
|
|
||||||
paging: false,
|
|
||||||
'sorts[0].name': 'createTime',
|
|
||||||
'sorts[0].order': 'desc',
|
|
||||||
};
|
|
||||||
getDeviceProduct_api(params).then((resp: any) => {
|
|
||||||
const result = resp.result.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}));
|
|
||||||
resolve(result);
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '注册时间',
|
|
||||||
dataIndex: 'registryTime',
|
|
||||||
key: 'registryTime',
|
|
||||||
ellipsis: true,
|
|
||||||
fixed: 'left',
|
|
||||||
search: {
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'state',
|
|
||||||
key: 'state',
|
|
||||||
ellipsis: true,
|
|
||||||
fixed: 'left',
|
|
||||||
search: {
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: '在线',
|
|
||||||
value: 'online',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '离线',
|
|
||||||
value: 'offline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '禁用',
|
|
||||||
value: 'notActive',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
params: ref({}),
|
|
||||||
search: (params: any) => {
|
|
||||||
query.params.value = params;
|
|
||||||
},
|
},
|
||||||
};
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属产品',
|
||||||
|
dataIndex: 'productName',
|
||||||
|
key: 'productName',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
rename: 'productId$product-info',
|
||||||
|
type: 'select',
|
||||||
|
options: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const params = {
|
||||||
|
paging: false,
|
||||||
|
'sorts[0].name': 'createTime',
|
||||||
|
'sorts[0].order': 'desc',
|
||||||
|
};
|
||||||
|
getDeviceProduct_api(params).then((resp: any) => {
|
||||||
|
const result = resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '资产权限',
|
||||||
|
dataIndex: 'permission',
|
||||||
|
key: 'permission',
|
||||||
|
ellipsis: true,
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注册时间',
|
||||||
|
dataIndex: 'registryTime',
|
||||||
|
key: 'registryTime',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '正常',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '禁用',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const queryParams = ref({});
|
||||||
|
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
const table = {
|
const table = {
|
||||||
|
@ -285,6 +325,32 @@ const table = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getActions: (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
) => {
|
||||||
|
if (!data) return [];
|
||||||
|
else
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'edit',
|
||||||
|
tooltip: { title: '编辑' },
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => table.clickEdit(data),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'unbind',
|
||||||
|
tooltip: { title: '解除绑定' },
|
||||||
|
popConfirm: {
|
||||||
|
title: `是否解除绑定`,
|
||||||
|
onConfirm: () => table.clickUnBind(data),
|
||||||
|
},
|
||||||
|
icon: 'DisconnectOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
// 获取权限数据字典
|
// 获取权限数据字典
|
||||||
getPermissionDict: () => {
|
getPermissionDict: () => {
|
||||||
getPermissionDict_api().then((resp: any) => {
|
getPermissionDict_api().then((resp: any) => {
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="product-container">
|
<div class="product-container">
|
||||||
<Search :columns="query.columns" @search="query.search" />
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
@search="(params:any)=>queryParams = {...params}"
|
||||||
|
/>
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:request="table.requestFun"
|
:request="table.requestFun"
|
||||||
:gridColumn="2"
|
:gridColumn="2"
|
||||||
model="CARD"
|
:params="queryParams"
|
||||||
:params="query.params.value"
|
|
||||||
:rowSelection="{
|
:rowSelection="{
|
||||||
selectedRowKeys: table._selectedRowKeys.value,
|
selectedRowKeys: table._selectedRowKeys.value,
|
||||||
}"
|
}"
|
||||||
|
:columns="columns"
|
||||||
@cancelSelect="table.cancelSelect"
|
@cancelSelect="table.cancelSelect"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
|
@ -56,7 +59,7 @@
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<CardBox
|
<CardBox
|
||||||
:value="slotProps"
|
:value="slotProps"
|
||||||
:actions="[{ key: 1 }]"
|
:actions="table.getActions(slotProps, 'card')"
|
||||||
v-bind="slotProps"
|
v-bind="slotProps"
|
||||||
:active="
|
:active="
|
||||||
table._selectedRowKeys.value.includes(slotProps.id)
|
table._selectedRowKeys.value.includes(slotProps.id)
|
||||||
|
@ -110,8 +113,52 @@
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions="item">
|
||||||
<PermissionButton
|
<a-tooltip
|
||||||
|
v-bind="item.tooltip"
|
||||||
|
:title="item.disabled && item.tooltip.title"
|
||||||
|
>
|
||||||
|
<a-dropdown
|
||||||
|
placement="bottomRight"
|
||||||
|
v-if="item.key === 'others'"
|
||||||
|
>
|
||||||
|
<a-button>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item.text }}</span>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item
|
||||||
|
v-for="(o, i) in item.children"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click="o.onClick"
|
||||||
|
>
|
||||||
|
<AIcon :type="o.icon" />
|
||||||
|
<span>{{ o.text }}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
<PermissionButton
|
||||||
|
v-else
|
||||||
|
:uhasPermission="item.permission"
|
||||||
|
:tooltip="item.tooltip"
|
||||||
|
:pop-confirm="item.popConfirm"
|
||||||
|
@click="item.onClick"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span v-if="item.key !== 'delete'">{{
|
||||||
|
item.text
|
||||||
|
}}</span>
|
||||||
|
</PermissionButton>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<!-- <PermissionButton
|
||||||
:uhasPermission="`${permission}:assert`"
|
:uhasPermission="`${permission}:assert`"
|
||||||
@click="() => table.clickEdit(slotProps)"
|
@click="() => table.clickEdit(slotProps)"
|
||||||
>
|
>
|
||||||
|
@ -126,17 +173,50 @@
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<AIcon type="DisconnectOutlined" />
|
<AIcon type="DisconnectOutlined" />
|
||||||
</PermissionButton>
|
</PermissionButton> -->
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #permission="slotProps">
|
||||||
|
{{
|
||||||
|
table.permissionList.value.length &&
|
||||||
|
table.getPermissLabel(slotProps.permission)
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="slotProps.state.value"
|
||||||
|
:text="slotProps.state.text"
|
||||||
|
:statusNames="{
|
||||||
|
online: 'success',
|
||||||
|
offline: 'error',
|
||||||
|
notActive: 'warning',
|
||||||
|
}"
|
||||||
|
></BadgeStatus>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space :size="16">
|
||||||
|
<PermissionButton
|
||||||
|
v-for="i in table.getActions(slotProps, 'table')"
|
||||||
|
:uhasPermission="i.permission"
|
||||||
|
type="link"
|
||||||
|
:tooltip="i?.tooltip"
|
||||||
|
:pop-confirm="i.popConfirm"
|
||||||
|
@click="i.onClick"
|
||||||
|
:disabled="i?.disabled"
|
||||||
|
>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</PermissionButton>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
|
|
||||||
<div class="dialogs">
|
<div class="dialogs">
|
||||||
<AddDeviceOrProductDialog
|
<AddDeviceOrProductDialog
|
||||||
v-if="dialogs.addShow"
|
v-if="dialogs.addShow"
|
||||||
v-model:visible="dialogs.addShow"
|
v-model:visible="dialogs.addShow"
|
||||||
:query-columns="query.columns"
|
:query-columns="columns"
|
||||||
:parent-id="props.parentId"
|
:parent-id="props.parentId"
|
||||||
:all-permission="table.permissionList.value"
|
:all-permission="table.permissionList.value"
|
||||||
asset-type="product"
|
asset-type="product"
|
||||||
|
@ -185,54 +265,70 @@ const emits = defineEmits(['openDeviceBind']);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
parentId: string;
|
parentId: string;
|
||||||
}>();
|
}>();
|
||||||
const query = {
|
|
||||||
columns: [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
key: 'id',
|
key: 'id',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
search: {
|
search: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
ellipsis: true,
|
|
||||||
fixed: 'left',
|
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'state',
|
|
||||||
key: 'state',
|
|
||||||
ellipsis: true,
|
|
||||||
fixed: 'left',
|
|
||||||
search: {
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: '正常',
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '禁用',
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
params: ref({}),
|
|
||||||
search: (params: any) => {
|
|
||||||
query.params.value = params;
|
|
||||||
},
|
},
|
||||||
};
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '资产权限',
|
||||||
|
dataIndex: 'permission',
|
||||||
|
key: 'permission',
|
||||||
|
ellipsis: true,
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'describe',
|
||||||
|
key: 'describe',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '正常',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '禁用',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const queryParams = ref({});
|
||||||
|
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
const table = {
|
const table = {
|
||||||
|
@ -250,6 +346,33 @@ const table = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getActions: (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
) => {
|
||||||
|
if (!data) return [];
|
||||||
|
else
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'edit',
|
||||||
|
tooltip: { title: '编辑' },
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => table.clickEdit(data),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: true,
|
||||||
|
key: 'unbind',
|
||||||
|
tooltip: { title: '解除绑定' },
|
||||||
|
popConfirm: {
|
||||||
|
title: `是否解除绑定`,
|
||||||
|
onConfirm: () => table.clickUnBind(data),
|
||||||
|
},
|
||||||
|
icon: 'DisconnectOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
// 获取权限数据字典
|
// 获取权限数据字典
|
||||||
getPermissionDict: () => {
|
getPermissionDict: () => {
|
||||||
getPermissionDict_api().then((resp: any) => {
|
getPermissionDict_api().then((resp: any) => {
|
||||||
|
@ -261,7 +384,7 @@ const table = {
|
||||||
const permissionList = table.permissionList.value;
|
const permissionList = table.permissionList.value;
|
||||||
if (permissionList.length < 1 || values.length < 1) return '';
|
if (permissionList.length < 1 || values.length < 1) return '';
|
||||||
const result = values.map(
|
const result = values.map(
|
||||||
(key) => permissionList.find((item:any) => item.id === key)?.name,
|
(key) => permissionList.find((item: any) => item.id === key)?.name,
|
||||||
);
|
);
|
||||||
return result.join(',');
|
return result.join(',');
|
||||||
},
|
},
|
||||||
|
@ -442,6 +565,15 @@ const dialogs = reactive({
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.product-container {
|
.product-container {
|
||||||
|
:deep(.ant-table-tbody) {
|
||||||
|
.ant-table-cell {
|
||||||
|
.ant-space-item {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.card {
|
.card {
|
||||||
.card-warp {
|
.card-warp {
|
||||||
&.active {
|
&.active {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="basic-info-container">
|
<div class="basic-info-container">
|
||||||
<a-card>
|
<div class="card">
|
||||||
<h3>基本信息</h3>
|
<h3>基本信息</h3>
|
||||||
<a-form ref="basicFormRef" :model="form.data" class="basic-form">
|
<j-form ref="basicFormRef" :model="form.data" class="basic-form">
|
||||||
<div class="row" style="display: flex">
|
<div class="row" style="display: flex">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="菜单图标"
|
label="菜单图标"
|
||||||
name="icon"
|
name="icon"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -20,25 +20,28 @@
|
||||||
:type="form.data.icon"
|
:type="form.data.icon"
|
||||||
style="font-size: 90px"
|
style="font-size: 90px"
|
||||||
/>
|
/>
|
||||||
<span class="mark" @click="dialog.openDialog"
|
<span class="mark" @click="dialogVisible = true"
|
||||||
>点击修改</span
|
>点击修改</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
@click="dialog.openDialog"
|
@click="dialogVisible = true"
|
||||||
class="icon-upload no-icon"
|
class="icon-upload no-icon"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<plus-outlined style="font-size: 30px" />
|
<AIcon
|
||||||
|
type="PlusOutlined"
|
||||||
|
style="font-size: 30px"
|
||||||
|
/>
|
||||||
<p>点击选择图标</p>
|
<p>点击选择图标</p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<a-row :gutter="24" style="flex: 1 1 auto">
|
<j-row :gutter="24" style="flex: 1 1 auto">
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="名称"
|
label="名称"
|
||||||
name="name"
|
name="name"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -46,11 +49,11 @@
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="form.data.name" />
|
<j-input v-model:value="form.data.name" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="编码"
|
label="编码"
|
||||||
name="code"
|
name="code"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -58,11 +61,11 @@
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="form.data.code" />
|
<j-input v-model:value="form.data.code" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="页面地址"
|
label="页面地址"
|
||||||
name="url"
|
name="url"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -73,11 +76,11 @@
|
||||||
{ max: 128, message: '最多可输入128字符' },
|
{ max: 128, message: '最多可输入128字符' },
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="form.data.url" />
|
<j-input v-model:value="form.data.url" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
<a-col :span="12">
|
<j-col :span="12">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
label="排序"
|
label="排序"
|
||||||
name="sortIndex"
|
name="sortIndex"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -87,80 +90,84 @@
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="form.data.sortIndex" />
|
<j-input v-model:value="form.data.sortIndex" />
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-col>
|
</j-col>
|
||||||
</a-row>
|
</j-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-form-item label="说明" name="describe">
|
<j-form-item label="说明" name="describe">
|
||||||
<a-textarea
|
<j-textarea
|
||||||
v-model:value="form.data.describe"
|
v-model:value="form.data.describe"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form>
|
</j-form>
|
||||||
</a-card>
|
</div>
|
||||||
<a-card>
|
<div class="card">
|
||||||
<h3>权限配置</h3>
|
<h3>权限配置</h3>
|
||||||
<a-form
|
<j-form
|
||||||
ref="permissFormRef"
|
ref="permissFormRef"
|
||||||
:model="form.data"
|
:model="form.data"
|
||||||
class="basic-form permiss-form"
|
class="basic-form permiss-form"
|
||||||
>
|
>
|
||||||
<a-form-item name="accessSupport" required>
|
<j-form-item name="accessSupport" required>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span style="margin-right: 3px">数据权限控制</span>
|
<span style="margin-right: 3px">数据权限控制</span>
|
||||||
<a-tooltip title="此菜单页面数据所对应的资产类型">
|
<j-tooltip title="此菜单页面数据所对应的资产类型">
|
||||||
<question-circle-outlined
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
class="img-style"
|
class="img-style"
|
||||||
style="color: #a6a6a6"
|
style="color: #a6a6a6"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-radio-group
|
<j-radio-group
|
||||||
v-model:value="form.data.accessSupport"
|
v-model:value="form.data.accessSupport"
|
||||||
name="radioGroup"
|
name="radioGroup"
|
||||||
>
|
>
|
||||||
<a-radio value="unsupported">不支持</a-radio>
|
<j-radio value="unsupported">不支持</j-radio>
|
||||||
<a-radio value="support">支持</a-radio>
|
<j-radio value="support">支持</j-radio>
|
||||||
<a-radio value="indirect">
|
<j-radio value="indirect">
|
||||||
<span style="margin-right: 3px">间接控制</span>
|
<span style="margin-right: 3px">间接控制</span>
|
||||||
<a-tooltip
|
<j-tooltip
|
||||||
title="此菜单内的数据基于其他菜单的数据权限控制"
|
title="此菜单内的数据基于其他菜单的数据权限控制"
|
||||||
>
|
>
|
||||||
<question-circle-filled class="img-style" />
|
<AIcon
|
||||||
</a-tooltip>
|
type="QuestionCircleFilled"
|
||||||
</a-radio>
|
class="img-style"
|
||||||
</a-radio-group>
|
/>
|
||||||
|
</j-tooltip>
|
||||||
|
</j-radio>
|
||||||
|
</j-radio-group>
|
||||||
|
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="assetType"
|
name="assetType"
|
||||||
v-if="form.data.accessSupport === 'support'"
|
v-if="form.data.accessSupport === 'support'"
|
||||||
:rules="[{ required: true, message: '请选择资产类型' }]"
|
:rules="[{ required: true, message: '请选择资产类型' }]"
|
||||||
style="margin-top: 24px; margin-bottom: 0"
|
style="margin-top: 24px; margin-bottom: 0"
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
v-model:value="form.data.assetType"
|
v-model:value="form.data.assetType"
|
||||||
style="width: 500px"
|
style="width: 500px"
|
||||||
placeholder="请选择资产类型"
|
placeholder="请选择资产类型"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<j-select-option
|
||||||
v-for="item in form.assetsType"
|
v-for="item in form.assetsType"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
>{{ item.label }}</a-select-option
|
>{{ item.label }}</j-select-option
|
||||||
>
|
>
|
||||||
</a-select>
|
</j-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
|
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="indirectMenus"
|
name="indirectMenus"
|
||||||
v-if="form.data.accessSupport === 'indirect'"
|
v-if="form.data.accessSupport === 'indirect'"
|
||||||
:rules="[{ required: true, message: '请选择关联菜单' }]"
|
:rules="[{ required: true, message: '请选择关联菜单' }]"
|
||||||
style="margin-top: 24px; margin-bottom: 0"
|
style="margin-top: 24px; margin-bottom: 0"
|
||||||
>
|
>
|
||||||
<a-tree-select
|
<j-tree-select
|
||||||
v-model:value="form.data.indirectMenus"
|
v-model:value="form.data.indirectMenus"
|
||||||
style="width: 400px"
|
style="width: 400px"
|
||||||
:dropdown-style="{
|
:dropdown-style="{
|
||||||
|
@ -177,18 +184,18 @@
|
||||||
value: 'id',
|
value: 'id',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
</a-tree-select>
|
</j-tree-select>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<a-form-item label="权限">
|
<j-form-item label="权限">
|
||||||
<PermissChoose
|
<PermissChoose
|
||||||
:first-width="3"
|
:first-width="3"
|
||||||
max-height="350px"
|
max-height="350px"
|
||||||
v-model:value="form.data.permissions"
|
v-model:value="form.data.permissions"
|
||||||
:key="form.data.id || ''"
|
:key="form.data.id || ''"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form>
|
</j-form>
|
||||||
|
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -197,22 +204,20 @@
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</a-card>
|
</div>
|
||||||
|
|
||||||
<!-- 弹窗 -->
|
<!-- 弹窗 -->
|
||||||
<div class="dialogs">
|
<ChooseIconDialog
|
||||||
<ChooseIconDialog ref="ChooseIconRef" @confirm="dialog.confirm" />
|
v-if="dialogVisible"
|
||||||
</div>
|
v-model:visible="dialogVisible"
|
||||||
|
@confirm="(typeStr:string)=>form.data.icon = typeStr"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PermissionButton from '@/components/PermissionButton/index.vue';
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
import {
|
|
||||||
PlusOutlined,
|
|
||||||
QuestionCircleFilled,
|
|
||||||
QuestionCircleOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import { FormInstance, message } from 'ant-design-vue';
|
import { FormInstance, message } from 'ant-design-vue';
|
||||||
import ChooseIconDialog from '../components/ChooseIconDialog.vue';
|
import ChooseIconDialog from '../components/ChooseIconDialog.vue';
|
||||||
import PermissChoose from '../components/PermissChoose.vue';
|
import PermissChoose from '../components/PermissChoose.vue';
|
||||||
|
@ -260,9 +265,12 @@ const form = reactive({
|
||||||
// 获取菜单详情
|
// 获取菜单详情
|
||||||
routeParams.id &&
|
routeParams.id &&
|
||||||
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
||||||
|
console.log(1111);
|
||||||
|
|
||||||
form.data = {
|
form.data = {
|
||||||
...(resp.result as formType),
|
...(resp.result as formType),
|
||||||
accessSupport: resp.result.accessSupport.value,
|
accessSupport:
|
||||||
|
resp.result?.accessSupport?.value || 'unsupported',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// 获取关联菜单
|
// 获取关联菜单
|
||||||
|
@ -323,15 +331,7 @@ const form = reactive({
|
||||||
form.init();
|
form.init();
|
||||||
|
|
||||||
// 弹窗
|
// 弹窗
|
||||||
const ChooseIconRef = ref<any>(null);
|
const dialogVisible = ref(false);
|
||||||
const dialog = {
|
|
||||||
openDialog: () => {
|
|
||||||
ChooseIconRef.value && ChooseIconRef.value.openDialog();
|
|
||||||
},
|
|
||||||
confirm: (typeStr: string) => {
|
|
||||||
form.data.icon = typeStr || form.data.icon;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
type formType = {
|
type formType = {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -356,8 +356,10 @@ type assetType = {
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.basic-info-container {
|
.basic-info-container {
|
||||||
.ant-card {
|
.card {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -11,18 +11,18 @@
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
:uhasPermission="`${permission}:update`"
|
:uhasPermission="`${permission}:update`"
|
||||||
@click="dialog.openDialog('新增')"
|
@click="openDialog('新增', {})"
|
||||||
>
|
>
|
||||||
<AIcon type="PlusOutlined" />新增
|
<AIcon type="PlusOutlined" />新增
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<j-space :size="16">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="link"
|
type="link"
|
||||||
:uhasPermission="`${permission}:update`"
|
:uhasPermission="`${permission}:update`"
|
||||||
:tooltip="{ title: '编辑' }"
|
:tooltip="{ title: '编辑' }"
|
||||||
@click="dialog.openDialog('编辑', slotProps)"
|
@click="openDialog('编辑', slotProps)"
|
||||||
>
|
>
|
||||||
<AIcon type="EditOutlined" />
|
<AIcon type="EditOutlined" />
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
type="link"
|
type="link"
|
||||||
:uhasPermission="true"
|
:uhasPermission="true"
|
||||||
:tooltip="{ title: '查看' }"
|
:tooltip="{ title: '查看' }"
|
||||||
@click="dialog.openDialog('查看', slotProps)"
|
@click="openDialog('查看', slotProps)"
|
||||||
>
|
>
|
||||||
<AIcon type="SearchOutlined" />
|
<AIcon type="SearchOutlined" />
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
@ -45,15 +45,18 @@
|
||||||
>
|
>
|
||||||
<AIcon type="DeleteOutlined" />
|
<AIcon type="DeleteOutlined" />
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
|
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
<ButtonAddDialog
|
<ButtonAddDialog
|
||||||
ref="dialogRef"
|
v-if="dialogVisible"
|
||||||
@confirm="dialog.confirm"
|
v-model:visible="dialogVisible"
|
||||||
:menu-info="menuInfo"
|
:menu-info="menuInfo"
|
||||||
|
:mode="dialogTitle"
|
||||||
|
:data="selectItem"
|
||||||
|
@confirm="table.getList"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,15 +79,13 @@ const routeParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const dialogRef = ref<any>(null);
|
const selectItem = ref<any>({});
|
||||||
const dialog = {
|
const dialogVisible = ref(false);
|
||||||
// 打开弹窗
|
const dialogTitle = ref<'查看' | '新增' | '编辑'>('新增');
|
||||||
openDialog: (mode: string, row?: object) => {
|
const openDialog = (mode: '查看' | '新增' | '编辑', row: object) => {
|
||||||
dialogRef.value && dialogRef.value.openDialog(mode, { ...row });
|
selectItem.value = { ...row };
|
||||||
},
|
dialogTitle.value = mode;
|
||||||
confirm: () => {
|
dialogVisible.value = true;
|
||||||
table.getList();
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
// 菜单的基本信息-其中包括了表格的数据
|
// 菜单的基本信息-其中包括了表格的数据
|
||||||
const menuInfo = ref<any>({});
|
const menuInfo = ref<any>({});
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div class="menu-detail-container">
|
<div class="menu-detail-container">
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<j-tabs v-model:activeKey="activeKey">
|
||||||
<a-tab-pane key="basic" tab="基本信息">
|
<j-tab-pane key="basic" tab="基本信息">
|
||||||
<BasicInfo />
|
<BasicInfo />
|
||||||
</a-tab-pane>
|
</j-tab-pane>
|
||||||
<a-tab-pane key="button" tab="按钮管理">
|
<j-tab-pane key="button" tab="按钮管理">
|
||||||
<ButtonMange />
|
<ButtonMange />
|
||||||
</a-tab-pane>
|
</j-tab-pane>
|
||||||
</a-tabs>
|
</j-tabs>
|
||||||
</div>
|
</div>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="dialog.visible"
|
visible
|
||||||
:title="form.mode"
|
:title="props.mode"
|
||||||
width="660px"
|
width="660px"
|
||||||
@ok="dialog.handleOk"
|
@ok="confirm"
|
||||||
|
@cancel="emits('update:visible', false)"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
cancelText="取消"
|
:confirmLoading="loading"
|
||||||
okText="确定"
|
|
||||||
:confirmLoading="dialog.loading"
|
|
||||||
>
|
>
|
||||||
<a-form :model="form.data" class="basic-form" ref="formRef">
|
<a-form :model="form.data" class="basic-form" ref="formRef">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
|
@ -20,7 +19,7 @@
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="form.data.id"
|
v-model:value="form.data.id"
|
||||||
:disabled="form.mode !== '新增'"
|
:disabled="props.mode !== '新增'"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
|
@ -33,7 +32,7 @@
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="form.data.name"
|
v-model:value="form.data.name"
|
||||||
:disabled="form.mode === '查看'"
|
:disabled="props.mode === '查看'"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
|
@ -47,19 +46,11 @@
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<!-- <a-form-item-rest>
|
|
||||||
<PermissChoose
|
|
||||||
:first-width="8"
|
|
||||||
max-height="350px"
|
|
||||||
v-model:value="form.data.permissions"
|
|
||||||
:disabled="form.mode === '查看'"
|
|
||||||
/>
|
|
||||||
</a-form-item-rest> -->
|
|
||||||
<PermissChoose
|
<PermissChoose
|
||||||
:first-width="8"
|
:first-width="8"
|
||||||
max-height="350px"
|
max-height="350px"
|
||||||
v-model:value="form.data.permissions"
|
v-model:value="form.data.permissions"
|
||||||
:disabled="form.mode === '查看'"
|
:disabled="props.mode === '查看'"
|
||||||
:key="form.data.id || ''"
|
:key="form.data.id || ''"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -68,7 +59,7 @@
|
||||||
v-model:value="form.data.describe"
|
v-model:value="form.data.describe"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
:disabled="form.mode === '查看'"
|
:disabled="props.mode === '查看'"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
@ -81,54 +72,42 @@ import { Rule } from 'ant-design-vue/es/form';
|
||||||
import PermissChoose from '../components/PermissChoose.vue';
|
import PermissChoose from '../components/PermissChoose.vue';
|
||||||
import { saveMenuInfo_api } from '@/api/system/menu';
|
import { saveMenuInfo_api } from '@/api/system/menu';
|
||||||
|
|
||||||
|
const emits = defineEmits(['confirm', 'update:visible']);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
menuInfo: {
|
menuInfo: {
|
||||||
buttons: formType[];
|
buttons: formType[];
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
visible: boolean;
|
||||||
|
mode: '查看' | '新增' | '编辑';
|
||||||
|
data: formType;
|
||||||
}>();
|
}>();
|
||||||
const emits = defineEmits(['confirm']);
|
|
||||||
const dialog = reactive({
|
|
||||||
visible: false,
|
|
||||||
loading: false,
|
|
||||||
handleOk: () => {
|
|
||||||
props.menuInfo.id &&
|
|
||||||
formRef.value &&
|
|
||||||
formRef.value
|
|
||||||
.validate()
|
|
||||||
.then(() => {
|
|
||||||
const buttons = toRaw(props.menuInfo.buttons);
|
|
||||||
const button = buttons.find(
|
|
||||||
(item) => item.id === form.data.id,
|
|
||||||
);
|
|
||||||
if (button) {
|
|
||||||
Object.entries(form.data).forEach(([key, value]) => {
|
|
||||||
button[key] = value;
|
|
||||||
});
|
|
||||||
} else buttons.push({ ...form.data });
|
|
||||||
const params = {
|
|
||||||
...props.menuInfo,
|
|
||||||
buttons,
|
|
||||||
};
|
|
||||||
dialog.loading = true;
|
|
||||||
saveMenuInfo_api(params)
|
|
||||||
.then((resp) => {
|
|
||||||
dialog.changeVisible();
|
|
||||||
message.success('操作成功');
|
|
||||||
emits('confirm');
|
|
||||||
})
|
|
||||||
.finally(() => (dialog.loading = false));
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
},
|
|
||||||
changeVisible: (mode?: string, formValue?: formType) => {
|
|
||||||
dialog.visible = !dialog.visible;
|
|
||||||
form.data = { ...initForm, ...formValue };
|
|
||||||
form.mode = mode || '';
|
|
||||||
|
|
||||||
formRef.value && formRef.value.clearValidate();
|
const loading = ref(false);
|
||||||
},
|
const confirm = () => {
|
||||||
});
|
loading.value = true;
|
||||||
|
formRef.value &&
|
||||||
|
formRef.value.validate().then(() => {
|
||||||
|
const buttons = toRaw(props.menuInfo.buttons);
|
||||||
|
const button = buttons.find((item) => item.id === form.data.id);
|
||||||
|
if (button) {
|
||||||
|
Object.entries(form.data).forEach(([key, value]) => {
|
||||||
|
button[key] = value;
|
||||||
|
});
|
||||||
|
} else buttons.push({ ...form.data });
|
||||||
|
const params = {
|
||||||
|
...props.menuInfo,
|
||||||
|
buttons,
|
||||||
|
};
|
||||||
|
saveMenuInfo_api(params)
|
||||||
|
.then((resp) => {
|
||||||
|
message.success('操作成功');
|
||||||
|
emits('confirm');
|
||||||
|
emits('update:visible', false);
|
||||||
|
})
|
||||||
|
.finally(() => (loading.value = false));
|
||||||
|
});
|
||||||
|
};
|
||||||
const initForm = {
|
const initForm = {
|
||||||
name: '',
|
name: '',
|
||||||
id: '',
|
id: '',
|
||||||
|
@ -137,19 +116,13 @@ const initForm = {
|
||||||
} as formType;
|
} as formType;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
data: { ...initForm },
|
data: { ...initForm, ...props.data },
|
||||||
mode: '',
|
checkPermission: (_rule: Rule, value: string[]) => {
|
||||||
checkPermission: async (_rule: Rule, value: string[]) => {
|
|
||||||
if (!value || value.length < 1) return Promise.reject('请选择权限');
|
if (!value || value.length < 1) return Promise.reject('请选择权限');
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 将打开弹窗的操作暴露给父组件
|
|
||||||
defineExpose({
|
|
||||||
openDialog: dialog.changeVisible,
|
|
||||||
});
|
|
||||||
|
|
||||||
type formType = {
|
type formType = {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -1,24 +1,33 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
v-model:visible="dialog.visible"
|
visible
|
||||||
title="菜单图标"
|
title="菜单图标"
|
||||||
width="800px"
|
width="800px"
|
||||||
@ok="dialog.handleOk"
|
@cancel="emits('update:visible', false)"
|
||||||
|
@ok="confirm"
|
||||||
>
|
>
|
||||||
<a-radio-group v-model:value="selected" class="radio">
|
<j-radio-group v-model:value="selected" class="radio">
|
||||||
<a-radio-button
|
<j-radio-button
|
||||||
v-for="typeStr in iconKeys"
|
v-for="typeStr in iconKeys"
|
||||||
:value="typeStr"
|
:value="typeStr"
|
||||||
:class="{ active: selected === typeStr }"
|
:class="{ active: selected === typeStr }"
|
||||||
>
|
>
|
||||||
<a-icon :type="typeStr" />
|
<AIcon :type="typeStr" />
|
||||||
</a-radio-button>
|
</j-radio-button>
|
||||||
</a-radio-group>
|
</j-radio-group>
|
||||||
</a-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const emits = defineEmits(['confirm']);
|
const emits = defineEmits(['confirm', 'update:visible']);
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const confirm = () => {
|
||||||
|
emits('confirm', selected.value);
|
||||||
|
emits('update:visible', false);
|
||||||
|
};
|
||||||
const iconKeys = [
|
const iconKeys = [
|
||||||
'EyeOutlined',
|
'EyeOutlined',
|
||||||
'EditOutlined',
|
'EditOutlined',
|
||||||
|
@ -48,29 +57,11 @@ const iconKeys = [
|
||||||
'TeamOutlined',
|
'TeamOutlined',
|
||||||
'MenuUnfoldOutlined',
|
'MenuUnfoldOutlined',
|
||||||
'MenuFoldOutlined',
|
'MenuFoldOutlined',
|
||||||
'QuestionCircleOutlined',
|
|
||||||
'InfoCircleOutlined',
|
'InfoCircleOutlined',
|
||||||
'SearchOutlined',
|
'SearchOutlined',
|
||||||
];
|
];
|
||||||
|
|
||||||
const dialog = reactive({
|
|
||||||
visible: false,
|
|
||||||
handleOk: () => {
|
|
||||||
emits('confirm', selected.value);
|
|
||||||
dialog.changeVisible();
|
|
||||||
selected.value = '';
|
|
||||||
},
|
|
||||||
changeVisible: (show?: boolean) => {
|
|
||||||
dialog.visible = show === undefined ? !dialog.visible : show;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const selected = ref<string>('');
|
const selected = ref<string>('');
|
||||||
|
|
||||||
// 将打开弹窗的操作暴露给父组件
|
|
||||||
defineExpose({
|
|
||||||
openDialog: dialog.changeVisible,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -155,10 +155,12 @@ const permission = reactive({
|
||||||
const checked = checkedValue?.find(
|
const checked = checkedValue?.find(
|
||||||
(checkedItem) => checkedItem.permission === item.id,
|
(checkedItem) => checkedItem.permission === item.id,
|
||||||
);
|
);
|
||||||
const options = item.actions.map((actionItem: any) => ({
|
|
||||||
label: actionItem.name,
|
const options =
|
||||||
value: actionItem.action,
|
item.actions && item.actions.map((actionItem: any) => ({
|
||||||
}));
|
label: actionItem.name,
|
||||||
|
value: actionItem.action,
|
||||||
|
})) || [];
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
|
@ -182,7 +184,6 @@ const permission = reactive({
|
||||||
});
|
});
|
||||||
permission.init();
|
permission.init();
|
||||||
|
|
||||||
|
|
||||||
type permissionType = {
|
type permissionType = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div class="menu-container">
|
<div class="menu-container">
|
||||||
<Search :columns="query.columns" @search="query.search" />
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
@search="(params:any)=>queryParams = {...params}"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:columns="table.columns"
|
:columns="columns"
|
||||||
:request="table.getList"
|
:request="table.getList"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:params="query.params"
|
:params="queryParams"
|
||||||
|
noPagination
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
|
@ -18,17 +22,11 @@
|
||||||
>
|
>
|
||||||
<AIcon type="PlusOutlined" />新增
|
<AIcon type="PlusOutlined" />新增
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<a-button
|
<j-button
|
||||||
style="margin-left: 12px"
|
style="margin-left: 12px"
|
||||||
@click="router.push('/system/Menu/Setting')"
|
@click="router.push('/system/Menu/Setting')"
|
||||||
>菜单配置</a-button
|
>菜单配置</j-button
|
||||||
>
|
>
|
||||||
<!-- <PermissionButton
|
|
||||||
:uhasPermission="true"
|
|
||||||
@click="router.push('/system/Menu/Setting')"
|
|
||||||
>
|
|
||||||
菜单配置
|
|
||||||
</PermissionButton> -->
|
|
||||||
</template>
|
</template>
|
||||||
<template #createTime="slotProps">
|
<template #createTime="slotProps">
|
||||||
{{
|
{{
|
||||||
|
@ -38,17 +36,17 @@
|
||||||
}}
|
}}
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<j-space :size="16">
|
||||||
<a-tooltip>
|
<j-tooltip>
|
||||||
<template #title>查看</template>
|
<template #title>查看</template>
|
||||||
<a-button
|
<j-button
|
||||||
style="padding: 0"
|
style="padding: 0"
|
||||||
type="link"
|
type="link"
|
||||||
@click="table.toDetails(slotProps)"
|
@click="table.toDetails(slotProps)"
|
||||||
>
|
>
|
||||||
<search-outlined />
|
<AIcon type="SearchOutlined" />
|
||||||
</a-button>
|
</j-button>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
|
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="link"
|
type="link"
|
||||||
|
@ -69,7 +67,7 @@
|
||||||
>
|
>
|
||||||
<AIcon type="DeleteOutlined" />
|
<AIcon type="DeleteOutlined" />
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</a-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,7 +78,6 @@
|
||||||
import PermissionButton from '@/components/PermissionButton/index.vue';
|
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 { SearchOutlined } from '@ant-design/icons-vue';
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
@ -88,112 +85,75 @@ const permission = 'system/Menu';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// 筛选
|
const columns = [
|
||||||
const query = reactive({
|
{
|
||||||
columns: [
|
title: '编码',
|
||||||
{
|
dataIndex: 'code',
|
||||||
title: '编码',
|
key: 'code',
|
||||||
dataIndex: 'code',
|
ellipsis: true,
|
||||||
key: 'code',
|
fixed: 'left',
|
||||||
ellipsis: true,
|
search: {
|
||||||
fixed: 'left',
|
type: 'string',
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
width: 300,
|
||||||
title: '名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
ellipsis: true,
|
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '页面地址',
|
|
||||||
dataIndex: 'url',
|
|
||||||
key: 'url',
|
|
||||||
ellipsis: true,
|
|
||||||
search: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '排序',
|
|
||||||
dataIndex: 'sortIndex',
|
|
||||||
key: 'sortIndex',
|
|
||||||
ellipsis: true,
|
|
||||||
search: {
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'createTime',
|
|
||||||
key: 'createTime',
|
|
||||||
ellipsis: true,
|
|
||||||
search: {
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
params: {
|
|
||||||
terms: [],
|
|
||||||
},
|
},
|
||||||
search: (params: any) => {
|
{
|
||||||
query.params = params;
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
width: 220,
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
title: '页面地址',
|
||||||
|
dataIndex: 'url',
|
||||||
|
key: 'url',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '排序',
|
||||||
|
dataIndex: 'sortIndex',
|
||||||
|
key: 'sortIndex',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'describe',
|
||||||
|
key: 'describe',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
key: 'createTime',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
scopedSlots: true,
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const queryParams = ref({ terms: [] });
|
||||||
|
|
||||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||||
const table = reactive({
|
const table = reactive({
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
title: '编码',
|
|
||||||
dataIndex: 'code',
|
|
||||||
key: 'code',
|
|
||||||
width: 300,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
width: 220,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '页面地址',
|
|
||||||
dataIndex: 'url',
|
|
||||||
key: 'url',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '排序',
|
|
||||||
dataIndex: 'sortIndex',
|
|
||||||
key: 'sortIndex',
|
|
||||||
width: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '说明',
|
|
||||||
dataIndex: 'describe',
|
|
||||||
key: 'describe',
|
|
||||||
width: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'createTime',
|
|
||||||
key: 'createTime',
|
|
||||||
scopedSlots: true,
|
|
||||||
width: 180,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
dataIndex: 'action',
|
|
||||||
key: 'action',
|
|
||||||
scopedSlots: true,
|
|
||||||
width: 140,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tableData: [],
|
|
||||||
total: 0,
|
total: 0,
|
||||||
getList: async (_params: any) => {
|
getList: async (_params: any) => {
|
||||||
//过滤非集成的菜单
|
//过滤非集成的菜单
|
||||||
|
@ -220,7 +180,7 @@ const table = reactive({
|
||||||
..._params,
|
..._params,
|
||||||
terms:
|
terms:
|
||||||
_params.terms && _params.length !== 0
|
_params.terms && _params.length !== 0
|
||||||
? [...query.params.terms, item]
|
? [..._params.terms, item]
|
||||||
: [item],
|
: [item],
|
||||||
sorts: [{ name: 'sortIndex', order: 'asc' }],
|
sorts: [{ name: 'sortIndex', order: 'asc' }],
|
||||||
paging: false,
|
paging: false,
|
||||||
|
@ -233,9 +193,9 @@ const table = reactive({
|
||||||
code: resp.message,
|
code: resp.message,
|
||||||
result: {
|
result: {
|
||||||
data: resp.result,
|
data: resp.result,
|
||||||
pageIndex: 0,
|
pageIndex: resp.pageIndex,
|
||||||
pageSize: 0,
|
pageSize: resp.pageSize,
|
||||||
total: 0,
|
total: resp.total,
|
||||||
},
|
},
|
||||||
status: resp.status,
|
status: resp.status,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="role-permiss-container">
|
<div class="role-permiss-container">
|
||||||
<a-card>
|
<section class="card">
|
||||||
<h5>基本信息</h5>
|
<h5>基本信息</h5>
|
||||||
<a-form ref="formRef" class="basic-form" :model="form.data" layout="vertical">
|
<j-form ref="formRef" class="basic-form" :model="form.data" layout="vertical">
|
||||||
<a-form-item
|
<j-form-item
|
||||||
name="name"
|
name="name"
|
||||||
label="名称"
|
label="名称"
|
||||||
:rules="[{ required: true, message: '请输入名称' }]"
|
:rules="[{ required: true, message: '请输入名称' }]"
|
||||||
>
|
>
|
||||||
<a-input
|
<j-input
|
||||||
v-model:value="form.data.name"
|
v-model:value="form.data.name"
|
||||||
placeholder="请输入角色名称"
|
placeholder="请输入角色名称"
|
||||||
:maxlength="64"
|
:maxlength="64"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
<a-form-item name="name" label="说明">
|
<j-form-item name="name" label="说明">
|
||||||
<a-textarea
|
<j-textarea
|
||||||
v-model:value="form.data.description"
|
v-model:value="form.data.description"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
:maxlength="200"
|
:maxlength="200"
|
||||||
show-count
|
show-count
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</j-form-item>
|
||||||
</a-form>
|
</j-form>
|
||||||
</a-card>
|
</section>
|
||||||
|
|
||||||
<a-card>
|
<section class="card">
|
||||||
<h5>权限分配</h5>
|
<h5>权限分配</h5>
|
||||||
<PermissTree v-model:select-items="form.menus" />
|
<PermissTree v-model:select-items="form.menus" />
|
||||||
|
|
||||||
<a-button
|
<j-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:disabled="form.loading"
|
:disabled="form.loading"
|
||||||
@click="form.clickSave"
|
@click="form.clickSave"
|
||||||
style="margin-top: 24px;"
|
style="margin-top: 24px;"
|
||||||
>保存</a-button
|
>保存</j-button
|
||||||
>
|
>
|
||||||
</a-card>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -86,8 +86,10 @@ form.getForm();
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.role-permiss-container {
|
.role-permiss-container {
|
||||||
.ant-card {
|
.card {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -1,134 +1,149 @@
|
||||||
<template>
|
<template>
|
||||||
<a-card class="role-user-container">
|
<div class="role-user-container">
|
||||||
<Search :columns="query.columns" />
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
@search="(params:any)=>queryParams = {...params}"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:columns="table.columns"
|
:columns="columns"
|
||||||
:request="getUserByRole_api"
|
:request="table.getList"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:defaultParams="query.params"
|
:params="queryParams"
|
||||||
:rowSelection="{
|
:rowSelection="{
|
||||||
selectedRowKeys: table._selectedRowKeys,
|
selectedRowKeys: selectedRowKeys,
|
||||||
onChange: table.onSelectChange,
|
onChange: (keys:string[])=>selectedRowKeys = keys,
|
||||||
}"
|
}"
|
||||||
@cancelSelect="table.cancelSelect"
|
@cancelSelect="selectedRowKeys = []"
|
||||||
|
size="small"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button type="primary" @click="table.clickAdd"
|
<j-button type="primary" @click="dialogVisible = true">
|
||||||
><plus-outlined />新增</a-button
|
<AIcon type="PlusOutlined" />新增
|
||||||
>
|
</j-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #status="slotProps">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="slotProps.status"
|
||||||
|
:text="slotProps.status ? '正常' : '禁用'"
|
||||||
|
:statusNames="{
|
||||||
|
1: 'success',
|
||||||
|
0: 'error',
|
||||||
|
}"
|
||||||
|
></BadgeStatus>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<j-space :size="16">
|
||||||
<a-popconfirm
|
<PermissionButton
|
||||||
title="确认解绑"
|
type="link"
|
||||||
ok-text="确定"
|
:tooltip="{ title: '解绑' }"
|
||||||
cancel-text="取消"
|
:pop-confirm="{
|
||||||
@confirm="table.clickUnBind(slotProps)"
|
title: `确认解绑`,
|
||||||
|
onConfirm: () => table.unbind([slotProps.id]),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<a-tooltip>
|
<AIcon type="DisconnectOutlined" />
|
||||||
<template #title>解绑</template>
|
</PermissionButton>
|
||||||
<a-button style="padding: 0" type="link">
|
</j-space>
|
||||||
<disconnect-outlined />
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
|
|
||||||
<div class="dialogs">
|
<AddUserDialog
|
||||||
<AddUserDialog :open="dialog.openAdd" @refresh="table.refresh" />
|
v-if="dialogVisible"
|
||||||
</div>
|
v-model:visible="dialogVisible"
|
||||||
</a-card>
|
:role-id="roleId"
|
||||||
|
@refresh="table.refresh"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="RoleUser">
|
<script setup lang="ts" name="RoleUser">
|
||||||
import { PlusOutlined, DisconnectOutlined } from '@ant-design/icons-vue';
|
import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
import AddUserDialog from '../components/AddUserDialog.vue';
|
import AddUserDialog from '../components/AddUserDialog.vue';
|
||||||
import { getUserByRole_api, unbindUser_api } from '@/api/system/role';
|
import { getUserByRole_api, unbindUser_api } from '@/api/system/role';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import userType from './index';
|
|
||||||
|
|
||||||
const route = useRoute();
|
const roleId = useRoute().params.id as string;
|
||||||
const roleId = route.params.id as string;
|
|
||||||
const query = reactive<userType.queryType>({
|
const columns = [
|
||||||
columns: [
|
{
|
||||||
{
|
title: '姓名',
|
||||||
title: '姓名',
|
dataIndex: 'name',
|
||||||
dataIndex: 'name',
|
key: 'name',
|
||||||
key: 'name',
|
search: {
|
||||||
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '用户名',
|
|
||||||
dataIndex: 'username',
|
|
||||||
key: 'username',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'createTime',
|
|
||||||
key: 'createTime',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'status',
|
|
||||||
key: 'status',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
params: {
|
|
||||||
terms: [
|
|
||||||
{
|
|
||||||
terms: [
|
|
||||||
{
|
|
||||||
column: 'id$in-dimension$role',
|
|
||||||
value: route.params.id,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
title: '用户名',
|
||||||
|
dataIndex: 'username',
|
||||||
|
key: 'username',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
key: 'createTime',
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '正常',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '禁用',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
width: '200px',
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const queryParams = ref({});
|
||||||
|
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
const table = reactive<userType.tableType>({
|
const selectedRowKeys = ref<string[]>([]);
|
||||||
columns: [
|
const table = {
|
||||||
{
|
getList: (oParams: any) => {
|
||||||
title: '姓名',
|
const params = {
|
||||||
dataIndex: 'name',
|
...oParams,
|
||||||
key: 'name',
|
terms: [
|
||||||
},
|
{
|
||||||
{
|
terms: [
|
||||||
title: '用户名',
|
{
|
||||||
dataIndex: 'username',
|
column: 'id$in-dimension$role',
|
||||||
key: 'username',
|
value: roleId,
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
title: '创建时间',
|
},
|
||||||
dataIndex: 'createTime',
|
],
|
||||||
key: 'createTime',
|
};
|
||||||
},
|
if (oParams.terms[0])
|
||||||
{
|
params.terms.unshift({
|
||||||
title: '状态',
|
terms: oParams.terms[0].terms,
|
||||||
dataIndex: 'status',
|
});
|
||||||
key: 'status',
|
return getUserByRole_api(params);
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
dataIndex: 'action',
|
|
||||||
key: 'action',
|
|
||||||
scopedSlots: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tableData: [],
|
|
||||||
// 点击打开新增弹窗
|
|
||||||
clickAdd: () => {
|
|
||||||
dialog.openAdd += 1;
|
|
||||||
},
|
|
||||||
// 点击解绑
|
|
||||||
clickUnBind: (row: any) => {
|
|
||||||
table.unbind([row.id]);
|
|
||||||
},
|
},
|
||||||
// 批量解绑
|
// 批量解绑
|
||||||
unbind: (ids: string[] = []) => {
|
unbind: (ids: string[] = []) => {
|
||||||
|
@ -143,20 +158,24 @@ const table = reactive<userType.tableType>({
|
||||||
refresh: () => {
|
refresh: () => {
|
||||||
tableRef.value.reload();
|
tableRef.value.reload();
|
||||||
},
|
},
|
||||||
// 多选
|
};
|
||||||
_selectedRowKeys: [] as string[],
|
|
||||||
onSelectChange: (keys: string[]) => {
|
|
||||||
table._selectedRowKeys = [...keys];
|
|
||||||
},
|
|
||||||
cancelSelect: () => {
|
|
||||||
table._selectedRowKeys = [];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const dialog = reactive({
|
const dialogVisible = ref(false);
|
||||||
openAdd: 0,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped>
|
||||||
|
.role-user-container {
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
:deep(.ant-table-tbody) {
|
||||||
|
.ant-table-cell {
|
||||||
|
.ant-space-item {
|
||||||
|
.ant-btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,123 +1,99 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<j-modal
|
||||||
v-model:visible="dialog.visible"
|
visible
|
||||||
title="新增"
|
title="新增"
|
||||||
width="1000px"
|
width="1000px"
|
||||||
@ok="dialog.handleOk"
|
@ok="confirm"
|
||||||
|
@cancel="emits('update:visible', false)"
|
||||||
>
|
>
|
||||||
<Search :columns="query.columns" type="simple" />
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
type="simple"
|
||||||
|
@search="(params:any)=>queryParams = {...params}"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:columns="table.columns"
|
:columns="columns"
|
||||||
:request="getUserByRole_api"
|
:request="getUserList"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:params="query.params"
|
:params="queryParams"
|
||||||
:rowSelection="{
|
:rowSelection="{
|
||||||
selectedRowKeys: table._selectedRowKeys,
|
selectedRowKeys: selectedRowKeys,
|
||||||
onChange: table.onSelectChange,
|
onChange: (keys:string[])=>selectedRowKeys = keys,
|
||||||
}"
|
}"
|
||||||
@cancelSelect="table.cancelSelect"
|
@cancelSelect="selectedRowKeys = []"
|
||||||
>
|
>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
|
</j-modal>
|
||||||
<template #footer>
|
|
||||||
<a-button key="back" @click="dialog.visible = false">取消</a-button>
|
|
||||||
<a-button key="submit" type="primary" @click="dialog.handleOk"
|
|
||||||
>确定</a-button
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</a-modal>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getUserByRole_api, bindUser_api } from '@/api/system/role';
|
import { getUserByRole_api, bindUser_api } from '@/api/system/role';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const emits = defineEmits(['refresh']);
|
const emits = defineEmits(['refresh', 'update:visible']);
|
||||||
const props = defineProps({
|
const props = defineProps<{
|
||||||
open: Number,
|
visible: boolean;
|
||||||
});
|
roleId: string
|
||||||
|
}>();
|
||||||
|
|
||||||
const query = reactive({
|
const columns = [
|
||||||
columns: [
|
{
|
||||||
{
|
title: '姓名',
|
||||||
title: '姓名',
|
dataIndex: 'name',
|
||||||
dataIndex: 'name',
|
key: 'name',
|
||||||
key: 'name',
|
search: {
|
||||||
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
title: '用户名',
|
{
|
||||||
dataIndex: 'username',
|
title: '用户名',
|
||||||
key: 'username',
|
dataIndex: 'username',
|
||||||
|
key: 'username',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
params: {
|
];
|
||||||
|
const queryParams = ref({});
|
||||||
|
|
||||||
|
const selectedRowKeys = ref<string[]>([]);
|
||||||
|
const getUserList = (oParams: any) => {
|
||||||
|
const params = {
|
||||||
|
...oParams,
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
terms: [
|
terms: [
|
||||||
{
|
{
|
||||||
terms: [
|
terms: [
|
||||||
{
|
{
|
||||||
column: 'id$in-dimension$role$not',
|
column: 'id$in-dimension$role$not',
|
||||||
value: route.params.id,
|
value: props.roleId,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
};
|
||||||
});
|
if (oParams.terms[0])
|
||||||
const tableRef = ref<Record<string, any>>({});
|
params.terms.unshift({
|
||||||
const table = reactive({
|
terms: oParams.terms[0].terms,
|
||||||
_selectedRowKeys: [] as string[],
|
});
|
||||||
columns: [
|
return getUserByRole_api(params);
|
||||||
{
|
};
|
||||||
title: '姓名',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '用户名',
|
|
||||||
dataIndex: 'username',
|
|
||||||
key: 'username',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tableData: [],
|
|
||||||
onSelectChange: (keys: string[]) => {
|
|
||||||
table._selectedRowKeys = [...keys];
|
|
||||||
},
|
|
||||||
cancelSelect: () => {
|
|
||||||
table._selectedRowKeys = [];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 弹窗相关
|
const confirm = () => {
|
||||||
const dialog = reactive({
|
if (selectedRowKeys.value.length < 1) {
|
||||||
visible: false,
|
message.error('请至少选择一项');
|
||||||
handleOk: () => {
|
} else {
|
||||||
if (table._selectedRowKeys.length < 1) {
|
bindUser_api(props.roleId, selectedRowKeys.value).then(
|
||||||
message.error('请至少选择一项');
|
(resp) => {
|
||||||
} else {
|
|
||||||
bindUser_api(
|
|
||||||
route.params.id as string,
|
|
||||||
table._selectedRowKeys,
|
|
||||||
).then((resp) => {
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功');
|
message.success('操作成功');
|
||||||
emits('refresh');
|
emits('refresh');
|
||||||
dialog.visible = false;
|
emits('update:visible', false);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
}
|
);
|
||||||
},
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.open,
|
|
||||||
() => {
|
|
||||||
dialog.visible = true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="permiss-tree-container">
|
<div class="permiss-tree-container">
|
||||||
<a-table
|
<j-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data-source="tableData"
|
:data-source="tableData"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
|
@ -10,28 +10,28 @@
|
||||||
<!-- 表头 -->
|
<!-- 表头 -->
|
||||||
<template #headerCell="{ column }">
|
<template #headerCell="{ column }">
|
||||||
<div v-if="column.key === 'menu'">
|
<div v-if="column.key === 'menu'">
|
||||||
<a-checkbox
|
<j-checkbox
|
||||||
v-model:checked="selectedAll"
|
v-model:checked="selectedAll"
|
||||||
:indeterminate="indeterminate"
|
:indeterminate="indeterminate"
|
||||||
@change="selectAllChange"
|
@change="selectAllChange"
|
||||||
>菜单权限</a-checkbox
|
>菜单权限</j-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="column.key === 'data'">
|
<div v-else-if="column.key === 'data'">
|
||||||
<span style="">数据权限</span>
|
<span style="">数据权限</span>
|
||||||
<a-tooltip>
|
<j-tooltip>
|
||||||
<template #title
|
<template #title
|
||||||
>勾选任意数据权限均能看到自己创建的数据权限</template
|
>勾选任意数据权限均能看到自己创建的数据权限</template
|
||||||
>
|
>
|
||||||
<question-circle-outlined />
|
<AIcon type="QuestionCircleOutlined" />
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
<a-checkbox
|
<j-checkbox
|
||||||
v-model:checked="bulkShow"
|
v-model:checked="bulkShow"
|
||||||
@change="bulkValue = ''"
|
@change="bulkValue = ''"
|
||||||
style="margin-left: 10px"
|
style="margin-left: 10px"
|
||||||
>批量设置</a-checkbox
|
>批量设置</j-checkbox
|
||||||
>
|
>
|
||||||
<a-select
|
<j-select
|
||||||
v-show="bulkShow"
|
v-show="bulkShow"
|
||||||
v-model:value="bulkValue"
|
v-model:value="bulkValue"
|
||||||
:size="'middle'"
|
:size="'middle'"
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
:options="bulkOptions"
|
:options="bulkOptions"
|
||||||
@change="bulkChange"
|
@change="bulkChange"
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
></a-select>
|
></j-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<span>{{ column.title }}</span>
|
<span>{{ column.title }}</span>
|
||||||
|
@ -48,21 +48,21 @@
|
||||||
<!-- 表格内容 -->
|
<!-- 表格内容 -->
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<div v-if="column.key === 'menu'">
|
<div v-if="column.key === 'menu'">
|
||||||
<a-checkbox
|
<j-checkbox
|
||||||
v-model:checked="record.granted"
|
v-model:checked="record.granted"
|
||||||
:indeterminate="record.indeterminate"
|
:indeterminate="record.indeterminate"
|
||||||
@change="menuChange(record, true)"
|
@change="menuChange(record, true)"
|
||||||
>{{ record.name }}</a-checkbox
|
>{{ record.name }}</j-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="column.key === 'action'">
|
<div v-else-if="column.key === 'action'">
|
||||||
<div v-if="record.buttons && record.buttons.length > 0">
|
<div v-if="record.buttons && record.buttons.length > 0">
|
||||||
<a-checkbox
|
<j-checkbox
|
||||||
v-for="button in record.buttons"
|
v-for="button in record.buttons"
|
||||||
v-model:checked="button.granted"
|
v-model:checked="button.granted"
|
||||||
@change="actionChange(record)"
|
@change="actionChange(record)"
|
||||||
>{{ button.name }}</a-checkbox
|
>{{ button.name }}</j-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,13 +72,16 @@
|
||||||
不支持数据权限配置,默认可查看全部数据
|
不支持数据权限配置,默认可查看全部数据
|
||||||
</span>
|
</span>
|
||||||
<div v-else-if="record.accessSupport.value === 'support'">
|
<div v-else-if="record.accessSupport.value === 'support'">
|
||||||
<a-radio-group v-model:value="record.selectAccesses">
|
<j-radio-group
|
||||||
<a-radio
|
v-model:value="record.selectAccesses"
|
||||||
|
@change="resetBulk"
|
||||||
|
>
|
||||||
|
<j-radio
|
||||||
:value="asset.supportId"
|
:value="asset.supportId"
|
||||||
v-for="asset in record.assetAccesses"
|
v-for="asset in record.assetAccesses"
|
||||||
>{{ asset.name }}</a-radio
|
>{{ asset.name }}</j-radio
|
||||||
>
|
>
|
||||||
</a-radio-group>
|
</j-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-else-if="
|
v-else-if="
|
||||||
|
@ -89,12 +92,11 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</j-table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { getPrimissTree_api } from '@/api/system/role';
|
import { getPrimissTree_api } from '@/api/system/role';
|
||||||
|
|
||||||
|
@ -167,6 +169,12 @@ const bulkChange = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 重置批量设置
|
||||||
|
const resetBulk = () => {
|
||||||
|
bulkValue.value = '';
|
||||||
|
bulkShow.value = false;
|
||||||
|
};
|
||||||
// ------------下面为表格内容部分------------------
|
// ------------下面为表格内容部分------------------
|
||||||
const flatTableData: tableItemType[] = []; // 表格数据的扁平化版本--浅克隆 方便进行对表格数据进行操作
|
const flatTableData: tableItemType[] = []; // 表格数据的扁平化版本--浅克隆 方便进行对表格数据进行操作
|
||||||
|
|
||||||
|
@ -273,6 +281,9 @@ function menuChange(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消批量设置
|
||||||
|
resetBulk();
|
||||||
|
|
||||||
// 改变头部节点状态
|
// 改变头部节点状态
|
||||||
const selectList = flatTableData.filter((item) => item.granted); // 第一列选中的项
|
const selectList = flatTableData.filter((item) => item.granted); // 第一列选中的项
|
||||||
if (selectList.length === flatTableData.length) {
|
if (selectList.length === flatTableData.length) {
|
||||||
|
@ -390,8 +401,3 @@ type tableItemType = {
|
||||||
assetAccesses?: any[];
|
assetAccesses?: any[];
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.permiss-tree-container {
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div class="details-container">
|
<div class="details-container">
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<j-tabs v-model:activeKey="activeKey">
|
||||||
<a-tab-pane key="1" tab="权限分配"><Permiss /></a-tab-pane>
|
<j-tab-pane key="1" tab="权限分配"><Permiss /></j-tab-pane>
|
||||||
<a-tab-pane key="2" tab="用户管理"><User /></a-tab-pane>
|
<j-tab-pane key="2" tab="用户管理"><User /></j-tab-pane>
|
||||||
</a-tabs>
|
</j-tabs>
|
||||||
</div>
|
</div>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
@ -12,7 +12,6 @@
|
||||||
<script setup lang="ts" name="Detail">
|
<script setup lang="ts" name="Detail">
|
||||||
import Permiss from './Permiss/index.vue';
|
import Permiss from './Permiss/index.vue';
|
||||||
import User from './User/index.vue';
|
import User from './User/index.vue';
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const activeKey = ref('1');
|
const activeKey = ref('1');
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="dialog.visible"
|
visible
|
||||||
title="新增"
|
title="新增"
|
||||||
width="670px"
|
width="670px"
|
||||||
@ok="dialog.handleOk"
|
@cancel="emits('update:visible', false)"
|
||||||
|
@ok="confirm"
|
||||||
|
:confirm-loading="loading"
|
||||||
>
|
>
|
||||||
<a-form ref="formRef" :model="form.data" layout="vertical">
|
<a-form ref="formRef" :model="form" layout="vertical">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
name="name"
|
name="name"
|
||||||
label="名称"
|
label="名称"
|
||||||
:rules="[{ required: true, message: '请输入名称' }]"
|
:rules="[{ required: true, message: '请输入名称' }]"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="form.data.name"
|
v-model:value="form.name"
|
||||||
placeholder="请输入角色名称"
|
placeholder="请输入角色名称"
|
||||||
allow-clear
|
allow-clear
|
||||||
:maxlength="64"
|
:maxlength="64"
|
||||||
|
@ -20,72 +22,52 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item name="name" label="说明">
|
<a-form-item name="name" label="说明">
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="form.data.description"
|
v-model:value="form.description"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
allow-clear
|
allow-clear
|
||||||
:maxlength="200"
|
:maxlength="200"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<a-button key="back" @click="dialog.visible = false">取消</a-button>
|
|
||||||
<a-button
|
|
||||||
key="submit"
|
|
||||||
type="primary"
|
|
||||||
:loading="form.loading"
|
|
||||||
@click="dialog.handleOk"
|
|
||||||
>确定</a-button
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FormInstance, message } from 'ant-design-vue';
|
import { FormInstance, message } from 'ant-design-vue';
|
||||||
import { saveRole_api } from '@/api/system/role';
|
import { saveRole_api } from '@/api/system/role';
|
||||||
const router = useRouter();
|
import { useMenuStore } from '@/store/menu';
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const { jumpPage } = useMenuStore();
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:visible']);
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
}>();
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const dialog = reactive({
|
const loading = ref(false);
|
||||||
visible: false,
|
const form = ref<any>({});
|
||||||
handleOk: () => {
|
|
||||||
formRef.value
|
|
||||||
?.validate()
|
|
||||||
.then(() => saveRole_api(form.data))
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.status === 200) {
|
|
||||||
message.success('操作成功');
|
|
||||||
dialog.visible = false;
|
|
||||||
|
|
||||||
if (route.query.save) {
|
|
||||||
// @ts-ignore
|
|
||||||
window?.onSaveSuccess && window.onSaveSuccess(resp.result.id);
|
|
||||||
window.close();
|
|
||||||
} else router.push(`/system/Role/detail/${resp.result.id}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 控制弹窗的打开与关闭
|
|
||||||
changeVisible: (status: boolean, defaultForm: object = {}) => {
|
|
||||||
dialog.visible = status;
|
|
||||||
form.data = { name: '', description: '', ...defaultForm };
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 表单相关
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const form = reactive({
|
|
||||||
loading: false,
|
|
||||||
data: {
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将打开弹窗的操作暴露给父组件
|
const confirm = () => {
|
||||||
defineExpose({
|
loading.value = true;
|
||||||
openDialog: dialog.changeVisible,
|
formRef.value
|
||||||
});
|
?.validate()
|
||||||
|
.then(() => saveRole_api(form.value))
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功');
|
||||||
|
emits('update:visible', false);
|
||||||
|
|
||||||
|
if (route.query.save) {
|
||||||
|
// @ts-ignore
|
||||||
|
window?.onSaveSuccess(resp.result.id);
|
||||||
|
window.close();
|
||||||
|
} else jumpPage(`system/Role/detail`, { id: resp.result.id });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => (loading.value = false));
|
||||||
|
};
|
||||||
|
// 表单相关
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<a-card class="role-container">
|
<a-card class="role-container">
|
||||||
<Search :columns="query.columns" />
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
@search="(params:any)=>queryParams = params"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:columns="table.columns"
|
:columns="columns"
|
||||||
:request="getRoleList_api"
|
:request="getRoleList_api"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:params="query.params"
|
:params="queryParams"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="primary"
|
type="primary"
|
||||||
:uhasPermission="`${permission}:add`"
|
:uhasPermission="`${permission}:add`"
|
||||||
@click="table.clickAdd"
|
@click="dialogVisible = true"
|
||||||
>
|
>
|
||||||
<AIcon type="PlusOutlined" />新增
|
<AIcon type="PlusOutlined" />新增
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
@ -47,9 +50,7 @@
|
||||||
</template>
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
|
|
||||||
<div class="dialogs">
|
<AddDialog v-if="dialogVisible" v-model:visible="dialogVisible" />
|
||||||
<AddDialog ref="addDialogRef" />
|
|
||||||
</div>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
@ -59,73 +60,56 @@ import PermissionButton from '@/components/PermissionButton/index.vue';
|
||||||
import AddDialog from './components/AddDialog.vue';
|
import AddDialog from './components/AddDialog.vue';
|
||||||
import { getRoleList_api, delRole_api } from '@/api/system/role';
|
import { getRoleList_api, delRole_api } from '@/api/system/role';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
|
||||||
const permission = 'system/Role';
|
const permission = 'system/Role';
|
||||||
|
const { jumpPage } = useMenuStore();
|
||||||
|
|
||||||
const addDialogRef = ref(); // 新增弹窗实例
|
const addDialogRef = ref(); // 新增弹窗实例
|
||||||
const router = useRouter();
|
const isSave = !!useRoute().query.save;
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
// 筛选
|
const columns = [
|
||||||
const query = reactive({
|
{
|
||||||
columns: [
|
title: '标识',
|
||||||
{
|
dataIndex: 'id',
|
||||||
title: '标识',
|
key: 'id',
|
||||||
dataIndex: 'id',
|
ellipsis: true,
|
||||||
key: 'id',
|
fixed: 'left',
|
||||||
ellipsis: true,
|
search: {
|
||||||
fixed: 'left',
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
title: '名称',
|
{
|
||||||
dataIndex: 'name',
|
title: '名称',
|
||||||
key: 'name',
|
dataIndex: 'name',
|
||||||
ellipsis: true,
|
key: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
title: '描述',
|
{
|
||||||
key: 'description',
|
title: '描述',
|
||||||
ellipsis: true,
|
key: 'description',
|
||||||
dataIndex: 'description',
|
ellipsis: true,
|
||||||
filters: true,
|
dataIndex: 'description',
|
||||||
onFilter: true,
|
search: {
|
||||||
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
title: '操作',
|
{
|
||||||
valueType: 'option',
|
title: '操作',
|
||||||
width: 200,
|
dataIndex: 'action',
|
||||||
fixed: 'right',
|
key: 'action',
|
||||||
},
|
width: 200,
|
||||||
],
|
fixed: 'right',
|
||||||
params: {},
|
scopedSlots: true,
|
||||||
});
|
},
|
||||||
|
];
|
||||||
|
const queryParams = ref({});
|
||||||
// 表格
|
// 表格
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
const table = reactive({
|
const table = {
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
title: '标识',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '说明',
|
|
||||||
dataIndex: 'description',
|
|
||||||
key: 'description',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
dataIndex: 'action',
|
|
||||||
key: 'action',
|
|
||||||
scopedSlots: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tableData: [],
|
|
||||||
clickAdd: () => {
|
clickAdd: () => {
|
||||||
addDialogRef.value.openDialog(true, {});
|
addDialogRef.value.openDialog(true, {});
|
||||||
},
|
},
|
||||||
|
@ -137,13 +121,11 @@ const table = reactive({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
clickEdit: (row: any) => {
|
clickEdit: ({ id }: { id: string }) => {
|
||||||
router.push(`/system/Role/detail/${row.id}`);
|
jumpPage(`system/Role/Detail`, { id });
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
nextTick(() => {
|
const dialogVisible = ref(isSave);
|
||||||
route.query.save && table.clickAdd();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div class="user-container">
|
<div class="user-container">
|
||||||
<Search :columns="columns" @search="query.search" />
|
<j-advanced-search :columns="columns" @search="(params:any)=>queryParams = {...params}" />
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="getUserList_api"
|
:request="getUserList_api"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:params="query.params.value"
|
:params="queryParams"
|
||||||
:defaultParams="{
|
:defaultParams="{
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
}"
|
}"
|
||||||
|
@ -208,12 +208,7 @@ const columns = [
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const query = {
|
const queryParams = ({});
|
||||||
params: ref({}),
|
|
||||||
search: (params: object) => {
|
|
||||||
query.params.value = params;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||||
const table = {
|
const table = {
|
||||||
|
|
|
@ -3903,8 +3903,8 @@ jetlinks-store@^0.0.3:
|
||||||
|
|
||||||
jetlinks-ui-components@^1.0.4:
|
jetlinks-ui-components@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.4.tgz#38f2abbb6c686ce1e1d7c08a318a6c2f50267adc"
|
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.4.tgz#4c7dc568b20a9bda7decbbb6b30f0191cb4340af"
|
||||||
integrity sha512-bpmSoKVpdgZ2FUxIBaqBpewt2T9g1tTf4tECam9uTdww6SDxyvLzWmeKIUfVIwp2Ts7uKpHPFsCtI8fPmZuRjw==
|
integrity sha512-V1lydb150+9N+wKPaZSF+WYF1bPj1bQ8nCge8EP093tqH/wVHEs3mYRlHEEfNf/+SLIcrWuPCISihfMatVIlTQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vueuse/core" "^9.12.0"
|
"@vueuse/core" "^9.12.0"
|
||||||
ant-design-vue "^3.2.15"
|
ant-design-vue "^3.2.15"
|
||||||
|
|
Loading…
Reference in New Issue