Browse Source

设备异常反馈

李富豪 1 year ago
parent
commit
102f508048

+ 54 - 42
Web/src/components/devices/deviceList/components/FeedbackDrawer.vue

@@ -1,16 +1,17 @@
 <template>
     <a-drawer width="70%" title="设备异常反馈记录" :visible="visible" @close="onClose">
         <div class="top">
-            <a-button type="primary">
+            <a-button type="primary" @click="() => state.visible = true">
                 新建异常反馈
             </a-button>
             <div>
-                <a-form ref="formRef" layout="inline" :model="state.query" :colon="false">
+                <a-form ref="formRef" layout="inline" :model="formModel" :colon="false">
                     <a-form-item name="rangeDate">
-                        <a-range-picker style="width: 250px;" v-model:value="state.query.rangeDate" />
+                        <a-range-picker style="width: 250px;" valueFormat="YYYY-MM-DD"
+                            v-model:value="formModel.rangeDate" />
                     </a-form-item>
                     <a-form-item name="device_name">
-                        <a-select style="width: 200px;" placeholder="请选择反馈人" v-model:value="state.query.device_name">
+                        <a-select style="width: 200px;" placeholder="请选择反馈人" v-model:value="formModel.device_name">
                             <a-select-option value="1">
                                 pilot
                             </a-select-option>
@@ -18,7 +19,7 @@
                     </a-form-item>
                     <a-form-item name="search_info">
                         <a-input style="width: 200px;" placeholder="设备SN、设备异常描述"
-                            v-model:value="state.query.search_info" />
+                            v-model:value="formModel.search_info" />
                     </a-form-item>
                     <a-form-item>
                         <a-button style="margin-right: 10px;" @click="handleClicSekarch">
@@ -43,25 +44,21 @@
                 </template>
                 <!-- 操作 -->
                 <template #operation="{ record }">
-                    <div class="editable-row-operations">
-                        <div class="flex-align-center flex-row" style="color: #2d8cf0">
-                            <a-tooltip title="详情">
-                                <FileSearchOutlined style="margin-right: 10px;" @click="onClickDetail(record.id)" />
-                            </a-tooltip>
-                            <a-tooltip title="删除">
-                                <DeleteOutlined @click="onClickDelete(record.id)" />
-                            </a-tooltip>
-                        </div>
-                    </div>
+                    <a-tooltip title="详情">
+                        <FileSearchOutlined />
+                    </a-tooltip>
                 </template>
             </a-table>
         </div>
     </a-drawer>
+    <!-- 异常反馈-信息弹出层 -->
+    <FeedbackInfoModal :currentId="state.currentId" :visible="state.visible" :onClose="() => state.visible = false" />
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted } from 'vue';
-import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue';
+import { SearchOutlined, ReloadOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
+import FeedbackInfoModal from '../components/FeedbackInfoModal.vue';
 import { apis } from '/@/api/custom';
 import { Device } from '/@/types/device';
 
@@ -77,24 +74,33 @@ const props = withDefaults(defineProps<Props>(), {
 
 const formRef = ref();
 
+const formModel = reactive({
+    rangeDate: undefined,
+    device_name: undefined,
+    search_info: undefined,
+})
+
+type Query = Partial<{
+    begin_time: string,
+    end_time: string,
+    device_name: undefined,
+    search_info: undefined,
+}>;
+
 interface State {
-    query: any,
+    query?: Query,
     listLoading: boolean,
     list: any[],
     currentId: string,
-    drawerVisible: boolean,
+    visible: boolean,
 };
 
 const state: State = reactive({
-    query: {
-        rangeDate: [],
-        device_name: undefined,
-        search_info: '',
-    },
+    query: undefined,
     listLoading: false,
     list: [],
     currentId: '',
-    drawerVisible: false,
+    visible: false,
 });
 
 const paginationConfig = reactive({
@@ -103,7 +109,15 @@ const paginationConfig = reactive({
     showSizeChanger: true,
     pageSize: 20,
     current: 1,
-    total: 0
+    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 () => {
@@ -173,38 +187,36 @@ const columns = [
         sorter: (a: any, b: any) => a.status.localeCompare(b.status),
         width: 150,
     },
-    // {
-    //     title: '操作',
-    //     dataIndex: 'operation',
-    //     fixed: 'right',
-    //     width: 80,
-    //     slots: { customRender: 'operation' }
-    // },
+    {
+        title: '操作',
+        dataIndex: 'operation',
+        fixed: 'right',
+        width: 80,
+        slots: { customRender: 'operation' }
+    },
 ]
 
 // 点击查询
 const handleClicSekarch = async () => {
     const values = formRef.value?.getFieldsValue();
-    const data = {
-        ...values,
-    };
+    const data = { ...values };
     delete data.rangeDate;
     if (values.rangeDate.length === 2) {
-        data.begin_time = moment(values.rangeDate[0]).valueOf();
-        data.end_time = moment(values.rangeDate[1]).valueOf();
+        data.begin_time = values.rangeDate[0];
+        data.end_time = values.rangeDate[1];
     }
-    await props.onClickSearch(data);
+    state.query = data;
+    await fetchList();
 }
 
 // 点击重置
 const handleClickReset = async () => {
     formRef.value?.resetFields();
     const values = formRef.value?.getFieldsValue();
-    const data = {
-        ...values,
-    };
+    const data = { ...values };
     delete data.rangeDate;
-    await props.onClickReset(data);
+    state.query = data;
+    await fetchList();
 }
 </script>
 

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

@@ -0,0 +1,199 @@
+<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>

+ 18 - 11
Web/src/components/devices/deviceList/index.vue

@@ -51,21 +51,28 @@
           </div>
           <!-- 非编辑态操作 -->
           <div v-else class="flex-align-center flex-row" style="color: #2d8cf0">
-            <a-tooltip title="异常反馈" v-if="record.domain === 3">
-              <CloudServerOutlined style="margin-right: 10px;" @click="onClickFeedback(record)" />
-            </a-tooltip>
-            <a-tooltip title="告警信息" v-if="record.domain === 3">
-              <FileSearchOutlined style="margin-right: 10px;" @click="onClickDeviceHms(record)" />
-            </a-tooltip>
-            <a-tooltip title="设备运维" v-if="record.domain === 3">
-              <SettingOutlined style="margin-right: 10px;" />
-            </a-tooltip>
             <a-tooltip title="编辑">
               <EditOutlined style="margin-right: 10px;" @click="onClickEdit(record)" />
             </a-tooltip>
             <a-tooltip title="删除">
               <DeleteOutlined @click="onClickDelete(record)" />
             </a-tooltip>
+            <a-dropdown v-if="record.domain === 3">
+              <EllipsisOutlined style="font-size: 20px;margin-left: 10px;" />
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item @click="onClickFeedback(record)">
+                    异常反馈
+                  </a-menu-item>
+                  <a-menu-item @click="onClickDeviceHms(record)">
+                    告警信息
+                  </a-menu-item>
+                  <a-menu-item>
+                    设备运维
+                  </a-menu-item>
+                </a-menu>
+              </template>
+            </a-dropdown>
           </div>
         </template>
       </a-table>
@@ -81,7 +88,7 @@
 <script lang="ts" setup>
 import { reactive, onMounted, onUnmounted } from 'vue';
 import { Modal } from 'ant-design-vue';
-import { EditOutlined, CheckOutlined, CloseOutlined, SettingOutlined, DeleteOutlined, FileSearchOutlined, CloudServerOutlined } from '@ant-design/icons-vue';
+import { EditOutlined, CheckOutlined, CloseOutlined, DeleteOutlined, EllipsisOutlined } from '@ant-design/icons-vue';
 import Search from './components/Search.vue';
 import CustomCell from './components/CustomCell.vue';
 import DeviceFirmwareUpgrade from '/@/components/devices/device-upgrade/DeviceFirmwareUpgrade.vue';
@@ -261,7 +268,7 @@ const columns = [
     title: '操作',
     dataIndex: 'actions',
     fixed: 'right',
-    width: 140,
+    width: 100,
     slots: { customRender: 'action' },
   },
 ]