From 0c7cc21d8a51e164dd2fe4ce73ab566b3a9081a9 Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期四, 26 三月 2026 10:25:05 +0800
Subject: [PATCH] 测试完成
---
src/components/Assistant/index.vue | 1132 ++++++++++++++++++++++++++++++++++++++---------------------
1 files changed, 731 insertions(+), 401 deletions(-)
diff --git a/src/components/Assistant/index.vue b/src/components/Assistant/index.vue
index 80e9bd5..2ee2946 100644
--- a/src/components/Assistant/index.vue
+++ b/src/components/Assistant/index.vue
@@ -1,479 +1,809 @@
<template>
<div
- ref="floatDrag"
- class="float-position"
- id="float-box"
- :style="{
- left: left + 'px',
- top: top + 'px',
- right: right + 'px !important',
- zIndex: zIndex,
+ ref="floatBall"
+ class="float-ball"
+ :class="{
+ 'float-ball-hidden': isHidden && !isHovering,
+ 'float-ball-expanded': isExpanded,
}"
- @touchmove.prevent
- @mousemove.prevent
- @mousedown="mouseDown"
- @mouseup="mouseUp"
+ :style="{
+ left: position.x + 'px',
+ top: position.y + 'px',
+ '--primary-color': primaryColor,
+ '--hover-color': hoverColor,
+ }"
+ @mouseenter="handleMouseEnter"
+ @mouseleave="handleMouseLeave"
>
- <div class="drag">
- <svg
- t="1682058484158"
- class="icon"
- viewBox="0 0 1024 1024"
- version="1.1"
- xmlns="http://www.w3.org/2000/svg"
- p-id="2023"
- width="32"
- height="32"
- >
- <path
- d="M556.297 172.715a42.407 42.407 0 0 1 42.426 42.398l0.837 267.69c-0.118 1.703 0.63 2.737 1.408 2.737 0.63 0 1.29-0.699 1.506-2.284l37.74-208.953c3.732-20.672 21.844-36.166 42.162-36.166a40.074 40.074 0 0 1 7.136 0.64c23.064 4.164 38.391 27.562 34.217 50.587l-33.656 244.529c0 2.559 0.483 4.478 1.32 4.478 0.58 0 1.328-0.935 2.175-3.218l50.144-134.063c6.27-17.65 23.034-29.403 40.793-29.403A39.798 39.798 0 0 1 797.892 374c22.08 7.875 33.626 33.41 25.78 55.47l-87.904 287.191c-0.453 1.585-0.984 3.16-1.437 4.725l-0.187 0.591v0.128a187.031 187.031 0 0 1-177.847 129.1c-53.156 0-108.42-18.752-150.472-51-45.419-27.336-190.968-183.783-190.968-183.783-22.09-22.07-18.792-55.882 3.297-77.962 11.537-11.537 25.919-17.6 40.173-17.6 13.033 0 25.967 5.05 36.51 15.592l63.138 63.157c8.603 8.594 18.132 12.699 26.922 12.699a26.952 26.952 0 0 0 20.88-9.893c7.658-9.037 4.635-36.914 2.49-54.594l-31.668-260.259c-2.825-23.26 13.781-45.724 37.003-48.549a40.497 40.497 0 0 1 4.853-0.295c21.282 0 39.749 16.98 42.387 38.597l34.926 204.425c0.905 2.54 2.342 4.036 3.602 4.036s2.353-1.496 2.58-4.922l11.88-265.741a42.417 42.417 0 0 1 42.467-42.398m0-70.875a113.36 113.36 0 0 0-104.344 69.153c-0.246 0.57-0.482 1.152-0.718 1.732a111.234 111.234 0 0 0-90.022 10.976 113.597 113.597 0 0 0-32.415 29.207 115.23 115.23 0 0 0-19.067 38.489 113.843 113.843 0 0 0-3.465 44.68l21.36 175.77a120.842 120.842 0 0 0-69.3-21.863c-33.468 0-65.549 13.614-90.286 38.332-23.212 23.202-36.993 53.363-38.863 84.952a120.92 120.92 0 0 0 34.502 92.216c5.532 5.906 39.64 42.407 79.203 82.412 74.586 75.422 105.328 99.648 122.702 110.771 53.973 40.36 123.254 63.414 190.674 63.414A257.906 257.906 0 0 0 801.14 745.1c0.247-0.709 0.483-1.417 0.7-2.136l0.117-0.374a178.56 178.56 0 0 0 1.723-5.64l87.413-285.578a113.203 113.203 0 0 0 5.729-42.86 115.585 115.585 0 0 0-35.772-77.135 111.431 111.431 0 0 0-67.45-30.19l0.148-0.985a113.676 113.676 0 0 0-1.201-43.155 115.408 115.408 0 0 0-16.872-39.523 113.774 113.774 0 0 0-30.703-30.968 111.077 111.077 0 0 0-84.981-17.06 113.203 113.203 0 0 0-103.694-67.656z"
- fill="#ffffff"
- p-id="2024"
- ></path>
- </svg>
- </div>
- <div class="content" id="content" @click="handelFlex">
- <!-- <img src="../../../../assets/image/alarm.png" alt="" /> -->
- <div class="label">
- <div v-if="flag">灞曞紑</div>
- <div v-else>鏀惰捣</div>
- </div>
- <div class="item-container">
- <div
- v-for="(item, index) in powerList"
- :key="index"
- @click.stop="activeHandle(index,item.url)"
+ <!-- 涓荤悆浣� -->
+ <div
+ class="ball-main"
+ :class="{ 'ball-main-expanded': isExpanded }"
+ @click="toggleExpand"
+ @mousedown="startDrag"
+ @touchstart="startDrag"
+ >
+ <!-- 鎶樺彔鐘舵�佸浘鏍� -->
+ <div v-if="!isExpanded" class="ball-icon">
+ <svg
+ class="fold-icon"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
>
+ <path d="M4 6h16M4 12h16M4 18h16" />
+ </svg>
+ </div>
+
+ <!-- 灞曞紑鐘舵�佸叧闂寜閽� -->
+ <div v-else class="close-btn" @click.stop="toggleExpand">
+ <svg
+ class="close-icon"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path d="M6 18L18 6M6 6l12 12" />
+ </svg>
+ </div>
+
+ <!-- 瑙掓爣鎻愮ず锛堟湁鏈鏁版椂鏄剧ず锛� -->
+ <div v-if="totalUnread > 0" class="ball-badge">
+ {{ totalUnread > 99 ? "99+" : totalUnread }}
+ </div>
+ </div>
+
+ <!-- 灞曞紑鐨勫唴瀹归潰鏉� -->
+ <transition name="ball-expand">
+ <div v-if="isExpanded" class="ball-content">
+ <div class="content-header">
+ <h3>闅忚宸ヤ綔鍙�</h3>
+ <div class="update-time">鏇存柊浜� {{ updateTime }}</div>
+ </div>
+
+ <div class="stats-grid">
<div
- :class="activeIndex == index ? 'active power-item' : 'power-item'"
+ v-for="(item, index) in statsItems"
+ :key="index"
+ class="stat-item"
+ :class="{ 'stat-item-highlight': item.highlight }"
+ @click="handleItemClick(item)"
>
- <img :src="item.path" alt="" style="width: 26px" />
+ <div class="stat-icon">
+ <svg
+ v-if="item.icon === 'IconUsers'"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path
+ d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13-7.157a4 4 0 11-8 0 4 4 0 018 0z"
+ />
+ </svg>
+ <svg
+ v-else-if="item.icon === 'IconAlertCircle'"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
+ </svg>
+ <svg
+ v-else-if="item.icon === 'IconTask'"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path
+ d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
+ />
+ </svg>
+ </div>
+ <div class="stat-info">
+ <div class="stat-label">{{ item.label }}</div>
+ <div class="stat-value">{{ item.value }}</div>
+ <div
+ v-if="item.trend"
+ class="stat-trend"
+ :class="'trend-' + item.trend.type"
+ >
+ <span class="trend-arrow">{{ item.trend.arrow }}</span>
+ <span class="trend-value">{{ item.trend.value }}</span>
+ </div>
+ </div>
+ <div v-if="item.unread > 0" class="stat-badge">
+ {{ item.unread > 99 ? "99+" : item.unread }}
+ </div>
</div>
- <div :class="activeIndex == index ? 'active-des des' : 'des'">
- {{ item.label }}
+ </div>
+
+ <div class="quick-actions">
+ <div
+ v-for="(action, index) in quickActions"
+ :key="index"
+ class="action-item"
+ @click="handleActionClick(action)"
+ >
+ <div class="action-icon">
+ <svg
+ v-if="action.icon === 'IconMessageCircle'"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path
+ d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
+ />
+ </svg>
+ <svg
+ v-else-if="action.icon === 'IconPhone'"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path
+ d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
+ />
+ </svg>
+ </div>
+ <div class="action-label">{{ action.label }}</div>
</div>
</div>
</div>
- </div>
+ </transition>
</div>
</template>
<script>
+import { getCurrentUserServiceSubtaskCount } from "@/api/AiCentre/index";
export default {
- name: "DragBall",
+ name: "FloatBall",
+
props: {
- distanceRight: {
- type: Number,
- default: 36,
+ // 鍒濆浣嶇疆
+ initialPosition: {
+ type: Object,
+ default: () => ({ x: 20, y: 100 }),
},
- distanceBottom: {
- type: Number,
- default: 600,
- },
- isScrollHidden: {
- type: Boolean,
- default: false,
- },
- isCanDraggable: {
+ // 鏄惁鑷姩闅愯棌
+ autoHide: {
type: Boolean,
default: true,
},
- zIndex: {
+ // 闅愯棌寤惰繜锛堟绉掞級
+ hideDelay: {
type: Number,
- default: 50,
+ default: 2000,
},
- value: {
+ // 涓婚棰滆壊
+ primaryColor: {
type: String,
- default: "鎮诞鐞冿紒",
+ default: "#4f46e5",
+ },
+ // 鎮仠棰滆壊
+ hoverColor: {
+ type: String,
+ default: "#4338ca",
+ },
+ // 鏁版嵁婧愶紙鍙粠澶栭儴浼犲叆锛�
+ statsData: {
+ type: Object,
+ default: null,
},
},
+
data() {
return {
- clientWidth: null,
- clientHeight: null,
- left: null,
- top: null,
- right: null,
- timer: null,
- currentTop: 0,
- mousedownX: 0,
- mousedownY: 0,
+ isExpanded: false,
+ isHovering: false,
+ isHidden: false,
+ isDragging: false,
+ position: { ...this.initialPosition },
+ dragStart: { x: 0, y: 0 },
+ hideTimer: null,
+ updateTime: "",
+ roles: null,
+ // 缁熻鏁版嵁
+ statsItems: [
+ {
+ id: "pending",
+ label: "寰呴殢璁�",
+ value: "0",
+ unread: 0,
+ urltype: 2,
+ icon: "IconUsers",
+ url: "/followvisit/discharge",
+ trend: { type: "up", arrow: "", value: "" },
+ highlight: true,
+ },
+ {
+ id: "failed",
+ label: "闅忚澶辫触",
+ value: "0",
+ unread: 0,
+ urltype: 3,
+ icon: "IconAlertCircle",
+ url: "/followvisit/discharge",
+ trend: { type: "down", arrow: "", value: "" },
+ },
+ {
+ id: "abnormal",
+ label: "浠诲姟寮傚父",
+ value: "0",
+ unread: 0,
+ urltype: 4,
+ icon: "IconAlertCircle",
+ url: "/followvisit/discharge",
+ trend: { type: "up", arrow: "", value: "" },
+ },
+ {
+ id: "myTasks",
+ label: "鎴戠殑浠诲姟",
+ value: "0",
+ unread: 0,
+ urltype: 5,
+ icon: "IconTask",
+ url: "/followvisit/discharge",
+ trend: { type: "stable", arrow: "", value: "" },
+ },
+ ],
- flag: true, // 鎺у埗鎮诞妗嗘槸鍚﹀睍寮�
- box: "", // 鎮诞鐞冪殑dom
- activeIndex: 0, //楂樹寒鏄剧ず
- powerList: [
+ // 蹇嵎鎿嶄綔
+ quickActions: [
{
- path: require("@/assets/images/huanzheliebiao.png"),
- url:'/patient/patient/',
- label: "鎮h��",
+ id: "sms",
+ label: "鍒涘缓闂嵎浠诲姟",
+ icon: "IconMessageCircle",
+ url: "/followvisit/QuestionnaireTask?type=2&serviceType=2",
},
{
- path: require("@/assets/images/fwwu.png"),
- url:'/followvisit/tasklist/',
- label: "浠诲姟",
+ id: "call",
+ label: "鍒涘缓璇煶浠诲姟",
+ icon: "IconPhone",
+ url: "/followvisit/particty?type=1&serviceType=2",
},
- {
- path: require("@/assets/images/duanxinjilu.png"),
- url:'',
- label: "鐭俊",
- },
- {
- path: require("@/assets/images/dianhua.png"),
- url:'',
- label: "鐢佃瘽",
- },
- {
- path: require("@/assets/images/zxlt.png"),
- url:'',
- label: "鍦ㄧ嚎鑱婂ぉ",
- },
+ // {
+ // id: 'chat',
+ // label: '鍦ㄧ嚎鑱婂ぉ',
+ // icon: 'IconMessageCircle',
+ // url: '/chat'
+ // }
],
};
},
- created() {
- this.clientWidth = document.documentElement.clientWidth;
- this.clientHeight = document.documentElement.clientHeight;
+
+ computed: {
+ totalUnread() {
+ return this.statsItems.reduce((sum, item) => sum + item.unread, 0);
+ },
},
+
mounted() {
- this.isCanDraggable &&
- this.$nextTick(() => {
- this.floatDrag = this.$refs.floatDrag;
- // 鑾峰彇鍏冪礌浣嶇疆灞炴��
- this.floatDragDom = this.floatDrag.getBoundingClientRect();
- // 璁剧疆鍒濆浣嶇疆
- this.left = this.clientWidth - this.floatDragDom.width - this.distanceRight;
- // this.right = 0;
- this.top =
- this.clientHeight - this.floatDragDom.height - this.distanceBottom;
- this.initDraggable();
- });
- // this.isScrollHidden && window.addEventListener('scroll', this.handleScroll);
+ this.roles = this.$store.state.user.roles;
+ this.loadPosition();
+
+ if (this.autoHide) {
+ this.startAutoHide();
+ }
+
+ // 鐐瑰嚮澶栭儴鍏抽棴
+ document.addEventListener("click", this.handleClickOutside);
+
+ // 绐楀彛澶у皬鍙樺寲鏃堕噸鏂板畾浣�
window.addEventListener("resize", this.handleResize);
-
- this.box = document.getElementById("float-box");
},
- beforeUnmount() {
- window.removeEventListener("scroll", this.handleScroll);
+
+ beforeDestroy() {
+ document.removeEventListener("click", this.handleClickOutside);
window.removeEventListener("resize", this.handleResize);
+ clearTimeout(this.hideTimer);
},
- methods: {
- // 浼哥缉鎮诞鐞�
- handelFlex() {
- if (this.flag) {
- this.buffer(this.box, "height", 600);
- } else {
- this.buffer(this.box, "height", 70);
- }
- this.flag = !this.flag;
- console.log("鏄惁灞曞紑", this.flag);
- },
- // 鐐瑰嚮鍝釜power
- activeHandle(index,url) {
- //鎶婃垜浠嚜瀹氫箟鐨勪笅鏍囪祴鍊�
- this.activeIndex = index;
- this.$router.push({
- path: url,
- })
- console.log("HHHH", index);
- },
- // 鑾峰彇瑕佹敼鍙樺緱鏍峰紡灞炴��
- getStyleAttr(obj, attr) {
- if (obj.currentStyle) {
- // IE 鍜� opera
- return obj.currentStyle[attr];
- } else {
- return window.getComputedStyle(obj, null)[attr];
- }
- },
- // 鍔ㄧ敾鍑芥暟
- buffer(eleObj, attr, target) {
- // setInterval鏂瑰紡寮�鍚姩鐢�
- //鍏堟竻鍚庤
- // clearInterval(eleObj.timer);
- // let speed = 0
- // let begin = 0
- // //璁剧疆瀹氭椂鍣�
- // eleObj.timer = setInterval(() => {
- // //鑾峰彇鍔ㄧ敾灞炴�х殑鍒濆鍊�
- // begin = parseInt(this.getStyleAttr(eleObj, attr));
- // speed = (target - begin) * 0.2;
- // speed = target > begin ? Math.ceil(speed) : Math.floor(speed);
- // eleObj.style[attr] = begin + speed + "px";
- // if (begin === target) {
- // clearInterval(eleObj.timer);
- // }
- // }, 20);
- // cancelAnimationFrame寮�鍚姩鐢�
- // 鍏堟竻鍚庤
- cancelAnimationFrame(eleObj.timer);
- let speed = 0;
- let begin = 0;
- let _this = this;
- eleObj.timer = requestAnimationFrame(function fn() {
- begin = parseInt(_this.getStyleAttr(eleObj, attr));
- // 鍔ㄧ敾閫熷害
- speed = (target - begin) * 0.9;
- speed = target > begin ? Math.ceil(speed) : Math.floor(speed);
- eleObj.style[attr] = begin + speed + "px";
- eleObj.timer = requestAnimationFrame(fn);
- if (begin === target) {
- cancelAnimationFrame(eleObj.timer);
- }
- });
- },
- /**
- * 绐楀彛resize鐩戝惉
- */
- handleResize() {
- // this.clientWidth = document.documentElement.clientWidth;
- // this.clientHeight = document.documentElement.clientHeight;
- // console.log(window.innerWidth);
- // console.log(document.documentElement.clientWidth);
- this.checkDraggablePosition();
- },
- /**
- * 鍒濆鍖杁raggable
- */
- initDraggable() {
- this.floatDrag.addEventListener("touchstart", this.toucheStart);
- this.floatDrag.addEventListener("touchmove", (e) => this.touchMove(e));
- this.floatDrag.addEventListener("touchend", this.touchEnd);
- },
- mouseDown(e) {
- const event = e || window.event;
- this.mousedownX = event.screenX;
- this.mousedownY = event.screenY;
- const that = this;
- let floatDragWidth = this.floatDragDom.width / 2;
- let floatDragHeight = this.floatDragDom.height / 2;
- if (event.preventDefault) {
- event.preventDefault();
+ methods: {
+ toggleExpand() {
+ this.isExpanded = !this.isExpanded;
+ if (this.isExpanded) {
+ this.isHidden = false;
+ clearTimeout(this.hideTimer);
+ this.updateStats();
}
- this.canClick = false;
- this.floatDrag.style.transition = "none";
- document.onmousemove = function (e) {
- var event = e || window.event;
- that.left = event.clientX - floatDragWidth;
- that.top = event.clientY - floatDragHeight;
- if (that.left < 0) that.left = 0;
- if (that.top < 0) that.top = 0;
- // 榧犳爣绉诲嚭鍙鍖哄煙鍚庣粰鎸夐挳杩樺師
- if (
- event.clientY < 0 ||
- event.clientY > Number(this.clientHeight) ||
- event.clientX > Number(this.clientWidth) ||
- event.clientX < 0
- ) {
- this.right = 0;
- this.top =
- this.clientHeight - this.floatDragDom.height - this.distanceBottom;
- document.onmousemove = null;
- this.floatDrag.style.transition = "all 0.3s";
- return;
+ },
+
+ handleMouseEnter() {
+ this.isHovering = true;
+ if (this.autoHide) {
+ clearTimeout(this.hideTimer);
+ this.isHidden = false;
+ }
+ },
+
+ handleMouseLeave() {
+ this.isHovering = false;
+ if (this.autoHide && !this.isExpanded) {
+ this.startAutoHide();
+ }
+ },
+
+ startAutoHide() {
+ this.hideTimer = setTimeout(() => {
+ if (!this.isExpanded && !this.isHovering) {
+ this.isHidden = true;
}
- if (
- that.left >=
- document.documentElement.clientWidth - floatDragWidth * 2
- ) {
- that.left = document.documentElement.clientWidth - floatDragWidth * 2;
+ }, this.hideDelay);
+ },
+
+ startDrag(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.isDragging = true;
+
+ const clientX = e.type.includes("touch")
+ ? e.touches[0].clientX
+ : e.clientX;
+ const clientY = e.type.includes("touch")
+ ? e.touches[0].clientY
+ : e.clientY;
+
+ this.dragStart = {
+ x: clientX - this.position.x,
+ y: clientY - this.position.y,
+ };
+
+ const onMove = (moveEvent) => {
+ if (!this.isDragging) return;
+
+ const moveX = moveEvent.type.includes("touch")
+ ? moveEvent.touches[0].clientX
+ : moveEvent.clientX;
+ const moveY = moveEvent.type.includes("touch")
+ ? moveEvent.touches[0].clientY
+ : moveEvent.clientY;
+
+ const newX = moveX - this.dragStart.x;
+ const newY = moveY - this.dragStart.y;
+
+ // 杈圭晫妫�鏌�
+ const maxX = window.innerWidth - 60;
+ const maxY = window.innerHeight - 60;
+
+ this.position.x = Math.max(0, Math.min(newX, maxX));
+ this.position.y = Math.max(0, Math.min(newY, maxY));
+ };
+
+ const onEnd = () => {
+ this.isDragging = false;
+ document.removeEventListener("mousemove", onMove);
+ document.removeEventListener("mouseup", onEnd);
+ document.removeEventListener("touchmove", onMove);
+ document.removeEventListener("touchend", onEnd);
+
+ // 濡傛灉闈犺繎杈圭紭锛岃嚜鍔ㄥ惛闄�
+ if (this.position.x < 20) {
+ this.position.x = 0;
+ } else if (this.position.x > window.innerWidth - 80) {
+ this.position.x = window.innerWidth - 60;
}
- if (that.top >= that.clientHeight - floatDragHeight * 2) {
- that.top = that.clientHeight - floatDragHeight * 2;
+
+ // 淇濆瓨浣嶇疆鍒版湰鍦板瓨鍌�
+ try {
+ localStorage.setItem(
+ "floatBallPosition",
+ JSON.stringify(this.position)
+ );
+ } catch (e) {
+ console.error("淇濆瓨浣嶇疆澶辫触:", e);
}
};
+
+ document.addEventListener("mousemove", onMove);
+ document.addEventListener("mouseup", onEnd);
+ document.addEventListener("touchmove", onMove, { passive: false });
+ document.addEventListener("touchend", onEnd);
},
- mouseUp(e) {
- const event = e || window.event;
- //鍒ゆ柇鍙槸鍗曠函鐨勭偣鍑伙紝娌℃湁鎷栨嫿
+
+ handleItemClick(item) {
+ if (item.url) {
+ console.log(item.url, "item.url");
+
+ // this.$router.push(item.url);
+ this.$router.replace({
+ path: item.url,
+ query: {
+ errtype: item.urltype,
+ },
+ });
+ this.toggleExpand();
+ }
+ },
+
+ handleActionClick(action) {
+ console.log(this.roles, "this.roles");
if (
- this.mousedownY == event.screenY &&
- this.mousedownX == event.screenX
+ action.url &&
+ (this.roles.includes("admin") || this.roles.includes("sysadmin"))
) {
- this.$emit("handlepaly");
- }
- document.onmousemove = null;
- this.checkDraggablePosition();
- this.floatDrag.style.transition = "all 0.3s";
- },
- toucheStart() {
- this.canClick = false;
- this.floatDrag.style.transition = "none";
- },
- touchMove(e) {
- this.canClick = true;
- if (e.targetTouches.length === 1) {
- // 鍗曟寚鎷栧姩
- let touch = event.targetTouches[0];
- this.left = touch.clientX - this.floatDragDom.width / 2;
- this.top = touch.clientY - this.floatDragDom.height / 2;
- }
- },
- touchEnd() {
- if (!this.canClick) return; // 瑙e喅鐐瑰嚮浜嬩欢鍜宼ouch浜嬩欢鍐茬獊鐨勯棶棰�
- this.floatDrag.style.transition = "all 0.3s";
- this.checkDraggablePosition();
- },
- /**
- * 鍒ゆ柇鍏冪礌鏄剧ず浣嶇疆
- * 鍦ㄧ獥鍙f敼鍙樺拰move end鏃惰皟鐢�
- */
- checkDraggablePosition() {
- this.clientWidth = document.documentElement.clientWidth;
- this.clientHeight = document.documentElement.clientHeight;
- if (this.left + this.floatDragDom.width / 2 >= this.clientWidth / 2) {
- // 鍒ゆ柇浣嶇疆鏄線宸﹀線鍙虫粦鍔�
- this.left = this.clientWidth - this.floatDragDom.width;
+ this.$router.replace(action.url);
+ this.toggleExpand();
} else {
- this.left = 0;
+ this.$modal.msgError("闈炵鐞嗗憳鐢ㄦ埛鏆傛棤鍒涘缓浠诲姟鏉冮檺");
}
- if (this.top < 0) {
- // 鍒ゆ柇鏄惁瓒呭嚭灞忓箷涓婃部
- this.top = 0;
+ },
+
+ async updateStats() {
+ try {
+ // 杩欓噷鍙互鏇挎崲涓哄疄闄呯殑 API 璋冪敤
+ // const response = await this.$api.getFollowupStats()
+ // this.statsItems = response.data
+
+ // 妯℃嫙鏁版嵁鏇存柊
+ const mockData = {
+ pending: {
+ value: "128",
+ unread: null,
+ trend: { type: "up", arrow: "鈫�", value: "5" },
+ },
+ failed: {
+ value: "24",
+ unread: null,
+ trend: { type: "down", arrow: "鈫�", value: "2" },
+ },
+ abnormal: {
+ value: "8",
+ unread: null,
+ trend: { type: "up", arrow: "鈫�", value: "3" },
+ },
+ myTasks: {
+ value: "156",
+ unread: null,
+ trend: { type: "stable", arrow: "鈫�", value: "0" },
+ },
+ };
+ const response = await getCurrentUserServiceSubtaskCount();
+ mockData.pending.value = response.pendingVisitCount;
+ mockData.failed.value = response.failedVisitCount;
+ mockData.abnormal.value = response.abnormalVisitCount;
+ mockData.myTasks.value = response.allVisitCount;
+ this.statsItems = this.statsItems.map((item) => {
+ const data = mockData[item.id] || {};
+ return {
+ ...item,
+ value: data.value || item.value,
+ unread: data.unread || item.unread,
+ trend: data.trend || item.trend,
+ };
+ });
+
+ // 鏇存柊鏃堕棿
+ const now = new Date();
+ this.updateTime = `${now.getHours().toString().padStart(2, "0")}:${now
+ .getMinutes()
+ .toString()
+ .padStart(2, "0")}`;
+ } catch (error) {
+ console.error("鏇存柊缁熻鏁版嵁澶辫触:", error);
}
- if (this.top + this.floatDragDom.height >= this.clientHeight) {
- // 鍒ゆ柇鏄惁瓒呭嚭灞忓箷涓嬫部
- this.top = this.clientHeight - this.floatDragDom.height;
+ },
+
+ loadPosition() {
+ try {
+ const savedPosition = localStorage.getItem("floatBallPosition");
+ if (savedPosition) {
+ const parsed = JSON.parse(savedPosition);
+ this.position = parsed;
+ }
+ } catch (e) {
+ console.error("鍔犺浇浣嶇疆澶辫触:", e);
}
+ },
+
+ handleClickOutside(e) {
+ if (
+ this.isExpanded &&
+ this.$refs.floatBall &&
+ !this.$refs.floatBall.contains(e.target)
+ ) {
+ this.toggleExpand();
+ }
+ },
+
+ handleResize() {
+ const maxX = window.innerWidth - 60;
+ const maxY = window.innerHeight - 60;
+
+ this.position.x = Math.min(this.position.x, maxX);
+ this.position.y = Math.min(this.position.y, maxY);
},
},
};
</script>
-<style>
-html,
-body {
- overflow: hidden;
-}
-</style>
-<style scoped lang="scss">
-.float-position {
+
+<style scoped>
+.float-ball {
position: fixed;
- z-index: 10003 !important;
- left: 0;
- top: 20%;
- width: 70px;
- height: 70px;
- border-radius: 32px;
+ z-index: 9999;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ pointer-events: auto;
+}
+
+.float-ball-hidden {
+ opacity: 0.3;
+ transform: translateX(10px);
+}
+
+.float-ball-hidden:hover {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+.ball-main {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, var(--primary-color), #7c3aed);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: move;
+ box-shadow: 0 4px 20px rgba(79, 70, 229, 0.3);
+ transition: all 0.3s ease;
+ position: relative;
+ z-index: 10000;
+}
+
+.ball-main:hover {
+ background: linear-gradient(135deg, var(--hover-color), #6d28d9);
+ box-shadow: 0 6px 25px rgba(79, 70, 229, 0.4);
+ transform: scale(1.05);
+}
+
+.ball-main-expanded {
+ background: linear-gradient(135deg, #6366f1, #8b5cf6);
+}
+
+.ball-icon {
+ width: 24px;
+ height: 24px;
+ color: white;
+}
+
+.fold-icon {
+ width: 100%;
+ height: 100%;
+}
+
+.close-btn {
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
cursor: pointer;
- overflow: hidden;
- user-select: none;
+ color: white;
+ transition: transform 0.2s ease;
+}
- display: block;
- background: black;
- background: -webkit-radial-gradient(100px 100px, circle, #5788fe, #292929);
- // background: -moz-radial-gradient(100px 100px, circle, #35a1a1, #000);Firefox 娴忚鍣ㄧ殑瀹炵幇
- // background: radial-gradient(100px 100px, circle, #35a1a1, #000);鏍囧噯 HTML5 灞炴��
- margin: 0;
- .drag {
- width: 70px;
- height: 35px;
- // background: #f2e96a;
- text-align: center;
- line-height: 35px;
- border-bottom: 1px solid #fff;
+.close-btn:hover {
+ transform: rotate(90deg);
+}
+
+.close-icon {
+ width: 20px;
+ height: 20px;
+}
+
+.ball-badge {
+ position: absolute;
+ top: -5px;
+ right: -5px;
+ min-width: 20px;
+ height: 20px;
+ padding: 0 6px;
+ background: #ef4444;
+ color: white;
+ font-size: 12px;
+ font-weight: 600;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 2px solid white;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ transform: scale(1);
}
- .content {
- width: 70px;
- height: 35px;
- // background: #716af2;
- .label {
- width: 70px;
- height: 35px;
- text-align: center;
- line-height: 35px;
- color: white;
- }
- .label:hover {
- color: rgb(19, 217, 243);
- transition: all 0.5;
- }
-
- .item-container {
- margin-top: 10px;
- width: 70px;
- height: 500px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-direction: column;
-
- .power-item {
- width: 40px;
- height: 40px;
- border-radius: 50%;
- background-color: #f1f7ff;
- display: flex;
- justify-content: center;
- align-items: center;
- flex-direction: column;
- }
- .des {
- width: 40px;
- text-align: center;
- margin-bottom: 5px;
- font-size: 10px;
- color: #fff;
- }
- }
- }
-
- .close {
- width: 20px;
- height: 20px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- background: rgba(0, 0, 0, 0.6);
- position: absolute;
- right: -10px;
- top: -12px;
- cursor: pointer;
+ 50% {
+ transform: scale(1.1);
}
}
-.cart {
- border-radius: 50%;
- width: 5em;
- height: 5em;
+.ball-content {
+ position: absolute;
+ top: 70px;
+ left: 0;
+ width: 320px;
+ background: white;
+ border-radius: 16px;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ z-index: 9999;
+}
+
+.ball-expand-enter-active,
+.ball-expand-leave-active {
+ transition: all 0.3s ease;
+}
+
+.ball-expand-enter,
+.ball-expand-leave-to {
+ opacity: 0;
+ transform: translateY(-10px);
+}
+
+.content-header {
+ padding: 20px 20px 16px;
+ background: linear-gradient(135deg, var(--primary-color), #7c3aed);
+ color: white;
+}
+
+.content-header h3 {
+ margin: 0 0 8px 0;
+ font-size: 18px;
+ font-weight: 600;
+}
+
+.update-time {
+ font-size: 12px;
+ opacity: 0.9;
+}
+
+.stats-grid {
+ padding: 16px;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12px;
+}
+
+.stat-item {
+ padding: 16px;
+ background: #f8fafc;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ position: relative;
+ border: 2px solid transparent;
+}
+
+.stat-item:hover {
+ background: #f1f5f9;
+ border-color: #e2e8f0;
+ transform: translateY(-2px);
+}
+
+.stat-item-highlight {
+ border-color: var(--primary-color);
+ background: linear-gradient(to bottom right, #f0f9ff, #f8fafc);
+}
+
+.stat-icon {
+ width: 32px;
+ height: 32px;
+ background: white;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 12px;
+ color: var(--primary-color);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.stat-icon svg {
+ width: 18px;
+ height: 18px;
+}
+
+.stat-label {
+ font-size: 12px;
+ color: #64748b;
+ margin-bottom: 4px;
+}
+
+.stat-value {
+ font-size: 20px;
+ font-weight: 700;
+ color: #1e293b;
+ margin-bottom: 4px;
+}
+
+.stat-trend {
+ font-size: 11px;
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+
+.trend-up {
+ color: #10b981;
+}
+
+.trend-down {
+ color: #ef4444;
+}
+
+.trend-stable {
+ color: #64748b;
+}
+
+.trend-arrow {
+ font-size: 10px;
+}
+
+.stat-badge {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ min-width: 18px;
+ height: 18px;
+ padding: 0 4px;
+ background: #ef4444;
+ color: white;
+ font-size: 10px;
+ font-weight: 600;
+ border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
}
-.header-notice {
- display: inline-block;
- transition: all 0.3s;
-
- span {
- vertical-align: initial;
- }
-
- .notice-badge {
- color: inherit;
-
- .header-notice-icon {
- font-size: 16px;
- padding: 4px;
- }
- }
+.quick-actions {
+ padding: 12px 20px 20px;
+ border-top: 1px solid #f1f5f9;
+ display: flex;
+ gap: 12px;
+ justify-content: center;
}
-.drag-ball .drag-content {
- overflow-wrap: break-word;
- font-size: 14px;
- color: #fff;
- letter-spacing: 2px;
+.action-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ padding: 12px;
+ border-radius: 8px;
+ transition: all 0.2s ease;
+ flex: 1;
}
-.active {
- background-color: #f9f1db !important;
+.action-item:hover {
+ background: #f8fafc;
}
-.active-des {
- color: #71dcfa !important;
- font-size: 20px !important;
- font-weight: 500 !important;
+
+.action-icon {
+ width: 24px;
+ height: 24px;
+ color: var(--primary-color);
+}
+
+.action-icon svg {
+ width: 20px;
+ height: 20px;
+}
+
+.action-label {
+ font-size: 12px;
+ color: #475569;
+ font-weight: 500;
}
</style>
--
Gitblit v1.9.3