Kaynağa Gözat

供应商管理提交

luogang 9 ay önce
ebeveyn
işleme
b50c797a3f

+ 13 - 1
.eslintrc.cjs

@@ -31,7 +31,19 @@ module.exports = {
     'vue/no-v-model-argument': 'off',
     'prefer-rest-params': 'off',
     // prettier
-    'prettier/prettier': 'error',
+    'prettier/prettier': 'off',
+    'vue/v-on-event-hyphenation': 'off',
+    'vue/first-attribute-linebreak': [
+      'error',
+      {
+        singleline: 'ignore',
+        multiline: 'ignore',
+      },
+    ],
+    "vue/html-closing-bracket-newline": ['error', {
+      'singleline': 'never',
+      'multiline': 'never'
+    }],
     '@typescript-eslint/ban-types': [
       'error',
       {

+ 23 - 23
src/api/deviceManage/device.ts

@@ -7,21 +7,21 @@ import request from '@/utils/request';
  */
 
 export const listDevice = (query) => {
-  return request({
-    url: '/jdyw/device/list',
-    method: 'get',
-    params: query
-  });
+    return request({
+        url: '/jdyw/device/list',
+        method: 'get',
+        params: query
+    });
 };
 /**
  * 查询设备信息详细
  * @param id
  */
 export const getDevice = (id) => {
-  return request({
-    url: '/jdyw/device/' + id,
-    method: 'get'
-  });
+    return request({
+        url: '/jdyw/device/' + id,
+        method: 'get'
+    });
 };
 
 /**
@@ -29,11 +29,11 @@ export const getDevice = (id) => {
  * @param data
  */
 export const addDevice = (data) => {
-  return request({
-    url: '/jdyw/device',
-    method: 'post',
-    data: data
-  });
+    return request({
+        url: '/jdyw/device',
+        method: 'post',
+        data: data
+    });
 };
 
 /**
@@ -41,11 +41,11 @@ export const addDevice = (data) => {
  * @param data
  */
 export const updateDevice = (data) => {
-  return request({
-    url: '/jdyw/device',
-    method: 'put',
-    data: data
-  });
+    return request({
+        url: '/jdyw/device',
+        method: 'put',
+        data: data
+    });
 };
 
 /**
@@ -53,8 +53,8 @@ export const updateDevice = (data) => {
  * @param id
  */
 export const delDevice = (id: string | number | Array<string | number>) => {
-  return request({
-    url: '/jdyw/device/' + id,
-    method: 'delete'
-  });
+    return request({
+        url: '/jdyw/device/' + id,
+        method: 'delete'
+    });
 };

+ 118 - 0
src/api/supplierManage/index.ts

@@ -0,0 +1,118 @@
+import request from '@/utils/request';
+
+/**
+ * 查询供应商列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductor = (query) => {
+    return request({
+        url: '/jdyw/productor/list',
+        method: 'get',
+        params: query
+    });
+};
+/**
+ * 查询供应商信息详细
+ * @param id
+ */
+export const getProductor = (id) => {
+    return request({
+        url: '/jdyw/productor/' + id,
+        method: 'get'
+    });
+};
+
+/**
+ * 新增供应商信息
+ * @param data
+ */
+export const addProductor = (data) => {
+    return request({
+        url: '/jdyw/productor',
+        method: 'post',
+        data: data
+    });
+};
+
+/**
+ * 修改供应商信息
+ * @param data
+ */
+export const updateProductor = (data) => {
+    return request({
+        url: '/jdyw/productor',
+        method: 'put',
+        data: data
+    });
+};
+
+/**
+ * 删除供应商信息
+ * @param id
+ */
+export const delProductor = (id: string | number | Array<string | number>) => {
+    return request({
+        url: '/jdyw/productor/' + id,
+        method: 'delete'
+    });
+};
+
+/**
+ * 查询售后人员列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductorEngineers = (query) => {
+  return request({
+      url: '/jdyw/productorEngineers/list',
+      method: 'get',
+      params: query
+  });
+};
+/**
+* 查询售后人员信息详细
+* @param id
+*/
+export const getProductorEngineers = (id) => {
+  return request({
+      url: '/jdyw/productorEngineers/' + id,
+      method: 'get'
+  });
+};
+
+/**
+* 新增售后人员信息
+* @param data
+*/
+export const addProductorEngineers = (data) => {
+  return request({
+      url: '/jdyw/productorEngineers',
+      method: 'post',
+      data: data
+  });
+};
+
+/**
+* 修改售后人员信息
+* @param data
+*/
+export const updateProductorEngineers = (data) => {
+  return request({
+      url: '/jdyw/productorEngineers',
+      method: 'put',
+      data: data
+  });
+};
+/**
+* 删除售后人员信息
+* @param id
+*/
+export const delProductorEngineers = (id: string | number | Array<string | number>) => {
+  return request({
+      url: '/jdyw/productorEngineers/' + id,
+      method: 'delete'
+  });
+};

+ 165 - 165
src/utils/index.ts

@@ -4,15 +4,15 @@ import { parseTime } from '@/utils/ruoyi';
  * 表格时间格式化
  */
 export const formatDate = (cellValue: string) => {
-  if (cellValue == null || cellValue == '') return '';
-  const date = new Date(cellValue);
-  const year = date.getFullYear();
-  const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
-  const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
-  const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
-  const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
-  const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
-  return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
+    if (cellValue == null || cellValue == '') return '';
+    const date = new Date(cellValue);
+    const year = date.getFullYear();
+    const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
+    const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
+    const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
+    const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
+    const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
+    return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
 };
 
 /**
@@ -21,32 +21,32 @@ export const formatDate = (cellValue: string) => {
  * @returns {string}
  */
 export const formatTime = (time: string, option: string) => {
-  let t: number;
-  if (('' + time).length === 10) {
-    t = parseInt(time) * 1000;
-  } else {
-    t = +time;
-  }
-  const d: any = new Date(t);
-  const now = Date.now();
+    let t: number;
+    if (('' + time).length === 10) {
+        t = parseInt(time) * 1000;
+    } else {
+        t = +time;
+    }
+    const d: any = new Date(t);
+    const now = Date.now();
 
-  const diff = (now - d) / 1000;
+    const diff = (now - d) / 1000;
 
-  if (diff < 30) {
-    return '刚刚';
-  } else if (diff < 3600) {
-    // less 1 hour
-    return Math.ceil(diff / 60) + '分钟前';
-  } else if (diff < 3600 * 24) {
-    return Math.ceil(diff / 3600) + '小时前';
-  } else if (diff < 3600 * 24 * 2) {
-    return '1天前';
-  }
-  if (option) {
-    return parseTime(t, option);
-  } else {
-    return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分';
-  }
+    if (diff < 30) {
+        return '刚刚';
+    } else if (diff < 3600) {
+        // less 1 hour
+        return Math.ceil(diff / 60) + '分钟前';
+    } else if (diff < 3600 * 24) {
+        return Math.ceil(diff / 3600) + '小时前';
+    } else if (diff < 3600 * 24 * 2) {
+        return '1天前';
+    }
+    if (option) {
+        return parseTime(t, option);
+    } else {
+        return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分';
+    }
 };
 
 /**
@@ -54,18 +54,18 @@ export const formatTime = (time: string, option: string) => {
  * @returns {Object}
  */
 export const getQueryObject = (url: string) => {
-  url = url == null ? window.location.href : url;
-  const search = url.substring(url.lastIndexOf('?') + 1);
-  const obj: { [key: string]: string } = {};
-  const reg = /([^?&=]+)=([^?&=]*)/g;
-  search.replace(reg, (rs, $1, $2) => {
-    const name = decodeURIComponent($1);
-    let val = decodeURIComponent($2);
-    val = String(val);
-    obj[name] = val;
-    return rs;
-  });
-  return obj;
+    url = url == null ? window.location.href : url;
+    const search = url.substring(url.lastIndexOf('?') + 1);
+    const obj: { [key: string]: string } = {};
+    const reg = /([^?&=]+)=([^?&=]*)/g;
+    search.replace(reg, (rs, $1, $2) => {
+        const name = decodeURIComponent($1);
+        let val = decodeURIComponent($2);
+        val = String(val);
+        obj[name] = val;
+        return rs;
+    });
+    return obj;
 };
 
 /**
@@ -73,15 +73,15 @@ export const getQueryObject = (url: string) => {
  * @returns {number} output value
  */
 export const byteLength = (str: string) => {
-  // returns the byte length of an utf8 string
-  let s = str.length;
-  for (let i = str.length - 1; i >= 0; i--) {
-    const code = str.charCodeAt(i);
-    if (code > 0x7f && code <= 0x7ff) s++;
-    else if (code > 0x7ff && code <= 0xffff) s += 2;
-    if (code >= 0xdc00 && code <= 0xdfff) i--;
-  }
-  return s;
+    // returns the byte length of an utf8 string
+    let s = str.length;
+    for (let i = str.length - 1; i >= 0; i--) {
+        const code = str.charCodeAt(i);
+        if (code > 0x7f && code <= 0x7ff) s++;
+        else if (code > 0x7ff && code <= 0xffff) s += 2;
+        if (code >= 0xdc00 && code <= 0xdfff) i--;
+    }
+    return s;
 };
 
 /**
@@ -89,13 +89,13 @@ export const byteLength = (str: string) => {
  * @returns {Array}
  */
 export const cleanArray = (actual: Array<any>) => {
-  const newArray: any[] = [];
-  for (let i = 0; i < actual.length; i++) {
-    if (actual[i]) {
-      newArray.push(actual[i]);
+    const newArray: any[] = [];
+    for (let i = 0; i < actual.length; i++) {
+        if (actual[i]) {
+            newArray.push(actual[i]);
+        }
     }
-  }
-  return newArray;
+    return newArray;
 };
 
 /**
@@ -103,13 +103,13 @@ export const cleanArray = (actual: Array<any>) => {
  * @returns {Array}
  */
 export const param = (json: any) => {
-  if (!json) return '';
-  return cleanArray(
-    Object.keys(json).map((key) => {
-      if (json[key] === undefined) return '';
-      return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
-    })
-  ).join('&');
+    if (!json) return '';
+    return cleanArray(
+        Object.keys(json).map((key) => {
+            if (json[key] === undefined) return '';
+            return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
+        })
+    ).join('&');
 };
 
 /**
@@ -117,21 +117,21 @@ export const param = (json: any) => {
  * @returns {Object}
  */
 export const param2Obj = (url: string) => {
-  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ');
-  if (!search) {
-    return {};
-  }
-  const obj: any = {};
-  const searchArr = search.split('&');
-  searchArr.forEach((v) => {
-    const index = v.indexOf('=');
-    if (index !== -1) {
-      const name = v.substring(0, index);
-      const val = v.substring(index + 1, v.length);
-      obj[name] = val;
+    const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ');
+    if (!search) {
+        return {};
     }
-  });
-  return obj;
+    const obj: any = {};
+    const searchArr = search.split('&');
+    searchArr.forEach((v) => {
+        const index = v.indexOf('=');
+        if (index !== -1) {
+            const name = v.substring(0, index);
+            const val = v.substring(index + 1, v.length);
+            obj[name] = val;
+        }
+    });
+    return obj;
 };
 
 /**
@@ -139,9 +139,9 @@ export const param2Obj = (url: string) => {
  * @returns {string}
  */
 export const html2Text = (val: string) => {
-  const div = document.createElement('div');
-  div.innerHTML = val;
-  return div.textContent || div.innerText;
+    const div = document.createElement('div');
+    div.innerHTML = val;
+    return div.textContent || div.innerText;
 };
 
 /**
@@ -151,21 +151,21 @@ export const html2Text = (val: string) => {
  * @returns {Object}
  */
 export const objectMerge = (target: any, source: any | any[]) => {
-  if (typeof target !== 'object') {
-    target = {};
-  }
-  if (Array.isArray(source)) {
-    return source.slice();
-  }
-  Object.keys(source).forEach((property) => {
-    const sourceProperty = source[property];
-    if (typeof sourceProperty === 'object') {
-      target[property] = objectMerge(target[property], sourceProperty);
-    } else {
-      target[property] = sourceProperty;
+    if (typeof target !== 'object') {
+        target = {};
     }
-  });
-  return target;
+    if (Array.isArray(source)) {
+        return source.slice();
+    }
+    Object.keys(source).forEach((property) => {
+        const sourceProperty = source[property];
+        if (typeof sourceProperty === 'object') {
+            target[property] = objectMerge(target[property], sourceProperty);
+        } else {
+            target[property] = sourceProperty;
+        }
+    });
+    return target;
 };
 
 /**
@@ -173,17 +173,17 @@ export const objectMerge = (target: any, source: any | any[]) => {
  * @param {string} className
  */
 export const toggleClass = (element: HTMLElement, className: string) => {
-  if (!element || !className) {
-    return;
-  }
-  let classString = element.className;
-  const nameIndex = classString.indexOf(className);
-  if (nameIndex === -1) {
-    classString += '' + className;
-  } else {
-    classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length);
-  }
-  element.className = classString;
+    if (!element || !className) {
+        return;
+    }
+    let classString = element.className;
+    const nameIndex = classString.indexOf(className);
+    if (nameIndex === -1) {
+        classString += '' + className;
+    } else {
+        classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length);
+    }
+    element.className = classString;
 };
 
 /**
@@ -191,11 +191,11 @@ export const toggleClass = (element: HTMLElement, className: string) => {
  * @returns {Date}
  */
 export const getTime = (type: string) => {
-  if (type === 'start') {
-    return new Date().getTime() - 3600 * 1000 * 24 * 90;
-  } else {
-    return new Date(new Date().toDateString());
-  }
+    if (type === 'start') {
+        return new Date().getTime() - 3600 * 1000 * 24 * 90;
+    } else {
+        return new Date(new Date().toDateString());
+    }
 };
 
 /**
@@ -205,37 +205,37 @@ export const getTime = (type: string) => {
  * @return {*}
  */
 export const debounce = (func: any, wait: number, immediate: boolean) => {
-  let timeout: any, args: any, context: any, timestamp: any, result: any;
+    let timeout: any, args: any, context: any, timestamp: any, result: any;
 
-  const later = function () {
-    // 据上一次触发时间间隔
-    const last = +new Date() - timestamp;
+    const later = function () {
+        // 据上一次触发时间间隔
+        const last = +new Date() - timestamp;
 
-    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
-    if (last < wait && last > 0) {
-      timeout = setTimeout(later, wait - last);
-    } else {
-      timeout = null;
-      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
-      if (!immediate) {
-        result = func.apply(context, args);
-        if (!timeout) context = args = null;
-      }
-    }
-  };
+        // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
+        if (last < wait && last > 0) {
+            timeout = setTimeout(later, wait - last);
+        } else {
+            timeout = null;
+            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+            if (!immediate) {
+                result = func.apply(context, args);
+                if (!timeout) context = args = null;
+            }
+        }
+    };
 
-  return (...args: any) => {
-    context = this;
-    timestamp = +new Date();
-    const callNow = immediate && !timeout;
-    // 如果延时不存在,重新设定延时
-    if (!timeout) timeout = setTimeout(later, wait);
-    if (callNow) {
-      result = func.apply(context, args);
-      context = args = null;
-    }
-    return result;
-  };
+    return (...args: any) => {
+        context = this;
+        timestamp = +new Date();
+        const callNow = immediate && !timeout;
+        // 如果延时不存在,重新设定延时
+        if (!timeout) timeout = setTimeout(later, wait);
+        if (callNow) {
+            result = func.apply(context, args);
+            context = args = null;
+        }
+        return result;
+    };
 };
 
 /**
@@ -246,18 +246,18 @@ export const debounce = (func: any, wait: number, immediate: boolean) => {
  * @returns {Object}
  */
 export const deepClone = (source: any) => {
-  if (!source && typeof source !== 'object') {
-    throw new Error('error arguments', 'deepClone' as any);
-  }
-  const targetObj: any = source.constructor === Array ? [] : {};
-  Object.keys(source).forEach((keys) => {
-    if (source[keys] && typeof source[keys] === 'object') {
-      targetObj[keys] = deepClone(source[keys]);
-    } else {
-      targetObj[keys] = source[keys];
+    if (!source && typeof source !== 'object') {
+        throw new Error('error arguments', 'deepClone' as any);
     }
-  });
-  return targetObj;
+    const targetObj: any = source.constructor === Array ? [] : {};
+    Object.keys(source).forEach((keys) => {
+        if (source[keys] && typeof source[keys] === 'object') {
+            targetObj[keys] = deepClone(source[keys]);
+        } else {
+            targetObj[keys] = source[keys];
+        }
+    });
+    return targetObj;
 };
 
 /**
@@ -265,17 +265,17 @@ export const deepClone = (source: any) => {
  * @returns {Array}
  */
 export const uniqueArr = (arr: any) => {
-  return Array.from(new Set(arr));
+    return Array.from(new Set(arr));
 };
 
 /**
  * @returns {string}
  */
 export const createUniqueString = (): string => {
-  const timestamp = +new Date() + '';
-  const num = (1 + Math.random()) * 65536;
-  const randomNum = parseInt(num + '');
-  return (+(randomNum + timestamp)).toString(32);
+    const timestamp = +new Date() + '';
+    const num = (1 + Math.random()) * 65536;
+    const randomNum = parseInt(num + '');
+    return (+(randomNum + timestamp)).toString(32);
 };
 
 /**
@@ -285,7 +285,7 @@ export const createUniqueString = (): string => {
  * @returns {boolean}
  */
 export const hasClass = (ele: HTMLElement, cls: string): boolean => {
-  return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
+    return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
 };
 
 /**
@@ -294,7 +294,7 @@ export const hasClass = (ele: HTMLElement, cls: string): boolean => {
  * @param {string} cls
  */
 export const addClass = (ele: HTMLElement, cls: string) => {
-  if (!hasClass(ele, cls)) ele.className += ' ' + cls;
+    if (!hasClass(ele, cls)) ele.className += ' ' + cls;
 };
 
 /**
@@ -303,10 +303,10 @@ export const addClass = (ele: HTMLElement, cls: string) => {
  * @param {string} cls
  */
 export const removeClass = (ele: HTMLElement, cls: string) => {
-  if (hasClass(ele, cls)) {
-    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
-    ele.className = ele.className.replace(reg, ' ');
-  }
+    if (hasClass(ele, cls)) {
+        const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
+        ele.className = ele.className.replace(reg, ' ');
+    }
 };
 
 /**
@@ -314,5 +314,5 @@ export const removeClass = (ele: HTMLElement, cls: string) => {
  * @returns {Boolean}
  */
 export const isExternal = (path: string) => {
-  return /^(https?:|http?:|mailto:|tel:)/.test(path);
+    return /^(https?:|http?:|mailto:|tel:)/.test(path);
 };

+ 188 - 188
src/utils/ruoyi.ts

@@ -1,46 +1,46 @@
 // 日期格式化
 export function parseTime(time: any, pattern?: string) {
-  if (arguments.length === 0 || !time) {
-    return null;
-  }
-  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
-  let date;
-  if (typeof time === 'object') {
-    date = time;
-  } else {
-    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
-      time = parseInt(time);
-    } else if (typeof time === 'string') {
-      time = time
-        .replace(new RegExp(/-/gm), '/')
-        .replace('T', ' ')
-        .replace(new RegExp(/\.[\d]{3}/gm), '');
-    }
-    if (typeof time === 'number' && time.toString().length === 10) {
-      time = time * 1000;
-    }
-    date = new Date(time);
-  }
-  const formatObj: { [key: string]: any } = {
-    y: date.getFullYear(),
-    m: date.getMonth() + 1,
-    d: date.getDate(),
-    h: date.getHours(),
-    i: date.getMinutes(),
-    s: date.getSeconds(),
-    a: date.getDay()
-  };
-  return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => {
-    let value = formatObj[key];
-    // Note: getDay() returns 0 on Sunday
-    if (key === 'a') {
-      return ['日', '一', '二', '三', '四', '五', '六'][value];
-    }
-    if (result.length > 0 && value < 10) {
-      value = '0' + value;
-    }
-    return value || 0;
-  });
+    if (arguments.length === 0 || !time) {
+        return null;
+    }
+    const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
+    let date;
+    if (typeof time === 'object') {
+        date = time;
+    } else {
+        if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
+            time = parseInt(time);
+        } else if (typeof time === 'string') {
+            time = time
+                .replace(new RegExp(/-/gm), '/')
+                .replace('T', ' ')
+                .replace(new RegExp(/\.[\d]{3}/gm), '');
+        }
+        if (typeof time === 'number' && time.toString().length === 10) {
+            time = time * 1000;
+        }
+        date = new Date(time);
+    }
+    const formatObj: { [key: string]: any } = {
+        y: date.getFullYear(),
+        m: date.getMonth() + 1,
+        d: date.getDate(),
+        h: date.getHours(),
+        i: date.getMinutes(),
+        s: date.getSeconds(),
+        a: date.getDay()
+    };
+    return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => {
+        let value = formatObj[key];
+        // Note: getDay() returns 0 on Sunday
+        if (key === 'a') {
+            return ['日', '一', '二', '三', '四', '五', '六'][value];
+        }
+        if (result.length > 0 && value < 10) {
+            value = '0' + value;
+        }
+        return value || 0;
+    });
 }
 
 /**
@@ -50,102 +50,102 @@ export function parseTime(time: any, pattern?: string) {
  * @param propName
  */
 export const addDateRange = (params: any, dateRange: any[], propName?: string) => {
-  const search = params;
-  search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
-  dateRange = Array.isArray(dateRange) ? dateRange : [];
-  if (typeof propName === 'undefined') {
-    search.params['beginTime'] = dateRange[0];
-    search.params['endTime'] = dateRange[1];
-  } else {
-    search.params['begin' + propName] = dateRange[0];
-    search.params['end' + propName] = dateRange[1];
-  }
-  return search;
+    const search = params;
+    search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
+    dateRange = Array.isArray(dateRange) ? dateRange : [];
+    if (typeof propName === 'undefined') {
+        search.params['beginTime'] = dateRange[0];
+        search.params['endTime'] = dateRange[1];
+    } else {
+        search.params['begin' + propName] = dateRange[0];
+        search.params['end' + propName] = dateRange[1];
+    }
+    return search;
 };
 
 // 回显数据字典
 export const selectDictLabel = (datas: any, value: number | string) => {
-  if (value === undefined) {
-    return '';
-  }
-  const actions: Array<string | number> = [];
-  Object.keys(datas).some((key) => {
-    if (datas[key].value == '' + value) {
-      actions.push(datas[key].label);
-      return true;
-    }
-  });
-  if (actions.length === 0) {
-    actions.push(value);
-  }
-  return actions.join('');
+    if (value === undefined) {
+        return '';
+    }
+    const actions: Array<string | number> = [];
+    Object.keys(datas).some((key) => {
+        if (datas[key].value == '' + value) {
+            actions.push(datas[key].label);
+            return true;
+        }
+    });
+    if (actions.length === 0) {
+        actions.push(value);
+    }
+    return actions.join('');
 };
 
 // 回显数据字典(字符串数组)
 export const selectDictLabels = (datas: any, value: any, separator: any) => {
-  if (value === undefined || value.length === 0) {
-    return '';
-  }
-  if (Array.isArray(value)) {
-    value = value.join(',');
-  }
-  const actions: any[] = [];
-  const currentSeparator = undefined === separator ? ',' : separator;
-  const temp = value.split(currentSeparator);
-  Object.keys(value.split(currentSeparator)).some((val) => {
-    let match = false;
-    Object.keys(datas).some((key) => {
-      if (datas[key].value == '' + temp[val]) {
-        actions.push(datas[key].label + currentSeparator);
-        match = true;
-      }
-    });
-    if (!match) {
-      actions.push(temp[val] + currentSeparator);
+    if (value === undefined || value.length === 0) {
+        return '';
+    }
+    if (Array.isArray(value)) {
+        value = value.join(',');
     }
-  });
-  return actions.join('').substring(0, actions.join('').length - 1);
+    const actions: any[] = [];
+    const currentSeparator = undefined === separator ? ',' : separator;
+    const temp = value.split(currentSeparator);
+    Object.keys(value.split(currentSeparator)).some((val) => {
+        let match = false;
+        Object.keys(datas).some((key) => {
+            if (datas[key].value == '' + temp[val]) {
+                actions.push(datas[key].label + currentSeparator);
+                match = true;
+            }
+        });
+        if (!match) {
+            actions.push(temp[val] + currentSeparator);
+        }
+    });
+    return actions.join('').substring(0, actions.join('').length - 1);
 };
 
 // 字符串格式化(%s )
 export function sprintf(str: string) {
-  if (arguments.length !== 0) {
-    let flag = true,
-      i = 1;
-    str = str.replace(/%s/g, function () {
-      const arg = arguments[i++];
-      if (typeof arg === 'undefined') {
-        flag = false;
-        return '';
-      }
-      return arg;
-    });
-    return flag ? str : '';
-  }
+    if (arguments.length !== 0) {
+        let flag = true,
+            i = 1;
+        str = str.replace(/%s/g, function () {
+            const arg = arguments[i++];
+            if (typeof arg === 'undefined') {
+                flag = false;
+                return '';
+            }
+            return arg;
+        });
+        return flag ? str : '';
+    }
 }
 
 // 转换字符串,undefined,null等转化为""
 export const parseStrEmpty = (str: any) => {
-  if (!str || str == 'undefined' || str == 'null') {
-    return '';
-  }
-  return str;
+    if (!str || str == 'undefined' || str == 'null') {
+        return '';
+    }
+    return str;
 };
 
 // 数据合并
 export const mergeRecursive = (source: any, target: any) => {
-  for (const p in target) {
-    try {
-      if (target[p].constructor == Object) {
-        source[p] = mergeRecursive(source[p], target[p]);
-      } else {
-        source[p] = target[p];
-      }
-    } catch (e) {
-      source[p] = target[p];
-    }
-  }
-  return source;
+    for (const p in target) {
+        try {
+            if (target[p].constructor == Object) {
+                source[p] = mergeRecursive(source[p], target[p]);
+            } else {
+                source[p] = target[p];
+            }
+        } catch (e) {
+            source[p] = target[p];
+        }
+    }
+    return source;
 };
 
 /**
@@ -156,51 +156,51 @@ export const mergeRecursive = (source: any, target: any) => {
  * @param {*} children 孩子节点字段 默认 'children'
  */
 export const handleTree = <T>(data: any[], id?: string, parentId?: string, children?: string): T[] => {
-  const config: {
-    id: string;
-    parentId: string;
-    childrenList: string;
-  } = {
-    id: id || 'id',
-    parentId: parentId || 'parentId',
-    childrenList: children || 'children'
-  };
-
-  const childrenListMap: any = {};
-  const nodeIds: any = {};
-  const tree: T[] = [];
-
-  for (const d of data) {
-    const parentId = d[config.parentId];
-    if (childrenListMap[parentId] == null) {
-      childrenListMap[parentId] = [];
-    }
-    nodeIds[d[config.id]] = d;
-    childrenListMap[parentId].push(d);
-  }
-
-  for (const d of data) {
-    const parentId = d[config.parentId];
-    if (nodeIds[parentId] == null) {
-      tree.push(d);
-    }
-  }
-  const adaptToChildrenList = (o: any) => {
-    if (childrenListMap[o[config.id]] !== null) {
-      o[config.childrenList] = childrenListMap[o[config.id]];
-    }
-    if (o[config.childrenList]) {
-      for (const c of o[config.childrenList]) {
-        adaptToChildrenList(c);
-      }
-    }
-  };
-
-  for (const t of tree) {
-    adaptToChildrenList(t);
-  }
-
-  return tree;
+    const config: {
+        id: string;
+        parentId: string;
+        childrenList: string;
+    } = {
+        id: id || 'id',
+        parentId: parentId || 'parentId',
+        childrenList: children || 'children'
+    };
+
+    const childrenListMap: any = {};
+    const nodeIds: any = {};
+    const tree: T[] = [];
+
+    for (const d of data) {
+        const parentId = d[config.parentId];
+        if (childrenListMap[parentId] == null) {
+            childrenListMap[parentId] = [];
+        }
+        nodeIds[d[config.id]] = d;
+        childrenListMap[parentId].push(d);
+    }
+
+    for (const d of data) {
+        const parentId = d[config.parentId];
+        if (nodeIds[parentId] == null) {
+            tree.push(d);
+        }
+    }
+    const adaptToChildrenList = (o: any) => {
+        if (childrenListMap[o[config.id]] !== null) {
+            o[config.childrenList] = childrenListMap[o[config.id]];
+        }
+        if (o[config.childrenList]) {
+            for (const c of o[config.childrenList]) {
+                adaptToChildrenList(c);
+            }
+        }
+    };
+
+    for (const t of tree) {
+        adaptToChildrenList(t);
+    }
+
+    return tree;
 };
 
 /**
@@ -208,44 +208,44 @@ export const handleTree = <T>(data: any[], id?: string, parentId?: string, child
  * @param {*} params  参数
  */
 export const tansParams = (params: any) => {
-  let result = '';
-  for (const propName of Object.keys(params)) {
-    const value = params[propName];
-    const part = encodeURIComponent(propName) + '=';
-    if (value !== null && value !== '' && typeof value !== 'undefined') {
-      if (typeof value === 'object') {
-        for (const key of Object.keys(value)) {
-          if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
-            const params = propName + '[' + key + ']';
-            const subPart = encodeURIComponent(params) + '=';
-            result += subPart + encodeURIComponent(value[key]) + '&';
-          }
+    let result = '';
+    for (const propName of Object.keys(params)) {
+        const value = params[propName];
+        const part = encodeURIComponent(propName) + '=';
+        if (value !== null && value !== '' && typeof value !== 'undefined') {
+            if (typeof value === 'object') {
+                for (const key of Object.keys(value)) {
+                    if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
+                        const params = propName + '[' + key + ']';
+                        const subPart = encodeURIComponent(params) + '=';
+                        result += subPart + encodeURIComponent(value[key]) + '&';
+                    }
+                }
+            } else {
+                result += part + encodeURIComponent(value) + '&';
+            }
         }
-      } else {
-        result += part + encodeURIComponent(value) + '&';
-      }
     }
-  }
-  return result;
+    return result;
 };
 
 // 返回项目路径
 export const getNormalPath = (p: string): string => {
-  if (p.length === 0 || !p || p === 'undefined') {
-    return p;
-  }
-  const res = p.replace('//', '/');
-  if (res[res.length - 1] === '/') {
-    return res.slice(0, res.length - 1);
-  }
-  return res;
+    if (p.length === 0 || !p || p === 'undefined') {
+        return p;
+    }
+    const res = p.replace('//', '/');
+    if (res[res.length - 1] === '/') {
+        return res.slice(0, res.length - 1);
+    }
+    return res;
 };
 
 // 验证是否为blob格式
 export const blobValidate = (data: any) => {
-  return data.type !== 'application/json';
+    return data.type !== 'application/json';
 };
 
 export default {
-  handleTree
+    handleTree
 };

+ 502 - 502
src/views/dashboard/dashboard.vue

@@ -1,588 +1,588 @@
 <template>
-    <div class="dashbord">
-        <div class="chart-group">
-            <el-card style="margin-right: 2px">
-                <SubTitle title="设备运行" />
-                <div class="chart-content">
-                    <img class="positionImg" src="@/assets/images/position.png" alt="" />
+  <div class="dashbord">
+    <div class="chart-group">
+      <el-card style="margin-right: 2px">
+        <SubTitle title="设备运行" />
+        <div class="chart-content">
+          <img class="positionImg" src="@/assets/images/position.png" alt="" />
+        </div>
+      </el-card>
+      <div class="chart-two" style="padding-left: 4px">
+        <el-card>
+          <SubTitle title="设备总览" />
+          <div class="chart-content">
+            <BaseChart width="100%" height="100%" :option="equipOption" />
+          </div>
+        </el-card>
+        <el-card>
+          <SubTitle title="告警处理" />
+          <div class="chart-content">
+            <el-table class="warningTable" :data="warningData" border style="margin-top: 10px" max-height="300">
+              <el-table-column prop="deviceNo" align="center" label="设备编号" />
+              <el-table-column prop="warningDate" align="center" width="100" label="告警时间" />
+              <el-table-column prop="warningType" align="center" label="告警类型" />
+              <el-table-column label="操作" align="center" fixed="right" width="70">
+                <template #default="scope">
+                  <div class="warnBtns">
+                    <el-button link size="small" type="primary" text>详情</el-button>
+                    <el-button link size="small" type="danger" text>处理</el-button>
+                  </div>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-card>
+      </div>
+    </div>
+    <div class="chart-group">
+      <div class="chart-two">
+        <el-card>
+          <SubTitle title="巡检人员" />
+          <div class="chart-content">
+            <div class="check-summary">
+              <div>
+                <img src="@/assets/images/home/banzu.svg" alt="" />
+                <div class="check-name">
+                  <div>8</div>
+                  <div>班组</div>
+                </div>
+              </div>
+              <div>
+                <img src="@/assets/images/home/person.svg" alt="" />
+                <div class="check-name">
+                  <div>40</div>
+                  <div>巡检人员</div>
                 </div>
-            </el-card>
-            <div class="chart-two" style="padding-left: 4px">
-                <el-card>
-                    <SubTitle title="设备总览" />
-                    <div class="chart-content">
-                        <BaseChart width="100%" height="100%" :option="equipOption" />
-                    </div>
-                </el-card>
-                <el-card>
-                    <SubTitle title="告警处理" />
-                    <div class="chart-content">
-                        <el-table class="warningTable" :data="warningData" border style="margin-top: 10px" max-height="300">
-                            <el-table-column prop="deviceNo" align="center" label="设备编号" />
-                            <el-table-column prop="warningDate" align="center" width="100" label="告警时间" />
-                            <el-table-column prop="warningType" align="center" label="告警类型" />
-                            <el-table-column label="操作" align="center" fixed="right" width="70">
-                                <template #default="scope">
-                                    <div class="warnBtns">
-                                        <el-button link size="small" type="primary" text>详情</el-button>
-                                        <el-button link size="small" type="danger" text>处理</el-button>
-                                    </div>
-                                </template>
-                            </el-table-column>
-                        </el-table>
-                    </div>
-                </el-card>
+              </div>
             </div>
-        </div>
-        <div class="chart-group">
-            <div class="chart-two">
-                <el-card>
-                    <SubTitle title="巡检人员" />
-                    <div class="chart-content">
-                        <div class="check-summary">
-                            <div>
-                                <img src="@/assets/images/home/banzu.svg" alt="" />
-                                <div class="check-name">
-                                    <div>8</div>
-                                    <div>班组</div>
-                                </div>
-                            </div>
-                            <div>
-                                <img src="@/assets/images/home/person.svg" alt="" />
-                                <div class="check-name">
-                                    <div>40</div>
-                                    <div>巡检人员</div>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="check-rank">
-                            <div>当月巡检排名</div>
-                            <CustomTabs v-model:active="checkActive" :tabs="checkTabs" />
-                        </div>
-                        <el-table :data="tableData" style="width: 100%; margin-top: 10px" max-height="200">
-                            <el-table-column prop="name" label="巡检员">
-                                <template #default="scope">
-                                    {{ `${scope.$index + 1}、${scope.row.name}` }}
-                                </template>
-                            </el-table-column>
-
-                            <el-table-column prop="group" label="所在班组" />
-                            <el-table-column prop="num" label="数量" />
-                        </el-table>
-                    </div>
-                </el-card>
-                <el-card>
-                    <SubTitle title="售后工程师" />
-                    <div class="chart-content">
-                        <div class="check-summary">
-                            <div>
-                                <img src="@/assets/images/home/banzu.svg" alt="" />
-                                <div class="check-name">
-                                    <div>16</div>
-                                    <div>供应商</div>
-                                </div>
-                            </div>
-                            <div>
-                                <img src="@/assets/images/home/person.svg" alt="" />
-                                <div class="check-name">
-                                    <div>62</div>
-                                    <div>售后工程师</div>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="check-rank">
-                            <div>累计故障处理</div>
-                            <CustomTabs v-model:active="serviceActive" :tabs="serviceTabs" />
-                        </div>
-                        <el-table :data="serviceData" style="width: 100%; margin-top: 10px" max-height="200">
-                            <el-table-column prop="name" label="售后工程师">
-                                <template #default="scope">
-                                    {{ `${scope.$index + 1}、${scope.row.name}` }}
-                                </template>
-                            </el-table-column>
-
-                            <el-table-column prop="group" label="所属供应商" />
-                            <el-table-column prop="num" label="工单完成率" />
-                        </el-table>
-                    </div>
-                </el-card>
+            <div class="check-rank">
+              <div>当月巡检排名</div>
+              <CustomTabs v-model:active="checkActive" :tabs="checkTabs" />
             </div>
-            <el-card>
-                <SubTitle title="运营数据" />
-                <div class="chart-content">
-                    <BaseChart width="100%" height="50%" :option="checkOption" />
-                    <BaseChart width="100%" height="50%" :option="lineOption" />
+            <el-table :data="tableData" style="width: 100%; margin-top: 10px" max-height="200">
+              <el-table-column prop="name" label="巡检员">
+                <template #default="scope">
+                  {{ `${scope.$index + 1}、${scope.row.name}` }}
+                </template>
+              </el-table-column>
+
+              <el-table-column prop="group" label="所在班组" />
+              <el-table-column prop="num" label="数量" />
+            </el-table>
+          </div>
+        </el-card>
+        <el-card>
+          <SubTitle title="售后工程师" />
+          <div class="chart-content">
+            <div class="check-summary">
+              <div>
+                <img src="@/assets/images/home/banzu.svg" alt="" />
+                <div class="check-name">
+                  <div>16</div>
+                  <div>供应商</div>
+                </div>
+              </div>
+              <div>
+                <img src="@/assets/images/home/person.svg" alt="" />
+                <div class="check-name">
+                  <div>62</div>
+                  <div>售后工程师</div>
                 </div>
-            </el-card>
+              </div>
+            </div>
+            <div class="check-rank">
+              <div>累计故障处理</div>
+              <CustomTabs v-model:active="serviceActive" :tabs="serviceTabs" />
+            </div>
+            <el-table :data="serviceData" style="width: 100%; margin-top: 10px" max-height="200">
+              <el-table-column prop="name" label="售后工程师">
+                <template #default="scope">
+                  {{ `${scope.$index + 1}、${scope.row.name}` }}
+                </template>
+              </el-table-column>
+
+              <el-table-column prop="group" label="所属供应商" />
+              <el-table-column prop="num" label="工单完成率" />
+            </el-table>
+          </div>
+        </el-card>
+      </div>
+      <el-card>
+        <SubTitle title="运营数据" />
+        <div class="chart-content">
+          <BaseChart width="100%" height="50%" :option="checkOption" />
+          <BaseChart width="100%" height="50%" :option="lineOption" />
         </div>
+      </el-card>
     </div>
+  </div>
 </template>
 <script setup lang="ts">
 import CustomTabs from './components/CustomTabs.vue';
 const checkTabs = [
-    {
-        name: '按巡检设备数',
-        value: '1'
-    },
-    {
-        name: '按发现故障数',
-        value: '2'
-    }
+  {
+    name: '按巡检设备数',
+    value: '1'
+  },
+  {
+    name: '按发现故障数',
+    value: '2'
+  }
 ];
 const serviceTabs = [
-    {
-        name: '按完成率',
-        value: '1'
-    },
-    {
-        name: '按专业度',
-        value: '2'
-    },
-    {
-        name: '按服务态度',
-        value: '3'
-    }
+  {
+    name: '按完成率',
+    value: '1'
+  },
+  {
+    name: '按专业度',
+    value: '2'
+  },
+  {
+    name: '按服务态度',
+    value: '3'
+  }
 ];
 const checkActive = ref('1');
 const serviceActive = ref('1');
 const tableData = [
-    {
-        name: '王刚',
-        group: '班组4',
-        num: '123'
-    },
-    {
-        name: '李思',
-        group: '班组2',
-        num: '88'
-    },
-    {
-        name: '张伟',
-        group: '班组3',
-        num: '65'
-    },
-    {
-        name: '张强',
-        group: '班组1',
-        num: '54'
-    }
+  {
+    name: '王刚',
+    group: '班组4',
+    num: '123'
+  },
+  {
+    name: '李思',
+    group: '班组2',
+    num: '88'
+  },
+  {
+    name: '张伟',
+    group: '班组3',
+    num: '65'
+  },
+  {
+    name: '张强',
+    group: '班组1',
+    num: '54'
+  }
 ];
 const serviceData = [
-    {
-        name: '王伟',
-        group: '供应商3',
-        num: '100%'
-    },
-    {
-        name: '李成',
-        group: '供应商2',
-        num: '88%'
-    },
-    {
-        name: '张雷',
-        group: '供应商1',
-        num: '65%'
-    },
-    {
-        name: '刘飞',
-        group: '供应商4',
-        num: '54%'
-    }
+  {
+    name: '王伟',
+    group: '供应商3',
+    num: '100%'
+  },
+  {
+    name: '李成',
+    group: '供应商2',
+    num: '88%'
+  },
+  {
+    name: '张雷',
+    group: '供应商1',
+    num: '65%'
+  },
+  {
+    name: '刘飞',
+    group: '供应商4',
+    num: '54%'
+  }
 ];
 const warningData = [
-    {
-        deviceNo: 'No12332',
-        warningDate: '2024-10-30 11:45:00',
-        warningType: '故障告警'
-    },
-    {
-        deviceNo: 'No125',
-        warningDate: '2024-10-30 11:25:00',
-        warningType: '离线告警'
-    },
-    {
-        deviceNo: 'No1282',
-        warningDate: '2024-10-30 10:45:00',
-        warningType: '故障告警'
-    },
-    {
-        deviceNo: 'No1236',
-        warningDate: '2024-10-30 09:45:00',
-        warningType: '故障告警'
-    }
+  {
+    deviceNo: 'No12332',
+    warningDate: '2024-10-30 11:45:00',
+    warningType: '故障告警'
+  },
+  {
+    deviceNo: 'No125',
+    warningDate: '2024-10-30 11:25:00',
+    warningType: '离线告警'
+  },
+  {
+    deviceNo: 'No1282',
+    warningDate: '2024-10-30 10:45:00',
+    warningType: '故障告警'
+  },
+  {
+    deviceNo: 'No1236',
+    warningDate: '2024-10-30 09:45:00',
+    warningType: '故障告警'
+  }
 ];
 const equipData = ref<any>([
-    {
-        name: '报废',
-        value: 1546
-    },
-    {
-        name: '维修中',
-        value: 189
-    },
-    {
-        name: '故障',
-        value: 1452
-    },
-    {
-        name: '其他',
-        value: 189
-    },
-    {
-        name: '正在运行',
-        value: 600
-    },
-    {
-        name: '停役',
-        value: 200
-    }
+  {
+    name: '报废',
+    value: 1546
+  },
+  {
+    name: '维修中',
+    value: 189
+  },
+  {
+    name: '故障',
+    value: 1452
+  },
+  {
+    name: '其他',
+    value: 189
+  },
+  {
+    name: '正在运行',
+    value: 600
+  },
+  {
+    name: '停役',
+    value: 200
+  }
 ]);
 const color = ['#1990FF', '#8543E0', '#30C25B', '#16C2C2', '#FACC14', '#F04864'];
 equipData.value.forEach((item, index) => {
-    const tag = index % 6;
-    item.itemStyle = {
-        color: color[tag] || ''
-    };
+  const tag = index % 6;
+  item.itemStyle = {
+    color: color[tag] || ''
+  };
 });
 let equipTotal = 0;
 equipData.value.forEach((v) => {
-    equipTotal += v.value;
+  equipTotal += v.value;
 });
 const equipOption = computed(() => {
-    return {
-        legend: {
-            show: true,
-            left: '0%',
-            bottom: '5%',
-            itemGap: 10,
-            borderRadius: 5,
-            itemWidth: 10,
-            icon: 'circle',
-            itemHeight: 10,
-            data: equipData.value,
-            formatter: function (name) {
-                const res = equipData.value.filter((n) => {
-                    return n.name === name;
-                });
-                if (!res.length) return;
-                return `${name}  ${res[0].value ? ((res[0].value / equipTotal) * 100).toFixed(2) : 0}%  ${res[0].value}`;
-            }
-        },
-        tooltip: {
-            trigger: 'item',
-            backgroundColor: 'rgba(13,5,30,.6)',
-            borderWidth: 1,
-            borderColor: '#32A1FF',
-            padding: 5,
-            textStyle: {
-                color: '#fff'
-            },
-            formatter: (params) => {
-                return `${params.name}<br/>${params.marker}${params.value}`;
-            }
+  return {
+    legend: {
+      show: true,
+      left: '0%',
+      bottom: '5%',
+      itemGap: 10,
+      borderRadius: 5,
+      itemWidth: 10,
+      icon: 'circle',
+      itemHeight: 10,
+      data: equipData.value,
+      formatter: function (name) {
+        const res = equipData.value.filter((n) => {
+          return n.name === name;
+        });
+        if (!res.length) return;
+        return `${name}  ${res[0].value ? ((res[0].value / equipTotal) * 100).toFixed(2) : 0}%  ${res[0].value}`;
+      }
+    },
+    tooltip: {
+      trigger: 'item',
+      backgroundColor: 'rgba(13,5,30,.6)',
+      borderWidth: 1,
+      borderColor: '#32A1FF',
+      padding: 5,
+      textStyle: {
+        color: '#fff'
+      },
+      formatter: (params) => {
+        return `${params.name}<br/>${params.marker}${params.value}`;
+      }
+    },
+    title: {
+      show: true,
+      text: '设备总数',
+      itemGap: 5, //主副标题之间的距离
+      left: 'center',
+      top: '28%',
+      textStyle: {
+        fontSize: 13,
+        color: '#9E9E9E',
+        fontWeight: 'normal'
+      },
+      subtext: '4226',
+      subtextStyle: {
+        fontSize: 18,
+        fontWeight: 500,
+        color: '#333'
+      }
+    },
+    series: [
+      {
+        name: '',
+        type: 'pie',
+        radius: ['40%', '60%'],
+        center: ['50%', '35%'],
+        itemStyle: {
+          borderWidth: 2, //描边线宽
+          borderColor: '#fff'
         },
-        title: {
-            show: true,
-            text: '设备总数',
-            itemGap: 5, //主副标题之间的距离
-            left: 'center',
-            top: '28%',
-            textStyle: {
-                fontSize: 13,
-                color: '#9E9E9E',
-                fontWeight: 'normal'
-            },
-            subtext: '4226',
-            subtextStyle: {
-                fontSize: 18,
-                fontWeight: 500,
-                color: '#333'
-            }
+        label: {
+          show: false
         },
-        series: [
-            {
-                name: '',
-                type: 'pie',
-                radius: ['40%', '60%'],
-                center: ['50%', '35%'],
-                itemStyle: {
-                    borderWidth: 2, //描边线宽
-                    borderColor: '#fff'
-                },
-                label: {
-                    show: false
-                },
-                labelLine: {},
-                data: equipData.value
-            }
-        ]
-    };
+        labelLine: {},
+        data: equipData.value
+      }
+    ]
+  };
 });
 const getRecentWeekDates = () => {
-    const today = new Date();
-    const dates = [];
-    for (let i = 0; i < 7; i++) {
-        // 创建一个新的日期对象,将今天的日期减去i天
-        const date = new Date(today);
-        date.setDate(date.getDate() - i);
+  const today = new Date();
+  const dates = [];
+  for (let i = 0; i < 7; i++) {
+    // 创建一个新的日期对象,将今天的日期减去i天
+    const date = new Date(today);
+    date.setDate(date.getDate() - i);
 
-        // 将日期格式化为 YYYY/MM/DD 或者其他你想要的格式
-        const formattedDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;
-        dates.push(formattedDate);
-    }
-    // 由于我们是从今天开始向过去推算的,所以需要反转数组以使日期递增
-    dates.reverse();
-    return dates;
+    // 将日期格式化为 YYYY/MM/DD 或者其他你想要的格式
+    const formattedDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;
+    dates.push(formattedDate);
+  }
+  // 由于我们是从今天开始向过去推算的,所以需要反转数组以使日期递增
+  dates.reverse();
+  return dates;
 };
 const checkData = getRecentWeekDates().map((item) => ({
-    xData: item.substr(5),
-    yData1: Math.floor(Math.random() * 100),
-    yData2: Math.floor(Math.random() * 100)
+  xData: item.substr(5),
+  yData1: Math.floor(Math.random() * 100),
+  yData2: Math.floor(Math.random() * 100)
 }));
 const checkOption = computed(() => {
-    return {
-        tooltip: {
-            trigger: 'axis',
-            axisPointer: {
-                type: 'shadow'
-            }
-        },
-        legend: {
-            show: true,
-            left: '10%',
-            top: '5%',
-            itemWidth: 15,
-            itemHeight: 10
-        },
-        grid: {
-            left: '3%',
-            right: '4%',
-            bottom: '1%',
-            top: '20%',
-            containLabel: true
-        },
-        xAxis: [
-            {
-                type: 'category',
-                data: checkData.map((item) => item.xData),
-                axisTick: {
-                    alignWithLabel: true
-                }
-            }
-        ],
-        yAxis: [
-            {
-                type: 'value'
-            }
-        ],
-        series: [
-            {
-                name: '巡检设备数',
-                type: 'bar',
-                barWidth: '12',
-                data: checkData.map((item) => item.yData1),
-                itemStyle: {
-                    color: '#6395FA'
-                }
-            },
-            {
-                name: '维修设备数',
-                type: 'bar',
-                barWidth: '12',
-                data: checkData.map((item) => item.yData2),
-                itemStyle: {
-                    color: '#65DAAB'
-                }
-            }
-        ]
-    };
+  return {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow'
+      }
+    },
+    legend: {
+      show: true,
+      left: '10%',
+      top: '5%',
+      itemWidth: 15,
+      itemHeight: 10
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '1%',
+      top: '20%',
+      containLabel: true
+    },
+    xAxis: [
+      {
+        type: 'category',
+        data: checkData.map((item) => item.xData),
+        axisTick: {
+          alignWithLabel: true
+        }
+      }
+    ],
+    yAxis: [
+      {
+        type: 'value'
+      }
+    ],
+    series: [
+      {
+        name: '巡检设备数',
+        type: 'bar',
+        barWidth: '12',
+        data: checkData.map((item) => item.yData1),
+        itemStyle: {
+          color: '#6395FA'
+        }
+      },
+      {
+        name: '维修设备数',
+        type: 'bar',
+        barWidth: '12',
+        data: checkData.map((item) => item.yData2),
+        itemStyle: {
+          color: '#65DAAB'
+        }
+      }
+    ]
+  };
 });
 const lineData = getRecentWeekDates().map((item) => ({
-    xData: item.substr(5),
-    yData1: Math.floor(Math.random() * 100),
-    yData2: Math.floor(Math.random() * 100),
-    yData3: Math.floor(Math.random() * 100)
+  xData: item.substr(5),
+  yData1: Math.floor(Math.random() * 100),
+  yData2: Math.floor(Math.random() * 100),
+  yData3: Math.floor(Math.random() * 100)
 }));
 const lineOption = computed(() => {
-    return {
-        tooltip: {
-            trigger: 'axis',
-            axisPointer: {
-                type: 'cross',
-                label: {
-                    backgroundColor: '#6a7985'
-                }
-            }
+  return {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross',
+        label: {
+          backgroundColor: '#6a7985'
+        }
+      }
+    },
+    legend: {
+      show: true,
+      left: '10%',
+      top: '5%',
+      itemWidth: 20,
+      itemHeight: 10
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      top: '20%',
+      containLabel: true
+    },
+    xAxis: [
+      {
+        type: 'category',
+        boundaryGap: false,
+        data: lineData.map((item) => item.xData)
+      }
+    ],
+    yAxis: [
+      {
+        type: 'value'
+      }
+    ],
+    series: [
+      {
+        name: '故障数',
+        type: 'line',
+        itemStyle: {
+          color: '#6395FA'
+        },
+        areaStyle: {},
+        emphasis: {
+          focus: 'series'
+        },
+        data: lineData.map((item) => item.yData1)
+      },
+      {
+        name: '告警数',
+        type: 'line',
+        itemStyle: {
+          color: '#5AD8A6'
+        },
+        areaStyle: {},
+        emphasis: {
+          focus: 'series'
         },
-        legend: {
-            show: true,
-            left: '10%',
-            top: '5%',
-            itemWidth: 20,
-            itemHeight: 10
+        data: lineData.map((item) => item.yData2)
+      },
+      {
+        name: '离线数',
+        type: 'line',
+        itemStyle: {
+          color: '#5E7092'
         },
-        grid: {
-            left: '3%',
-            right: '4%',
-            bottom: '3%',
-            top: '20%',
-            containLabel: true
+        areaStyle: {},
+        emphasis: {
+          focus: 'series'
         },
-        xAxis: [
-            {
-                type: 'category',
-                boundaryGap: false,
-                data: lineData.map((item) => item.xData)
-            }
-        ],
-        yAxis: [
-            {
-                type: 'value'
-            }
-        ],
-        series: [
-            {
-                name: '故障数',
-                type: 'line',
-                itemStyle: {
-                    color: '#6395FA'
-                },
-                areaStyle: {},
-                emphasis: {
-                    focus: 'series'
-                },
-                data: lineData.map((item) => item.yData1)
-            },
-            {
-                name: '告警数',
-                type: 'line',
-                itemStyle: {
-                    color: '#5AD8A6'
-                },
-                areaStyle: {},
-                emphasis: {
-                    focus: 'series'
-                },
-                data: lineData.map((item) => item.yData2)
-            },
-            {
-                name: '离线数',
-                type: 'line',
-                itemStyle: {
-                    color: '#5E7092'
-                },
-                areaStyle: {},
-                emphasis: {
-                    focus: 'series'
-                },
-                data: lineData.map((item) => item.yData3)
-            }
-        ]
-    };
+        data: lineData.map((item) => item.yData3)
+      }
+    ]
+  };
 });
 </script>
 <style lang="scss" scoped>
 .positionImg {
-    padding: 10px 0;
-    width: 100%;
-    height: 100%;
+  padding: 10px 0;
+  width: 100%;
+  height: 100%;
 }
 
 .chart-group {
-    display: flex;
-    margin-top: 5px;
+  display: flex;
+  margin-top: 5px;
 
-    > div {
-        width: 50%;
-    }
+  >div {
+    width: 50%;
+  }
 
-    .chart-two {
-        display: flex;
+  .chart-two {
+    display: flex;
 
-        > div {
-            flex: 1;
-            flex-shrink: 0;
-        }
+    >div {
+      flex: 1;
+      flex-shrink: 0;
     }
+  }
 
-    :deep(.el-card__body) {
-        height: 100%;
-        padding: 15px 10px !important;
-    }
+  :deep(.el-card__body) {
+    height: 100%;
+    padding: 15px 10px !important;
+  }
 
-    .el-card {
-        height: 350px;
-        background: #fff;
+  .el-card {
+    height: 350px;
+    background: #fff;
 
-        &:not(:first-child) {
-            margin-left: 5px;
-        }
+    &:not(:first-child) {
+      margin-left: 5px;
     }
+  }
 
-    .chart-content {
-        height: calc(100% - 10px);
-        margin-top: 5px;
-        border-top: 1px solid #eaebee;
-    }
+  .chart-content {
+    height: calc(100% - 10px);
+    margin-top: 5px;
+    border-top: 1px solid #eaebee;
+  }
 }
 
 .check-summary {
-    display: flex;
-    margin-top: 10px;
+  display: flex;
+  margin-top: 10px;
 
-    > div {
-        flex: 1;
-        padding: 5px 10px;
-        display: flex;
-        background: #fafbfc;
-        border: 1px solid #e4e5e9;
-        border-radius: 4px;
+  >div {
+    flex: 1;
+    padding: 5px 10px;
+    display: flex;
+    background: #fafbfc;
+    border: 1px solid #e4e5e9;
+    border-radius: 4px;
 
-        &:not(:first-child) {
-            margin-left: 10px;
-        }
+    &:not(:first-child) {
+      margin-left: 10px;
+    }
 
-        img {
-            height: 40px;
-        }
+    img {
+      height: 40px;
+    }
 
-        .check-name {
-            margin-left: 10px;
+    .check-name {
+      margin-left: 10px;
 
-            div:first-child {
-                font-size: 16px;
-                font-weight: 500;
-            }
+      div:first-child {
+        font-size: 16px;
+        font-weight: 500;
+      }
 
-            div:last-child {
-                font-size: 14px;
-            }
-        }
+      div:last-child {
+        font-size: 14px;
+      }
     }
+  }
 }
 
 .check-rank {
-    margin-top: 10px;
-    display: flex;
-    justify-content: space-between;
+  margin-top: 10px;
+  display: flex;
+  justify-content: space-between;
 
-    > div:first-child {
-        font-size: 14px;
-    }
+  >div:first-child {
+    font-size: 14px;
+  }
 }
 
 .warnBtns {
-    display: flex;
+  display: flex;
 
-    .el-button + .el-button {
-        margin-left: 5px;
-    }
+  .el-button+.el-button {
+    margin-left: 5px;
+  }
 }
 
 .warningTable {
-    :deep(.cell) {
-        padding: 0 3px !important;
-        font-size: 12px;
-    }
+  :deep(.cell) {
+    padding: 0 3px !important;
+    font-size: 12px;
+  }
 }
 </style>

+ 679 - 679
src/views/dashboard/dashboardAdmin.vue

@@ -1,352 +1,352 @@
 <template>
-    <div class="dashbord">
-        <div class="chart-group">
-            <el-card>
-                <SubTitle title="设备运行" />
-                <div class="chart-content">
-                    <img class="positionImg" src="@/assets/images/position.png" alt="" />
-                </div>
-            </el-card>
+  <div class="dashbord">
+    <div class="chart-group">
+      <el-card>
+        <SubTitle title="设备运行" />
+        <div class="chart-content">
+          <img class="positionImg" src="@/assets/images/position.png" alt="" />
         </div>
-        <div class="chart-group">
-            <el-card>
-                <SubTitle title="项目情况" />
-                <div class="chart-content">
-                    <BaseChart width="100%" height="100%" :option="projectOption" />
-                </div>
-            </el-card>
-            <el-card>
-                <SubTitle title="设备总览" />
-                <div class="chart-content">
-                    <BaseChart width="100%" height="100%" :option="equipOption" />
-                </div>
-            </el-card>
-            <el-card>
-                <SubTitle title="巡检人员" />
-                <div class="chart-content">
-                    <div class="check-summary">
-                        <div>
-                            <img src="@/assets/images/home/banzu.svg" alt="" />
-                            <div class="check-name">
-                                <div>8</div>
-                                <div>班组</div>
-                            </div>
-                        </div>
-                        <div>
-                            <img src="@/assets/images/home/person.svg" alt="" />
-                            <div class="check-name">
-                                <div>40</div>
-                                <div>巡检人员</div>
-                            </div>
-                        </div>
-                    </div>
-                    <div class="check-rank">
-                        <div>当月巡检排名</div>
-                        <CustomTabs v-model:active="checkActive" :tabs="checkTabs" />
-                    </div>
-                    <el-table :data="tableData" style="width: 100%; margin-top: 10px" max-height="200">
-                        <el-table-column prop="name" label="巡检员">
-                            <template #default="scope">
-                                {{ `${scope.$index + 1}、${scope.row.name}` }}
-                            </template>
-                        </el-table-column>
-
-                        <el-table-column prop="group" label="所在班组" />
-                        <el-table-column prop="num" label="数量" />
-                    </el-table>
-                </div>
-            </el-card>
-            <el-card>
-                <SubTitle title="设备故障" />
-                <div class="chart-content">
-                    <div style="display: flex; justify-content: flex-end; margin-top: 10px">
-                        <CustomTabs v-model:active="faultActive" :tabs="faultTabs" />
-                    </div>
-                    <BaseChart width="100%" height="100%" :option="rankOption" />
-                </div>
-            </el-card>
+      </el-card>
+    </div>
+    <div class="chart-group">
+      <el-card>
+        <SubTitle title="项目情况" />
+        <div class="chart-content">
+          <BaseChart width="100%" height="100%" :option="projectOption" />
+        </div>
+      </el-card>
+      <el-card>
+        <SubTitle title="设备总览" />
+        <div class="chart-content">
+          <BaseChart width="100%" height="100%" :option="equipOption" />
+        </div>
+      </el-card>
+      <el-card>
+        <SubTitle title="巡检人员" />
+        <div class="chart-content">
+          <div class="check-summary">
+            <div>
+              <img src="@/assets/images/home/banzu.svg" alt="" />
+              <div class="check-name">
+                <div>8</div>
+                <div>班组</div>
+              </div>
+            </div>
+            <div>
+              <img src="@/assets/images/home/person.svg" alt="" />
+              <div class="check-name">
+                <div>40</div>
+                <div>巡检人员</div>
+              </div>
+            </div>
+          </div>
+          <div class="check-rank">
+            <div>当月巡检排名</div>
+            <CustomTabs v-model:active="checkActive" :tabs="checkTabs" />
+          </div>
+          <el-table :data="tableData" style="width: 100%; margin-top: 10px" max-height="180">
+            <el-table-column prop="name" label="巡检员">
+              <template #default="scope">
+                {{ `${scope.$index + 1}、${scope.row.name}` }}
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="group" label="所在班组" />
+            <el-table-column prop="num" label="数量" />
+          </el-table>
         </div>
+      </el-card>
+      <el-card>
+        <SubTitle title="设备故障" />
+        <div class="chart-content">
+          <div style="display: flex; justify-content: flex-end; margin-top: 10px">
+            <CustomTabs v-model:active="faultActive" :tabs="faultTabs" />
+          </div>
+          <BaseChart width="100%" height="100%" :option="rankOption" />
+        </div>
+      </el-card>
     </div>
+  </div>
 </template>
 <script setup lang="ts">
 import CustomTabs from './components/CustomTabs.vue';
 const checkTabs = [
-    {
-        name: '按巡检设备数',
-        value: '1'
-    },
-    {
-        name: '按发现故障数',
-        value: '2'
-    }
+  {
+    name: '按巡检设备数',
+    value: '1'
+  },
+  {
+    name: '按发现故障数',
+    value: '2'
+  }
 ];
 const faultTabs = [
-    {
-        name: '按设备类型',
-        value: '1'
-    },
-    {
-        name: '按故障类型',
-        value: '2'
-    }
+  {
+    name: '按设备类型',
+    value: '1'
+  },
+  {
+    name: '按故障类型',
+    value: '2'
+  }
 ];
 const checkActive = ref('1');
 const faultActive = ref('1');
 const tableData = [
-    {
-        name: '王刚',
-        group: '班组4',
-        num: '123'
-    },
-    {
-        name: '李思',
-        group: '班组2',
-        num: '88'
-    },
-    {
-        name: '张伟',
-        group: '班组3',
-        num: '65'
-    },
-    {
-        name: '张强',
-        group: '班组1',
-        num: '54'
-    }
+  {
+    name: '王刚',
+    group: '班组4',
+    num: '123'
+  },
+  {
+    name: '李思',
+    group: '班组2',
+    num: '88'
+  },
+  {
+    name: '张伟',
+    group: '班组3',
+    num: '65'
+  },
+  {
+    name: '张强',
+    group: '班组1',
+    num: '54'
+  }
 ];
 function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, height) {
-    // 计算
-    let midRatio = (startRatio + endRatio) / 2;
-
-    let startRadian = startRatio * Math.PI * 2;
-    let endRadian = endRatio * Math.PI * 2;
-    let midRadian = midRatio * Math.PI * 2;
-
-    // 如果只有一个扇形,则不实现选中效果。
-    if (startRatio === 0 && endRatio === 1) {
-        isSelected = false;
-    }
-
-    // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
-    k = typeof k !== 'undefined' ? k : 1 / 3;
-
-    // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
-    let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
-    let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
-
-    // 计算高亮效果的放大比例(未高亮,则比例为 1)
-    let hoverRate = isHovered ? 1.05 : 1;
-
-    // 返回曲面参数方程
-    return {
-        u: {
-            min: -Math.PI,
-            max: Math.PI * 3,
-            step: Math.PI / 32
-        },
+  // 计算
+  let midRatio = (startRatio + endRatio) / 2;
+
+  let startRadian = startRatio * Math.PI * 2;
+  let endRadian = endRatio * Math.PI * 2;
+  let midRadian = midRatio * Math.PI * 2;
+
+  // 如果只有一个扇形,则不实现选中效果。
+  if (startRatio === 0 && endRatio === 1) {
+    isSelected = false;
+  }
+
+  // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
+  k = typeof k !== 'undefined' ? k : 1 / 3;
+
+  // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
+  let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
+  let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
+
+  // 计算高亮效果的放大比例(未高亮,则比例为 1)
+  let hoverRate = isHovered ? 1.05 : 1;
+
+  // 返回曲面参数方程
+  return {
+    u: {
+      min: -Math.PI,
+      max: Math.PI * 3,
+      step: Math.PI / 32
+    },
 
-        v: {
-            min: 0,
-            max: Math.PI * 2,
-            step: Math.PI / 20
-        },
+    v: {
+      min: 0,
+      max: Math.PI * 2,
+      step: Math.PI / 20
+    },
 
-        x: function (u, v) {
-            if (u < startRadian) {
-                return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            if (u > endRadian) {
-                return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
-        },
+    x: function (u, v) {
+      if (u < startRadian) {
+        return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
+      }
+      if (u > endRadian) {
+        return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
+      }
+      return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
+    },
 
-        y: function (u, v) {
-            if (u < startRadian) {
-                return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            if (u > endRadian) {
-                return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
-            }
-            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
-        },
+    y: function (u, v) {
+      if (u < startRadian) {
+        return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
+      }
+      if (u > endRadian) {
+        return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
+      }
+      return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
+    },
 
-        z: function (u, v) {
-            if (u < -Math.PI * 0.5) {
-                return Math.sin(u);
-            }
-            if (u > Math.PI * 2.5) {
-                return Math.sin(u);
-            }
-            return Math.sin(v) > 0 ? 0.2 * height : -1;
-        }
-    };
+    z: function (u, v) {
+      if (u < -Math.PI * 0.5) {
+        return Math.sin(u);
+      }
+      if (u > Math.PI * 2.5) {
+        return Math.sin(u);
+      }
+      return Math.sin(v) > 0 ? 0.2 * height : -1;
+    }
+  };
 }
 
 // 生成模拟 3D 饼图的配置项
 function getPie3D(pieData, internalDiameterRatio) {
-    let series = [];
-    let sumValue = 0;
-    let startValue = 0;
-    let endValue = 0;
-    let legendData = [];
-    let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
-
-    // 为每一个饼图数据,生成一个 series-surface 配置
-    for (let i = 0; i < pieData.length; i++) {
-        sumValue += pieData[i].value;
-
-        let seriesItem = {
-            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
-            type: 'surface',
-            parametric: true,
-            wireframe: {
-                show: false
-            },
-            pieData: pieData[i],
-            pieStatus: {
-                selected: false,
-                hovered: false,
-                k: k
-            }
-        };
+  let series = [];
+  let sumValue = 0;
+  let startValue = 0;
+  let endValue = 0;
+  let legendData = [];
+  let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
+
+  // 为每一个饼图数据,生成一个 series-surface 配置
+  for (let i = 0; i < pieData.length; i++) {
+    sumValue += pieData[i].value;
+
+    let seriesItem = {
+      name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
+      type: 'surface',
+      parametric: true,
+      wireframe: {
+        show: false
+      },
+      pieData: pieData[i],
+      pieStatus: {
+        selected: false,
+        hovered: false,
+        k: k
+      }
+    };
 
-        if (typeof pieData[i].itemStyle != 'undefined') {
-            let itemStyle = {};
+    if (typeof pieData[i].itemStyle != 'undefined') {
+      let itemStyle = {};
 
-            typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
-            typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
+      typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
+      typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
 
-            seriesItem.itemStyle = itemStyle;
-        }
-        series.push(seriesItem);
+      seriesItem.itemStyle = itemStyle;
     }
-
-    // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
-    // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
-    for (let i = 0; i < series.length; i++) {
-        endValue = startValue + series[i].pieData.value;
-        console.log(series[i]);
-        series[i].pieData.startRatio = startValue / sumValue;
-        series[i].pieData.endRatio = endValue / sumValue;
-        series[i].parametricEquation = getParametricEquation(
-            series[i].pieData.startRatio,
-            series[i].pieData.endRatio,
-            false,
-            false,
-            k,
-            series[i].pieData.value
-        );
-
-        startValue = endValue;
-
-        legendData.push(series[i].name);
+    series.push(seriesItem);
+  }
+
+  // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
+  // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
+  for (let i = 0; i < series.length; i++) {
+    endValue = startValue + series[i].pieData.value;
+    console.log(series[i]);
+    series[i].pieData.startRatio = startValue / sumValue;
+    series[i].pieData.endRatio = endValue / sumValue;
+    series[i].parametricEquation = getParametricEquation(
+      series[i].pieData.startRatio,
+      series[i].pieData.endRatio,
+      false,
+      false,
+      k,
+      series[i].pieData.value
+    );
+
+    startValue = endValue;
+
+    legendData.push(series[i].name);
+  }
+
+  // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
+  series.push({
+    name: 'mouseoutSeries',
+    type: 'surface',
+    parametric: true,
+    wireframe: {
+      show: false
+    },
+    itemStyle: {
+      opacity: 0.1,
+      color: '#8997DE'
+    },
+    parametricEquation: {
+      u: {
+        min: 0,
+        max: Math.PI * 2,
+        step: Math.PI / 20
+      },
+      v: {
+        min: 0,
+        max: Math.PI,
+        step: Math.PI / 20
+      },
+      x: function (u, v) {
+        return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
+      },
+      y: function (u, v) {
+        return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
+      },
+      z: function (u, v) {
+        return Math.cos(v) > 0 ? -0.5 : -5;
+      }
     }
+  });
+
+  // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
+  series.push({
+    name: 'mouseoutSeries',
+    type: 'surface',
+    parametric: true,
+    wireframe: {
+      show: false
+    },
+    itemStyle: {
+      opacity: 0.1,
+      color: '#8997DE'
+    },
+    parametricEquation: {
+      u: {
+        min: 0,
+        max: Math.PI * 2,
+        step: Math.PI / 20
+      },
+      v: {
+        min: 0,
+        max: Math.PI,
+        step: Math.PI / 20
+      },
+      x: function (u, v) {
+        return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
+      },
+      y: function (u, v) {
+        return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
+      },
+      z: function (u, v) {
+        return Math.cos(v) > 0 ? -5 : -7;
+      }
+    }
+  });
+  series.push({
+    name: 'mouseoutSeries',
+    type: 'surface',
+    parametric: true,
+    wireframe: {
+      show: false
+    },
+    itemStyle: {
+      opacity: 0.1,
+      color: '#8997DE'
+    },
 
-    // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
-    series.push({
-        name: 'mouseoutSeries',
-        type: 'surface',
-        parametric: true,
-        wireframe: {
-            show: false
-        },
-        itemStyle: {
-            opacity: 0.1,
-            color: '#8997DE'
-        },
-        parametricEquation: {
-            u: {
-                min: 0,
-                max: Math.PI * 2,
-                step: Math.PI / 20
-            },
-            v: {
-                min: 0,
-                max: Math.PI,
-                step: Math.PI / 20
-            },
-            x: function (u, v) {
-                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
-            },
-            y: function (u, v) {
-                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
-            },
-            z: function (u, v) {
-                return Math.cos(v) > 0 ? -0.5 : -5;
-            }
-        }
-    });
-
-    // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
-    series.push({
-        name: 'mouseoutSeries',
-        type: 'surface',
-        parametric: true,
-        wireframe: {
-            show: false
-        },
-        itemStyle: {
-            opacity: 0.1,
-            color: '#8997DE'
-        },
-        parametricEquation: {
-            u: {
-                min: 0,
-                max: Math.PI * 2,
-                step: Math.PI / 20
-            },
-            v: {
-                min: 0,
-                max: Math.PI,
-                step: Math.PI / 20
-            },
-            x: function (u, v) {
-                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
-            },
-            y: function (u, v) {
-                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
-            },
-            z: function (u, v) {
-                return Math.cos(v) > 0 ? -5 : -7;
-            }
-        }
-    });
-    series.push({
-        name: 'mouseoutSeries',
-        type: 'surface',
-        parametric: true,
-        wireframe: {
-            show: false
-        },
-        itemStyle: {
-            opacity: 0.1,
-            color: '#8997DE'
-        },
-
-        parametricEquation: {
-            u: {
-                min: 0,
-                max: Math.PI * 2,
-                step: Math.PI / 20
-            },
-            v: {
-                min: 0,
-                max: Math.PI,
-                step: Math.PI / 20
-            },
-            x: function (u, v) {
-                return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.2;
-            },
-            y: function (u, v) {
-                return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.2;
-            },
-            z: function (u, v) {
-                return Math.cos(v) > 0 ? -7 : -7;
-            }
-        }
-    });
-    return series;
+    parametricEquation: {
+      u: {
+        min: 0,
+        max: Math.PI * 2,
+        step: Math.PI / 20
+      },
+      v: {
+        min: 0,
+        max: Math.PI,
+        step: Math.PI / 20
+      },
+      x: function (u, v) {
+        return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.2;
+      },
+      y: function (u, v) {
+        return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.2;
+      },
+      z: function (u, v) {
+        return Math.cos(v) > 0 ? -7 : -7;
+      }
+    }
+  });
+  return series;
 }
 
 let colors = ['#085AC7', '#24525E', '#C3972E'];
@@ -356,425 +356,425 @@ let yData = [568, 175, 396];
 let optionsData = [];
 let total = 0;
 yData.forEach((v) => {
-    total += v;
+  total += v;
 });
 for (let i = 0; i < xData.length; i++) {
-    optionsData.push({
-        name: xData[i],
-        value: yData[i],
-        itemStyle: {
-            color: colors[i],
-            opacity: 0.7
-        }
-    });
+  optionsData.push({
+    name: xData[i],
+    value: yData[i],
+    itemStyle: {
+      color: colors[i],
+      opacity: 0.7
+    }
+  });
 }
 
 const series = getPie3D(optionsData, 0.8);
 const projectOption = computed(() => {
-    return {
-        legend: {
-            tooltip: {
-                show: true
-            },
-            data: xData,
-            orient: 'vertial',
-            bottom: '5%',
-            left: 'center',
-            itemGap: 14,
-            itemHeight: 10,
-            itemWidth: 15,
-            formatter: (name) => {
-                const res = optionsData.filter((n) => {
-                    return n.name === name;
-                });
-                if (!res.length) return;
-                return `${name}  ${res[0].value}个  ${res[0].value ? ((res[0].value / total) * 100).toFixed(2) : 0}%`;
-            }
-        },
-        animation: true,
-        tooltip: {
-            formatter: (params) => {
-                if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
-                    return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${projectOption.value.series[params.seriesIndex].pieData.value}个`;
-                }
-            },
-            textStyle: {
-                fontSize: 12
-            }
-        },
-        xAxis3D: {
-            min: -1,
-            max: 1
-        },
-        yAxis3D: {
-            min: -1,
-            max: 1
-        },
-        zAxis3D: {
-            min: -1,
-            max: 1
-        },
-        grid3D: {
-            show: false,
-            boxHeight: 0.5,
-            top: '-10%',
-            viewControl: {
-                distance: 240,
-                alpha: 30,
-                beta: 10,
-                autoRotate: true // 自动旋转
-            }
-        },
-
-        series: series
-    };
-});
-const equipData = ref<any>([
-    {
-        name: '报废',
-        value: 1546
+  return {
+    legend: {
+      tooltip: {
+        show: true
+      },
+      data: xData,
+      orient: 'vertial',
+      bottom: '5%',
+      left: 'center',
+      itemGap: 14,
+      itemHeight: 10,
+      itemWidth: 15,
+      formatter: (name) => {
+        const res = optionsData.filter((n) => {
+          return n.name === name;
+        });
+        if (!res.length) return;
+        return `${name}  ${res[0].value}个  ${res[0].value ? ((res[0].value / total) * 100).toFixed(2) : 0}%`;
+      }
     },
-    {
-        name: '维修中',
-        value: 189
+    animation: true,
+    tooltip: {
+      formatter: (params) => {
+        if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
+          return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${projectOption.value.series[params.seriesIndex].pieData.value}个`;
+        }
+      },
+      textStyle: {
+        fontSize: 12
+      }
     },
-    {
-        name: '故障',
-        value: 1452
+    xAxis3D: {
+      min: -1,
+      max: 1
     },
-    {
-        name: '其他',
-        value: 189
+    yAxis3D: {
+      min: -1,
+      max: 1
     },
-    {
-        name: '正在运行',
-        value: 600
+    zAxis3D: {
+      min: -1,
+      max: 1
     },
-    {
-        name: '停役',
-        value: 200
-    }
+    grid3D: {
+      show: false,
+      boxHeight: 0.5,
+      top: '-10%',
+      viewControl: {
+        distance: 240,
+        alpha: 30,
+        beta: 10,
+        autoRotate: true // 自动旋转
+      }
+    },
+
+    series: series
+  };
+});
+const equipData = ref<any>([
+  {
+    name: '报废',
+    value: 1546
+  },
+  {
+    name: '维修中',
+    value: 189
+  },
+  {
+    name: '故障',
+    value: 1452
+  },
+  {
+    name: '其他',
+    value: 189
+  },
+  {
+    name: '正在运行',
+    value: 600
+  },
+  {
+    name: '停役',
+    value: 200
+  }
 ]);
 const color = ['#1990FF', '#8543E0', '#30C25B', '#16C2C2', '#FACC14', '#F04864'];
 equipData.value.forEach((item, index) => {
-    const tag = index % 6;
-    item.itemStyle = {
-        color: color[tag] || ''
-    };
+  const tag = index % 6;
+  item.itemStyle = {
+    color: color[tag] || ''
+  };
 });
 let equipTotal = 0;
 equipData.value.forEach((v) => {
-    equipTotal += v.value;
+  equipTotal += v.value;
 });
 const equipOption = computed(() => {
-    return {
-        legend: {
-            show: true,
-            left: '0%',
-            bottom: '5%',
-            itemGap: 10,
-            borderRadius: 5,
-            itemWidth: 10,
-            icon: 'circle',
-            itemHeight: 10,
-            data: equipData.value,
-            formatter: function (name) {
-                const res = equipData.value.filter((n) => {
-                    return n.name === name;
-                });
-                if (!res.length) return;
-                return `${name}  ${res[0].value ? ((res[0].value / equipTotal) * 100).toFixed(2) : 0}%  ${res[0].value}`;
-            }
-        },
-        tooltip: {
-            trigger: 'item',
-            backgroundColor: 'rgba(13,5,30,.6)',
-            borderWidth: 1,
-            borderColor: '#32A1FF',
-            padding: 5,
-            textStyle: {
-                color: '#fff'
-            },
-            formatter: (params) => {
-                return `${params.name}<br/>${params.marker}${params.value}`;
-            }
+  return {
+    legend: {
+      show: true,
+      left: '0%',
+      bottom: '5%',
+      itemGap: 10,
+      borderRadius: 5,
+      itemWidth: 10,
+      icon: 'circle',
+      itemHeight: 10,
+      data: equipData.value,
+      formatter: function (name) {
+        const res = equipData.value.filter((n) => {
+          return n.name === name;
+        });
+        if (!res.length) return;
+        return `${name}  ${res[0].value ? ((res[0].value / equipTotal) * 100).toFixed(2) : 0}%  ${res[0].value}`;
+      }
+    },
+    tooltip: {
+      trigger: 'item',
+      backgroundColor: 'rgba(13,5,30,.6)',
+      borderWidth: 1,
+      borderColor: '#32A1FF',
+      padding: 5,
+      textStyle: {
+        color: '#fff'
+      },
+      formatter: (params) => {
+        return `${params.name}<br/>${params.marker}${params.value}`;
+      }
+    },
+    title: {
+      show: true,
+      text: '设备总数',
+      itemGap: 5, //主副标题之间的距离
+      left: 'center',
+      top: '28%',
+      textStyle: {
+        fontSize: 13,
+        color: '#9E9E9E',
+        fontWeight: 'normal'
+      },
+      subtext: '4226',
+      subtextStyle: {
+        fontSize: 18,
+        fontWeight: 500,
+        color: '#333'
+      }
+    },
+    series: [
+      {
+        name: '',
+        type: 'pie',
+        radius: ['40%', '60%'],
+        center: ['50%', '35%'],
+        itemStyle: {
+          borderWidth: 2, //描边线宽
+          borderColor: '#fff'
         },
-        title: {
-            show: true,
-            text: '设备总数',
-            itemGap: 5, //主副标题之间的距离
-            left: 'center',
-            top: '28%',
-            textStyle: {
-                fontSize: 13,
-                color: '#9E9E9E',
-                fontWeight: 'normal'
-            },
-            subtext: '4226',
-            subtextStyle: {
-                fontSize: 18,
-                fontWeight: 500,
-                color: '#333'
-            }
+        label: {
+          show: false
         },
-        series: [
-            {
-                name: '',
-                type: 'pie',
-                radius: ['40%', '60%'],
-                center: ['50%', '35%'],
-                itemStyle: {
-                    borderWidth: 2, //描边线宽
-                    borderColor: '#fff'
-                },
-                label: {
-                    show: false
-                },
-                labelLine: {},
-                data: equipData.value
-            }
-        ]
-    };
+        labelLine: {},
+        data: equipData.value
+      }
+    ]
+  };
 });
 
 const rankData = [
-    { name: '设备类型一', value: 323 },
-    { name: '设备类型二', value: 108 },
-    { name: '设备类型三', value: 95 },
-    { name: '设备类型四', value: 43 },
-    { name: '设备类型五', value: 10 }
+  { name: '设备类型一', value: 323 },
+  { name: '设备类型二', value: 108 },
+  { name: '设备类型三', value: 95 },
+  { name: '设备类型四', value: 43 },
+  { name: '设备类型五', value: 10 }
 ]; // 类别
 let rankTotal = 0; // 数据总数
 rankData.forEach((v) => {
-    rankTotal += v.value;
+  rankTotal += v.value;
 });
 const rankOption = computed(() => {
-    return {
-        grid: {
-            left: '5%',
-            top: '3%', // 设置条形图的边距
-            right: '12%',
-            bottom: '15%'
+  return {
+    grid: {
+      left: '5%',
+      top: '3%', // 设置条形图的边距
+      right: '12%',
+      bottom: '15%'
+    },
+    xAxis: {
+      splitLine: {
+        show: false,
+        lineStyle: {
+          color: 'rgba(255,255,255,0.2)',
+          type: 'dashed'
+        }
+      },
+      axisLine: {
+        show: false
+      },
+      axisLabel: {
+        show: false,
+        color: '#ABBFE3'
+      },
+      axisTick: {
+        show: false
+      }
+    },
+    yAxis: [
+      {
+        type: 'category',
+        inverse: true,
+        data: rankData.map((item) => item.name),
+        axisLine: {
+          show: false
         },
-        xAxis: {
-            splitLine: {
-                show: false,
-                lineStyle: {
-                    color: 'rgba(255,255,255,0.2)',
-                    type: 'dashed'
+        axisTick: {
+          show: false
+        },
+        axisLabel: {
+          show: true,
+          textStyle: {
+            verticalAlign: 'bottom',
+            color: '#000',
+            fontSize: 12,
+            fontFamily: 'Microsoft YaHei',
+            align: 'left',
+            padding: [0, 0, 9, 5]
+          },
+          formatter: (name, index) => {
+            const _index = index + 1;
+            return `NO${_index}. ${name}`;
+          }
+        },
+        offset: 0
+      }
+    ],
+    series: [
+      {
+        // 内
+        type: 'bar',
+        barWidth: 10,
+        barCateGoryGap: 20,
+        legendHoverLink: false,
+        silent: true,
+        itemStyle: {
+          normal: {
+            barBorderRadius: 10,
+            color: {
+              type: 'linear',
+              x: 0,
+              y: 0,
+              x2: 1,
+              y2: 0,
+              colorStops: [
+                {
+                  offset: 0,
+                  color: '#FFFFFF' // 0% 处的颜色
+                },
+                {
+                  offset: 1,
+                  color: '#0768FF' // 100% 处的颜色
                 }
-            },
-            axisLine: {
-                show: false
-            },
-            axisLabel: {
-                show: false,
-                color: '#ABBFE3'
-            },
-            axisTick: {
-                show: false
+              ]
             }
+          }
         },
-        yAxis: [
-            {
-                type: 'category',
-                inverse: true,
-                data: rankData.map((item) => item.name),
-                axisLine: {
-                    show: false
-                },
-                axisTick: {
-                    show: false
-                },
-                axisLabel: {
-                    show: true,
-                    textStyle: {
-                        verticalAlign: 'bottom',
-                        color: '#000',
-                        fontSize: 12,
-                        fontFamily: 'Microsoft YaHei',
-                        align: 'left',
-                        padding: [0, 0, 9, 5]
-                    },
-                    formatter: (name, index) => {
-                        const _index = index + 1;
-                        return `NO${_index}. ${name}`;
-                    }
-                },
-                offset: 0
+        label: {
+          normal: {
+            show: false,
+            position: '[0, 0, 15, 10]',
+            formatter: '{b}',
+            textStyle: {
+              color: '#fff',
+              fontSize: 14
             }
-        ],
-        series: [
-            {
-                // 内
-                type: 'bar',
-                barWidth: 10,
-                barCateGoryGap: 20,
-                legendHoverLink: false,
-                silent: true,
-                itemStyle: {
-                    normal: {
-                        barBorderRadius: 10,
-                        color: {
-                            type: 'linear',
-                            x: 0,
-                            y: 0,
-                            x2: 1,
-                            y2: 0,
-                            colorStops: [
-                                {
-                                    offset: 0,
-                                    color: '#FFFFFF' // 0% 处的颜色
-                                },
-                                {
-                                    offset: 1,
-                                    color: '#0768FF' // 100% 处的颜色
-                                }
-                            ]
-                        }
-                    }
-                },
-                label: {
-                    normal: {
-                        show: false,
-                        position: '[0, 0, 15, 10]',
-                        formatter: '{b}',
-                        textStyle: {
-                            color: '#fff',
-                            fontSize: 14
-                        }
-                    }
-                },
-                data: rankData,
-                z: 2,
-                animationEasing: 'elasticOut'
-            },
-            {
-                // 外边框
-                type: 'pictorialBar',
-                symbol: 'rect',
-                symbolBoundingData: rankTotal,
-                itemStyle: {
-                    barBorderRadius: 10,
-                    normal: {
-                        color: 'none'
-                    }
-                },
-                label: {
-                    normal: {
-                        padding: [0, 10, 0, 14],
-                        formatter: (params) => {
-                            return params.data;
-                        },
-                        color: '#03FF00',
-                        fontWeight: 'bold',
-                        position: 'right',
-                        distance: 1, // 向右偏移位置
-                        show: true
-                    }
-                },
-                data: rankData.map((item) => item.value),
-                z: 0,
-                animationEasing: 'elasticOut'
+          }
+        },
+        data: rankData,
+        z: 2,
+        animationEasing: 'elasticOut'
+      },
+      {
+        // 外边框
+        type: 'pictorialBar',
+        symbol: 'rect',
+        symbolBoundingData: rankTotal,
+        itemStyle: {
+          barBorderRadius: 10,
+          normal: {
+            color: 'none'
+          }
+        },
+        label: {
+          normal: {
+            padding: [0, 10, 0, 14],
+            formatter: (params) => {
+              return params.data;
             },
-            {
-                name: '外框',
-                type: 'bar',
-                barCateGoryGap: 20,
-                barGap: '-100%', // 设置外框粗细
-                data: new Array(rankData.length).fill(rankTotal),
-                barWidth: 10,
-                itemStyle: {
-                    normal: {
-                        barBorderRadius: [0, 6, 6, 0],
-                        color: '#F2F2F2'
-                    },
-                    emphasis: {
-                        barBorderRadius: [0, 6, 6, 0],
-                        color: '#F2F2F2'
-                    }
-                },
-                z: 0
-            }
-        ]
-    };
+            color: '#03FF00',
+            fontWeight: 'bold',
+            position: 'right',
+            distance: 1, // 向右偏移位置
+            show: true
+          }
+        },
+        data: rankData.map((item) => item.value),
+        z: 0,
+        animationEasing: 'elasticOut'
+      },
+      {
+        name: '外框',
+        type: 'bar',
+        barCateGoryGap: 20,
+        barGap: '-100%', // 设置外框粗细
+        data: new Array(rankData.length).fill(rankTotal),
+        barWidth: 10,
+        itemStyle: {
+          normal: {
+            barBorderRadius: [0, 6, 6, 0],
+            color: '#F2F2F2'
+          },
+          emphasis: {
+            barBorderRadius: [0, 6, 6, 0],
+            color: '#F2F2F2'
+          }
+        },
+        z: 0
+      }
+    ]
+  };
 });
 </script>
 <style lang="scss" scoped>
 .positionImg {
-    width: 100%;
-    height: 100%;
-    padding: 10px 0;
+  width: 100%;
+  height: 100%;
+  padding: 10px 0;
 }
 
 .chart-group {
-    display: flex;
-    margin-top: 5px;
+  display: flex;
+  margin-top: 5px;
 
-    :deep(.el-card__body) {
-        height: 100%;
-        padding: 15px 10px !important;
-    }
+  :deep(.el-card__body) {
+    height: 100%;
+    padding: 15px 10px !important;
+  }
 
-    .el-card {
-        flex: 1;
-        height: 350px;
-        background: #fff;
+  .el-card {
+    flex: 1;
+    height: 350px;
+    background: #fff;
 
-        &:not(:first-child) {
-            margin-left: 5px;
-        }
+    &:not(:first-child) {
+      margin-left: 5px;
     }
+  }
 
-    .chart-content {
-        height: calc(100% - 10px);
-        margin-top: 5px;
-        border-top: 1px solid #eaebee;
-    }
+  .chart-content {
+    height: calc(100% - 10px);
+    margin-top: 5px;
+    border-top: 1px solid #eaebee;
+  }
 }
 
 .check-summary {
+  display: flex;
+  margin-top: 10px;
+
+  >div {
+    flex: 1;
+    padding: 5px 10px;
     display: flex;
-    margin-top: 10px;
-
-    > div {
-        flex: 1;
-        padding: 5px 10px;
-        display: flex;
-        background: #fafbfc;
-        border: 1px solid #e4e5e9;
-        border-radius: 4px;
-
-        &:not(:first-child) {
-            margin-left: 10px;
-        }
+    background: #fafbfc;
+    border: 1px solid #e4e5e9;
+    border-radius: 4px;
 
-        img {
-            height: 40px;
-        }
+    &:not(:first-child) {
+      margin-left: 10px;
+    }
 
-        .check-name {
-            margin-left: 10px;
+    img {
+      height: 40px;
+    }
 
-            div:first-child {
-                font-size: 16px;
-                font-weight: 500;
-            }
+    .check-name {
+      margin-left: 10px;
 
-            div:last-child {
-                font-size: 14px;
-            }
-        }
+      div:first-child {
+        font-size: 16px;
+        font-weight: 500;
+      }
+
+      div:last-child {
+        font-size: 14px;
+      }
     }
+  }
 }
 
 .check-rank {
-    margin-top: 10px;
-    display: flex;
-    justify-content: space-between;
+  margin-top: 10px;
+  display: flex;
+  justify-content: space-between;
 
-    > div:first-child {
-        font-size: 14px;
-    }
+  >div:first-child {
+    font-size: 14px;
+  }
 }
 </style>

+ 7 - 7
src/views/dashboard/index.vue

@@ -1,7 +1,7 @@
 <template>
-    <div class="home">
-        <component :is="comp"></component>
-    </div>
+  <div class="home">
+    <component :is="comp"></component>
+  </div>
 </template>
 
 <script setup name="Index" lang="ts">
@@ -9,13 +9,13 @@ import useUserStore from '@/store/modules/user';
 import DashbordAdmin from './dashboardAdmin.vue';
 import Dashbord from './dashboard.vue';
 console.log(useUserStore().roles);
-const comp = !useUserStore().roles.includes('superadmin') ? DashbordAdmin : Dashbord;
+const comp = useUserStore().roles.includes('superadmin') ? DashbordAdmin : Dashbord;
 </script>
 
 <style scoped lang="scss">
 .home {
-    height: 100%;
-    padding: 10px 20px;
-    background: #fcfcfc;
+  height: 100%;
+  padding: 10px 20px;
+  background: #fcfcfc;
 }
 </style>

+ 327 - 314
src/views/deviceManage/archives/index.vue

@@ -1,195 +1,208 @@
 <template>
-  <div class="p-2">
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="80px">
-            <el-form-item label="设备名称" prop="name">
-              <el-input v-model="queryParams.name" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="设备编号" prop="sn">
-              <el-input v-model="queryParams.sn" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="使用状态" prop="status">
-              <el-select v-model="form.deviceTypeId" clearable placeholder="请选择使用状态">
-                <el-option v-for="dict in use_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
-              <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
+    <div class="p-2">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+            <div v-show="showSearch" class="mb-[10px]">
+                <el-card shadow="hover">
+                    <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="80px">
+                        <el-form-item label="设备名称" prop="name">
+                            <el-input v-model="queryParams.name" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
+                        </el-form-item>
+                        <el-form-item label="设备编号" prop="sn">
+                            <el-input v-model="queryParams.sn" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" />
+                        </el-form-item>
+                        <el-form-item label="使用状态" prop="status">
+                            <el-select v-model="form.deviceTypeId" clearable placeholder="请选择使用状态">
+                                <el-option v-for="dict in use_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                            </el-select>
+                        </el-form-item>
+                        <el-form-item>
+                            <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+                            <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+                        </el-form-item>
+                    </el-form>
+                </el-card>
+            </div>
+        </transition>
 
-    <el-card shadow="never">
-      <template #header>
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button v-hasPermi="['jdyw:deviceType:add']" type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button v-hasPermi="['jdyw:deviceType:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate">
-              修改
-            </el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button v-hasPermi="['jdyw:deviceType:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">
-              删除
-            </el-button>
-          </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
-        </el-row>
-      </template>
+        <el-card shadow="never">
+            <template #header>
+                <el-row :gutter="10" class="mb8">
+                    <el-col :span="1.5">
+                        <el-button v-hasPermi="['jdyw:deviceType:add']" type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+                    </el-col>
+                    <el-col :span="1.5">
+                        <el-button v-hasPermi="['jdyw:deviceType:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate">
+                            修改
+                        </el-button>
+                    </el-col>
+                    <el-col :span="1.5">
+                        <el-button
+                            v-hasPermi="['jdyw:deviceType:remove']"
+                            type="danger"
+                            plain
+                            icon="Delete"
+                            :disabled="multiple"
+                            @click="handleDelete"
+                        >
+                            删除
+                        </el-button>
+                    </el-col>
+                    <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+                </el-row>
+            </template>
 
-      <el-table v-loading="loading" :data="deviceList" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="设备名称" align="center" prop="name" />
-        <el-table-column label="设备编号" align="center" prop="sn" />
-        <el-table-column label="所属系统" align="center" prop="deviceSystem" />
-        <el-table-column label="品牌" align="center" prop="brand" />
-        <el-table-column label="型号" align="center" prop="xh" />
-        <el-table-column label="固定资产编号" align="center" prop="fixedId" width="120" />
-        <el-table-column label="领用日期" align="center" prop="useDate" width="120" />
-        <el-table-column label="质保日期" align="center" prop="qaDate" width="120" />
-        <el-table-column label="维保状态" align="center" prop="qaStatus" />
-        <el-table-column label="使用状态" align="center">
-          <template #default="scope">
-            <dict-tag :options="use_status" :value="scope.row.useStatus" />
-          </template>
-        </el-table-column>
-        <el-table-column label="运行状态" align="center" prop="runningStatus" />
-        <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="180">
-          <template #default="scope">
-            <el-button size="small" link type="primary">详情</el-button>
-            <el-button size="small" link type="primary" @click="showQrCode(scope.row)">二维码</el-button>
-            <el-button size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
-            <el-button size="small" link type="danger" @click="handleScrap(scope.row)">报废</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
-    </el-card>
+            <el-table v-loading="loading" :data="deviceList" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" align="center" />
+                <el-table-column label="设备名称" align="center" prop="name" />
+                <el-table-column label="设备编号" align="center" prop="sn" />
+                <el-table-column label="所属系统" align="center" prop="deviceSystem" />
+                <el-table-column label="品牌" align="center" prop="brand" />
+                <el-table-column label="型号" align="center" prop="xh" />
+                <el-table-column label="固定资产编号" align="center" prop="fixedId" width="120" />
+                <el-table-column label="领用日期" align="center" prop="useDate" width="120" />
+                <el-table-column label="质保日期" align="center" prop="qaDate" width="120" />
+                <el-table-column label="维保状态" align="center" prop="qaStatus" />
+                <el-table-column label="使用状态" align="center">
+                    <template #default="scope">
+                        <dict-tag :options="use_status" :value="scope.row.useStatus" />
+                    </template>
+                </el-table-column>
+                <el-table-column label="运行状态" align="center" prop="runningStatus" />
+                <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="180">
+                    <template #default="scope">
+                        <el-button size="small" link type="primary">详情</el-button>
+                        <el-button size="small" link type="primary" @click="showQrCode(scope.row)">二维码</el-button>
+                        <el-button size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
+                        <el-button size="small" link type="danger" @click="handleScrap(scope.row)">报废</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <pagination
+                v-show="total > 0"
+                v-model:page="queryParams.pageNum"
+                v-model:limit="queryParams.pageSize"
+                :total="total"
+                @pagination="getList"
+            />
+        </el-card>
 
-    <!-- 添加或修改设备类型信息对话框 -->
-    <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
-      <el-form ref="deviceFormRef" :model="form" label-width="80px">
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item
-              label="设备名称"
-              prop="deviceTypeId"
-              :disabled="ifEdit"
-              :rules="[{ required: true, message: '设备名称不能为空', trigger: 'blur' }]"
-            >
-              <el-input v-model="form.brand" placeholder="请输入设备名称" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item
-              label="设备编号"
-              prop="deviceTypeId"
-              :disabled="ifEdit"
-              :rules="[{ required: true, message: '设备编号不能为空', trigger: 'blur' }]"
-            >
-              <el-input v-model="form.brand" placeholder="请输入(支持批量新增,以逗号分隔)" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item
-              label="设备类型"
-              prop="deviceTypeId"
-              :disabled="ifEdit"
-              :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]"
-            >
-              <el-select v-model="form.deviceTypeId" placeholder="请选择设备类型">
-                <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id"></el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="品牌" prop="brand">
-              <el-input v-model="form.brand" disabled />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="型号" prop="xh">
-              <el-input v-model="form.xh" disabled />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="领用日期" prop="xh">
-              <el-date-picker v-model="form.xh" style="width: 100%" :disabled="ifEdit" type="date" placeholder="领用日期" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="质保日期" prop="xh">
-              <el-date-picker v-model="form.xh" style="width: 100%" :disabled="ifEdit" type="date" placeholder="领用日期" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="使用状态" prop="xh">
-              <el-select v-model="form.deviceTypeId" clearable placeholder="请选择使用状态">
-                <el-option v-for="dict in use_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-form-item label="备注" prop="remark">
-          <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
-        </el-form-item>
-        <el-form-item label="地理位置" prop="remark">
-          <div id="map" class="bdmap"></div>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
-          <el-button @click="cancel"> 取 消 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-    <el-drawer v-model="drawer" title="报废设备" size="40%">
-      <el-form ref="deviceScrapFormRef" :model="scrapForm" label-width="80px">
-        <el-form-item label="设备名称">
-          <div>{{ scrapForm.name }}</div>
-        </el-form-item>
-        <el-form-item label="设备编号">
-          <div>{{ scrapForm.sn }}</div>
-        </el-form-item>
-        <el-form-item label="品牌">
-          <div>{{ scrapForm.brand }}</div>
-        </el-form-item>
-        <el-form-item label="型号">
-          <div>{{ scrapForm.xh }}</div>
-        </el-form-item>
-        <el-form-item label="报废理由" prop="reason" :rules="[{ required: true, message: '报废理由不能为空', trigger: 'blur' }]">
-          <el-input v-model="scrapForm.reason" placeholder="请输入" />
-        </el-form-item>
-        <el-form-item label="报废时间" prop="scrapDate" :rules="[{ required: true, message: '报废时间不能为空', trigger: 'change' }]">
-          <el-date-picker v-model="scrapForm.scrapDate" style="width: 100%" type="date" placeholder="请选择" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div style="flex: auto">
-          <el-button @click="drawer = false">取消</el-button>
-          <el-button type="primary" @click="confirmClick">确认</el-button>
-        </div>
-      </template>
-    </el-drawer>
-    <el-dialog v-model="qrDialog" title="设备二维码" width="600px" append-to-body>
-      <el-descriptions class="custom-descriptions" title="" :column="1" border>
-        <el-descriptions-item label="设备名称"> {{ qrInfo.name }} </el-descriptions-item>
-        <el-descriptions-item label="设备编号"> {{ qrInfo.sn }} </el-descriptions-item>
-        <el-descriptions-item label="设备品牌"> {{ qrInfo.brand }} </el-descriptions-item>
-        <el-descriptions-item label="设备型号"> {{ qrInfo.xh }} </el-descriptions-item>
-        <el-descriptions-item label="存放位置"> {{ qrInfo.position }} </el-descriptions-item>
-      </el-descriptions>
-      <img class="qrImg" src="@/assets/images/qrcode.png" alt="" />
-    </el-dialog>
-  </div>
+        <!-- 添加或修改设备类型信息对话框 -->
+        <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
+            <el-form ref="deviceFormRef" :model="form" label-width="80px">
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item
+                            label="设备名称"
+                            prop="deviceTypeId"
+                            :disabled="ifEdit"
+                            :rules="[{ required: true, message: '设备名称不能为空', trigger: 'blur' }]"
+                        >
+                            <el-input v-model="form.brand" placeholder="请输入设备名称" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item
+                            label="设备编号"
+                            prop="deviceTypeId"
+                            :disabled="ifEdit"
+                            :rules="[{ required: true, message: '设备编号不能为空', trigger: 'blur' }]"
+                        >
+                            <el-input v-model="form.brand" placeholder="请输入(支持批量新增,以逗号分隔)" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item
+                            label="设备类型"
+                            prop="deviceTypeId"
+                            :disabled="ifEdit"
+                            :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]"
+                        >
+                            <el-select v-model="form.deviceTypeId" placeholder="请选择设备类型">
+                                <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="品牌" prop="brand">
+                            <el-input v-model="form.brand" disabled />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="型号" prop="xh">
+                            <el-input v-model="form.xh" disabled />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="领用日期" prop="xh">
+                            <el-date-picker v-model="form.xh" style="width: 100%" :disabled="ifEdit" type="date" placeholder="领用日期" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="质保日期" prop="xh">
+                            <el-date-picker v-model="form.xh" style="width: 100%" :disabled="ifEdit" type="date" placeholder="领用日期" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="使用状态" prop="xh">
+                            <el-select v-model="form.deviceTypeId" clearable placeholder="请选择使用状态">
+                                <el-option v-for="dict in use_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-form-item label="备注" prop="remark">
+                    <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+                </el-form-item>
+                <el-form-item label="地理位置" prop="remark">
+                    <div id="map" class="bdmap"></div>
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+                    <el-button @click="cancel"> 取 消 </el-button>
+                </div>
+            </template>
+        </el-dialog>
+        <el-drawer v-model="drawer" title="报废设备" size="40%">
+            <el-form ref="deviceScrapFormRef" :model="scrapForm" label-width="80px">
+                <el-form-item label="设备名称">
+                    <div>{{ scrapForm.name }}</div>
+                </el-form-item>
+                <el-form-item label="设备编号">
+                    <div>{{ scrapForm.sn }}</div>
+                </el-form-item>
+                <el-form-item label="品牌">
+                    <div>{{ scrapForm.brand }}</div>
+                </el-form-item>
+                <el-form-item label="型号">
+                    <div>{{ scrapForm.xh }}</div>
+                </el-form-item>
+                <el-form-item label="报废理由" prop="reason" :rules="[{ required: true, message: '报废理由不能为空', trigger: 'blur' }]">
+                    <el-input v-model="scrapForm.reason" placeholder="请输入" />
+                </el-form-item>
+                <el-form-item label="报废时间" prop="scrapDate" :rules="[{ required: true, message: '报废时间不能为空', trigger: 'change' }]">
+                    <el-date-picker v-model="scrapForm.scrapDate" style="width: 100%" type="date" placeholder="请选择" />
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <div style="flex: auto">
+                    <el-button @click="drawer = false">取消</el-button>
+                    <el-button type="primary" @click="confirmClick">确认</el-button>
+                </div>
+            </template>
+        </el-drawer>
+        <el-dialog v-model="qrDialog" title="设备二维码" width="600px" append-to-body>
+            <el-descriptions class="custom-descriptions" title="" :column="1" border>
+                <el-descriptions-item label="设备名称"> {{ qrInfo.name }} </el-descriptions-item>
+                <el-descriptions-item label="设备编号"> {{ qrInfo.sn }} </el-descriptions-item>
+                <el-descriptions-item label="设备品牌"> {{ qrInfo.brand }} </el-descriptions-item>
+                <el-descriptions-item label="设备型号"> {{ qrInfo.xh }} </el-descriptions-item>
+                <el-descriptions-item label="存放位置"> {{ qrInfo.position }} </el-descriptions-item>
+            </el-descriptions>
+            <img class="qrImg" src="@/assets/images/qrcode.png" alt="" />
+        </el-dialog>
+    </div>
 </template>
 
 <script setup name="Archives" lang="ts">
@@ -208,204 +221,204 @@ const queryFormRef = ref<ElFormInstance>();
 const deviceFormRef = ref<ElFormInstance>();
 const deviceScrapFormRef = ref<ElFormInstance>();
 const dialog = reactive<DialogOption>({
-  visible: false,
-  title: ''
+    visible: false,
+    title: ''
 });
 const qrDialog = ref(false);
 const deviceTypeOptions = ref([]);
 const { use_status } = toRefs<any>(proxy?.useDict('use_status'));
 const bdmap = ref(null);
 const scrapForm = ref<any>({
-  reason: '',
-  scrapDate: ''
+    reason: '',
+    scrapDate: ''
 });
 const qrInfo = ref<any>({});
 const initFormData = {
-  id: undefined,
-  deviceTypeId: undefined,
-  xh: null,
-  remark: undefined,
-  brand: undefined,
-  linkName: '',
-  linkPhone: '',
-  ext1: undefined,
-  ext2: undefined
+    id: undefined,
+    deviceTypeId: undefined,
+    xh: null,
+    remark: undefined,
+    brand: undefined,
+    linkName: '',
+    linkPhone: '',
+    ext1: undefined,
+    ext2: undefined
 };
 const formData = reactive({
-  form: { ...initFormData },
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    name: undefined,
-    sn: undefined,
-    status: undefined,
-    params: {}
-  }
+    form: { ...initFormData },
+    queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: undefined,
+        sn: undefined,
+        status: undefined,
+        params: {}
+    }
 });
 
 const { queryParams, form } = toRefs(formData);
 let ifEdit = computed(() => {
-  return dialog.title.includes('修改');
+    return dialog.title.includes('修改');
 });
 const drawer = ref(false);
 /** 查询设备类型信息列表 */
 const getList = async () => {
-  loading.value = true;
-  const res = await listDevice(queryParams.value);
-  deviceList.value = res.rows;
-  deviceList.value = [
-    {
-      name: '设备一',
-      deviceSystem: '制冷系统',
-      sn: '1002301',
-      brand: '格力',
-      xh: 'GL18L',
-      fixedId: '202410280101',
-      useDate: '2024-10-28',
-      qaDate: '2028-10-29',
-      qaStatus: '在保',
-      useStatus: '1',
-      runningStatus: '在线'
-    }
-  ];
-  total.value = res.total;
-  loading.value = false;
+    loading.value = true;
+    const res = await listDevice(queryParams.value);
+    deviceList.value = res.rows;
+    deviceList.value = [
+        {
+            name: '设备一',
+            deviceSystem: '制冷系统',
+            sn: '1002301',
+            brand: '格力',
+            xh: 'GL18L',
+            fixedId: '202410280101',
+            useDate: '2024-10-28',
+            qaDate: '2028-10-29',
+            qaStatus: '在保',
+            useStatus: '1',
+            runningStatus: '在线'
+        }
+    ];
+    total.value = res.total;
+    loading.value = false;
 };
 const showQrCode = (row) => {
-  qrDialog.value = true;
-  Object.assign(qrInfo.value, row);
+    qrDialog.value = true;
+    Object.assign(qrInfo.value, row);
 };
 const initMap = () => {
-  var map = new BMapGL.Map('map'); // 创建地图实例
-  var point = new BMapGL.Point(118.879999, 32.016216); // 创建点坐标
-  map.centerAndZoom(point, 20);
-  map.enableScrollWheelZoom(true);
-  map.addEventListener('click', (evt) => {
-    bdmap.value.clearOverlays();
-    const { lng, lat } = evt.latlng;
-    const pt = new BMapGL.Point(lng, lat);
-    const marker = new BMapGL.Marker(pt);
-    bdmap.value.addOverlay(marker);
-  });
-  bdmap.value = map;
+    var map = new BMapGL.Map('map'); // 创建地图实例
+    var point = new BMapGL.Point(118.879999, 32.016216); // 创建点坐标
+    map.centerAndZoom(point, 20);
+    map.enableScrollWheelZoom(true);
+    map.addEventListener('click', (evt) => {
+        bdmap.value.clearOverlays();
+        const { lng, lat } = evt.latlng;
+        const pt = new BMapGL.Point(lng, lat);
+        const marker = new BMapGL.Marker(pt);
+        bdmap.value.addOverlay(marker);
+    });
+    bdmap.value = map;
 };
 /** 取消按钮 */
 const cancel = () => {
-  reset();
-  dialog.visible = false;
+    reset();
+    dialog.visible = false;
 };
 
 /** 表单重置 */
 const reset = () => {
-  form.value = { ...initFormData };
-  deviceFormRef.value?.resetFields();
+    form.value = { ...initFormData };
+    deviceFormRef.value?.resetFields();
 };
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  getList();
+    queryParams.value.pageNum = 1;
+    getList();
 };
 
 /** 重置按钮操作 */
 const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  handleQuery();
+    queryFormRef.value?.resetFields();
+    handleQuery();
 };
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection) => {
-  ids.value = selection.map((item) => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+    ids.value = selection.map((item) => item.id);
+    single.value = selection.length != 1;
+    multiple.value = !selection.length;
 };
 
 /** 新增按钮操作 */
 const handleAdd = () => {
-  reset();
-  dialog.visible = true;
-  dialog.title = '新增设备';
-  setTimeout(() => {
-    initMap();
-  }, 100);
+    reset();
+    dialog.visible = true;
+    dialog.title = '新增设备';
+    setTimeout(() => {
+        initMap();
+    }, 100);
 };
 
 /** 修改按钮操作 */
 const handleUpdate = async (row) => {
-  reset();
-  // const _id = row?.id || ids.value[0];
-  // const res = await getDevice(_id);
-  // Object.assign(form.value, res.data);
-  dialog.visible = true;
-  dialog.title = '修改设备';
-  setTimeout(() => {
-    initMap();
-  }, 100);
+    reset();
+    // const _id = row?.id || ids.value[0];
+    // const res = await getDevice(_id);
+    // Object.assign(form.value, res.data);
+    dialog.visible = true;
+    dialog.title = '修改设备';
+    setTimeout(() => {
+        initMap();
+    }, 100);
 };
 const handleScrap = (row) => {
-  drawer.value = true;
-  deviceScrapFormRef.value?.resetFields();
-  Object.assign(scrapForm.value, row);
+    drawer.value = true;
+    deviceScrapFormRef.value?.resetFields();
+    Object.assign(scrapForm.value, row);
 };
 /** 提交按钮 */
 const submitForm = () => {
-  deviceFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      buttonLoading.value = true;
-      if (form.value.id) {
-        await updateDevice(form.value).finally(() => (buttonLoading.value = false));
-      } else {
-        await addDevice(form.value).finally(() => (buttonLoading.value = false));
-      }
-      proxy?.$modal.msgSuccess('操作成功');
-      dialog.visible = false;
-      await getList();
-    }
-  });
+    deviceFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            buttonLoading.value = true;
+            if (form.value.id) {
+                await updateDevice(form.value).finally(() => (buttonLoading.value = false));
+            } else {
+                await addDevice(form.value).finally(() => (buttonLoading.value = false));
+            }
+            proxy?.$modal.msgSuccess('操作成功');
+            dialog.visible = false;
+            await getList();
+        }
+    });
 };
 
 /** 删除按钮操作 */
 const handleDelete = async (row) => {
-  const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
-  await delDevice(_ids);
-  proxy?.$modal.msgSuccess('删除成功');
-  await getList();
+    const _ids = row?.id || ids.value;
+    await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
+    await delDevice(_ids);
+    proxy?.$modal.msgSuccess('删除成功');
+    await getList();
 };
 const getDeviceTypeList = () => {
-  listDeviceType({}).then(({ code, rows }) => {
-    if (code === 200) {
-      deviceTypeOptions.value = rows;
-    }
-  });
+    listDeviceType({}).then(({ code, rows }) => {
+        if (code === 200) {
+            deviceTypeOptions.value = rows;
+        }
+    });
 };
 const confirmClick = () => {
-  deviceScrapFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      drawer.value = false;
-    }
-  });
+    deviceScrapFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            drawer.value = false;
+        }
+    });
 };
 onMounted(() => {
-  getList();
-  getDeviceTypeList();
+    getList();
+    getDeviceTypeList();
 });
 </script>
 <style lang="scss" scoped>
 .bdmap {
-  width: 100%;
-  height: 250px;
+    width: 100%;
+    height: 250px;
 }
 .custom-descriptions {
-  :deep(.is-bordered-label) {
-    width: 25%;
-    font-weight: 500;
-  }
+    :deep(.is-bordered-label) {
+        width: 25%;
+        font-weight: 500;
+    }
 }
 .qrImg {
-  position: absolute;
-  bottom: 35px;
-  right: 35px;
-  height: 150px;
+    position: absolute;
+    bottom: 35px;
+    right: 35px;
+    height: 150px;
 }
 </style>

+ 377 - 377
src/views/deviceManage/modeling/index.vue

@@ -1,24 +1,24 @@
 <template>
-  <div class="content-main">
-    <div class="tool-container">
-      <el-button type="primary" text @click="copy">复制</el-button>
-      <el-divider direction="vertical" />
-      <el-button type="primary" text @click="paste">粘贴</el-button>
-      <el-divider direction="vertical" />
-      <el-button type="primary" text @click="del">删除</el-button>
-      <el-divider direction="vertical" />
-      <el-button type="primary" text @click="save">保存</el-button>
-      <!-- <el-divider direction="vertical" />
+    <div class="content-main">
+        <div class="tool-container">
+            <el-button type="primary" text @click="copy">复制</el-button>
+            <el-divider direction="vertical" />
+            <el-button type="primary" text @click="paste">粘贴</el-button>
+            <el-divider direction="vertical" />
+            <el-button type="primary" text @click="del">删除</el-button>
+            <el-divider direction="vertical" />
+            <el-button type="primary" text @click="save">保存</el-button>
+            <!-- <el-divider direction="vertical" />
       <el-button type="primary" text @click="exportPng">导出PNG</el-button> -->
+        </div>
+        <div id="" class="content-container">
+            <div class="content">
+                <div ref="stencilContainer" class="stencil"></div>
+                <div id="graphContainer" ref="graphContainer" class="graph-content"></div>
+                <!-- <img src="@/assets/images/road.png" class="roadback" alt="" /> -->
+            </div>
+        </div>
     </div>
-    <div id="" class="content-container">
-      <div class="content">
-        <div ref="stencilContainer" class="stencil"></div>
-        <div id="graphContainer" ref="graphContainer" class="graph-content"></div>
-        <!-- <img src="@/assets/images/road.png" class="roadback" alt="" /> -->
-      </div>
-    </div>
-  </div>
 </template>
 
 <script setup lang="ts">
@@ -37,451 +37,451 @@ const graphContainer = ref();
 let graph: any = null;
 
 const state = reactive({
-  data: {
-    nodes: [
-      {
-        id: 'ac51fb2f-2753-4852-8239-53672a29bb14',
-        position: {
-          x: 160,
-          y: 250
-        },
-        data: {
-          name: '烟感系统'
-        }
-      },
-      {
-        id: '81004c2f-0413-4cc6-8622-127004b3befa',
-        position: {
-          x: 220,
-          y: 110
-        },
-        data: {
-          name: '烟感系统'
-        }
-      },
-      {
-        id: 'f2db8c64-6ffe-4c9b-83a2-037c9154e599',
-        position: {
-          x: 50,
-          y: 120
-        },
-        data: {
-          name: '摄像头'
-        }
-      }
-    ]
-  }
+    data: {
+        nodes: [
+            {
+                id: 'ac51fb2f-2753-4852-8239-53672a29bb14',
+                position: {
+                    x: 160,
+                    y: 250
+                },
+                data: {
+                    name: '烟感系统'
+                }
+            },
+            {
+                id: '81004c2f-0413-4cc6-8622-127004b3befa',
+                position: {
+                    x: 220,
+                    y: 110
+                },
+                data: {
+                    name: '烟感系统'
+                }
+            },
+            {
+                id: 'f2db8c64-6ffe-4c9b-83a2-037c9154e599',
+                position: {
+                    x: 50,
+                    y: 120
+                },
+                data: {
+                    name: '摄像头'
+                }
+            }
+        ]
+    }
 });
 const imageShapes = [
-  {
-    label: '摄像头',
-    image: new URL('@/assets/images/camera.svg', import.meta.url).href
-  },
-  {
-    label: '风机',
-    image: new URL('@/assets/images/fan.svg', import.meta.url).href
-  },
-  {
-    label: '照明设施',
-    image: new URL('@/assets/images/lighting.svg', import.meta.url).href
-  },
-  {
-    label: '烟感系统',
-    image: new URL('@/assets/images/smoke.svg', import.meta.url).href
-  }
+    {
+        label: '摄像头',
+        image: new URL('@/assets/images/camera.svg', import.meta.url).href
+    },
+    {
+        label: '风机',
+        image: new URL('@/assets/images/fan.svg', import.meta.url).href
+    },
+    {
+        label: '照明设施',
+        image: new URL('@/assets/images/lighting.svg', import.meta.url).href
+    },
+    {
+        label: '烟感系统',
+        image: new URL('@/assets/images/smoke.svg', import.meta.url).href
+    }
 ];
 const getNodeAttrs = (label) => {
-  const [{ image }] = imageShapes.filter((item) => item.label === label);
-  return {
-    image: {
-      'xlink:href': image
-    }
-  };
+    const [{ image }] = imageShapes.filter((item) => item.label === label);
+    return {
+        image: {
+            'xlink:href': image
+        }
+    };
 };
 const init = () => {
-  graph = new Graph({
-    container: graphContainer.value,
-    grid: true,
-    mousewheel: {
-      enabled: true,
-      zoomAtMousePosition: true,
-      modifiers: 'ctrl',
-      minScale: 0.5,
-      maxScale: 3
-    },
-    highlighting: {
-      magnetAdsorbed: {
-        name: 'stroke',
-        args: {
-          attrs: {
-            fill: '#fff',
-            stroke: '#31d0c6',
-            strokeWidth: 4
-          }
+    graph = new Graph({
+        container: graphContainer.value,
+        grid: true,
+        mousewheel: {
+            enabled: true,
+            zoomAtMousePosition: true,
+            modifiers: 'ctrl',
+            minScale: 0.5,
+            maxScale: 3
+        },
+        highlighting: {
+            magnetAdsorbed: {
+                name: 'stroke',
+                args: {
+                    attrs: {
+                        fill: '#fff',
+                        stroke: '#31d0c6',
+                        strokeWidth: 4
+                    }
+                }
+            }
         }
-      }
-    }
-  });
-  // graph.centerContent();
+    });
+    // graph.centerContent();
 
-  // #region 使用插件
-  graph
-    .use(
-      new Transform({
-        resizing: true,
-        rotating: true
-      })
-    )
-    .use(
-      new Selection({
-        rubberband: true,
-        showNodeSelectionBox: true
-      })
-    )
-    .use(new Snapline())
-    .use(new Keyboard())
-    .use(new Clipboard())
-    .use(new Export());
-  Graph.registerNode(
-    'custom-image',
-    {
-      inherit: 'rect',
-      width: 52,
-      height: 52,
-      markup: [
+    // #region 使用插件
+    graph
+        .use(
+            new Transform({
+                resizing: true,
+                rotating: true
+            })
+        )
+        .use(
+            new Selection({
+                rubberband: true,
+                showNodeSelectionBox: true
+            })
+        )
+        .use(new Snapline())
+        .use(new Keyboard())
+        .use(new Clipboard())
+        .use(new Export());
+    Graph.registerNode(
+        'custom-image',
         {
-          tagName: 'rect',
-          selector: 'body'
+            inherit: 'rect',
+            width: 52,
+            height: 52,
+            markup: [
+                {
+                    tagName: 'rect',
+                    selector: 'body'
+                },
+                {
+                    tagName: 'image'
+                },
+                {
+                    tagName: 'text',
+                    selector: 'label'
+                }
+            ],
+            attrs: {
+                body: {
+                    stroke: '#5F95FF',
+                    fill: 'transparent',
+                    strokeWidth: 0
+                },
+                image: {
+                    width: 45,
+                    height: 45,
+                    refX: 5,
+                    refY: 5
+                },
+                label: {
+                    refX: 3,
+                    refY: 2,
+                    textAnchor: 'left',
+                    textVerticalAnchor: 'top',
+                    fontSize: 12,
+                    fill: 'transparent'
+                }
+            }
         },
-        {
-          tagName: 'image'
-        },
-        {
-          tagName: 'text',
-          selector: 'label'
+        true
+    );
+    const stencil = new Stencil({
+        title: '道路元器',
+        target: graph,
+        stencilGraphWidth: 200,
+        stencilGraphHeight: 180,
+        collapsable: true,
+        groups: [
+            {
+                title: '基础元器',
+                name: 'processLibrary',
+                graphHeight: 250,
+                layoutOptions: {
+                    rowHeight: 70
+                }
+            }
+        ],
+        layoutOptions: {
+            columns: 2,
+            columnWidth: 80,
+            rowHeight: 55
         }
-      ],
-      attrs: {
-        body: {
-          stroke: '#5F95FF',
-          fill: 'transparent',
-          strokeWidth: 0
+    });
+    stencilContainer.value.appendChild(stencil.container);
+    graph.drawBackground({
+        color: '#ccc',
+        position: 'center',
+        size: {
+            width: '100%'
+            // height: 100
         },
-        image: {
-          width: 45,
-          height: 45,
-          refX: 5,
-          refY: 5
-        },
-        label: {
-          refX: 3,
-          refY: 2,
-          textAnchor: 'left',
-          textVerticalAnchor: 'top',
-          fontSize: 12,
-          fill: 'transparent'
+        image: new URL('@/assets/images/road.png', import.meta.url).href // 设置背景图片
+    });
+    // #region 快捷键与事件
+    graph.bindKey(['meta+c', 'ctrl+c'], () => {
+        copy();
+    });
+    graph.bindKey(['meta+x', 'ctrl+x'], () => {
+        const cells = graph.getSelectedCells();
+        if (cells.length) {
+            graph.cut(cells);
         }
-      }
-    },
-    true
-  );
-  const stencil = new Stencil({
-    title: '道路元器',
-    target: graph,
-    stencilGraphWidth: 200,
-    stencilGraphHeight: 180,
-    collapsable: true,
-    groups: [
-      {
-        title: '基础元器',
-        name: 'processLibrary',
-        graphHeight: 250,
-        layoutOptions: {
-          rowHeight: 70
+        return false;
+    });
+    graph.bindKey(['meta+v', 'ctrl+v'], () => {
+        paste();
+    });
+    // select all
+    graph.bindKey(['meta+a', 'ctrl+a'], () => {
+        const nodes = graph.getNodes();
+        if (nodes) {
+            graph.select(nodes);
         }
-      }
-    ],
-    layoutOptions: {
-      columns: 2,
-      columnWidth: 80,
-      rowHeight: 55
-    }
-  });
-  stencilContainer.value.appendChild(stencil.container);
-  graph.drawBackground({
-    color: '#ccc',
-    position: 'center',
-    size: {
-      width: '100%'
-      // height: 100
-    },
-    image: new URL('@/assets/images/road.png', import.meta.url).href // 设置背景图片
-  });
-  // #region 快捷键与事件
-  graph.bindKey(['meta+c', 'ctrl+c'], () => {
-    copy();
-  });
-  graph.bindKey(['meta+x', 'ctrl+x'], () => {
-    const cells = graph.getSelectedCells();
-    if (cells.length) {
-      graph.cut(cells);
-    }
-    return false;
-  });
-  graph.bindKey(['meta+v', 'ctrl+v'], () => {
-    paste();
-  });
-  // select all
-  graph.bindKey(['meta+a', 'ctrl+a'], () => {
-    const nodes = graph.getNodes();
-    if (nodes) {
-      graph.select(nodes);
-    }
-  });
+    });
 
-  // delete
-  graph.bindKey('backspace', () => {
-    del();
-  });
+    // delete
+    graph.bindKey('backspace', () => {
+        del();
+    });
 
-  // zoom
-  graph.bindKey(['ctrl+1', 'meta+1'], () => {
-    const zoom = graph.zoom();
-    if (zoom < 1.5) {
-      graph.zoom(0.1);
-    }
-  });
-  graph.bindKey(['ctrl+2', 'meta+2'], () => {
-    const zoom = graph.zoom();
-    if (zoom > 0.5) {
-      graph.zoom(-0.1);
-    }
-  });
+    // zoom
+    graph.bindKey(['ctrl+1', 'meta+1'], () => {
+        const zoom = graph.zoom();
+        if (zoom < 1.5) {
+            graph.zoom(0.1);
+        }
+    });
+    graph.bindKey(['ctrl+2', 'meta+2'], () => {
+        const zoom = graph.zoom();
+        if (zoom > 0.5) {
+            graph.zoom(-0.1);
+        }
+    });
 
-  //节点被取消选中时触发。
-  graph.on('node:unselected', (args: { cell: Cell; node: Node; options: Model.SetOptions }) => {
-    args.node.removeTools();
-  });
+    //节点被取消选中时触发。
+    graph.on('node:unselected', (args: { cell: Cell; node: Node; options: Model.SetOptions }) => {
+        args.node.removeTools();
+    });
 
-  //边选中事件
-  graph.on('edge:selected', (args: { cell: Cell; edge: Edge; options: Model.SetOptions }) => {
-    args.edge.attr('line/strokeWidth', 3);
-  });
+    //边选中事件
+    graph.on('edge:selected', (args: { cell: Cell; edge: Edge; options: Model.SetOptions }) => {
+        args.edge.attr('line/strokeWidth', 3);
+    });
 
-  //边被取消选中时触发。
-  graph.on('edge:unselected', (args: { cell: Cell; edge: Edge; options: Model.SetOptions }) => {
-    args.edge.attr('line/strokeWidth', 1);
-  });
+    //边被取消选中时触发。
+    graph.on('edge:unselected', (args: { cell: Cell; edge: Edge; options: Model.SetOptions }) => {
+        args.edge.attr('line/strokeWidth', 1);
+    });
 
-  const nodes = imageShapes.map((item) => {
-    const node = {
-      shape: 'custom-image',
-      label: item.label,
-      attrs: getNodeAttrs(item.label)
-    };
-    const newNode = graph.addNode(node);
-    return newNode;
-  });
-  stencil.load(nodes, 'processLibrary');
+    const nodes = imageShapes.map((item) => {
+        const node = {
+            shape: 'custom-image',
+            label: item.label,
+            attrs: getNodeAttrs(item.label)
+        };
+        const newNode = graph.addNode(node);
+        return newNode;
+    });
+    stencil.load(nodes, 'processLibrary');
 };
 
 //保存
 function save() {
-  console.log('save');
-  const graphData = graph.toJSON();
-  console.log(graphData);
+    console.log('save');
+    const graphData = graph.toJSON();
+    console.log(graphData);
 }
 //复制
 function copy() {
-  const cells = graph.getSelectedCells();
-  if (cells.length) {
-    graph.copy(cells);
-  }
-  return false;
+    const cells = graph.getSelectedCells();
+    if (cells.length) {
+        graph.copy(cells);
+    }
+    return false;
 }
 //粘贴
 function paste() {
-  if (!graph.isClipboardEmpty()) {
-    const cells = graph.paste({ offset: 32 });
-    graph.cleanSelection();
-    graph.select(cells);
-  }
-  return false;
+    if (!graph.isClipboardEmpty()) {
+        const cells = graph.paste({ offset: 32 });
+        graph.cleanSelection();
+        graph.select(cells);
+    }
+    return false;
 }
 //删除
 function del() {
-  const cells = graph.getSelectedCells();
-  if (cells.length) {
-    graph.removeCells(cells);
-  }
+    const cells = graph.getSelectedCells();
+    if (cells.length) {
+        graph.removeCells(cells);
+    }
 }
 
 //导出PNG
 function exportPng() {
-  graph.toPNG(
-    (dataUri: string) => {
-      // 下载
-      DataUri.downloadDataUri(dataUri, 'chart.png');
-    },
-    {
-      padding: {
-        top: 20,
-        right: 20,
-        bottom: 20,
-        left: 20
-      }
-    }
-  );
+    graph.toPNG(
+        (dataUri: string) => {
+            // 下载
+            DataUri.downloadDataUri(dataUri, 'chart.png');
+        },
+        {
+            padding: {
+                top: 20,
+                right: 20,
+                bottom: 20,
+                left: 20
+            }
+        }
+    );
 }
 
 //加载初始节点
 function getData() {
-  let cells = [] as any;
-  const location = state.data;
-  location.nodes.map((node) => {
-    cells.push(
-      graph.addNode({
-        id: node.id,
-        x: node.position.x,
-        y: node.position.y,
-        shape: 'custom-image',
-        attrs: getNodeAttrs(node.data.name),
-        label: node.data.name,
-        data: node.data
-      })
-    );
-  });
-  graph.resetCells(cells);
+    let cells = [] as any;
+    const location = state.data;
+    location.nodes.map((node) => {
+        cells.push(
+            graph.addNode({
+                id: node.id,
+                x: node.position.x,
+                y: node.position.y,
+                shape: 'custom-image',
+                attrs: getNodeAttrs(node.data.name),
+                label: node.data.name,
+                data: node.data
+            })
+        );
+    });
+    graph.resetCells(cells);
 }
 
 onMounted(() => {
-  init();
-  getData();
+    init();
+    getData();
 });
 
 onUnmounted(() => {
-  graph.dispose();
+    graph.dispose();
 });
 </script>
 
 <style lang="scss" scoped>
 .content-main {
-  display: flex;
-  width: 100%;
-  flex-direction: column;
-  height: calc(100vh - 85px - 40px);
-  background-color: #ffffff;
-  position: relative;
-
-  .tool-container {
-    padding: 8px;
     display: flex;
-    align-items: center;
-    color: rgba(0, 0, 0, 0.45);
-  }
+    width: 100%;
+    flex-direction: column;
+    height: calc(100vh - 85px - 40px);
+    background-color: #ffffff;
+    position: relative;
+
+    .tool-container {
+        padding: 8px;
+        display: flex;
+        align-items: center;
+        color: rgba(0, 0, 0, 0.45);
+    }
 }
 .content-container {
-  position: relative;
-  width: 100%;
-  height: 100%;
-  .content {
+    position: relative;
     width: 100%;
     height: 100%;
-    position: relative;
-    min-width: 400px;
-    min-height: 600px;
-    display: flex;
-    border: 1px solid #dfe3e8;
-    flex-direction: row;
-    flex: 1;
-    .stencil {
-      width: 200px;
-      height: 100%;
-      border-right: 1px solid #dfe3e8;
-      position: relative;
+    .content {
+        width: 100%;
+        height: 100%;
+        position: relative;
+        min-width: 400px;
+        min-height: 600px;
+        display: flex;
+        border: 1px solid #dfe3e8;
+        flex-direction: row;
+        flex: 1;
+        .stencil {
+            width: 200px;
+            height: 100%;
+            border-right: 1px solid #dfe3e8;
+            position: relative;
 
-      :deep(.x6-widget-stencil) {
-        background-color: #fff;
-      }
-      :deep(.x6-widget-stencil-title) {
-        background-color: #fff;
-        display: none;
-      }
-      :deep(.x6-widget-stencil-content) {
-        top: 0;
-      }
-      :deep(.x6-widget-stencil-group-title) {
-        background-color: #fff !important;
-      }
-    }
-    .graph-content {
-      width: calc(100% - 180px);
-      height: 100%;
-    }
+            :deep(.x6-widget-stencil) {
+                background-color: #fff;
+            }
+            :deep(.x6-widget-stencil-title) {
+                background-color: #fff;
+                display: none;
+            }
+            :deep(.x6-widget-stencil-content) {
+                top: 0;
+            }
+            :deep(.x6-widget-stencil-group-title) {
+                background-color: #fff !important;
+            }
+        }
+        .graph-content {
+            width: calc(100% - 180px);
+            height: 100%;
+        }
 
-    .editor-sidebar {
-      display: flex;
-      flex-direction: column;
-      border-left: 1px solid #e6f7ff;
-      background: #fafafa;
-      z-index: 9;
+        .editor-sidebar {
+            display: flex;
+            flex-direction: column;
+            border-left: 1px solid #e6f7ff;
+            background: #fafafa;
+            z-index: 9;
 
-      .el-card {
-        border: none;
-      }
-      .edit-panel {
-        flex: 1 1;
-        background-color: #fff;
-      }
+            .el-card {
+                border: none;
+            }
+            .edit-panel {
+                flex: 1 1;
+                background-color: #fff;
+            }
 
-      :deep(.x6-widget-minimap-viewport) {
-        border: 1px solid #8f8f8f;
-      }
+            :deep(.x6-widget-minimap-viewport) {
+                border: 1px solid #8f8f8f;
+            }
 
-      :deep(.x6-widget-minimap-viewport-zoom) {
-        border: 1px solid #8f8f8f;
-      }
+            :deep(.x6-widget-minimap-viewport-zoom) {
+                border: 1px solid #8f8f8f;
+            }
+        }
     }
-  }
 }
 
 :deep(.x6-widget-transform) {
-  margin: -1px 0 0 -1px;
-  padding: 0px;
-  border: 1px solid #239edd;
+    margin: -1px 0 0 -1px;
+    padding: 0px;
+    border: 1px solid #239edd;
 }
 :deep(.x6-widget-transform > div) {
-  border: 1px solid #239edd;
+    border: 1px solid #239edd;
 }
 :deep(.x6-widget-transform > div:hover) {
-  background-color: #3dafe4;
+    background-color: #3dafe4;
 }
 :deep(.x6-widget-transform-active-handle) {
-  background-color: #3dafe4;
+    background-color: #3dafe4;
 }
 :deep(.x6-widget-transform-resize) {
-  border-radius: 0;
+    border-radius: 0;
 }
 :deep(.x6-widget-selection-inner) {
-  border: 1px solid #239edd;
+    border: 1px solid #239edd;
 }
 :deep(.x6-widget-selection-box) {
-  opacity: 0;
+    opacity: 0;
 }
 
 .topic-image {
-  visibility: hidden;
-  cursor: pointer;
+    visibility: hidden;
+    cursor: pointer;
 }
 .x6-node:hover .topic-image {
-  visibility: visible;
+    visibility: visible;
 }
 .x6-node-selected rect {
-  stroke-width: 2px;
+    stroke-width: 2px;
 }
 .roadback {
-  position: absolute;
-  bottom: 0;
-  left: 200px;
+    position: absolute;
+    bottom: 0;
+    left: 200px;
 }
 </style>

+ 94 - 62
src/views/deviceManage/versionManage/index.vue

@@ -8,30 +8,22 @@
           </template>
           <div>
             <el-input v-model="filterText" style="width: 200px" placeholder="请输入关键字" />
-            <el-tree
-              ref="treeRef"
-              style="max-width: 600px; margin-top: 5px"
-              class="filter-tree"
-              :props="defaultProps"
-              :data="treeData"
-              node-key="id"
-              default-expand-all
-              highlight-current
-              :expand-on-click-node="false"
-              :filter-node-method="filterNode"
-              @node-click="handleNodeClick"
-            />
+            <el-tree ref="treeRef" style="max-width: 600px; margin-top: 5px" class="filter-tree" :props="defaultProps"
+              :data="treeData" node-key="id" default-expand-all highlight-current :expand-on-click-node="false"
+              :filter-node-method="filterNode" @node-click="handleNodeClick" />
           </div>
         </el-card>
       </el-col>
       <el-col :span="19">
-        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+          :leave-active-class="proxy?.animate.searchAnimate.leave">
           <div v-show="showSearch" class="mb-[10px]">
             <el-card shadow="hover">
               <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
                 <el-form-item label="设备类型名称" prop="deviceTypeId">
                   <el-select v-model="queryParams.deviceTypeId" placeholder="请选择设备类型名称">
-                    <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id"></el-option>
+                    <el-option v-for="dict in dictGroup.deviceTypeOptions" :key="dict.id" :label="dict.name"
+                      :value="dict.id"></el-option>
                   </el-select>
                 </el-form-item>
                 <el-form-item label="型号" prop="xh">
@@ -50,17 +42,13 @@
           <template #header>
             <el-row :gutter="10" class="mb8">
               <el-col :span="1.5">
-                <el-button v-hasPermi="['jdyw:deviceType:add']" type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+                <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
               </el-col>
               <el-col :span="1.5">
-                <el-button v-hasPermi="['jdyw:deviceType:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate">
-                  修改
-                </el-button>
+                <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改 </el-button>
               </el-col>
               <el-col :span="1.5">
-                <el-button v-hasPermi="['jdyw:deviceType:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">
-                  删除
-                </el-button>
+                <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除 </el-button>
               </el-col>
               <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
             </el-row>
@@ -68,28 +56,33 @@
 
           <el-table v-loading="loading" :data="deviceTypeList" @selection-change="handleSelectionChange">
             <el-table-column type="selection" width="55" align="center" />
-            <el-table-column label="设备名称" align="center" prop="deviceTypeId" />
-            <el-table-column label="品牌" align="center" prop="ext1" />
+            <el-table-column label="设备名称" align="center">
+              <template #default="scope">
+                {{ formatDict(scope.row.deviceTypeId, 'deviceTypeOptions') }}
+              </template>
+            </el-table-column>
+            <el-table-column label="品牌" align="center" prop="ext1.brand" />
             <el-table-column label="型号" align="center" prop="xh" />
-            <el-table-column label="供应商" align="center" prop="productorId" />
-            <el-table-column label="联系人" align="center" prop="ext1" />
-            <el-table-column label="联系方式" align="center" prop="ext1" />
+            <el-table-column label="供应商" align="center" prop="productorId">
+              <template #default="scope">
+                {{ formatDict(scope.row.productorId, 'productorOptions') }}
+              </template>
+            </el-table-column>
+            <el-table-column label="联系人" align="center" prop="ext1.linkName" />
+            <el-table-column label="联系方式" align="center" prop="ext1.linkPhone" width="120" />
             <el-table-column label="备注" align="center" prop="remark" />
-            <!-- <el-table-column label="创建时间" align="center" prop="remark" /> -->
-            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+            <el-table-column label="创建时间" align="center" prop="createTime" width="160" />
+            <el-table-column label="操作" align="center" width="120" fixed="right" class-name="small-padding fixed-width">
               <template #default="scope">
-                <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
-                <el-button v-hasPermi="['jdyw:deviceType:remove']" size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+                <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary"
+                  @click="handleUpdate(scope.row)">修改</el-button>
+                <el-button v-hasPermi="['jdyw:deviceType:remove']" size="small" link type="danger"
+                  @click="handleDelete(scope.row)">删除</el-button>
               </template>
             </el-table-column>
           </el-table>
-          <pagination
-            v-show="total > 0"
-            v-model:page="queryParams.pageNum"
-            v-model:limit="queryParams.pageSize"
-            :total="total"
-            @pagination="getList"
-          />
+          <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+            :total="total" @pagination="getList" />
         </el-card>
       </el-col>
     </el-row>
@@ -97,27 +90,31 @@
     <!-- 添加或修改设备类型信息对话框 -->
     <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="deviceTypeFormRef" :model="form" label-width="110px">
-        <el-form-item label="设备类型名称" prop="deviceTypeId" :rules="[{ required: true, message: '设备类型名称不能为空', trigger: 'change' }]">
+        <el-form-item label="设备类型名称" prop="deviceTypeId"
+          :rules="[{ required: true, message: '设备类型名称不能为空', trigger: 'change' }]">
           <el-select v-model="form.deviceTypeId" placeholder="请选择设备类型名称">
-            <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id"></el-option>
+            <el-option v-for="dict in dictGroup.deviceTypeOptions" :key="dict.id" :label="dict.name"
+              :value="dict.id"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="品牌" prop="brand" :rules="[{ required: true, message: '品牌不能为空', trigger: 'blur' }]">
-          <el-input v-model="form.brand" placeholder="请输入品牌" />
+        <el-form-item label="品牌" prop="ext1.brand" :rules="[{ required: true, message: '品牌不能为空', trigger: 'blur' }]">
+          <el-input v-model="form.ext1.brand" placeholder="请输入品牌" />
         </el-form-item>
         <el-form-item label="型号" prop="xh" :rules="[{ required: true, message: '型号不能为空', trigger: 'blur' }]">
           <el-input v-model="form.xh" placeholder="请输入型号" />
         </el-form-item>
-        <el-form-item label="供应商" prop="productorId" :rules="[{ required: true, message: '供应商不能为空', trigger: 'change' }]">
-          <el-select v-model="form.deviceTypeId" placeholder="请选择供应商">
-            <!-- <el-option v-for="dict in deviceTypeOptions" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> -->
+        <el-form-item label="供应商" prop="productorId"
+          :rules="[{ required: true, message: '供应商不能为空', trigger: 'change' }]">
+          <el-select v-model="form.productorId" clearable placeholder="请选择供应商" @change="setProductorInfo">
+            <el-option v-for="dict in dictGroup.productorOptions" :key="dict.id" :label="dict.name"
+              :value="dict.id"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="供应商联系人" prop="linkName">
-          <el-input v-model="form.linkName" disabled />
+        <el-form-item label="供应商联系人" prop="ext1.linkName">
+          <el-input v-model="form.ext1.linkName" disabled />
         </el-form-item>
-        <el-form-item label="联系电话" prop="linkPhone">
-          <el-input v-model="form.linkPhone" disabled />
+        <el-form-item label="联系电话" prop="ext1.linkPhone">
+          <el-input v-model="form.ext1.linkPhone" disabled />
         </el-form-item>
         <el-form-item label="备注" prop="remark">
           <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
@@ -143,6 +140,8 @@ import {
   getDeviceSystemTree,
   getDeviceTypeDetailList
 } from '@/api/deviceManage/deviceType';
+import { listProductor } from '@/api/supplierManage/index';
+import { deepClone } from '@/utils/index';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const deviceTypeList = ref([]);
 const buttonLoading = ref(false);
@@ -172,16 +171,21 @@ const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
 });
-const deviceTypeOptions = ref([]);
+const dictGroup = reactive({
+  deviceTypeOptions: [],
+  productorOptions: []
+});
 const initFormData = {
   id: undefined,
+  productorId: '',
   deviceTypeId: undefined,
   xh: null,
   remark: undefined,
-  brand: undefined,
-  linkName: '',
-  linkPhone: '',
-  ext1: undefined,
+  ext1: <any>{
+    brand: undefined,
+    linkName: '',
+    linkPhone: ''
+  },
   ext2: undefined
 };
 const formData = reactive({
@@ -189,6 +193,7 @@ const formData = reactive({
   queryParams: {
     pageNum: 1,
     pageSize: 10,
+    systemId: undefined,
     deviceTypeId: undefined,
     xh: undefined,
     params: {}
@@ -196,12 +201,14 @@ const formData = reactive({
 });
 
 const { queryParams, form } = toRefs(formData);
-
 /** 查询设备类型信息列表 */
 const getList = async () => {
   loading.value = true;
   const res = await getDeviceTypeDetailList(queryParams.value);
-  deviceTypeList.value = res.rows;
+  deviceTypeList.value = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null
+  }));
   total.value = res.total;
   loading.value = false;
 };
@@ -214,7 +221,7 @@ const cancel = () => {
 
 /** 表单重置 */
 const reset = () => {
-  form.value = { ...initFormData };
+  form.value = deepClone(initFormData);
   deviceTypeFormRef.value?.resetFields();
 };
 
@@ -249,6 +256,7 @@ const handleUpdate = async (row) => {
   reset();
   const _id = row?.id || ids.value[0];
   const res = await getDeviceTypeDetail(_id);
+  res.data.ext1 = res.data.ext1 && JSON.parse(res.data.ext1);
   Object.assign(form.value, res.data);
   dialog.visible = true;
   dialog.title = '修改品牌型号';
@@ -259,10 +267,12 @@ const submitForm = () => {
   deviceTypeFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
+      const { ext1 } = form.value;
+      const params = Object.assign(form.value, { ext1: JSON.stringify(ext1) });
       if (form.value.id) {
-        await updateDeviceTypeDetail(form.value).finally(() => (buttonLoading.value = false));
+        await updateDeviceTypeDetail(params).finally(() => (buttonLoading.value = false));
       } else {
-        await addDeviceTypeDetail(form.value).finally(() => (buttonLoading.value = false));
+        await addDeviceTypeDetail(params).finally(() => (buttonLoading.value = false));
       }
       proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
@@ -274,7 +284,7 @@ const submitForm = () => {
 /** 删除按钮操作 */
 const handleDelete = async (row) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除设备类型信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
   await delDeviceTypeDetail(_ids);
   proxy?.$modal.msgSuccess('删除成功');
   await getList();
@@ -327,18 +337,40 @@ const findNameWithParents = (data, idToFind, parentName = '') => {
 const getDeviceTypeList = () => {
   listDeviceType({}).then(({ code, rows }) => {
     if (code === 200) {
-      deviceTypeOptions.value = rows;
+      dictGroup.deviceTypeOptions = rows;
     }
   });
 };
+const getProductorList = () => {
+  listProductor({}).then(({ code, rows }) => {
+    if (code === 200) {
+      dictGroup.productorOptions = rows;
+    }
+  });
+};
+const setProductorInfo = (val) => {
+  const [{ contact, phone }] = dictGroup.productorOptions.filter((item) => item.id === val);
+  form.value.ext1.linkName = contact;
+  form.value.ext1.linkPhone = phone;
+};
 const handleNodeClick = (data, node) => {
-  // queryParams.value.systemId = data.id;
+  queryParams.value.systemId = data.id;
   handleQuery();
 };
+const formatDict = (val, option: string) => {
+  let label = '';
+  dictGroup[option].forEach((item) => {
+    if (val === item.id) {
+      label = item.name;
+    }
+  });
+  return label;
+};
 onMounted(() => {
   getList();
   getTreeData();
   getDeviceTypeList();
+  getProductorList();
 });
 </script>
 <style lang="scss" scoped></style>

+ 363 - 0
src/views/supplierManage/serviceEngineer/index.vue

@@ -0,0 +1,363 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+      :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+            <el-form-item label="供应商" prop="productorId">
+              <el-select v-model="queryParams.productorId" clearable placeholder="请选择供应商">
+                <el-option v-for="dict in dictGroup.productorOptions" :key="dict.id" :label="dict.name"
+                  :value="dict.id"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="姓名" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+              <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改 </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除 </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="售后工程师" align="center" prop="name" width="150" />
+        <el-table-column label="供应商" align="center"  width="150" >
+          <template #default="scope">
+            {{ formatDict(scope.row.productorId, 'productorOptions') }}
+          </template>
+        </el-table-column>
+        <el-table-column label="联系电话" align="center" prop="phone" width="150" />
+        <el-table-column label="负责设备" align="center" prop="deviceTypeIds" show-overflow-tooltip width="150" >
+          <template #default="scope">
+            {{formatDevice(scope.row.deviceTypeIds)}}
+          </template>
+        </el-table-column>
+        <el-table-column label="所在城市" align="center" prop="city" width="100" />
+        <el-table-column label="详细地址" align="center" prop="address" show-overflow-tooltip width="180" />
+        <el-table-column label="备注" align="center" show-overflow-tooltip prop="remark" width="150" />
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+        <el-table-column label="操作" align="center" width="100" fixed="right" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
+            <el-button size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
+      <el-form ref="addFormRef" :model="form" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="供应商" prop="productorId"
+              :rules="[{ required: true, message: '供应商不能为空', trigger: 'change' }]">
+              <el-select v-model="form.productorId" clearable placeholder="请选择供应商" @change="productorChange">
+                <el-option v-for="dict in dictGroup.productorOptions" :key="dict.id" :label="dict.name"
+                  :value="dict.id"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="姓名" prop="name" :rules="[{ required: true, message: '姓名不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.name" placeholder="请输入姓名" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系电话" prop="phone" :rules="[{ required: true, message: '联系电话不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.phone" placeholder="请输入联系电话" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="负责设备" prop="deviceTypeIds"
+              :rules="[{ required: true, message: '负责设备不能为空', trigger: 'change' }]">
+              <el-select v-model="form.deviceTypeIds" multiple clearable placeholder="请选择负责设备">
+                <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
+                  :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
+                  <span style="float: left">{{ dict.ext1.brand }}</span>
+                  <span style="float: right;color: var(--el-text-color-secondary);font-size: 13px;">
+                    {{ dict.xh }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="地区" prop="city">
+              <el-input v-model="form.city" placeholder="请输入地区" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="详细地址" prop="address">
+              <el-input v-model="form.address" placeholder="请输入详细地址" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+          <el-button @click="cancel"> 取 消 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ServiceEngineer" lang="ts">
+import { listProductor, listProductorEngineers, getProductorEngineers, delProductorEngineers, addProductorEngineers, updateProductorEngineers } from '@/api/supplierManage/index';
+import {
+  getDeviceTypeDetailList
+} from '@/api/deviceManage/deviceType';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const tableList = ref([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const addFormRef = ref<ElFormInstance>();
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const dictGroup = reactive({
+  productorOptions: [],
+  deviceTypeDetailOptions: []
+})
+const initFormData = {
+  id: undefined,
+  name: undefined,
+  phone: undefined,
+  productorId: undefined,
+  deviceTypeIds: [],
+  city: undefined,
+  address: undefined,
+  remark: undefined,
+  ext1: <any>{},
+  ext2: undefined
+};
+const formData = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    productorId: undefined,
+    params: {}
+  }
+});
+
+const { queryParams, form } = toRefs(formData);
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProductorEngineers(queryParams.value);
+  tableList.value = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null
+  }));
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  addFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '新增售后工程师';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const { data } = await getProductorEngineers(_id);
+  data.ext1 = data.ext1 && JSON.parse(data.ext1);
+  deviceTypeDetailList(data.productorId)
+  data.deviceTypeIds = data.deviceTypeIds.split(',');
+  Object.assign(form.value, data);
+  dialog.visible = true;
+  dialog.title = '修改售后工程师';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  addFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      const { ext1, deviceTypeIds } = form.value;
+      const params = Object.assign(form.value, { ext1: JSON.stringify(ext1), deviceTypeIds: deviceTypeIds.join() });
+      if (form.value.id) {
+        await updateProductorEngineers(params).finally(() => (buttonLoading.value = false));
+      } else {
+        await addProductorEngineers(params).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
+  await delProductorEngineers(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+const getProductorList = async () => {
+  const res = await listProductor({});
+  dictGroup.productorOptions = res.rows;
+};
+const deviceTypeDetailList = async (productorId = '') => {
+  const res = await getDeviceTypeDetailList({ productorId });
+  dictGroup.deviceTypeDetailOptions = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null
+  }));
+};
+const productorChange = (val) => {
+  deviceTypeDetailList(val)
+  form.value.deviceTypeIds = []
+}
+const formatDevice = (val) => {
+  const arr = val.split(',')
+  let label = ''
+  dictGroup.deviceTypeDetailOptions.forEach((item) => {
+    arr.forEach(el => {
+      if (el===item.id) {
+        label +=`${item.ext1.brand}${item.xh},`
+      }
+    })
+  })
+  return label.substring(0,label.length-1)
+}
+const formatDict = (val, option: string) => {
+  let label = '';
+  dictGroup[option].forEach((item) => {
+    if (val === item.id) {
+      label = item.name;
+    }
+  });
+  return label;
+};
+onMounted(() => {
+  getList();
+  getProductorList()
+  deviceTypeDetailList()
+});
+</script>
+<style lang="scss" scoped>
+.card-header {
+  display: flex;
+  justify-content: space-between;
+
+  .el-icon {
+    cursor: pointer;
+  }
+}
+
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding-right: 5px;
+}
+
+.node-operateBtns {
+  div {
+    margin-top: 3px;
+    text-align: center;
+    cursor: pointer;
+
+    &:hover {
+      color: #409eff;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.customDrawer {
+  .el-drawer__header {
+    align-items: flex-start !important;
+    margin-bottom: 10px;
+
+    .drawer-title {
+      .title-name {
+        color: #000;
+      }
+    }
+  }
+
+  .el-drawer__body {
+    padding-top: 0;
+  }
+}
+</style>

+ 310 - 0
src/views/supplierManage/supplierRelations/index.vue

@@ -0,0 +1,310 @@
+<template>
+    <div class="p-2">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+            <div v-show="showSearch" class="mb-[10px]">
+                <el-card shadow="hover">
+                    <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+                        <el-form-item label="供应商名称" prop="name">
+                            <el-input v-model="queryParams.name" placeholder="请输入供应商名称" clearable @keyup.enter="handleQuery" />
+                        </el-form-item>
+                        <el-form-item label="供应商类型" prop="productType">
+                            <el-select v-model="queryParams.productType" clearable placeholder="请选择供应商类型">
+                                <el-option v-for="dict in product_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                            </el-select>
+                        </el-form-item>
+                        <el-form-item>
+                            <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+                            <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+                        </el-form-item>
+                    </el-form>
+                </el-card>
+            </div>
+        </transition>
+
+        <el-card shadow="never">
+            <template #header>
+                <el-row :gutter="10" class="mb8">
+                    <el-col :span="1.5">
+                        <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+                    </el-col>
+                    <el-col :span="1.5">
+                        <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改 </el-button>
+                    </el-col>
+                    <el-col :span="1.5">
+                        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除 </el-button>
+                    </el-col>
+                    <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+                </el-row>
+            </template>
+
+            <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" align="center" />
+                <el-table-column label="供应商名称" align="center" prop="name" width="150" />
+                <el-table-column label="供应商类型" align="center" width="100">
+                    <template #default="scope">
+                        <dict-tag :options="product_type" :value="scope.row.productType" />
+                    </template>
+                </el-table-column>
+                <el-table-column label="联系人" align="center" prop="contact" />
+                <el-table-column label="联系电话" align="center" prop="phone" width="150" />
+                <el-table-column label="联系人邮箱" align="center" prop="ext1.email" width="150" />
+                <el-table-column label="联系人职位" align="center" prop="ext1.job" width="100" />
+                <el-table-column label="联系人地址" align="center" prop="ext1.address" show-overflow-tooltip width="180" />
+                <el-table-column label="备注" align="center" show-overflow-tooltip prop="ext1.remark" width="150" />
+                <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+                <el-table-column label="操作" align="center" width="100" fixed="right" class-name="small-padding fixed-width">
+                    <template #default="scope">
+                        <el-button v-hasPermi="['jdyw:deviceType:edit']" size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
+                        <el-button v-hasPermi="['jdyw:deviceType:remove']" size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <pagination
+                v-show="total > 0"
+                v-model:page="queryParams.pageNum"
+                v-model:limit="queryParams.pageSize"
+                :total="total"
+                @pagination="getList"/>
+        </el-card>
+        <!-- 添加或修改对话框 -->
+        <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
+            <el-form ref="addFormRef" :model="form" label-width="100px">
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="供应商名称" prop="name" :rules="[{ required: true, message: '供应商名称不能为空', trigger: 'blur' }]">
+                            <el-input v-model="form.name" placeholder="请输入供应商名称" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item
+                            label="供应商类型"
+                            prop="productType"
+                            :rules="[{ required: true, message: '供应商类型不能为空', trigger: 'change' }]">
+                            <el-select v-model="form.productType" clearable placeholder="请选择供应商类型">
+                                <el-option v-for="dict in product_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="联系人" prop="contact" :rules="[{ required: true, message: '联系人不能为空', trigger: 'blur' }]">
+                            <el-input v-model="form.contact" placeholder="请输入联系人" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="联系电话" prop="phone" :rules="[{ required: true, message: '联系电话不能为空', trigger: 'blur' }]">
+                            <el-input v-model="form.phone" placeholder="请输入联系电话" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="联系人邮箱" prop="ext1.email">
+                            <el-input v-model="form.ext1.email" placeholder="请输入联系人邮箱" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="联系人职位" prop="ext1.job">
+                            <el-input v-model="form.ext1.job" placeholder="请输入联系人职位" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <el-form-item label="联系人地址" prop="ext1.address">
+                            <el-input v-model="form.ext1.address"  placeholder="请输入联系人地址" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <el-form-item label="备注" prop="ext1.remark">
+                            <el-input v-model="form.ext1.remark" type="textarea" placeholder="请输入备注" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+                    <el-button @click="cancel"> 取 消 </el-button>
+                </div>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup name="SupplierRelations" lang="ts">
+import { listProductor, getProductor, delProductor, addProductor, updateProductor } from '@/api/supplierManage/index';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const tableList = ref([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const addFormRef = ref<ElFormInstance>();
+const dialog = reactive<DialogOption>({
+    visible: false,
+    title: ''
+});
+const { product_type } = toRefs<any>(proxy?.useDict('product_type'));
+const initFormData = {
+    id: undefined,
+    name: undefined,
+    productType: undefined,
+    contact: undefined,
+    phone: undefined,
+    ext1: <any>{},
+    ext2: undefined
+};
+const formData = reactive({
+    form: { ...initFormData },
+    queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: undefined,
+        productType: undefined,
+        params: {}
+    }
+});
+
+const { queryParams, form } = toRefs(formData);
+
+/** 查询设备类型信息列表 */
+const getList = async () => {
+    loading.value = true;
+    const res = await listProductor(queryParams.value);
+    tableList.value = res.rows.map((item) => ({
+        ...item,
+        ext1: item.ext1 ? JSON.parse(item.ext1) : null
+    }));
+    total.value = res.total;
+    loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+    reset();
+    dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+    form.value = { ...initFormData };
+    addFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+    queryParams.value.pageNum = 1;
+    getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+    queryFormRef.value?.resetFields();
+    handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection) => {
+    ids.value = selection.map((item) => item.id);
+    single.value = selection.length != 1;
+    multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+    reset();
+    dialog.visible = true;
+    dialog.title = '新增供应商';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row) => {
+    reset();
+    const _id = row?.id || ids.value[0];
+    const res = await getProductor(_id);
+    res.data.ext1 = res.data.ext1 && JSON.parse(res.data.ext1);
+    Object.assign(form.value, res.data);
+    dialog.visible = true;
+    dialog.title = '修改供应商';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+    addFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            buttonLoading.value = true;
+            const { ext1 } = form.value;
+            const params = Object.assign(form.value, { ext1: JSON.stringify(ext1) });
+            if (form.value.id) {
+                await updateProductor(params).finally(() => (buttonLoading.value = false));
+            } else {
+                await addProductor(params).finally(() => (buttonLoading.value = false));
+            }
+            proxy?.$modal.msgSuccess('操作成功');
+            dialog.visible = false;
+            await getList();
+        }
+    });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row) => {
+    const _ids = row?.id || ids.value;
+    await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
+    await delProductor(_ids);
+    proxy?.$modal.msgSuccess('删除成功');
+    await getList();
+};
+onMounted(() => {
+    getList();
+});
+</script>
+<style lang="scss" scoped>
+.card-header {
+    display: flex;
+    justify-content: space-between;
+
+    .el-icon {
+        cursor: pointer;
+    }
+}
+
+.custom-tree-node {
+    flex: 1;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-right: 5px;
+}
+
+.node-operateBtns {
+    div {
+        margin-top: 3px;
+        text-align: center;
+        cursor: pointer;
+
+        &:hover {
+            color: #409eff;
+        }
+    }
+}
+</style>
+<style lang="scss">
+.customDrawer {
+    .el-drawer__header {
+        align-items: flex-start !important;
+        margin-bottom: 10px;
+
+        .drawer-title {
+            .title-name {
+                color: #000;
+            }
+        }
+    }
+
+    .el-drawer__body {
+        padding-top: 0;
+    }
+}
+</style>