diff --git a/src/components/customer-service/index.vue b/src/components/customer-service/index.vue
new file mode 100644
index 0000000..e944a6a
--- /dev/null
+++ b/src/components/customer-service/index.vue
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
点击下方客服即可复制客服号码,可添加或添加微信号
+
+
![]()
+
+
+
+
+
+
+
+
+
+
diff --git a/src/util/ObserverInteraction.js b/src/util/ObserverInteraction.js
new file mode 100644
index 0000000..a3a5346
--- /dev/null
+++ b/src/util/ObserverInteraction.js
@@ -0,0 +1,284 @@
+// utils/ObserverInteraction.js
+
+class ObserverInteraction {
+ constructor(options = {}) {
+ this.options = {
+ attributes: true, // 观察属性变化
+ childList: false, // 观察子节点变化
+ subtree: false, // 观察后代节点
+ attributeFilter: [], // 观察特定属性
+ attributeOldValue: false, // 记录属性旧值
+ ...options,
+ };
+
+ this.observer = null;
+ this.callbacks = new Map();
+ this.targets = new Set();
+ }
+
+ /**
+ * 开始观察目标元素
+ * @param {HTMLElement} target - 要观察的DOM元素
+ * @param {Function} callback - 变化时的回调函数
+ * @param {Object} customOptions - 自定义观察选项
+ */
+ observe(target, callback, customOptions = {}) {
+ if (!target) {
+ console.error("ObserverInteraction: 目标元素不存在");
+ return;
+ }
+
+ const options = { ...this.options, ...customOptions };
+
+ // 如果已存在观察器,先断开
+ if (this.observer) {
+ this.disconnect();
+ }
+
+ // 创建新的观察器
+ this.observer = new MutationObserver((mutationsList) => {
+ const changes = this._processMutations(mutationsList);
+ if (changes.length > 0 && callback) {
+ callback(changes, target);
+ }
+
+ // 触发所有注册的回调
+ this.callbacks.forEach((cb, element) => {
+ if (element === target || element.contains(target)) {
+ cb(changes, target);
+ }
+ });
+ });
+
+ // 开始观察
+ this.observer.observe(target, options);
+ this.targets.add(target);
+
+ // 存储回调
+ if (callback) {
+ this.callbacks.set(target, callback);
+ }
+
+ return this;
+ }
+
+ /**
+ * 监听特定属性的变化
+ * @param {HTMLElement} target - 目标元素
+ * @param {string} attributeName - 属性名
+ * @param {Function} callback - 回调函数
+ */
+ watchAttribute(target, attributeName, callback) {
+ return this.observe(target, callback, {
+ attributes: true,
+ attributeFilter: [attributeName],
+ attributeOldValue: true,
+ });
+ }
+
+ /**
+ * 监听元素大小变化(使用 ResizeObserver)
+ * @param {HTMLElement} target - 目标元素
+ * @param {Function} callback - 回调函数
+ */
+ observeResize(target, callback) {
+ if (!window.ResizeObserver) {
+ console.warn("ResizeObserver 不支持当前浏览器");
+ return;
+ }
+
+ const resizeObserver = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ const { width, height } = entry.contentRect;
+ callback({ width, height, target: entry.target });
+ }
+ });
+
+ resizeObserver.observe(target);
+
+ // 存储以便清理
+ this.resizeObservers = this.resizeObservers || new Map();
+ this.resizeObservers.set(target, resizeObserver);
+
+ return () => this.unobserveResize(target);
+ }
+
+ /**
+ * 处理 mutations 数据
+ * @private
+ */
+ _processMutations(mutationsList) {
+ const changes = [];
+
+ for (const mutation of mutationsList) {
+ const change = {
+ type: mutation.type,
+ target: mutation.target,
+ timestamp: Date.now(),
+ };
+
+ if (mutation.type === "attributes") {
+ change.attributeName = mutation.attributeName;
+ change.oldValue = mutation.oldValue;
+ change.newValue = mutation.target.getAttribute(mutation.attributeName);
+ }
+
+ if (mutation.type === "childList") {
+ change.addedNodes = Array.from(mutation.addedNodes);
+ change.removedNodes = Array.from(mutation.removedNodes);
+ }
+
+ changes.push(change);
+ }
+
+ return changes;
+ }
+
+ /**
+ * 注册交互事件
+ * @param {HTMLElement} element - 元素
+ * @param {string} eventType - 事件类型
+ * @param {Function} callback - 回调
+ */
+ on(element, eventType, callback) {
+ if (!element || !callback) return;
+
+ const handler = (event) => {
+ callback(event, element);
+ };
+
+ element.addEventListener(eventType, handler);
+
+ // 存储以便清理
+ this.eventHandlers = this.eventHandlers || new Map();
+ if (!this.eventHandlers.has(element)) {
+ this.eventHandlers.set(element, new Map());
+ }
+ this.eventHandlers.get(element).set(eventType, handler);
+
+ return () => this.off(element, eventType);
+ }
+
+ /**
+ * 移除事件监听
+ * @param {HTMLElement} element - 元素
+ * @param {string} eventType - 事件类型
+ */
+ off(element, eventType) {
+ if (!this.eventHandlers?.has(element)) return;
+
+ const handlers = this.eventHandlers.get(element);
+ if (eventType) {
+ const handler = handlers.get(eventType);
+ if (handler) {
+ element.removeEventListener(eventType, handler);
+ handlers.delete(eventType);
+ }
+ } else {
+ handlers.forEach((handler, type) => {
+ element.removeEventListener(type, handler);
+ });
+ this.eventHandlers.delete(element);
+ }
+ }
+
+ /**
+ * 观察元素可见性(Intersection Observer)
+ * @param {HTMLElement} target - 目标元素
+ * @param {Function} callback - 可见性变化回调
+ * @param {Object} options - IntersectionObserver 配置
+ */
+ observeVisibility(target, callback, options = {}) {
+ const defaultOptions = {
+ threshold: 0.1,
+ rootMargin: "0px",
+ };
+
+ const intersectionObserver = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ callback({
+ isIntersecting: entry.isIntersecting,
+ intersectionRatio: entry.intersectionRatio,
+ boundingClientRect: entry.boundingClientRect,
+ target: entry.target,
+ });
+ });
+ },
+ { ...defaultOptions, ...options }
+ );
+
+ intersectionObserver.observe(target);
+
+ this.intersectionObservers = this.intersectionObservers || new Map();
+ this.intersectionObservers.set(target, intersectionObserver);
+
+ return () => this.unobserveVisibility(target);
+ }
+
+ /**
+ * 停止观察可见性
+ * @param {HTMLElement} target - 目标元素
+ */
+ unobserveVisibility(target) {
+ const observer = this.intersectionObservers?.get(target);
+ if (observer) {
+ observer.unobserve(target);
+ this.intersectionObservers.delete(target);
+ }
+ }
+
+ /**
+ * 停止观察大小变化
+ * @param {HTMLElement} target - 目标元素
+ */
+ unobserveResize(target) {
+ const observer = this.resizeObservers?.get(target);
+ if (observer) {
+ observer.unobserve(target);
+ this.resizeObservers.delete(target);
+ }
+ }
+
+ /**
+ * 断开所有观察器
+ */
+ disconnect() {
+ if (this.observer) {
+ this.observer.disconnect();
+ this.observer = null;
+ }
+
+ // 清理 ResizeObservers
+ this.resizeObservers?.forEach((observer) => {
+ observer.disconnect();
+ });
+ this.resizeObservers?.clear();
+
+ // 清理 IntersectionObservers
+ this.intersectionObservers?.forEach((observer) => {
+ observer.disconnect();
+ });
+ this.intersectionObservers?.clear();
+
+ this.targets.clear();
+ this.callbacks.clear();
+ }
+
+ /**
+ * 销毁实例
+ */
+ destroy() {
+ this.disconnect();
+
+ // 清理所有事件监听
+ this.eventHandlers?.forEach((handlers, element) => {
+ handlers.forEach((handler, type) => {
+ element.removeEventListener(type, handler);
+ });
+ });
+ this.eventHandlers?.clear();
+ }
+}
+
+export default ObserverInteraction;