Browse Source

设备异常反馈

李富豪 1 year ago
parent
commit
a9f048487a

+ 166 - 0
Web/src/components/devices/deviceList/components/FeedbackCreateModal.vue

@@ -0,0 +1,166 @@
+<template>
+    <a-modal :width="800" title="设备异常反馈" :maskClosable="false" :closable="false" okText="提交" v-model:visible="visible"
+        @ok="handleClickSubmit" @cancel="onClickClose">
+        <a-form ref="formRef" :model="formModel" :colon="false" :label-col="labelCol" :wrapper-col="wrapperCol">
+            <a-form-item label='异常描述' name="describe"
+                :rules="[{ required: true, message: '异常描述不能为空', whitespace: true }]">
+                <a-input style="width: 100%;" v-model:value="formModel.describe" placeholder="请输入异常描述" />
+            </a-form-item>
+            <a-form-item label='发生时间' name="date" :rules="[{ required: true, message: '发生时间不能为空' }]">
+                <a-date-picker style="width: 100%;" showTime valueFormat="YYYY-MM-DD HH:mm:ss"
+                    v-model:value="formModel.date" />
+            </a-form-item>
+            <a-form-item label='联系电话' name="phone">
+                <a-input style="width: 100%;" v-model:value="formModel.phone" placeholder="请输入联系电话" />
+            </a-form-item>
+            <a-form-item label='联系邮箱' name="email">
+                <a-input style="width: 100%;" v-model:value="formModel.email" placeholder="请输入发生时间" />
+            </a-form-item>
+            <a-form-item label='附件' name="file">
+                <div style="display: flex;align-items: center;">
+                    <a-button>上传附件</a-button>
+                    <div style="font-size: 12px;color: rgba(0,0,0,.45);margin-left: 10px;">
+                        支持上传:图片、文档、视频、RAR 及 ZIP 等文件(至多上传 3 个文件)
+                    </div>
+                </div>
+            </a-form-item>
+            <a-form-item label='上传日志' name="linkageSelect">
+                <div style="display: flex;align-items: center;">
+                    <div style="margin-right: 10px;">
+                        联动选择
+                    </div>
+                    <a-switch v-model:checked="formModel.linkageSelect" />
+                </div>
+            </a-form-item>
+        </a-form>
+        <div class="log-list">
+            <div class="log-list-item">
+                <a-table :columns="airportLogColumns" :scroll="{ x: '100%', y: 600 }"
+                    :data-source="state.airportLogList" :loading="state.listLoading"
+                    :row-selection="airportTableLogState.rowSelection" rowKey="boot_index" :pagination="false">
+                    <template #log_time="{ record }">
+                        <!-- <div>{{ getLogTime(record) }}</div> -->
+                    </template>
+                    <template #size="{ record }">
+                        <!-- <div>{{ getLogSize(record.size) }}</div> -->
+                    </template>
+                </a-table>
+            </div>
+            <div class="log-list-item">
+                <a-table :columns="droneLogColumns" :scroll="{ x: '100%', y: 600 }" :data-source="state.droneLogList"
+                    :loading="state.listLoading" :row-selection="droneTableLogState.rowSelection" rowKey="boot_index"
+                    :pagination="false">
+                    <template #log_time="{ record }">
+                        <!-- <div>{{ getLogTime(record) }}</div> -->
+                    </template>
+                    <template #size="{ record }">
+                        <!-- <div>{{ getLogSize(record.size) }}</div> -->
+                    </template>
+                </a-table>
+            </div>
+        </div>
+    </a-modal>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, onMounted } from 'vue';
+import { apis } from '/@/api/custom';
+
+interface Props {
+    visible: boolean,
+    onClickSubmit: () => Promise<any>
+    onClickClose: () => void,
+};
+
+const props = withDefaults(defineProps<Props>(), {
+
+});
+
+const formRef = ref();
+
+const formModel = reactive({
+    describe: undefined,
+    date: undefined,
+    phone: undefined,
+    email: undefined,
+    file: undefined,
+    linkageSelect: undefined
+})
+
+const labelCol = { span: 4 };
+
+const wrapperCol = { span: 18 };
+
+interface State {
+    listLoading: boolean,
+    airportLogList: any[]
+    droneLogList: any[],
+};
+
+const state: State = reactive({
+    listLoading: false,
+    airportLogList: [],
+    droneLogList: [],
+});
+
+onMounted(async () => {
+    // await fetchList();
+})
+
+// 表格
+const airportLogColumns = [
+    { title: '机场日志', dataIndex: 'time', width: 100, slots: { customRender: 'log_time' } },
+    { title: '文件大小', dataIndex: 'size', width: 25, slots: { customRender: 'size' } },
+]
+
+const droneLogColumns = [
+    { title: '飞行器日志', dataIndex: 'time', width: 100, slots: { customRender: 'log_time' } },
+    { title: '文件大小', dataIndex: 'size', width: 25, slots: { customRender: 'size' } },
+]
+
+const airportTableLogState = reactive({
+    logList: {} as DeviceLogFileInfo,
+    tableLoading: false,
+    selectRow: [],
+    rowSelection: {
+        columnWidth: 15,
+        selectedRowKeys: [] as number[],
+        onChange: (selectedRowKeys: number[], selectedRows: []) => {
+            airportTableLogState.rowSelection.selectedRowKeys = selectedRowKeys
+            airportTableLogState.selectRow = selectedRows
+            console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
+        },
+    }
+})
+
+const droneTableLogState = reactive({
+    logList: {} as DeviceLogFileInfo,
+    tableLoading: false,
+    selectRow: [],
+    rowSelection: {
+        columnWidth: 15,
+        selectedRowKeys: [] as number[],
+        onChange: (selectedRowKeys: number[], selectedRows: []) => {
+            droneTableLogState.rowSelection.selectedRowKeys = selectedRowKeys
+            droneTableLogState.selectRow = selectedRows
+            console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
+        },
+    }
+})
+
+// 点击提交
+const handleClickSubmit = () => {
+
+}
+</script>
+
+<style lang="scss" scoped>
+.log-list {
+    display: flex;
+    justify-content: space-between;
+
+    &-item {
+        width: 48%;
+    }
+}
+</style>

+ 88 - 0
Web/src/components/devices/deviceList/components/FeedbackDetailModal.vue

@@ -0,0 +1,88 @@
+<template>
+    <a-modal :width="800" title="设备异常反馈" :maskClosable="false" :closable="false" :okButtonProps="{ hidden: true }"
+        cancelText="关闭" v-model:visible="visible" @cancel="onClickClose">
+        <a-form :colon="false" :label-col="labelCol" :wrapper-col="wrapperCol">
+            <a-form-item label='异常描述' name="describe">
+                {{ formModel.describe || '--' }}
+            </a-form-item>
+            <a-form-item label='发生时间' name="date">
+                {{ formModel.date || '--' }}
+            </a-form-item>
+            <a-form-item label='联系电话' name="phone">
+                {{ formModel.phone || '--' }}
+            </a-form-item>
+            <a-form-item label='联系邮箱' name="email">
+                {{ formModel.email || '--' }}
+            </a-form-item>
+            <a-form-item label='附件' name="file">
+                <div>
+                    {{ '--' }}
+                </div>
+            </a-form-item>
+        </a-form>
+        <div class="log-list">
+            <div class="log-list-item">
+                <div>
+                    机场日志
+                </div>
+                <div>
+                    内容
+                </div>
+            </div>
+            <div class="log-list-item">
+                <div>
+                    飞行器日志
+                </div>
+                <div>
+                    内容
+                </div>
+            </div>
+        </div>
+    </a-modal>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, onMounted } from 'vue';
+import { apis } from '/@/api/custom';
+
+interface Props {
+    currentId: string,
+    visible: boolean,
+    onClickClose: () => void,
+};
+
+const props = withDefaults(defineProps<Props>(), {
+
+});
+
+const formModel = reactive({
+    describe: undefined,
+    date: undefined,
+    phone: undefined,
+    email: undefined,
+    file: undefined,
+})
+
+const labelCol = { span: 4 };
+
+const wrapperCol = { span: 18 };
+
+const state = reactive({
+    loading: false,
+});
+
+onMounted(async () => {
+    // await fetchList();
+})
+</script>
+
+<style lang="scss" scoped>
+.log-list {
+    display: flex;
+    justify-content: space-between;
+
+    &-item {
+        width: 48%;
+    }
+}
+</style>

+ 23 - 6
Web/src/components/devices/deviceList/components/FeedbackDrawer.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
     <a-drawer width="70%" title="设备异常反馈记录" :visible="visible" @close="onClose">
     <a-drawer width="70%" title="设备异常反馈记录" :visible="visible" @close="onClose">
         <div class="top">
         <div class="top">
-            <a-button type="primary" @click="() => state.visible = true">
+            <a-button type="primary" @click="() => state.createModalVisible = true">
                 新建异常反馈
                 新建异常反馈
             </a-button>
             </a-button>
             <div>
             <div>
@@ -45,20 +45,25 @@
                 <!-- 操作 -->
                 <!-- 操作 -->
                 <template #operation="{ record }">
                 <template #operation="{ record }">
                     <a-tooltip title="详情">
                     <a-tooltip title="详情">
-                        <FileSearchOutlined />
+                        <FileSearchOutlined @click="handleClickDetail(record.id)" />
                     </a-tooltip>
                     </a-tooltip>
                 </template>
                 </template>
             </a-table>
             </a-table>
         </div>
         </div>
     </a-drawer>
     </a-drawer>
     <!-- 异常反馈-信息弹出层 -->
     <!-- 异常反馈-信息弹出层 -->
-    <FeedbackInfoModal :currentId="state.currentId" :visible="state.visible" :onClose="() => state.visible = false" />
+    <FeedbackCreateModal :visible="state.createModalVisible" :onClickSubmit="createModalOnClickSubmit"
+        :onClickClose="() => state.createModalVisible = false" />
+    <!-- 异常反馈-详情弹出层 -->
+    <FeedbackDetailModal :currentId="state.currentId" :visible="state.detailModalVisible"
+        :onClickClose="() => state.detailModalVisible = false" />
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
 import { ref, reactive, onMounted } from 'vue';
 import { ref, reactive, onMounted } from 'vue';
 import { SearchOutlined, ReloadOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
 import { SearchOutlined, ReloadOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
-import FeedbackInfoModal from '../components/FeedbackInfoModal.vue';
+import FeedbackCreateModal from '../components/FeedbackCreateModal.vue';
+import FeedbackDetailModal from '../components/FeedbackDetailModal.vue';
 import { apis } from '/@/api/custom';
 import { apis } from '/@/api/custom';
 import { Device } from '/@/types/device';
 import { Device } from '/@/types/device';
 
 
@@ -92,7 +97,8 @@ interface State {
     listLoading: boolean,
     listLoading: boolean,
     list: any[],
     list: any[],
     currentId: string,
     currentId: string,
-    visible: boolean,
+    createModalVisible: boolean,
+    detailModalVisible: boolean,
 };
 };
 
 
 const state: State = reactive({
 const state: State = reactive({
@@ -100,7 +106,8 @@ const state: State = reactive({
     listLoading: false,
     listLoading: false,
     list: [],
     list: [],
     currentId: '',
     currentId: '',
-    visible: false,
+    createModalVisible: false,
+    detailModalVisible: false,
 });
 });
 
 
 const paginationConfig = reactive({
 const paginationConfig = reactive({
@@ -218,6 +225,16 @@ const handleClickReset = async () => {
     state.query = data;
     state.query = data;
     await fetchList();
     await fetchList();
 }
 }
+
+const createModalOnClickSubmit = async () => {
+    state.createModalVisible = false;
+}
+
+// 点击详情
+const handleClickDetail = (id: string) => {
+    state.currentId = id;
+    state.detailModalVisible = true;
+}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>

+ 0 - 199
Web/src/components/devices/deviceList/components/FeedbackInfoModal.vue

@@ -1,199 +0,0 @@
-<template>
-    <a-modal :width="800" title="设备异常反馈" :maskClosable="false" :closable="false" v-model:visible="visible"
-        @cancel="onClose">
-        <a-form ref="formRef" :model="formModel" :colon="false" :label-col="labelCol" :wrapper-col="wrapperCol">
-            <a-form-item label='异常描述' name="name" :rules="[{ required: true, message: '异常描述不能为空', whitespace: true }]">
-                <a-input style="width: 100%;" v-model:value="formModel.name" placeholder="请输入异常描述" />
-            </a-form-item>
-            <a-form-item label='发生时间' name="user_name"
-                :rules="[{ required: true, message: '发生时间不能为空', whitespace: true }]">
-                <a-date-picker v-model:value="value1" />
-            </a-form-item>
-            <a-form-item label='联系电话' name="phone_number">
-                <a-input style="width: 100%;" v-model:value="formModel.phone_number" placeholder="请输入联系电话"
-                    :maxLength="11" />
-            </a-form-item>
-            <a-form-item label='联系邮箱' name="password">
-                <a-input style="width: 100%;" v-model:value="formModel.user_name" placeholder="请输入发生时间" />
-            </a-form-item>
-            <a-form-item label='附件' name="rangeDate">
-                <a-button>上传附件</a-button>
-            </a-form-item>
-        </a-form>
-    </a-modal>
-</template>
-
-<script lang="ts" setup>
-import { ref, reactive, onMounted } from 'vue';
-import { SearchOutlined, ReloadOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
-import { apis } from '/@/api/custom';
-
-interface Props {
-    currentId: string,
-    visible: boolean,
-    onClose: () => void,
-};
-
-const props = withDefaults(defineProps<Props>(), {
-
-});
-
-const formRef = ref();
-
-const formModel = reactive({
-    rangeDate: undefined,
-    device_name: undefined,
-    search_info: undefined,
-})
-
-const labelCol = { span: 4 };
-
-const wrapperCol = { span: 18 };
-
-type Query = Partial<{
-    begin_time: string,
-    end_time: string,
-    device_name: undefined,
-    search_info: undefined,
-}>;
-
-interface State {
-    query?: Query,
-    listLoading: boolean,
-    list: any[],
-    currentId: string,
-    drawerVisible: boolean,
-};
-
-const state: State = reactive({
-    query: undefined,
-    listLoading: false,
-    list: [],
-    currentId: '',
-    drawerVisible: false,
-});
-
-const paginationConfig = reactive({
-    pageSizeOptions: ['20', '50', '100'],
-    showQuickJumper: true,
-    showSizeChanger: true,
-    pageSize: 20,
-    current: 1,
-    total: 0,
-    onChange: async (current: number) => {
-        paginationConfig.current = current;
-        await fetchList();
-    },
-    onShowSizeChange: async (current: number, pageSize: number) => {
-        paginationConfig.pageSize = pageSize;
-        await fetchList();
-    }
-})
-
-const fetchList = async () => {
-    state.listLoading = true;
-    try {
-        const res = await apis.fetchChangeRecordList({
-            ...state.query,
-            page: paginationConfig.current,
-            page_size: paginationConfig.pageSize
-        });
-        if (res.code === 0) {
-            paginationConfig.total = res.data.pagination.total
-            paginationConfig.current = res.data.pagination.page
-            paginationConfig.pageSize = res.data.pagination.page_size
-        }
-        state.list = res.data.list;
-    } catch (e) {
-        console.error(e);
-    } finally {
-        state.listLoading = false;
-    }
-}
-
-onMounted(async () => {
-    await fetchList();
-})
-
-const columns = [
-    {
-        title: '反馈时间',
-        dataIndex: 'create_time',
-        width: 200,
-        sorter: (a: any, b: any) => a.create_time.localeCompare(b.create_time),
-    },
-    {
-        title: '反馈人',
-        dataIndex: 'username',
-        width: 150,
-    },
-    {
-        title: '设备型号',
-        dataIndex: 'device_name',
-        width: 150,
-    },
-    {
-        title: '设备SN',
-        dataIndex: 'device_sn',
-        width: 250,
-        slots: { customRender: 'device_sn' }
-    },
-    {
-        title: '设备名称',
-        dataIndex: 'nick_name',
-        width: 150,
-        slots: { customRender: 'nick_name' },
-    },
-    {
-        title: '设备异常描述',
-        dataIndex: 'log_info',
-        width: 150,
-        ellipsis: true,
-    },
-    {
-        title: '上传状态',
-        dataIndex: 'status',
-        slots: { customRender: 'status' },
-        sorter: (a: any, b: any) => a.status.localeCompare(b.status),
-        width: 150,
-    },
-    {
-        title: '操作',
-        dataIndex: 'operation',
-        fixed: 'right',
-        width: 80,
-        slots: { customRender: 'operation' }
-    },
-]
-
-// 点击查询
-const handleClicSekarch = async () => {
-    const values = formRef.value?.getFieldsValue();
-    const data = { ...values };
-    delete data.rangeDate;
-    if (values.rangeDate.length === 2) {
-        data.begin_time = values.rangeDate[0];
-        data.end_time = values.rangeDate[1];
-    }
-    state.query = data;
-    await fetchList();
-}
-
-// 点击重置
-const handleClickReset = async () => {
-    formRef.value?.resetFields();
-    const values = formRef.value?.getFieldsValue();
-    const data = { ...values };
-    delete data.rangeDate;
-    state.query = data;
-    await fetchList();
-}
-</script>
-
-<style lang="scss" scoped>
-.top {
-    display: flex;
-    justify-content: space-between;
-    margin-bottom: 20px;
-}
-</style>

+ 1 - 1
Web/src/components/devices/deviceList/index.vue

@@ -185,7 +185,7 @@ const autoUpdateDeviceStatus = async () => {
 
 
 // 开始定时器
 // 开始定时器
 const startAutoUpdate = () => {
 const startAutoUpdate = () => {
-  state.interval = window.setInterval(autoUpdateDeviceStatus, 1000);
+  state.interval = window.setInterval(autoUpdateDeviceStatus, 10000);
 };
 };
 
 
 // 清除定时器
 // 清除定时器