| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- <template>
- <a-card
- style="width: 100%"
- :tab-list="tabListNoTitle"
- :active-tab-key="state.tasActive"
- @tabChange="onTabChange"
- >
- <template v-if="state.tasActive == 'msg'" >
- <a-alert :message="tipMessage" type="info" show-icon />
- <div class="subtitle" >注意:平台为每个设备默认最多保存20条消息,超过20条后,后续的消息会替换下发最早的消息。</div>
- <a-row justify="space-between" >
- <a-col></a-col>
- <a-col> <a-button type="primary" @click="state.visible = true">下发消息</a-button> </a-col>
- </a-row>
- </template>
- <template v-else >
- <a-alert :message="tipMessage" type="info" show-icon style="margin-bottom: 20px;" />
- <a-row justify="space-between" >
- <a-col class="msg-title" >
- <div class="title" >同步命令下发</div>
- <div class="subtitle" >同步命令暂不支持历史记录查看。</div>
- </a-col>
- <a-col> <a-button type="primary" @click="state.visible = true">命令下发</a-button> </a-col>
- </a-row>
- </template>
- <a-table
- style="margin-top: 20px;"
- :columns="state.tasActive === 'msg' ? columns :cmdColumns"
- :dataSource="state.msgDataSource"
- :loading="state.loading"
- :pagination="false"
- >
- <template #bodyCell="{ column, record }">
- <template v-if="column.key === 'cmdPayload'" >
- <a-tooltip color="white" :overlayStyle="{width: '800px'}" >
- <template #title>
- <a-textarea
- :bordered="false"
- style="width: 600px;"
- :auto-size="{ minRows: 5, maxRows: 15 }"
- :value="JSON.stringify(JSON.parse(record.cmdPayload), null, '\t')"
- >
- </a-textarea>
- </template>
- {{record.cmdPayload}}
- </a-tooltip>
- </template>
- <template v-if="column.key === 'cmdRet'" >
- <a-tooltip color="white" :overlayStyle="{width: '200px'}" >
- <template #title>
- <a-textarea
- :bordered="false"
- style="width: 600px;"
- :auto-size="{ minRows: 5, maxRows: 15 }"
- :value="record.cmdRet"
- >
- </a-textarea>
- </template>
- {{record.cmdRet}}
- </a-tooltip>
- </template>
- <template v-if="column.key === 'createAt'">
- {{dayjs(record.createAt).format('YYYY-MM-DD HH:mm:ss')}}
- </template>
- <template v-if="column.key === 'action'">
- <a @click="openDetailModal(record)" >详情</a>
- </template>
- </template>
- </a-table>
- </a-card>
- <modal-pro
- :title="modalTitle"
- style="width: 700px;"
- :open="state.visible"
- @cancel="state.visible = false"
- @ok="ok"
- >
- <a-form
- v-if="state.tasActive === 'msg'"
- :labelCol="{span: 4}"
- :wrapperCol="{span: 14}"
- >
- <a-form-item
- label="消息名称"
- v-bind="validateInfos.msgLabel"
- >
- <a-input allowClear v-model:value="msgState.msgLabel" > </a-input>
- </a-form-item>
- <a-form-item
- label="Topic"
- >
- <a-input allowClear v-model:value="msgState.topic" > </a-input>
- </a-form-item>
- <a-form-item
- label="消息内容"
- v-bind="validateInfos.msgPayload"
- >
- <a-textarea allowClear v-model:value="msgState.msgPayload" > </a-textarea>
- </a-form-item>
- </a-form>
- <template v-else >
- <a-alert message="同步命令成功下发后,设备需要在20秒内向平台回复响应,否则会认为命令请求超时。" type="info" show-icon style="margin-bottom: 20px;" />
- <a-form
- :labelCol="{span: 4}"
- :wrapperCol="{span: 14}"
- >
- <a-form-item
- label="命令名称"
- v-bind="validateInfosCmd.cmdId"
- >
- <a-select allowClear v-model:value="cmdState.cmdId" >
- <a-select-option
- v-for="item in state.cmdList"
- :key="item.id"
- :value="item.id"
- >
- {{item.cmdLabel}}
- </a-select-option>
- </a-select>
- </a-form-item>
- <a-form-item
- label="设置命令参数"
- v-bind="validateInfos.validateInfosCmd"
- v-if="cmdDetail?.cmdParams"
- >
- <a-row>
- <a-col v-for="(item, index) in cmdDetail?.cmdParams" :key="index" style="margin: 10px 0px;" >
- <a-input-group size="large" >
- <a-row :gutter="8" align="middle" >
- <a-col :span="8">
- <a-input allowClear placeholder="key" v-model:value="item.paramLabel" disabled />
- </a-col>
- <a-col :span="8">
- <a-input allowClear placeholder="value" v-model:value="item.dataUnit" />
- </a-col>
- </a-row>
- </a-input-group>
- </a-col>
- </a-row>
- </a-form-item>
- <a-form-item label="设置命令参数" >
- 产品尚未配置命令,请先去 <a @click="pushProductDetail" >产品详情</a> 定义命令。
- </a-form-item>
- </a-form>
- </template>
- </modal-pro>
- <modal-pro
- title="下发消息"
- layout="vertical"
- :open="state.detailVisible"
- @cancel="state.detailVisible = false"
- >
- <a-descriptions
- :column="1"
- :labelStyle="{width: '100px', textAlign: 'left', display: 'block'}"
- v-if="state.tasActive === 'msg'"
- >
- <a-descriptions-item :span="24" label="状态">{{ DeviceContriller.deviceMag.get(state.msgDetail!.status! )?.name }}</a-descriptions-item>
- <a-descriptions-item :span="24" label="消息 ID">{{state.msgDetail.msgId}}</a-descriptions-item>
- <a-descriptions-item :span="24" label="消息名称">{{state.msgDetail.msgLabel}}</a-descriptions-item>
- <a-descriptions-item :span="24" label="Topic">{{state.msgDetail.topic}}</a-descriptions-item>
- <a-descriptions-item :span="24" label="消息创建时间">
- {{dayjs(state.msgDetail.createAt).format('YYYY-MM-DD HH:mm:ss')}}
- </a-descriptions-item>
- </a-descriptions>
- <a-descriptions
- :column="1"
- :labelStyle="{width: '100px', textAlign: 'left', display: 'block'}"
- v-else
- >
- <a-descriptions-item :span="24" label="状态">{{ DeviceContriller.deviceMag.get(state.cmdDetail!.status! )?.name }}</a-descriptions-item>
- <a-descriptions-item :span="24" label="命令名称">{{state.cmdDetail.cmdLabel}}</a-descriptions-item>
- <a-descriptions-item :span="24" label="参数">{{state.cmdDetail.cmdPayload}}</a-descriptions-item>
- <!-- <a-descriptions-item :span="24" label="下发参数">{{state.cmdDetail.msgLabel}}</a-descriptions-item> -->
- <a-descriptions-item :span="24" label="消息创建时间">
- {{dayjs(state.cmdDetail.createAt).format('YYYY-MM-DD HH:mm:ss')}}
- </a-descriptions-item>
- </a-descriptions>
- </modal-pro>
- </template>
- <script lang="ts" setup >
- import { DeviceContriller, ModelCmdController } from '@/controller'
- import { computed, onMounted, reactive, ref } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- import { Form, message } from 'ant-design-vue'
- import dayjs from 'dayjs'
- import { CodeMirrorTsx } from '@/components/CodeMirror/index'
- const msg = '消息下发不依赖产品模型,平台会以异步方式(消息下发后无需等待设备侧回复响应)下发消息给设备。当前仅MQTT设备支持消息下发。'
- const cmdMsg = '如果设备所属产品定义了命令功能,则您可以通过应用调用平台接口或者操作下面的“下发命令”按钮下发命令。当前MQTT设备仅支持同步命令下发,设备仅支持异步命令下发 。'
- const modalTitle = '新增下发消息'
- const tabListNoTitle = [
- {
- key: 'msg',
- tab: '消息下发'
- },
- {
- key: 'cmd',
- tab: '命令下发'
- }
- ]
- const columns = [
- {
- title: '状态',
- dataIndex: 'status',
- key: 'status'
- },
- {
- title: '消息名称',
- dataIndex: 'msgLabel'
- },
- {
- title: '消息id',
- dataIndex: 'msgId'
- },
- {
- title: '消息内容',
- dataIndex: 'msgPayload'
- },
- {
- title: '消息创建时间',
- dataIndex: 'createAt',
- key: 'createAt'
- },
- {
- title: '操作',
- key: 'action'
- }
- ]
- const cmdColumns = [
- {
- title: '状态',
- dataIndex: 'status',
- key: 'status'
- },
- {
- title: '命令名称',
- dataIndex: 'cmdLabel'
- },
- {
- title: '命令id',
- dataIndex: 'msgId'
- },
- {
- title: '命令内容',
- dataIndex: 'cmdPayload',
- key: 'cmdPayload'
- },
- {
- title: '命令结果',
- dataIndex: 'cmdRet',
- key: 'cmdRet'
- },
- {
- title: '命令创建时间',
- dataIndex: 'createAt',
- key: 'createAt'
- },
- {
- title: '操作',
- key: 'action'
- }
- ]
- const useForm = Form.useForm
- const route = useRoute()
- const router = useRouter()
- const deviceId = route.query.id as string
- const tipMessage = computed(() => state.tasActive === 'msg' ? msg : cmdMsg)
- const cmdDetail = computed(() => state.cmdList.find(item => item.id === cmdState.cmdId))
- const cmdParametersTrans = computed(() => {
- return Object.keys(cmdState.cmdParameters).map(key => {
- return { key: key, value: cmdState.cmdParameters[key] }
- })
- })
- const state = reactive<{
- tasActive: 'msg' | 'cmd',
- msgDataSource: (IOT.API.DEVICE.Msg | IOT.API.DEVICE.Cmd)[],
- visible: boolean,
- loading: boolean,
- detailVisible: boolean,
- msgDetail: Partial<IOT.API.DEVICE.Msg >
- cmdDetail: Partial<IOT.API.DEVICE.Cmd>
- cmdList: IOT.API.CMD.Cmd[],
- deviceDetail: IOT.API.DEVICE.Device | null
- }>({
- tasActive: 'msg',
- msgDataSource: [],
- visible: false,
- loading: false,
- detailVisible: false,
- msgDetail: {},
- cmdDetail: {},
- cmdList: [],
- deviceDetail: null
- })
- const msgState = reactive({
- deviceId: deviceId,
- msgPayload: '',
- msgLabel: '',
- topic: ''
- })
- const cmdState = reactive<{
- cmdId: string
- deviceId: string,
- cmdLabel: string,
- cmdParameters: {key: string, value: string}[]
- }>({
- cmdId: '',
- deviceId: deviceId,
- cmdLabel: '',
- cmdParameters: []
- })
- const { resetFields, validate, validateInfos } = useForm(msgState, reactive({
- msgPayload: [{ required: true, message: '请填写消息内容' }],
- msgLabel: [{ required: true, message: '请填写消息标题' }]
- }))
- const { resetFields: resetFieldsCmd, validate: validateCmd, validateInfos: validateInfosCmd } = useForm(cmdState, reactive({
- cmdId: [{ required: true, message: '请填写命令名称' }]
- }))
- const getModelCmdList = async () => {
- const { data } = await ModelCmdController.list({ modelId: state.deviceDetail!.modelId })
- state.cmdList = data
- }
- const pushProductDetail = () => {
- router.push({ path: '/product/detail', query: { id: state.deviceDetail!.modelId } })
- }
- const ok = () => {
- if (state.tasActive === 'msg') {
- validate().then(async () => {
- await DeviceContriller.addDeviceMsg(msgState)
- state.visible = false
- getDeviceMsgList()
- })
- } else {
- validateCmd().then(async () => {
- const _cmdParameters: Record<string, string> = {}
- cmdDetail.value?.cmdParams.forEach(item => {
- _cmdParameters[item.paramLabel] = item.dataUnit
- })
- const $params = {
- ...cmdState,
- cmdLabel: cmdDetail.value!.cmdLabel,
- cmdParameters: _cmdParameters
- }
- await DeviceContriller.addDeviceCmd($params)
- state.visible = false
- getDeviceMsgList()
- })
- }
- }
- const openDetailModal = (record: IOT.API.DEVICE.Msg) => {
- const key = state.tasActive === 'cmd' ? 'cmdDetail' : 'msgDetail'
- state.detailVisible = true
- state[key] = record
- }
- const getDeviceMsgList = async () => {
- state.loading = true
- state.msgDataSource = state.tasActive === 'msg' ? await DeviceContriller.listDeviceMsg({ deviceId }) : await DeviceContriller.listDeviceCmd({ deviceId })
- state.loading = false
- }
- const getDeviceById = async () => {
- state.deviceDetail = await DeviceContriller.byId(deviceId)
- getModelCmdList()
- }
- const onTabChange = (value: 'msg' | 'cmd') => {
- state.tasActive = value
- getDeviceMsgList()
- }
- onMounted(() => {
- getDeviceMsgList()
- getDeviceById()
- })
- </script>
- <style lang="less" scoped >
- @import '~@/styles/theme.less';
- .subtitle {
- color: @sublabel-color;
- margin: 20px 0px;
- }
- .msg-title {
- .title {
- font-size: 20px;
- }
- .subtitle {
- color: @sublabel-color;
- font-size: 14px;
- }
- }
- </style>
|