瀏覽代碼

数据对接

zhangchong 1 年之前
父節點
當前提交
ebd94c7684
共有 7 個文件被更改,包括 367 次插入118 次删除
  1. 1 0
      src/App.vue
  2. 125 0
      src/utils/formatTime.ts
  3. 174 0
      src/utils/tools.ts
  4. 17 38
      src/views/index/left-bottom.vue
  5. 15 25
      src/views/index/left-top.vue
  6. 33 53
      src/views/index/right-bottom.vue
  7. 2 2
      src/views/index/right-top.vue

+ 1 - 0
src/App.vue

@@ -21,6 +21,7 @@ import signalR from "@/utils/signalR";
 import { RouterView } from "vue-router";
 //  signalR 初始化signalr
 signalR.init();
+signalR.joinGroup("BigScreen-DataShow");
 
 // 获取全局组件大小
 const getGlobalComponentSize = computed(() => {

+ 125 - 0
src/utils/formatTime.ts

@@ -0,0 +1,125 @@
+/**
+ * @description 时间日期转换
+ * @param date 当前时间,new Date() 格式
+ * @param format 需要转换的时间格式字符串
+ * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd`
+ * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ"
+ * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW"
+ * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ"
+ * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
+ * @returns {string} 返回拼接后的时间字符串
+ */
+export function formatDate(date: any, format: string): string {
+	if(!date) return '';
+	date = new Date(date)
+	let we = date.getDay(); // 星期
+	let z = getWeek(date); // 周
+	let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
+	const opt: { [key: string]: string } = {
+		'Y+': date.getFullYear().toString(), // 年
+		'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
+		'd+': date.getDate().toString(), // 日
+		'H+': date.getHours().toString(), // 时	
+		'M+': date.getMinutes().toString(), // 分
+		'S+': date.getSeconds().toString(), // 秒
+		'q+': qut, // 季度
+	};
+	// 中文数字 (星期)
+	const week: { [key: string]: string } = {
+		'0': '日',
+		'1': '一',
+		'2': '二',
+		'3': '三',
+		'4': '四',
+		'5': '五',
+		'6': '六',
+	};
+	// 中文数字(季度)
+	const quarter: { [key: string]: string } = {
+		'1': '一',
+		'2': '二',
+		'3': '三',
+		'4': '四',
+	};
+	if (/(W+)/.test(format))
+		format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]);
+	if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]);
+	if (/(Z+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '');
+	for (let k in opt) {
+		let r = new RegExp('(' + k + ')').exec(format);
+		// 若输入的长度不为1,则前面补零
+		if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
+	}
+	return format;
+}
+
+/**
+ * @description 获取当前日期是第几周
+ * @param dateTime 当前传入的日期值
+ * @returns {number} 返回第几周数字值
+ */
+export function getWeek(dateTime: Date): number {
+	let temptTime = new Date(dateTime.getTime());
+	// 周几
+	let weekday = temptTime.getDay() || 7;
+	// 周1+5天=周六
+	temptTime.setDate(temptTime.getDate() - weekday + 1 + 5);
+	let firstDay = new Date(temptTime.getFullYear(), 0, 1);
+	let dayOfWeek = firstDay.getDay();
+	let spendDay = 1;
+	if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1;
+	firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay);
+	let d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000);
+	return Math.ceil(d / 7);
+}
+
+/**
+ * @description 时间问候语
+ * @param param 当前时间,new Date() 格式
+ * @description param 调用 `formatAxis(new Date())` 输出 `上午好`
+ * @returns {String} 返回拼接后的时间字符串
+ */
+export function formatAxis(param: Date): string {
+	let hour: number = new Date(param).getHours();
+	// if (hour < 6) return '凌晨好';
+	if (hour < 9) return '早上好';
+	else if (hour < 12) return '上午好';
+	// else if (hour < 14) return '中午好';
+	else if (hour < 18) return '下午好';
+	// else if (hour < 19) return '傍晚好';
+	else if (hour < 24) return '晚上好';
+	else return '夜里好';
+}
+/**
+ * @description 秒转换成xx:xx:xx格式
+ * @param time 传入秒
+ * @param showHour 是否展示小时 默认是
+ * @description param 调用 `formatDuration(秒) 转换成xx:xx:xx的格式
+ * @returns {string} 输出 即xx:xx:xx的格式;
+ */
+export function formatDuration(time:any,showHour:boolean = true){
+	if(!time) return '';
+	if(time > -1){
+		const hour = Math.floor(time / 3600);
+		const min = Math.floor(time / 60) % 60;
+		const sec = time % 60;
+		if(showHour){ // 是否要展示小时
+			if(hour < 10) {
+				time = '0'+ hour + ":";
+			} else {
+				time = hour + ":";
+			}
+		}else{
+			time = '';
+		}
+		if(min < 10){
+			time += "0";
+		}
+		time += min + ":";
+		if(sec < 10){
+			time += "0";
+		}
+		time += sec;
+	}
+	return time;
+}

+ 174 - 0
src/utils/tools.ts

@@ -0,0 +1,174 @@
+import axios from "axios";
+/**
+ * @description 防抖
+ * @param func    功能函数(即要防抖的函数)
+ * @param delay   延时时间
+ * @param thisArg 功能函数内this的对象
+ */
+export function debounce(func: Function, delay: number, thisArg?: any) {
+	let timer: number;
+	return function (...args: any[]) {
+		if (timer) {
+			window.clearTimeout(timer);
+		}
+		timer = window.setTimeout(function () {
+			func.apply(thisArg, args);
+		}, delay);
+	};
+}
+/**
+ * @description 节流(原理同上)
+ * @param func    功能函数(即要节流的函数)
+ * @param delay   延时时间
+ * @param thisArg 功能函数内this的对象
+ */
+export function throttle(func: Function, delay: number, thisArg?: any) {
+	let timeStamp: number;
+	return function (...args: any[]) {
+		const nowTimeStamp = Date.now();
+		if (!timeStamp || nowTimeStamp - timeStamp >= delay) {
+			func.apply(thisArg, args);
+			timeStamp = nowTimeStamp;
+		}
+	};
+}
+/**
+ *  @description vite引入图片
+ * @param {string} name   ../assets/images 后面的文件路径+文件名后缀
+ * @returns {string} 返回处理后的文件地址
+ */
+export function getImageUrl(name: string) {
+	return new URL(`../assets/images/${name}`, import.meta.url).href;
+}
+/**
+ * 手机号脱敏处理
+ * @param  { string} phoneNumber  手机号码
+ * @returns {string} 返回处理后的手机号码
+ */
+export function desensitizationPhone(phoneNumber: string) {
+	if (!phoneNumber) return '';
+	return phoneNumber.replace(/^(.{3})(\d+)(.{4})$/, '$1****$2');
+}
+/**
+ * 姓名脱敏处理
+ * @param {string} name  姓名
+ * @returns {string} 返回处理后的姓名
+ */
+export function desensitizationName(name: string) {
+	if (!name) return '';
+	const len = name.length;
+	return len <= 3
+		? '*' + name.substring(1, len)
+		: len > 3 && len <= 6
+		? '**' + name.substring(2, len)
+		: len > 6
+		? name.substring(0, 2) + '****' + name.substring(6, len)
+		: '';
+}
+/**
+ * 检查文件类型
+ * @param  {string} fileValue  姓名
+ * @returns {string} 返回处理后的姓名
+ */
+export function checkFile(fileValue: string): string {
+	let index = fileValue.lastIndexOf('.'); //(考虑严谨用lastIndexOf(".")得到)得到"."在第几位
+	let fileValueSuffix = fileValue.substring(index); //截断"."之前的,得到后缀
+	if (/(.*)\.(mp4|avi|wmv|MP4|AVI|WMV)$/.test(fileValueSuffix)) {
+		//根据后缀,判断是否符合视频格式
+		return 'video';
+	} else if (/(.*)\.(jpg|JPG|bmp|BMP|mpg|MPG|mpeg|MPEG|tis|TIS|svg|png|jpeg|webp)$/.test(fileValueSuffix)) {
+		//根据后缀,判断是否符合图片格式
+		return 'image';
+	} else if (/(.*)\.(xls|XLS|xlsx|XLSX)$/.test(fileValueSuffix)) {
+		//根据后缀,判断是否符合OFFICE格式
+		return 'xls';
+	} else if (/(.*)\.(doc|DOC|docx|DOCX)$/.test(fileValueSuffix)) {
+		// 文档类型
+		return 'doc';
+	} else if (/(.*)\.(pdf|PDF)$/.test(fileValueSuffix)) {
+		// PDF
+		return 'pdf';
+	} else if (/(.*)\.(PPT|PPTX|ppt|pptx)$/.test(fileValueSuffix)) {
+		// ppt
+		return 'ppt';
+	}
+	return 'file';
+}
+/**
+ * 传入类型返回对应的文件类型图标
+ * @param  {string} file  文件名
+ * @returns {string} 返回处理后的图标
+ */
+export function fileType(file: string): string {
+	switch (file) {
+		case 'video':
+			return 'ele-VideoCamera';
+		case 'image':
+			return 'ele-Picture';
+		case 'xls':
+			return 'iconfont icon-excel';
+		case 'doc':
+			return 'ele-Document';
+		case 'pdf':
+			return 'iconfont icon-pdf';
+		case 'ppt':
+			return 'iconfont icon-ppt';
+		default:
+			return 'ele-Document';
+	}
+}
+/**
+ * @description 生成guid
+ * @returns {string} 返回guid
+ */
+export function guid(): string {
+	const S4 = (): string => {
+		return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+	};
+	return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
+}
+/**
+ * 根据id排除自己(树形结构选择 修改时用)
+ * @param {any} arr 需要排除的数组
+ * @param {string} id 当前id
+ * @returns {arr} 返回排除后的数组
+ */
+export function excludeSelfById(arr: Array<any>, id: string) {
+	if (!arr) return [];
+	return arr.filter((v: any) => {
+		if (v.id === id) return false;
+		if (v.children && v.children.length) {
+			v.children = excludeSelfById(v.children, id);
+		}
+		return true;
+	});
+}
+/**
+ * @description 下载文件
+ * @param src 文件地址
+ * @param filename 文件名
+ */
+export function downloadFile(src: string, filename: string) {
+	if(!src) {
+		return
+	}
+	let fileName: string = filename || '' // 文件名
+	axios({
+		method: 'get',
+		url: src,
+		responseType: 'blob',
+		headers: { 'content-type': 'audio/mpeg' },
+	}).then((res: any) => {
+		let blob:Blob = new Blob([res.data], { type: res.data.type }) // 创建blob 设置blob文件类型 data 设置为后端返回的文件(例如mp3,jpeg) type:这里设置后端返回的类型 为 mp3
+		let down: HTMLAnchorElement = document.createElement('a') // 创建A标签
+		let href:string = window.URL.createObjectURL(blob) // 创建下载的链接
+		down.href = href // 下载地址
+		down.download = fileName // 下载文件名
+		document.body.appendChild(down)
+		down.click() // 模拟点击A标签
+		document.body.removeChild(down) // 下载完成移除元素
+		window.URL.revokeObjectURL(href) // 释放blob对象
+	}).catch(function (error) {
+		console.log(error)
+	})
+}

+ 17 - 38
src/views/index/left-bottom.vue

@@ -11,22 +11,22 @@
       :singleWaitTime="state.defaultOption.singleWaitTime"
       :wheel="state.defaultOption.wheel"
     >
-      <ul class="left_boottom">
+      <ul class="left_boottom" v-if="state.list.length">
         <li class="left_boottom_item" v-for="(item, i) in state.list" :key="i">
           <span class="orderNum doudong">{{ i + 1 }}</span>
           <div class="inner_right">
             <div class="dibu"></div>
             <div class="flex">
               <div class="info">
-                <span class="labels">设备ID:</span>
+                <span class="labels">来电:</span>
                 <span class="text-content zhuyao doudong wangguan">
-                  {{ item.gatewayno }}</span
+                  {{ item.from }}</span
                 >
               </div>
               <div class="info">
-                <span class="labels">时间:</span>
+                <span class="labels">坐席:</span>
                 <span class="text-content" style="font-size: 12px">
-                  {{ item.createTime }}</span
+                  {{ item.to }}</span
                 >
               </div>
             </div>
@@ -35,31 +35,32 @@
               class="types doudong"
               :class="{
                 typeRed: item.onlineState == 0,
-                typeGreen: item.onlineState == 1,
+                typeGreen: item.creationTime,
               }"
-              >{{ item.onlineState == 1 ? "上线" : "下线" }}</span
+              >{{formatDate(item.creationTime, 'YYYY-mm-dd HH:MM:SS')}}</span
             >
 
             <div class="info addresswrap">
-              <span class="labels">地址:</span>
+              <span class="labels">通话时长:</span>
               <span class="text-content ciyao" style="font-size: 12px">
-                {{ addressHandle(item) }}</span
+                {{ item.duration }} 秒</span
               >
             </div>
           </div>
         </li>
       </ul>
     </component>
+    <EmptyCom>暂无数据</EmptyCom>
   </div>
 </template>
 <script setup lang="ts">
-import { currentGET } from "@/api";
 import SeamlessScroll from "@/components/seamless-scroll";
 import { computed, onMounted, reactive } from "vue";
 import { useSettingStore } from "@/stores/setting";
 import { storeToRefs } from "pinia";
 import EmptyCom from "@/components/empty-com";
 import signalR from "@/utils/signalR";
+import {formatDate,formatDuration} from "@/utils/formatTime";
 const settingStore = useSettingStore();
 const { defaultOption,indexConfig } = storeToRefs(settingStore);
 const state = reactive<any>({
@@ -72,29 +73,6 @@ const state = reactive<any>({
   scroll: true,
 });
 
-const getData = () => {
-  currentGET("leftBottom", { limitNum: 20 }).then((res) => {
-    console.log("来电实况", res);
-    if (res.success) {
-      state.list = res.data.list;
-    } else {
-      window.$message({
-        text: res.msg,
-        type: "warning",
-      });
-    }
-  });
-};
-const addressHandle = (item: any) => {
-  let name = item.provinceName;
-  if (item.cityName) {
-    name += "/" + item.cityName;
-    if (item.countyName) {
-      name += "/" + item.countyName;
-    }
-  }
-  return name;
-};
 const comName = computed(()=>{
     if(indexConfig.value.leftBottomSwiper){
         return SeamlessScroll
@@ -103,8 +81,10 @@ const comName = computed(()=>{
     }
 })
 onMounted(() => {
-  getData();
-  signalR.joinGroup("BsDataShowArea3");
+  signalR.SR.on("BsDataShowArea3", (res: any) => {
+    console.log('来点实况',res)
+    state.list = res;
+  });
 });
 </script>
 <style scoped lang="scss">
@@ -168,7 +148,7 @@ onMounted(() => {
     .inner_right {
       position: relative;
       height: 100%;
-      width: 380px;
+      width: 420px;
       flex-shrink: 0;
       line-height: 1;
       display: flex;
@@ -195,7 +175,7 @@ onMounted(() => {
       color: #1890ff;
       font-weight: 900;
       font-size: 15px;
-      width: 80px;
+      width: 90px;
       flex-shrink: 0;
     }
 
@@ -212,7 +192,6 @@ onMounted(() => {
     }
 
     .types {
-      width: 30px;
       flex-shrink: 0;
     }
 

+ 15 - 25
src/views/index/left-top.vue

@@ -2,57 +2,47 @@
   <ul class="user_Overview flex">
     <li class="user_Overview-item" style="color: #00fdfa">
       <div class="user_Overview_nums allnum">
-        <CountUp :endVal="state.totalNum" :duration="duration" />
+        <CountUp :endVal="state.monthTotal" :duration="duration" />
       </div>
       <p>月工单量</p>
     </li>
     <li class="user_Overview-item" style="color: #07f7a8">
       <div class="user_Overview_nums online">
-        <CountUp :endVal="state.onlineNum" :duration="duration" />
+        <CountUp :endVal="state.monthCompleted" :duration="duration" />
       </div>
       <p>月办结量</p>
     </li>
     <li class="user_Overview-item" style="color: #00fdfa">
       <div class="user_Overview_nums offline">
-        <CountUp :endVal="state.offlineNum" :duration="duration" />
+        <CountUp :endVal="state.todayTotal" :duration="duration" />
       </div>
       <p>日工单里</p>
     </li>
     <li class="user_Overview-item" style="color: #07f7a8">
       <div class="user_Overview_nums laramnum">
-        <CountUp :endVal="state.alarmNum" :duration="duration" />
+        <CountUp :endVal="state.todayCompleted" :duration="duration" />
       </div>
       <p>日办结量</p>
     </li>
   </ul>
 </template>
 <script setup lang="ts">
-import { reactive, ref,onMounted } from "vue";
-import { currentGET } from "@/api";
+import { reactive, ref } from "vue";
 import CountUp from "@/components/count-up";
 import signalR from "@/utils/signalR";
 const duration = ref(2);
 const state = reactive({
-  alarmNum: 759,
-  offlineNum: 44,
-  onlineNum: 654,
-  totalNum: 698,
+  monthTotal: 0,
+  monthCompleted: 0,
+  todayTotal: 0,
+  todayCompleted: 0,
 });
-
-const getData = () => {
-  currentGET("leftTop").then((res) => {
-    console.log('工单办理日',res);
-    if (res.success) {
-      state.alarmNum = res.data.alarmNum;
-      state.offlineNum = res.data.offlineNum;
-      state.onlineNum = res.data.onlineNum;
-      state.totalNum = res.data.totalNum;
-    }
-  });
-};
-onMounted(() => {
-  getData();
-  signalR.joinGroup("BsDataShowArea1");
+signalR.SR.on("BsDataShowArea1", (res: any) => {
+  console.log("工单概览", res)
+  state.monthTotal = res.monthTotal;
+  state.monthCompleted = res.monthCompleted;
+  state.todayTotal = res.todayTotal;
+  state.todayCompleted = res.todayCompleted;
 });
 </script>
 <style scoped lang="scss">

+ 33 - 53
src/views/index/right-bottom.vue

@@ -14,71 +14,68 @@
       :singleWaitTime="state.defaultOption.singleWaitTime"
       :wheel="state.defaultOption.wheel"
     >
-      <ul class="right_bottom">
+      <ul class="right_bottom" v-if="state.list.length">
         <li class="right_center_item" v-for="(item, i) in state.list" :key="i">
           <span class="orderNum">{{ i + 1 }}</span>
           <div class="inner_right">
             <div class="dibu"></div>
             <div class="flex">
-              <div class="info">
-                <span class="labels">设备ID:</span>
-                <span class="text-content zhuyao"> {{ item.gatewayno }}</span>
-              </div>
-              <div class="info">
-                <span class="labels">型号:</span>
-                <span class="text-content"> {{ item.terminalno }}</span>
+              <div class="info  flex-1">
+                <span class="labels">工单标题:</span>
+                <span class="text-content zhuyao  text-no-wrap"> {{ item.title }}</span>
               </div>
               <div class="info">
-                <span class="labels">告警值:</span>
-                <span class="text-content warning">
-                  {{ montionFilter(item.alertvalue) }}</span
+                <span class="labels">手里类型:</span>
+                <span class="text-content warning  text-no-wrap">
+                  {{ item.acceptType }}</span
                 >
               </div>
             </div>
 
             <div class="flex">
-              <div class="info">
-                <span class="labels shrink-0"> 地址:</span>
-                <span
-                  class="ciyao truncate"
-                  style="font-size: 12px; width: 220px"
-                  :title="handleAddress(item)"
-                >
-                  {{ handleAddress(item) }}</span
-                >
+              <div class="flex">
+                <div class="info">
+                  <span class="labels">工单编号:</span>
+                  <span
+                      class="text-content ciyao"
+                      :class="{ warning: item.alertdetail }"
+                  >
+                  {{item.no}}</span
+                  >
+                </div>
               </div>
               <div class="info time shrink-0">
                 <span class="labels">时间:</span>
                 <span class="text-content" style="font-size: 12px">
-                  {{ item.createtime }}</span
+                  {{ formatDate(item.creationTime, 'YYYY-mm-dd HH:MM:SS') }}</span
                 >
               </div>
             </div>
-            <div class="flex">
-              <div class="info">
-                <span class="labels">报警内容:</span>
-                <span
-                  class="text-content ciyao"
-                  :class="{ warning: item.alertdetail }"
-                >
-                  {{ item.alertdetail || "无" }}</span
-                >
-              </div>
+            <div class="info">
+              <span class="labels shrink-0"> 地址:</span>
+              <span
+                  class="ciyao truncate"
+                  style="font-size: 12px; width: 220px"
+                  :title="item.address"
+              >
+                  {{ item.address }}</span
+              >
             </div>
           </div>
         </li>
       </ul>
     </component>
+    <EmptyCom>暂无数据</EmptyCom>
   </div>
 </template>
 <script setup lang="ts">
-import { currentGET } from "@/api";
 import SeamlessScroll from "@/components/seamless-scroll";
 import { computed, onMounted, reactive } from "vue";
 import { useSettingStore } from "@/stores/setting";
 import { storeToRefs } from "pinia";
 import EmptyCom from "@/components/empty-com";
 import signalR from "@/utils/signalR";
+import {formatDuration,formatDate} from "@/utils/formatTime";
 const settingStore = useSettingStore();
 const { defaultOption, indexConfig } = storeToRefs(settingStore);
 const state = reactive<any>({
@@ -92,19 +89,6 @@ const state = reactive<any>({
   scroll: true,
 });
 
-const getData = () => {
-  currentGET("rightBottom", { limitNum: 20 }).then((res) => {
-    console.log("工单概览", res);
-    if (res.success) {
-      state.list = res.data.list;
-    } else {
-      window.$message({
-        text: res.msg,
-        type: "warning",
-      });
-    }
-  });
-};
 
 const comName = computed(() => {
   if (indexConfig.value.rightBottomSwiper) {
@@ -113,15 +97,11 @@ const comName = computed(() => {
     return EmptyCom;
   }
 });
-function montionFilter(val: any) {
-  return val ? Number(val).toFixed(2) : "--";
-}
-const handleAddress = (item: any) => {
-  return `${item.provinceName}/${item.cityName}/${item.countyName}`;
-};
 onMounted(() => {
-  getData();
-  signalR.joinGroup("BsDataShowArea8");
+  signalR.SR.on("BsDataShowArea8", (res: any) => {
+    console.log('工单概览',res)
+    state.list = res;
+  });
 });
 </script>
 

+ 2 - 2
src/views/index/right-top.vue

@@ -130,7 +130,7 @@ const setOption = async (data:any) => {
                 padding: [7, 14],
                 borderWidth: 0.5,
                 borderColor: "rgba(252,144,16,.5)",
-                formatter: "最高1:{c}",
+                formatter: "峰值1:{c}",
               },
             },
             {
@@ -197,7 +197,7 @@ const setOption = async (data:any) => {
                 borderRadius: 6,
                 borderColor: "rgba(9,202,243,.5)",
                 padding: [7, 14],
-                formatter: "最高2:{c}",
+                formatter: "峰值2:{c}",
                 borderWidth: 0.5,
               },
             },