Browse Source

优化意见反馈监控接口错误并显示到表单

wangzhiqiang 3 months ago
parent
commit
2a81cb5c3f

+ 7 - 1
src/hooks/useErrorTrigger.js

@@ -2,7 +2,13 @@ import { ref } from 'vue'
 
 export const errorTooltipVisible = ref(false)
 
-export function triggerTooltip() {
+export const errorTitle = ref('')
+
+export const errorContent = ref('')
+
+export function triggerTooltip(title, content) {
+    errorTitle.value = title
+    errorContent.value = content
     errorTooltipVisible.value = true
     setTimeout(() => {
         errorTooltipVisible.value = false

+ 20 - 7
src/layout/Feedback/index.vue

@@ -14,7 +14,7 @@
                     <el-input v-model="formEdit.title" placeholder="请输入反馈标题" clearable />
                 </el-form-item>
                 <el-form-item label="反馈内容" required prop="content">
-                    <el-input type="textarea" rows="5" v-model="formEdit.content" placeholder="请输入反馈内容" clearable />
+                    <el-input type="textarea" rows="13" v-model="formEdit.content" placeholder="请输入反馈内容" clearable />
                 </el-form-item>
                 <el-form-item label="反馈截图" prop="image">
                     <el-upload v-model:file-list="formEdit.imageList" action="#" list-type="picture-card"
@@ -51,11 +51,11 @@
 </template>
 
 <script setup>
-import { ref, reactive } from 'vue'
+import { ref, reactive, watch } from 'vue'
 import Layer from "@/components/layer/index.vue";
 import { useOssloader } from '@/hooks/useOssloader.js'
 import { Plus, ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue";
-import { errorTooltipVisible } from '@/hooks/useErrorTrigger'
+import { errorTooltipVisible, errorTitle, errorContent } from '@/hooks/useErrorTrigger'
 import { workorderCreate } from '@/api/common.js'
 import { ElMessage } from 'element-plus'
 
@@ -79,10 +79,7 @@ const formEdit = ref({
     attachList: undefined,
 })
 
-const openFeedback = () => {
-    ruleForm.value?.resetFields()
-    layer.value.show = !layer.value.show
-
+const resetForm = () => {
     // 重置内容
     formEdit.value = {
         title: '',//工单标题
@@ -92,6 +89,11 @@ const openFeedback = () => {
     }
 }
 
+const openFeedback = () => {
+    ruleForm.value?.resetFields()
+    layer.value.show = !layer.value.show
+}
+
 const rules = reactive({
     title: [
         { required: true, message: "请输入反馈标题", trigger: "blur" },
@@ -165,6 +167,17 @@ const nextImg = () => {
     dialogImageUrl.value = formEdit.value.imageList[currentPreviewIndex.value].url
 }
 
+watch(() => layer.value.show, (newVal) => {
+    console.log('newVal :===>>', newVal);
+    if (newVal) {
+         formEdit.value.title = errorTitle.value
+         formEdit.value.content = errorContent.value
+    } else {
+        resetForm()
+    }
+}, { deep: true })
+
+
 </script>
 
 <style lang='scss' scoped>

+ 1 - 1
src/layout/Header/index.vue

@@ -25,7 +25,7 @@
           <span class="el-dropdown-link" style="display: flex;align-items: center; min-width: 90px;justify-content: space-between;">
             <el-avatar :size="40" :src="userInfo.headImage" />
             <div style="margin: 0 5px;">
-              {{userInfo.loginName}}
+              {{userInfo.nickName}}
               <i class="sfont system-xiala"></i>
             </div>
           </span>

+ 92 - 2
src/utils/index.js

@@ -3,9 +3,10 @@
 /**
  * UTC时间字符串转为北京时间
  * @param {*} utcStr 2025-05-15T02:16:27.000+00:00
+ * @param {*} type 是否返回后缀时分秒,默认true返回
  * @returns 2025-05-15 10:16:27
  */
-export function convertUTCToBeijing(utcStr) {
+export function convertUTCToBeijing(utcStr, type = true) {
     if (utcStr) {
         const date = new Date(utcStr);
 
@@ -20,12 +21,63 @@ export function convertUTCToBeijing(utcStr) {
         const mm = pad(date.getMinutes());
         const ss = pad(date.getSeconds());
 
-        return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
+        const time = `${y}-${m}-${d} ${hh}:${mm}:${ss}`
+        const dateday = `${y}-${m}-${d}`
+
+        return type ? time : dateday;
+
     } else {
         return utcStr
     }
 }
 
+/**
+ * 获取当前时间或今日 0 点时间(本地)
+ * @param {'date' | 'ISO' | 'zone' | 'now' | 'utc'} type 返回格式类型
+ * - date: 今日日期(默认)
+ * - ISO: 今日 0 点 ISO 字符串,含时区偏移(默认)
+ * - zone: 今日 0 点字符串,格式为 "YYYY-MM-DD 00:00:00"
+ * - now: 当前时间字符串 "YYYY-MM-DD HH:mm:ss"
+ * - utc: 当前 UTC ISO 格式(new Date().toISOString())
+ * @returns string
+ */
+export function getTodayRangeLocal(type) {
+    const now = new Date()
+    const start = new Date(now.getFullYear(), now.getMonth(), now.getDate())
+
+    const offset = -start.getTimezoneOffset()
+    const sign = offset >= 0 ? '+' : '-'
+    const pad = (n) => String(Math.floor(Math.abs(n))).padStart(2, '0')
+    const timezone = `${sign}${pad(offset / 60)}:${pad(offset % 60)}`
+
+    const yyyy = start.getFullYear()
+    const mm = String(start.getMonth() + 1).padStart(2, '0')
+    const dd = String(start.getDate()).padStart(2, '0')
+    const hh = String(now.getHours()).padStart(2, '0')
+    const mi = String(now.getMinutes()).padStart(2, '0')
+    const ss = String(now.getSeconds()).padStart(2, '0')
+
+    const date = `${yyyy}-${mm}-${dd}` //当前日期
+    const timeISOZone = `${yyyy}-${mm}-${dd}T00:00:00${timezone}`  // 今日 0 点带时区
+    const timeZone = `${yyyy}-${mm}-${dd} 00:00:00`                 // 今日 0 点普通格式
+    const timeNow = `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`        // 当前时间
+    const ISO = now.toISOString()                                   // 当前 UTC 时间
+
+    switch (type) {
+        case 'zone':
+            return timeZone
+        case 'now':
+            return timeNow
+        case 'utc':
+            return ISO
+        case 'ISO':
+            return timeISOZone
+        default:
+            return date
+    }
+}
+
+
 /**
  * 转成小写字符
  * userStatus 转成 user_status
@@ -65,4 +117,42 @@ export function roundPrice(value, place = 2, type = false) {
     const num = Number(value)
     if (isNaN(num)) return (0).toFixed(place)
     return type ? Number(num.toFixed(place)) : num.toFixed(place)
+}
+
+/**
+ * 获取设置信息
+ * @returns 
+ */
+export function getUserEnvInfo() {
+    const ua = navigator.userAgent
+
+    // 操作系统判断
+    let os = 'Unknown'
+    if (/Windows/i.test(ua)) os = 'Windows'
+    else if (/Mac OS/i.test(ua)) os = 'macOS'
+    else if (/Android/i.test(ua)) os = 'Android'
+    else if (/iPhone|iPad|iPod/i.test(ua)) os = 'iOS'
+    else if (/Linux/i.test(ua)) os = 'Linux'
+
+    // 浏览器判断
+    let browser = 'Unknown'
+    if (/Edg\//i.test(ua)) browser = 'Edge'
+    else if (/Chrome/i.test(ua)) browser = 'Chrome'
+    else if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) browser = 'Safari'
+    else if (/Firefox/i.test(ua)) browser = 'Firefox'
+    else if (/MSIE|Trident/i.test(ua)) browser = 'IE'
+
+    // 网络类型(部分浏览器支持)
+    const connection = navigator.connection || navigator.webkitConnection || navigator.mozConnection
+    const networkType = connection?.effectiveType || 'Unknown'
+
+    // 是否移动端
+    const isMobile = /Android|iPhone|iPad|iPod/i.test(ua)
+
+    return {
+        os,
+        browser,
+        networkType,
+        isMobile
+    }
 }

+ 27 - 1
src/utils/system/request.js

@@ -2,6 +2,7 @@ import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } f
 import store from '@/store'
 import { ElMessage } from 'element-plus'
 import { triggerTooltip } from '@/hooks/useErrorTrigger'
+import { getUserEnvInfo, getTodayRangeLocal } from '@/utils'
 
 const baseURL = import.meta.env.VITE_BASE_URL
 
@@ -53,8 +54,33 @@ service.interceptors.response.use(
   (error) => {
     console.log(error)
 
+    const { config, response } = error || {}
+    const { url, method, data, params } = config || {}
+    const status = response?.status
+    const statusText = response?.statusText
+    const responseData = response?.data
+
+    const { os, browser, networkType, isMobile } = getUserEnvInfo()
+
+    let errorTitle = `请求错误接口: ${url}`
+
+    let errorTemplate = `
+      请求失败:
+      接口地址: ${method?.toUpperCase()} ${url}
+      请求参数: ${params}
+      请求体: ${data}
+      状态码: ${status} ${statusText || ''}
+      返回内容: ${responseData || error.message}
+      操作系统: ${os}
+      浏览器: ${browser}
+      网络类型: ${networkType}
+      是否移动设备: ${isMobile}
+      时间: ${getTodayRangeLocal('now')}
+    `
+    console.log('请求失败 :===>>', errorTemplate);
+
     // 触发 tooltip 展示
-    triggerTooltip()
+    triggerTooltip(errorTitle, errorTemplate)
 
     const badMessage = error.message || error
     const code = parseInt(badMessage.toString().replace('Error: Request failed with status code ', ''))